superpowers-manager 0.1.6 → 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/README.md +79 -24
- package/bin/superpowers-manager.js +66 -20
- package/package.json +1 -1
- package/scripts/adapters/codex/adapter +55 -50
- package/scripts/adapters/codex/lib.sh +75 -17
- package/scripts/core/adapter.sh +13 -2
- package/scripts/core/common.sh +24 -0
- package/scripts/core/lifecycle.sh +14 -0
- package/scripts/core/selection-state.py +414 -0
- package/scripts/core/selection.sh +168 -0
- package/scripts/core/upstream.sh +137 -3
- package/scripts/core/validate-adapter-response.py +42 -3
- package/scripts/install +12 -13
- package/scripts/pin +51 -0
- package/scripts/prepare +30 -39
- package/scripts/probe +46 -25
- package/scripts/track-latest +30 -0
- package/scripts/uninstall +2 -11
- package/scripts/unpin +41 -0
- package/scripts/update +5 -2
package/scripts/core/upstream.sh
CHANGED
|
@@ -22,6 +22,139 @@ spw_is_semver_base() {
|
|
|
22
22
|
printf '%s' "$version" | grep -Eq '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\.(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*))?$'
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
spw_is_pinnable_tag() {
|
|
26
|
+
ref="$1"
|
|
27
|
+
case "$ref" in
|
|
28
|
+
*'
|
|
29
|
+
'*|*"$(printf '\r')"*) return 1 ;;
|
|
30
|
+
v*) spw_is_semver_base "${ref#v}" ;;
|
|
31
|
+
*) return 1 ;;
|
|
32
|
+
esac
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
spw_is_full_commit() {
|
|
36
|
+
ref="$1"
|
|
37
|
+
[ "${#ref}" -eq 40 ] || return 1
|
|
38
|
+
case "$ref" in
|
|
39
|
+
*[!0-9A-Fa-f]*) return 1 ;;
|
|
40
|
+
*) return 0 ;;
|
|
41
|
+
esac
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
spw_git_safe_source() {
|
|
45
|
+
source="$1"
|
|
46
|
+
case "$source" in
|
|
47
|
+
/*|*://*|*:*|~*) printf '%s\n' "$source" ;;
|
|
48
|
+
*) printf '%s/%s\n' "$(pwd -P)" "$source" ;;
|
|
49
|
+
esac
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
spw_resolve_exact_tag() {
|
|
53
|
+
source="$1"
|
|
54
|
+
ref="$2"
|
|
55
|
+
source_display=$(spw_display_source "$source")
|
|
56
|
+
query_source=$(spw_git_safe_source "$source")
|
|
57
|
+
if ! output=$(git ls-remote --tags -- "$query_source" "refs/tags/$ref" "refs/tags/$ref^{}" 2>&1); then
|
|
58
|
+
spw_die "cannot query exact upstream tag $ref from $source_display: $output"
|
|
59
|
+
fi
|
|
60
|
+
commit=$(printf '%s\n' "$output" | awk -v direct="refs/tags/$ref" '
|
|
61
|
+
$2 == direct { direct_sha = $1 }
|
|
62
|
+
$2 == direct "^{}" { peeled_sha = $1 }
|
|
63
|
+
END { if (peeled_sha != "") print peeled_sha; else if (direct_sha != "") print direct_sha }
|
|
64
|
+
')
|
|
65
|
+
[ -n "$commit" ] || spw_die "upstream tag not found: $ref"
|
|
66
|
+
printf '%s\n' "$commit"
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
spw_verify_raw_commit() (
|
|
70
|
+
source="$1"
|
|
71
|
+
commit=$(printf '%s' "$2" | tr '[:upper:]' '[:lower:]')
|
|
72
|
+
workspace="$3"
|
|
73
|
+
source_display=$(spw_display_source "$source")
|
|
74
|
+
fetch_source=$(spw_git_safe_source "$source")
|
|
75
|
+
|
|
76
|
+
if ! verify_workspace=$(spw_make_workspace "$workspace" "superpowers-manager.commit"); then
|
|
77
|
+
spw_die "cannot create raw-commit verification workspace under $workspace"
|
|
78
|
+
fi
|
|
79
|
+
spw_install_workspace_trap "$verify_workspace"
|
|
80
|
+
|
|
81
|
+
if ! init_output=$(git init "$verify_workspace" 2>&1); then
|
|
82
|
+
spw_die "cannot initialize raw-commit verification workspace: $init_output"
|
|
83
|
+
fi
|
|
84
|
+
if fetch_output=$(git -C "$verify_workspace" fetch --no-tags -- "$fetch_source" "$commit" 2>&1); then
|
|
85
|
+
:
|
|
86
|
+
else
|
|
87
|
+
case "$fetch_output" in
|
|
88
|
+
*'not our ref'*|*'unadvertised object'*|*"couldn't find remote ref"*)
|
|
89
|
+
spw_die "source cannot supply requested commit: $commit"
|
|
90
|
+
;;
|
|
91
|
+
*)
|
|
92
|
+
spw_die "cannot fetch requested commit from $source_display"
|
|
93
|
+
;;
|
|
94
|
+
esac
|
|
95
|
+
fi
|
|
96
|
+
if ! object_type=$(git -C "$verify_workspace" cat-file -t "$commit" 2>/dev/null); then
|
|
97
|
+
spw_die "requested object is not a commit: $commit"
|
|
98
|
+
fi
|
|
99
|
+
if [ "$object_type" != commit ]; then
|
|
100
|
+
spw_die "requested object is not a commit: $commit"
|
|
101
|
+
fi
|
|
102
|
+
printf '%s\n' "$commit"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
spw_fetch_exact_commit() (
|
|
106
|
+
source="$1"
|
|
107
|
+
commit="$2"
|
|
108
|
+
repository="$3"
|
|
109
|
+
workspace="$4"
|
|
110
|
+
source_display=$(spw_display_source "$source")
|
|
111
|
+
fetch_source=$(spw_git_safe_source "$source")
|
|
112
|
+
|
|
113
|
+
if ! fetch_workspace=$(spw_make_workspace "$workspace" "superpowers-manager.fetch"); then
|
|
114
|
+
spw_die "cannot create exact-commit fetch workspace under $workspace"
|
|
115
|
+
fi
|
|
116
|
+
spw_install_workspace_trap "$fetch_workspace"
|
|
117
|
+
|
|
118
|
+
if ! init_output=$(git init "$fetch_workspace" 2>&1); then
|
|
119
|
+
spw_die "cannot initialize exact-commit fetch workspace: $init_output"
|
|
120
|
+
fi
|
|
121
|
+
if fetch_output=$(git -C "$fetch_workspace" fetch --no-tags -- "$fetch_source" "$commit" 2>&1); then
|
|
122
|
+
:
|
|
123
|
+
else
|
|
124
|
+
case "$fetch_output" in
|
|
125
|
+
*'not our ref'*|*'unadvertised object'*|*"couldn't find remote ref"*)
|
|
126
|
+
spw_die "source cannot supply requested commit: $commit"
|
|
127
|
+
;;
|
|
128
|
+
*)
|
|
129
|
+
spw_die "cannot fetch requested commit from $source_display"
|
|
130
|
+
;;
|
|
131
|
+
esac
|
|
132
|
+
fi
|
|
133
|
+
if ! object_type=$(git -C "$fetch_workspace" cat-file -t "$commit" 2>/dev/null); then
|
|
134
|
+
spw_die "requested object is not a commit: $commit"
|
|
135
|
+
fi
|
|
136
|
+
if [ "$object_type" != commit ]; then
|
|
137
|
+
spw_die "requested object is not a commit: $commit"
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
if [ ! -d "$repository/.git" ]; then
|
|
141
|
+
if ! init_output=$(git init "$repository" 2>&1); then
|
|
142
|
+
spw_die "cannot initialize upstream cache repository: $init_output"
|
|
143
|
+
fi
|
|
144
|
+
fi
|
|
145
|
+
if ! transfer_output=$(
|
|
146
|
+
git -C "$repository" fetch --no-tags -- "$fetch_workspace" "$commit" 2>&1
|
|
147
|
+
); then
|
|
148
|
+
spw_die "cannot transfer requested commit into upstream cache: $transfer_output"
|
|
149
|
+
fi
|
|
150
|
+
if ! object_type=$(git -C "$repository" cat-file -t "$commit" 2>/dev/null); then
|
|
151
|
+
spw_die "cannot verify requested commit in upstream cache: $commit"
|
|
152
|
+
fi
|
|
153
|
+
if [ "$object_type" != commit ]; then
|
|
154
|
+
spw_die "cannot verify requested commit in upstream cache: $commit"
|
|
155
|
+
fi
|
|
156
|
+
)
|
|
157
|
+
|
|
25
158
|
spw_sanitize_ref_for_version() {
|
|
26
159
|
ref="$1"
|
|
27
160
|
sanitized=$(printf '%s' "$ref" | sed 's/[^0-9A-Za-z-][^0-9A-Za-z-]*/-/g; s/^-*//; s/-*$//')
|
|
@@ -102,9 +235,10 @@ spw_resolve_ref() {
|
|
|
102
235
|
upstream_url="$1"
|
|
103
236
|
requested_ref="$2"
|
|
104
237
|
spw_require_command git
|
|
238
|
+
query_source=$(spw_git_safe_source "$upstream_url")
|
|
105
239
|
|
|
106
240
|
if [ "$requested_ref" = "latest-release" ]; then
|
|
107
|
-
if ! output=$(git ls-remote --tags "$
|
|
241
|
+
if ! output=$(git ls-remote --tags -- "$query_source" 'refs/tags/v*' 2>&1); then
|
|
108
242
|
spw_die "cannot query upstream tags from $upstream_url: $output"
|
|
109
243
|
fi
|
|
110
244
|
if ! selected=$(printf '%s\n' "$output" | spw_select_latest_release_from_ls_remote); then
|
|
@@ -119,7 +253,7 @@ spw_resolve_ref() {
|
|
|
119
253
|
return
|
|
120
254
|
fi
|
|
121
255
|
|
|
122
|
-
if ! tag_output=$(git ls-remote --tags "$
|
|
256
|
+
if ! tag_output=$(git ls-remote --tags -- "$query_source" "refs/tags/$requested_ref" "refs/tags/$requested_ref^{}" 2>&1); then
|
|
123
257
|
spw_die "cannot query upstream tag $requested_ref from $upstream_url: $tag_output"
|
|
124
258
|
fi
|
|
125
259
|
resolved=$(printf '%s\n' "$tag_output" | awk -v tag_ref="refs/tags/$requested_ref" '
|
|
@@ -133,7 +267,7 @@ spw_resolve_ref() {
|
|
|
133
267
|
return
|
|
134
268
|
fi
|
|
135
269
|
|
|
136
|
-
if ! ref_output=$(git ls-remote "$
|
|
270
|
+
if ! ref_output=$(git ls-remote -- "$query_source" "$requested_ref" 2>&1); then
|
|
137
271
|
spw_die "cannot query upstream ref $requested_ref from $upstream_url: $ref_output"
|
|
138
272
|
fi
|
|
139
273
|
resolved=$(printf '%s\n' "$ref_output" | awk 'NF >= 2 { print $1; exit }')
|
|
@@ -15,6 +15,7 @@ VERIFICATION_HINT_KEYS = {"mismatch", "missing"}
|
|
|
15
15
|
OPERATIONS = {"build", "inspect", "install", "uninstall"}
|
|
16
16
|
FINGERPRINT_RE = re.compile(r"(?:[0-9a-fA-F]{7}|[0-9a-fA-F]{40})\Z")
|
|
17
17
|
MAX_NESTING = 64
|
|
18
|
+
MAX_RESPONSE_BYTES = 1_048_576
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
class ProtocolError(ValueError):
|
|
@@ -25,6 +26,17 @@ def reject_constant(value: str) -> None:
|
|
|
25
26
|
raise ProtocolError(f"non-standard JSON constant: {value}")
|
|
26
27
|
|
|
27
28
|
|
|
29
|
+
def reject_duplicate_keys(
|
|
30
|
+
pairs: list[tuple[str, object]],
|
|
31
|
+
) -> dict[str, object]:
|
|
32
|
+
obj: dict[str, object] = {}
|
|
33
|
+
for key, value in pairs:
|
|
34
|
+
if key in obj:
|
|
35
|
+
raise ProtocolError("duplicate object key")
|
|
36
|
+
obj[key] = value
|
|
37
|
+
return obj
|
|
38
|
+
|
|
39
|
+
|
|
28
40
|
def nesting_exceeds_limit(value: object) -> bool:
|
|
29
41
|
stack = [(value, 0)]
|
|
30
42
|
while stack:
|
|
@@ -160,7 +172,23 @@ def validate_result(
|
|
|
160
172
|
"legacy_resources": legacy_resources,
|
|
161
173
|
"identity_state": identity_state,
|
|
162
174
|
}
|
|
163
|
-
|
|
175
|
+
if inspect_view == "update-control":
|
|
176
|
+
require_exact_keys(
|
|
177
|
+
result,
|
|
178
|
+
{"view", "update_control"},
|
|
179
|
+
"update-control inspect result",
|
|
180
|
+
)
|
|
181
|
+
if result.get("view") != "update-control":
|
|
182
|
+
raise ProtocolError("inspect result view must be update-control")
|
|
183
|
+
value = result.get("update_control")
|
|
184
|
+
if value not in {"managed", "unsupported"}:
|
|
185
|
+
raise ProtocolError(
|
|
186
|
+
"update-control value must be managed or unsupported"
|
|
187
|
+
)
|
|
188
|
+
return {"view": "update-control", "update_control": value}
|
|
189
|
+
raise ProtocolError(
|
|
190
|
+
"inspect view must be fingerprint or ownership, or update-control"
|
|
191
|
+
)
|
|
164
192
|
if operation == "install":
|
|
165
193
|
require_exact_keys(result, {"verification_hints"}, "install result")
|
|
166
194
|
hints = require_object(result.get("verification_hints"), "verification_hints")
|
|
@@ -216,11 +244,22 @@ def main() -> int:
|
|
|
216
244
|
parser.add_argument("--adapter-exit", type=int, required=True)
|
|
217
245
|
parser.add_argument("--response", type=Path, required=True)
|
|
218
246
|
parser.add_argument("--result", type=Path, required=True)
|
|
219
|
-
parser.add_argument(
|
|
247
|
+
parser.add_argument(
|
|
248
|
+
"--inspect-view", choices=("fingerprint", "ownership", "update-control")
|
|
249
|
+
)
|
|
220
250
|
args = parser.parse_args()
|
|
221
251
|
try:
|
|
252
|
+
response_size = args.response.stat().st_size
|
|
253
|
+
if response_size > MAX_RESPONSE_BYTES:
|
|
254
|
+
raise ProtocolError(
|
|
255
|
+
f"response exceeds {MAX_RESPONSE_BYTES}-byte limit"
|
|
256
|
+
)
|
|
222
257
|
with args.response.open(encoding="utf-8") as handle:
|
|
223
|
-
raw = json.load(
|
|
258
|
+
raw = json.load(
|
|
259
|
+
handle,
|
|
260
|
+
parse_constant=reject_constant,
|
|
261
|
+
object_pairs_hook=reject_duplicate_keys,
|
|
262
|
+
)
|
|
224
263
|
if nesting_exceeds_limit(raw):
|
|
225
264
|
raise ProtocolError("response JSON nesting exceeds limit")
|
|
226
265
|
messages, result, error = validate_envelope(
|
package/scripts/install
CHANGED
|
@@ -16,10 +16,10 @@ echo "Note: remove or disable conflicting Superpowers providers yourself before
|
|
|
16
16
|
# missing or stale — including validation — entirely before any Codex
|
|
17
17
|
# marketplace or plugin mutation. A failed prepare leaves Codex untouched.
|
|
18
18
|
probe_output=$(sh "$root/scripts/probe" --porcelain)
|
|
19
|
-
identity_state=$(
|
|
19
|
+
identity_state=$(spw_probe_field "$probe_output" identity_state)
|
|
20
20
|
[ -n "$identity_state" ] || spw_die "probe did not report adapter identity state"
|
|
21
21
|
spw_require_no_legacy_state "$identity_state"
|
|
22
|
-
status=$(
|
|
22
|
+
status=$(spw_probe_field "$probe_output" status)
|
|
23
23
|
case "$status" in
|
|
24
24
|
needs\ prepare)
|
|
25
25
|
sh "$root/scripts/prepare"
|
|
@@ -36,24 +36,23 @@ desired_commit=$(spw_metadata_commit_or_empty "$root/plugins/superpowers/.superp
|
|
|
36
36
|
[ -n "$desired_commit" ] || spw_die "generated metadata missing desired commit after prepare"
|
|
37
37
|
|
|
38
38
|
tmp_parent="${TMPDIR:-/tmp}"
|
|
39
|
-
if ! install_workspace=$(
|
|
39
|
+
if ! install_workspace=$(spw_make_workspace "$tmp_parent" "superpowers-manager.install"); then
|
|
40
40
|
spw_die "cannot create install workspace under $tmp_parent"
|
|
41
41
|
fi
|
|
42
|
-
|
|
43
|
-
status=$?
|
|
44
|
-
trap - 0 HUP INT TERM
|
|
45
|
-
rm -rf "$install_workspace" || :
|
|
46
|
-
exit "$status"
|
|
47
|
-
}
|
|
48
|
-
trap cleanup_install_workspace 0
|
|
49
|
-
trap 'exit 129' HUP
|
|
50
|
-
trap 'exit 130' INT
|
|
51
|
-
trap 'exit 143' TERM
|
|
42
|
+
spw_install_workspace_trap "$install_workspace"
|
|
52
43
|
|
|
53
44
|
TMPDIR="$install_workspace"
|
|
54
45
|
export TMPDIR
|
|
55
46
|
install_result="$install_workspace/install.json"
|
|
56
47
|
inspect_result="$install_workspace/inspect.json"
|
|
48
|
+
ownership_result="$install_workspace/ownership.json"
|
|
49
|
+
update_control_result="$install_workspace/update-control.json"
|
|
50
|
+
spw_inspect_ownership "$ownership_result"
|
|
51
|
+
spw_inspect_update_control "$update_control_result"
|
|
52
|
+
identity_state=$(spw_adapter_result_get "$ownership_result" "identity_state")
|
|
53
|
+
update_control=$(spw_adapter_result_get "$update_control_result" "update_control")
|
|
54
|
+
spw_require_no_legacy_state "$identity_state"
|
|
55
|
+
spw_require_managed_update_control "$update_control"
|
|
57
56
|
spw_adapter_install "$install_result" "$root"
|
|
58
57
|
if ! spw_verify_installed_fingerprint "$desired_commit" "$install_result" "$inspect_result"; then
|
|
59
58
|
exit 1
|
package/scripts/pin
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -eu
|
|
3
|
+
|
|
4
|
+
root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
|
|
5
|
+
. "$root/scripts/core/common.sh"
|
|
6
|
+
. "$root/scripts/core/upstream.sh"
|
|
7
|
+
. "$root/scripts/core/selection.sh"
|
|
8
|
+
|
|
9
|
+
[ "$#" -eq 1 ] || spw_usage_error 'usage: superpowers-manager pin REF'
|
|
10
|
+
requested_ref="$1"
|
|
11
|
+
|
|
12
|
+
if spw_is_pinnable_tag "$requested_ref"; then
|
|
13
|
+
requested_kind=tag
|
|
14
|
+
elif spw_is_full_commit "$requested_ref"; then
|
|
15
|
+
requested_kind=raw-commit
|
|
16
|
+
else
|
|
17
|
+
spw_usage_error 'pin REF must be an exact v-prefixed SemVer tag or full 40-hex commit'
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
spw_require_command python3
|
|
21
|
+
state_path=$(spw_selection_state_path)
|
|
22
|
+
tmp_parent="${TMPDIR:-/tmp}"
|
|
23
|
+
if ! pin_workspace=$(spw_make_workspace "$tmp_parent" "superpowers-manager.pin"); then
|
|
24
|
+
spw_die "cannot create pin workspace under $tmp_parent"
|
|
25
|
+
fi
|
|
26
|
+
spw_install_workspace_trap "$pin_workspace"
|
|
27
|
+
|
|
28
|
+
normalized="$pin_workspace/selection.json"
|
|
29
|
+
spw_selection_state "$root" read \
|
|
30
|
+
--path="$state_path" --output="$normalized"
|
|
31
|
+
|
|
32
|
+
source="${SUPERPOWERS_UPSTREAM_URL:-$SPW_UPSTREAM_URL_DEFAULT}"
|
|
33
|
+
spw_selection_state "$root" \
|
|
34
|
+
validate-source --source="$source"
|
|
35
|
+
|
|
36
|
+
if [ "$requested_kind" = tag ]; then
|
|
37
|
+
spw_require_command git
|
|
38
|
+
commit=$(spw_resolve_exact_tag "$source" "$requested_ref")
|
|
39
|
+
resolved_ref="$requested_ref"
|
|
40
|
+
else
|
|
41
|
+
spw_require_command git
|
|
42
|
+
requested_ref=$(printf '%s' "$requested_ref" | tr '[:upper:]' '[:lower:]')
|
|
43
|
+
resolved_ref="$requested_ref"
|
|
44
|
+
commit=$(spw_verify_raw_commit "$source" "$requested_ref" "$pin_workspace")
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
spw_selection_state "$root" write-pinned \
|
|
48
|
+
--path="$state_path" --source="$source" --requested-ref="$requested_ref" \
|
|
49
|
+
--resolved-ref="$resolved_ref" --commit="$commit"
|
|
50
|
+
|
|
51
|
+
printf 'pinned upstream selection to %s at %s\n' "$requested_ref" "$commit"
|
package/scripts/prepare
CHANGED
|
@@ -4,11 +4,11 @@ set -eu
|
|
|
4
4
|
root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
|
|
5
5
|
. "$root/scripts/core/common.sh"
|
|
6
6
|
. "$root/scripts/core/upstream.sh"
|
|
7
|
+
. "$root/scripts/core/selection.sh"
|
|
7
8
|
. "$root/scripts/core/provenance.sh"
|
|
8
9
|
. "$root/scripts/core/lifecycle.sh"
|
|
9
10
|
. "$root/scripts/core/adapter.sh"
|
|
10
11
|
|
|
11
|
-
upstream_url="${SUPERPOWERS_UPSTREAM_URL:-$SPW_UPSTREAM_URL_DEFAULT}"
|
|
12
12
|
cache_parent="${SUPERPOWERS_CACHE_DIR:-$root/.cache/upstream}"
|
|
13
13
|
plugin_root="${SUPERPOWERS_PLUGIN_ROOT:-$root/plugins/superpowers}"
|
|
14
14
|
manifest_template="${SUPERPOWERS_MANIFEST_TEMPLATE:-$root/plugins/superpowers/.codex-plugin/plugin.template.json}"
|
|
@@ -25,51 +25,40 @@ esac
|
|
|
25
25
|
cache="$cache_parent/superpowers"
|
|
26
26
|
tmp_parent=$(dirname "$plugin_root")
|
|
27
27
|
mkdir -p "$tmp_parent"
|
|
28
|
-
if ! prepare_workspace=$(
|
|
28
|
+
if ! prepare_workspace=$(spw_make_workspace "$tmp_parent" ".superpowers.prepare"); then
|
|
29
29
|
spw_die "cannot create prepare workspace under $tmp_parent"
|
|
30
30
|
fi
|
|
31
|
+
spw_install_workspace_trap "$prepare_workspace"
|
|
31
32
|
tmp_plugin="$prepare_workspace/superpowers"
|
|
32
33
|
adapter_result="$prepare_workspace/adapter-build.json"
|
|
33
34
|
|
|
34
|
-
cleanup_tmp_plugin() {
|
|
35
|
-
status=$?
|
|
36
|
-
trap - 0 HUP INT TERM
|
|
37
|
-
rm -rf "$prepare_workspace" || :
|
|
38
|
-
exit "$status"
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
trap cleanup_tmp_plugin 0
|
|
42
|
-
trap 'exit 129' HUP
|
|
43
|
-
trap 'exit 130' INT
|
|
44
|
-
trap 'exit 143' TERM
|
|
45
|
-
|
|
46
35
|
TMPDIR="$prepare_workspace"
|
|
47
36
|
export TMPDIR
|
|
48
37
|
|
|
49
|
-
requested_ref=$(spw_config_ref "$root")
|
|
50
|
-
|
|
51
|
-
spw_require_command git
|
|
52
38
|
spw_require_command python3
|
|
39
|
+
spw_compute_effective_selection "$root" "$prepare_workspace"
|
|
40
|
+
spw_require_command git
|
|
53
41
|
spw_require_command "$SPW_ADAPTER"
|
|
54
42
|
[ -f "$manifest_template" ] || spw_die "missing fallback manifest template: $manifest_template"
|
|
55
43
|
|
|
56
|
-
resolved_record=$(spw_resolve_ref "$upstream_url" "$requested_ref")
|
|
57
|
-
resolution_kind=$(printf '%s\n' "$resolved_record" | awk '{ print $1 }')
|
|
58
|
-
resolved_ref=$(printf '%s\n' "$resolved_record" | awk '{ print $2 }')
|
|
59
|
-
commit=$(printf '%s\n' "$resolved_record" | awk '{ print $3 }')
|
|
60
|
-
|
|
61
44
|
mkdir -p "$cache_parent" "$tmp_parent"
|
|
62
|
-
if [
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
fi
|
|
45
|
+
if [ "$SPW_SELECTION_MODE" = "pinned" ]; then
|
|
46
|
+
spw_fetch_exact_commit \
|
|
47
|
+
"$SPW_EFFECTIVE_SOURCE" "$SPW_DESIRED_COMMIT" "$cache" "$prepare_workspace"
|
|
66
48
|
else
|
|
67
|
-
|
|
68
|
-
|
|
49
|
+
git_source=$(spw_git_safe_source "$SPW_EFFECTIVE_SOURCE")
|
|
50
|
+
if [ -d "$cache/.git" ]; then
|
|
51
|
+
if ! git -C "$cache" fetch --tags --prune -- "$git_source"; then
|
|
52
|
+
spw_die "cannot fetch upstream repo: $SPW_EFFECTIVE_SOURCE"
|
|
53
|
+
fi
|
|
54
|
+
else
|
|
55
|
+
if ! git clone -- "$git_source" "$cache"; then
|
|
56
|
+
spw_die "cannot clone upstream repo: $SPW_EFFECTIVE_SOURCE"
|
|
57
|
+
fi
|
|
69
58
|
fi
|
|
70
59
|
fi
|
|
71
|
-
if ! git -C "$cache" checkout --detach "$
|
|
72
|
-
spw_die "cannot check out upstream commit: $
|
|
60
|
+
if ! git -C "$cache" checkout --detach "$SPW_DESIRED_COMMIT" >/dev/null; then
|
|
61
|
+
spw_die "cannot check out upstream commit: $SPW_DESIRED_COMMIT"
|
|
73
62
|
fi
|
|
74
63
|
|
|
75
64
|
spw_require_upstream_path "$cache/skills" "skills/"
|
|
@@ -94,21 +83,23 @@ fi
|
|
|
94
83
|
|
|
95
84
|
spw_write_metadata_json \
|
|
96
85
|
"$tmp_plugin/.superpowers-upstream.json" \
|
|
97
|
-
"$
|
|
98
|
-
"$
|
|
99
|
-
"$
|
|
100
|
-
"$
|
|
86
|
+
"$SPW_EFFECTIVE_SOURCE" \
|
|
87
|
+
"$SPW_REQUESTED_REF" \
|
|
88
|
+
"$SPW_RESOLVED_REF" \
|
|
89
|
+
"$SPW_DESIRED_COMMIT" \
|
|
101
90
|
"$upstream_manifest_version"
|
|
102
91
|
|
|
103
92
|
manager_version=$(
|
|
104
|
-
spw_manifest_version_for_ref
|
|
93
|
+
spw_manifest_version_for_ref \
|
|
94
|
+
"$SPW_REQUESTED_REF" "$SPW_RESOLUTION_KIND" \
|
|
95
|
+
"$SPW_RESOLVED_REF" "$SPW_DESIRED_COMMIT"
|
|
105
96
|
)
|
|
106
97
|
spw_invoke_adapter build "$adapter_result" "" -- \
|
|
107
98
|
--upstream-root "$cache" \
|
|
108
99
|
--candidate-root "$tmp_plugin" \
|
|
109
|
-
--requested-ref "$
|
|
110
|
-
--resolved-ref "$
|
|
111
|
-
--commit "$
|
|
100
|
+
--requested-ref "$SPW_REQUESTED_REF" \
|
|
101
|
+
--resolved-ref "$SPW_RESOLVED_REF" \
|
|
102
|
+
--commit "$SPW_DESIRED_COMMIT" \
|
|
112
103
|
--manager-version "$manager_version" \
|
|
113
104
|
--upstream-manifest-version "$upstream_manifest_version" \
|
|
114
105
|
--fallback-manifest "$manifest_template"
|
|
@@ -123,4 +114,4 @@ fi
|
|
|
123
114
|
|
|
124
115
|
spw_replace_generated_tree "$tmp_plugin" "$plugin_root"
|
|
125
116
|
|
|
126
|
-
echo "prepared $
|
|
117
|
+
echo "prepared $SPW_RESOLVED_REF at $SPW_DESIRED_COMMIT"
|
package/scripts/probe
CHANGED
|
@@ -4,58 +4,79 @@ set -eu
|
|
|
4
4
|
root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
|
|
5
5
|
. "$root/scripts/core/common.sh"
|
|
6
6
|
. "$root/scripts/core/upstream.sh"
|
|
7
|
+
. "$root/scripts/core/selection.sh"
|
|
7
8
|
. "$root/scripts/core/provenance.sh"
|
|
8
9
|
. "$root/scripts/core/status.sh"
|
|
9
10
|
. "$root/scripts/core/lifecycle.sh"
|
|
10
11
|
. "$root/scripts/core/adapter.sh"
|
|
11
12
|
|
|
12
|
-
upstream_url="${SUPERPOWERS_UPSTREAM_URL:-$SPW_UPSTREAM_URL_DEFAULT}"
|
|
13
|
-
requested_ref=$(spw_config_ref "$root")
|
|
14
|
-
resolved_record=$(spw_resolve_ref "$upstream_url" "$requested_ref")
|
|
15
|
-
resolved_ref=$(printf '%s\n' "$resolved_record" | awk '{ print $2 }')
|
|
16
|
-
desired_commit=$(printf '%s\n' "$resolved_record" | awk '{ print $3 }')
|
|
17
|
-
|
|
18
|
-
generated_commit=$(spw_generated_commit_or_empty "$root")
|
|
19
13
|
tmp_parent="${TMPDIR:-/tmp}"
|
|
20
|
-
if ! probe_workspace=$(
|
|
14
|
+
if ! probe_workspace=$(spw_make_workspace "$tmp_parent" "superpowers-manager.probe"); then
|
|
21
15
|
spw_die "cannot create probe workspace under $tmp_parent"
|
|
22
16
|
fi
|
|
17
|
+
spw_install_workspace_trap "$probe_workspace"
|
|
23
18
|
fingerprint_result="$probe_workspace/fingerprint.json"
|
|
24
19
|
ownership_result="$probe_workspace/ownership.json"
|
|
25
|
-
|
|
26
|
-
status=$?
|
|
27
|
-
trap - 0 HUP INT TERM
|
|
28
|
-
rm -rf "$probe_workspace" || :
|
|
29
|
-
exit "$status"
|
|
30
|
-
}
|
|
31
|
-
trap cleanup 0
|
|
32
|
-
trap 'exit 129' HUP
|
|
33
|
-
trap 'exit 130' INT
|
|
34
|
-
trap 'exit 143' TERM
|
|
20
|
+
update_control_result="$probe_workspace/update-control.json"
|
|
35
21
|
|
|
36
22
|
TMPDIR="$probe_workspace"
|
|
37
23
|
export TMPDIR
|
|
24
|
+
spw_compute_effective_selection "$root" "$probe_workspace"
|
|
25
|
+
effective_source=$(spw_display_source "$SPW_EFFECTIVE_SOURCE")
|
|
26
|
+
if [ -n "$SPW_SAVED_SOURCE" ]; then
|
|
27
|
+
saved_source=$(spw_display_source "$SPW_SAVED_SOURCE")
|
|
28
|
+
else
|
|
29
|
+
saved_source=""
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
generated_commit=$(spw_generated_commit_or_empty "$root")
|
|
38
33
|
spw_inspect_fingerprint "$fingerprint_result"
|
|
39
34
|
installed_commit=$(spw_adapter_result_get "$fingerprint_result" "fingerprint")
|
|
40
35
|
spw_inspect_ownership "$ownership_result"
|
|
41
36
|
identity_state=$(spw_adapter_result_get "$ownership_result" "identity_state")
|
|
37
|
+
spw_inspect_update_control "$update_control_result"
|
|
38
|
+
update_control=$(spw_adapter_result_get "$update_control_result" "update_control")
|
|
42
39
|
|
|
43
|
-
status=$(spw_status_for_commits "$
|
|
40
|
+
status=$(spw_status_for_commits "$SPW_DESIRED_COMMIT" "$generated_commit" "$installed_commit")
|
|
44
41
|
|
|
45
42
|
if [ "${1:-}" = "--porcelain" ]; then
|
|
46
|
-
printf 'requested_ref=%s\n' "$
|
|
47
|
-
printf 'resolved_ref=%s\n' "$
|
|
48
|
-
printf 'desired_commit=%s\n' "$
|
|
43
|
+
printf 'requested_ref=%s\n' "$SPW_REQUESTED_REF"
|
|
44
|
+
printf 'resolved_ref=%s\n' "$SPW_RESOLVED_REF"
|
|
45
|
+
printf 'desired_commit=%s\n' "$SPW_DESIRED_COMMIT"
|
|
49
46
|
printf 'generated_commit=%s\n' "$generated_commit"
|
|
50
47
|
printf 'installed_commit=%s\n' "$installed_commit"
|
|
51
48
|
printf 'identity_state=%s\n' "$identity_state"
|
|
52
49
|
printf 'status=%s\n' "$status"
|
|
50
|
+
printf 'selection_origin=%s\n' "$SPW_SELECTION_ORIGIN"
|
|
51
|
+
printf 'selection_mode=%s\n' "$SPW_SELECTION_MODE"
|
|
52
|
+
printf 'upstream_source_origin=%s\n' "$SPW_UPSTREAM_SOURCE_ORIGIN"
|
|
53
|
+
printf 'effective_source=%s\n' "$effective_source"
|
|
54
|
+
printf 'saved_mode=%s\n' "$SPW_SAVED_MODE"
|
|
55
|
+
printf 'saved_source=%s\n' "$saved_source"
|
|
56
|
+
printf 'saved_requested_ref=%s\n' "$SPW_SAVED_REQUESTED_REF"
|
|
57
|
+
printf 'saved_resolved_ref=%s\n' "$SPW_SAVED_RESOLVED_REF"
|
|
58
|
+
printf 'saved_commit=%s\n' "$SPW_SAVED_COMMIT"
|
|
59
|
+
printf 'update_control=%s\n' "$update_control"
|
|
53
60
|
else
|
|
54
|
-
printf 'requested ref: %s\n' "$
|
|
55
|
-
printf 'resolved ref: %s\n' "$
|
|
56
|
-
printf 'desired commit: %s\n' "$
|
|
61
|
+
printf 'requested ref: %s\n' "$SPW_REQUESTED_REF"
|
|
62
|
+
printf 'resolved ref: %s\n' "$SPW_RESOLVED_REF"
|
|
63
|
+
printf 'desired commit: %s\n' "$SPW_DESIRED_COMMIT"
|
|
57
64
|
printf 'generated plugin commit: %s\n' "${generated_commit:-not present}"
|
|
58
65
|
printf 'installed manager commit or fingerprint: %s\n' "${installed_commit:-not detected}"
|
|
59
66
|
printf 'Codex identity state: %s\n' "$identity_state"
|
|
60
67
|
printf 'status: %s\n' "$status"
|
|
68
|
+
printf 'selection origin: %s\n' "$SPW_SELECTION_ORIGIN"
|
|
69
|
+
printf 'selection mode: %s\n' "$SPW_SELECTION_MODE"
|
|
70
|
+
printf 'upstream source origin: %s\n' "$SPW_UPSTREAM_SOURCE_ORIGIN"
|
|
71
|
+
printf 'effective source: %s\n' "$effective_source"
|
|
72
|
+
printf 'saved mode: %s\n' "$SPW_SAVED_MODE"
|
|
73
|
+
printf 'saved source: %s\n' "$saved_source"
|
|
74
|
+
printf 'saved requested ref: %s\n' "$SPW_SAVED_REQUESTED_REF"
|
|
75
|
+
printf 'saved resolved ref: %s\n' "$SPW_SAVED_RESOLVED_REF"
|
|
76
|
+
printf 'saved commit: %s\n' "$SPW_SAVED_COMMIT"
|
|
77
|
+
printf 'update control: %s\n' "$update_control"
|
|
78
|
+
if [ "$SPW_SELECTION_ORIGIN" != "$SPW_UPSTREAM_SOURCE_ORIGIN" ]; then
|
|
79
|
+
printf 'warning: effective ref and source have mixed origins (ref: %s, source: %s)\n' \
|
|
80
|
+
"$SPW_SELECTION_ORIGIN" "$SPW_UPSTREAM_SOURCE_ORIGIN"
|
|
81
|
+
fi
|
|
61
82
|
fi
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -eu
|
|
3
|
+
|
|
4
|
+
root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
|
|
5
|
+
. "$root/scripts/core/common.sh"
|
|
6
|
+
. "$root/scripts/core/upstream.sh"
|
|
7
|
+
. "$root/scripts/core/selection.sh"
|
|
8
|
+
|
|
9
|
+
[ "$#" -eq 0 ] || spw_usage_error 'usage: superpowers-manager track-latest'
|
|
10
|
+
|
|
11
|
+
spw_require_command python3
|
|
12
|
+
state_path=$(spw_selection_state_path)
|
|
13
|
+
tmp_parent="${TMPDIR:-/tmp}"
|
|
14
|
+
if ! track_workspace=$(spw_make_workspace "$tmp_parent" "superpowers-manager.track-latest"); then
|
|
15
|
+
spw_die "cannot create track-latest workspace under $tmp_parent"
|
|
16
|
+
fi
|
|
17
|
+
spw_install_workspace_trap "$track_workspace"
|
|
18
|
+
|
|
19
|
+
normalized="$track_workspace/selection.json"
|
|
20
|
+
spw_selection_state "$root" read \
|
|
21
|
+
--path="$state_path" --output="$normalized"
|
|
22
|
+
|
|
23
|
+
source="${SUPERPOWERS_UPSTREAM_URL:-$SPW_UPSTREAM_URL_DEFAULT}"
|
|
24
|
+
spw_selection_state "$root" \
|
|
25
|
+
validate-source --source="$source"
|
|
26
|
+
|
|
27
|
+
spw_selection_state "$root" write-track-latest \
|
|
28
|
+
--path="$state_path" --source="$source"
|
|
29
|
+
|
|
30
|
+
printf '%s\n' 'saved upstream selection: latest stable release'
|
package/scripts/uninstall
CHANGED
|
@@ -10,21 +10,12 @@ root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
|
|
|
10
10
|
spw_require_command python3
|
|
11
11
|
|
|
12
12
|
tmp_parent="${TMPDIR:-/tmp}"
|
|
13
|
-
if ! uninstall_workspace=$(
|
|
13
|
+
if ! uninstall_workspace=$(spw_make_workspace "$tmp_parent" "superpowers-manager.uninstall"); then
|
|
14
14
|
spw_die "cannot create uninstall workspace under $tmp_parent"
|
|
15
15
|
fi
|
|
16
|
+
spw_install_workspace_trap "$uninstall_workspace"
|
|
16
17
|
inspect_result="$uninstall_workspace/inspect.json"
|
|
17
18
|
uninstall_result="$uninstall_workspace/uninstall.json"
|
|
18
|
-
cleanup() {
|
|
19
|
-
status=$?
|
|
20
|
-
trap - 0 HUP INT TERM
|
|
21
|
-
rm -rf "$uninstall_workspace" || :
|
|
22
|
-
exit "$status"
|
|
23
|
-
}
|
|
24
|
-
trap cleanup 0
|
|
25
|
-
trap 'exit 129' HUP
|
|
26
|
-
trap 'exit 130' INT
|
|
27
|
-
trap 'exit 143' TERM
|
|
28
19
|
|
|
29
20
|
TMPDIR="$uninstall_workspace"
|
|
30
21
|
export TMPDIR
|
package/scripts/unpin
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -eu
|
|
3
|
+
|
|
4
|
+
root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
|
|
5
|
+
. "$root/scripts/core/common.sh"
|
|
6
|
+
. "$root/scripts/core/upstream.sh"
|
|
7
|
+
. "$root/scripts/core/selection.sh"
|
|
8
|
+
|
|
9
|
+
[ "$#" -eq 0 ] || spw_usage_error 'usage: superpowers-manager unpin'
|
|
10
|
+
|
|
11
|
+
state_path=$(spw_selection_state_path)
|
|
12
|
+
fallback=$(SUPERPOWERS_REF= spw_config_ref "$root")
|
|
13
|
+
removed=false
|
|
14
|
+
|
|
15
|
+
if [ -L "$state_path" ]; then
|
|
16
|
+
spw_die "selection state path is not a regular file; remove it manually after inspecting: $state_path"
|
|
17
|
+
elif [ -e "$state_path" ]; then
|
|
18
|
+
if [ ! -f "$state_path" ]; then
|
|
19
|
+
spw_die "selection state path is not a regular file; remove it manually after inspecting: $state_path"
|
|
20
|
+
fi
|
|
21
|
+
if ! rm -f "$state_path"; then
|
|
22
|
+
spw_die "cannot remove selection state: $state_path"
|
|
23
|
+
fi
|
|
24
|
+
removed=true
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
if [ -e "$state_path" ] || [ -L "$state_path" ]; then
|
|
28
|
+
spw_die "selection state remains after removal attempt: $state_path"
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
if [ "$removed" = true ]; then
|
|
32
|
+
printf 'removed saved upstream selection; packaged fallback is %s\n' "$fallback"
|
|
33
|
+
else
|
|
34
|
+
printf 'no saved upstream selection; packaged fallback is %s\n' "$fallback"
|
|
35
|
+
fi
|
|
36
|
+
if [ -n "${SUPERPOWERS_REF:-}" ]; then
|
|
37
|
+
printf '%s\n' 'note: active SUPERPOWERS_REF override remains effective'
|
|
38
|
+
fi
|
|
39
|
+
if [ -n "${SUPERPOWERS_UPSTREAM_URL:-}" ]; then
|
|
40
|
+
printf '%s\n' 'note: active SUPERPOWERS_UPSTREAM_URL override remains effective'
|
|
41
|
+
fi
|