@webpresso/plugin-opencode 0.0.12 → 0.0.14

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/bin/wp ADDED
@@ -0,0 +1,250 @@
1
+ #!/bin/sh
2
+ # webpresso-wp-shim v1 — keep this marker on line 2 (shim loop guard).
3
+ #
4
+ # The one `wp` shim. Copied byte-identically into each plugin's bin/wp
5
+ # (packages/scripts/stage-plugin-runtime-artifacts.ts) and, once the release
6
+ # installer adopts it, into ~/.webpresso/bin/wp. It stores no state, writes
7
+ # nothing, and picks the runtime on EVERY call from where it is called:
8
+ #
9
+ # 1. WEBPRESSO_WP_BIN (an absolute executable) xor WP_FORCE_JIT_PATH (an
10
+ # absolute checkout)
11
+ # 2. the nearest ancestor of $PWD that is a webpresso source checkout
12
+ # (bin/wp + src/cli/cli.ts) ADMITTED in ~/.webpresso/source-checkouts
13
+ # -> <checkout>/bin/wp, whose own launcher guards freshness against HEAD.
14
+ # Layout is not trust: a source-shaped directory whose repository is not
15
+ # listed there fails with checkout-not-admitted instead of running its
16
+ # bin/wp. A linked worktree is admitted through its repository, and only
17
+ # when the admitted repository's own .git/worktrees/*/gitdir names it.
18
+ # 3. else (no checkout ancestor at all) the nearest ancestor
19
+ # .webpressorc.json: its top-level exact-semver "runtime" pin
20
+ # -> ~/.webpresso/runtimes/<pin>/wp (missing = error)
21
+ # 4. else the newest stable ~/.webpresso/runtimes/<x.y.z>/wp
22
+ # 5. else a named error, exit 127 — never a different runtime. A pre-shim
23
+ # ~/.webpresso/bin/wp is never run implicitly (nothing in it proves it is
24
+ # a release rather than a development build); name it with
25
+ # WEBPRESSO_WP_BIN.
26
+ #
27
+ # Bounded: a parent-directory walk, one-line reads of the user's admission file
28
+ # and of admitted repositories' worktree back-links, at most one size-capped
29
+ # JSON read (node, only in a consumer repo with a
30
+ # .webpressorc.json), one directory listing. No git, no network, no retries,
31
+ # no timeouts. Mode and error literals
32
+ # are the closed vocabulary in src/runtime/runtime-selector-vocab.ts.
33
+ #
34
+ # `exec`, not a spawn wrapper: the MCP server this launches is a long-lived
35
+ # stdio service, and a relay process could die and orphan it. Arguments are
36
+ # forwarded verbatim through "$@".
37
+
38
+ set -u
39
+
40
+ announced_modes='explicit-binary explicit-checkout'
41
+
42
+ fail() {
43
+ printf 'wp-shim: error %s: %s\n' "$1" "$2" >&2
44
+ exit 127
45
+ }
46
+
47
+ MODE=
48
+ TARGET=
49
+ choose() {
50
+ MODE=$1
51
+ TARGET=$2
52
+ }
53
+
54
+ admission_file=${HOME:-}/.webpresso/source-checkouts
55
+
56
+ is_source_shaped() {
57
+ [ -f "$1/bin/wp" ] && [ -x "$1/bin/wp" ] && [ -f "$1/src/cli/cli.ts" ]
58
+ }
59
+
60
+ # True when checkout $1 is an admitted repository's primary checkout, or one of
61
+ # its registered linked worktrees. The admission file is user-owned, one
62
+ # absolute primary-checkout path per line. Identity is by directory (-ef), and
63
+ # a linked worktree is recognized only from the admitted repository's own
64
+ # .git/worktrees/*/gitdir back-links: nothing inside the project (its .git
65
+ # file, symlink or metadata) is ever read or trusted, and stat never blocks.
66
+ is_admitted_checkout() {
67
+ [ -f "$admission_file" ] || return 1
68
+ while IFS= read -r entry || [ -n "$entry" ]; do
69
+ case $entry in /*) ;; *) continue ;; esac
70
+ [ "$entry" -ef "$1" ] && return 0
71
+ for meta in "$entry"/.git/worktrees/*/; do
72
+ [ -f "${meta}gitdir" ] || continue
73
+ back=
74
+ IFS= read -r back <"${meta}gitdir" || [ -n "$back" ] || continue
75
+ [ "${back%/.git}" -ef "$1" ] && return 0
76
+ done
77
+ done <"$admission_file"
78
+ return 1
79
+ }
80
+
81
+ # Exact semver x.y.z[-pre], no leading zeros, no build metadata. With a second
82
+ # argument "stable", a prerelease is rejected too.
83
+ is_exact_semver() {
84
+ version=$1
85
+ core=${version%%-*}
86
+ pre=
87
+ if [ "$core" != "$version" ]; then
88
+ pre=${version#*-}
89
+ [ "${2:-}" != stable ] || return 1
90
+ case $pre in '' | .* | *. | *..* | *[!0-9A-Za-z.-]*) return 1 ;; esac
91
+ saved_ifs=$IFS
92
+ IFS=.
93
+ # shellcheck disable=SC2086 # deliberate field split on "."
94
+ set -- $pre
95
+ IFS=$saved_ifs
96
+ for part in "$@"; do
97
+ case $part in 0?*) case $part in *[!0-9]*) ;; *) return 1 ;; esac ;; esac
98
+ done
99
+ fi
100
+ case $core in '' | .* | *. | *..* | *[!0-9.]*) return 1 ;; esac
101
+ saved_ifs=$IFS
102
+ IFS=.
103
+ # shellcheck disable=SC2086 # deliberate field split on "."
104
+ set -- $core
105
+ IFS=$saved_ifs
106
+ [ "$#" -eq 3 ] || return 1
107
+ for part in "$@"; do
108
+ case $part in '' | 0?* | *[!0-9]*) return 1 ;; esac
109
+ done
110
+ return 0
111
+ }
112
+
113
+ runtimes_dir=${HOME:-}/.webpresso/runtimes
114
+
115
+ # Sets PIN to the top-level "runtime" of $1 (empty when absent). The document
116
+ # is opened non-blocking, must be a regular file, and is read through a
117
+ # 64 KiB + 1 buffer on that one descriptor (growth after open cannot exceed
118
+ # it); anything longer is refused. The pin
119
+ # is validated as an exact semver BEFORE it crosses back into the shell, so no
120
+ # shell transport (such as a stripped trailing newline) can change it.
121
+ read_pin() {
122
+ PIN=$(node -e '
123
+ const fs = require("node:fs");
124
+ let fd;
125
+ try { fd = fs.openSync(process.argv[1], fs.constants.O_RDONLY | fs.constants.O_NONBLOCK); } catch { process.exit(2); }
126
+ if (!fs.fstatSync(fd).isFile()) process.exit(3);
127
+ const limit = 65536;
128
+ const buffer = Buffer.alloc(limit + 1);
129
+ let length = 0;
130
+ for (let n; length <= limit && (n = fs.readSync(fd, buffer, length, limit + 1 - length, null)) > 0; ) length += n;
131
+ if (length > limit) process.exit(3);
132
+ let v;
133
+ try { v = JSON.parse(buffer.subarray(0, length).toString("utf8")); } catch { process.exit(2); }
134
+ if (v === null || typeof v !== "object" || Array.isArray(v) || !Object.hasOwn(v, "runtime")) process.exit(0);
135
+ const pattern = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*)?$/u;
136
+ if (typeof v.runtime !== "string" || !pattern.test(v.runtime)) process.exit(4);
137
+ process.stdout.write(v.runtime);
138
+ ' "$1" 2>/dev/null)
139
+ case $? in
140
+ 0) return 0 ;;
141
+ 3) PIN_PROBLEM="is not a regular file of at most 64 KiB" ;;
142
+ 4) PIN_PROBLEM="has a \"runtime\" pin that is not an exact version such as 1.2.3 (no ranges, no build metadata)" ;;
143
+ 127) PIN_PROBLEM="cannot be read: node is not on PATH" ;;
144
+ *) PIN_PROBLEM="is not valid JSON" ;;
145
+ esac
146
+ return 1
147
+ }
148
+
149
+ choose_latest_release() {
150
+ best=
151
+ best_major=
152
+ best_minor=
153
+ best_patch=
154
+ for entry in "$runtimes_dir"/*/; do
155
+ [ -d "$entry" ] || continue
156
+ entry=${entry%/}
157
+ version=${entry##*/}
158
+ is_exact_semver "$version" stable || continue
159
+ [ -f "$entry/wp" ] && [ -x "$entry/wp" ] || continue
160
+ major=${version%%.*}
161
+ rest=${version#*.}
162
+ minor=${rest%%.*}
163
+ patch=${rest#*.}
164
+ if [ -z "$best" ] ||
165
+ [ "$major" -gt "$best_major" ] ||
166
+ { [ "$major" -eq "$best_major" ] && [ "$minor" -gt "$best_minor" ]; } ||
167
+ { [ "$major" -eq "$best_major" ] && [ "$minor" -eq "$best_minor" ] && [ "$patch" -gt "$best_patch" ]; }; then
168
+ best=$version
169
+ best_major=$major
170
+ best_minor=$minor
171
+ best_patch=$patch
172
+ fi
173
+ done
174
+ if [ -n "$best" ]; then
175
+ choose latest-release "$runtimes_dir/$best/wp"
176
+ return 0
177
+ fi
178
+ return 1
179
+ }
180
+
181
+ is_shim_file() {
182
+ [ -f "$1" ] || return 1
183
+ { head -c 256 "$1" | grep -q 'webpresso-wp-shim'; } 2>/dev/null
184
+ }
185
+
186
+ choose_release() {
187
+ if [ -n "${1:-}" ]; then
188
+ rc_file=$1/.webpressorc.json
189
+ read_pin "$rc_file" || fail pin-invalid "$rc_file $PIN_PROBLEM."
190
+ if [ -n "$PIN" ]; then
191
+ is_exact_semver "$PIN" || fail pin-invalid "$rc_file pins runtime \"$PIN\"; use an exact version such as 1.2.3 (no ranges)."
192
+ pinned=$runtimes_dir/$PIN/wp
193
+ [ -f "$pinned" ] && [ -x "$pinned" ] || fail pin-not-installed "$rc_file pins runtime $PIN, but $pinned is not installed under $runtimes_dir."
194
+ choose pinned-release "$pinned"
195
+ return 0
196
+ fi
197
+ fi
198
+ choose_latest_release && return 0
199
+ fail no-release-installed "no webpresso source checkout above $PWD and no release under $runtimes_dir; install the Webpresso app, or name a runtime explicitly with WEBPRESSO_WP_BIN (for example a pre-shim \$HOME/.webpresso/bin/wp release) or WP_FORCE_JIT_PATH (a source checkout)."
200
+ }
201
+
202
+ : "${WEBPRESSO_WP_BIN:=}"
203
+ : "${WP_FORCE_JIT_PATH:=}"
204
+
205
+ if [ -n "$WEBPRESSO_WP_BIN" ] && [ -n "$WP_FORCE_JIT_PATH" ]; then
206
+ fail both-overrides-set "WEBPRESSO_WP_BIN and WP_FORCE_JIT_PATH are both set; unset one."
207
+ elif [ -n "$WEBPRESSO_WP_BIN" ]; then
208
+ case $WEBPRESSO_WP_BIN in /*) ;; *) fail override-missing "WEBPRESSO_WP_BIN must be an absolute path (got $WEBPRESSO_WP_BIN); exec would otherwise search PATH for a different file." ;; esac
209
+ [ -f "$WEBPRESSO_WP_BIN" ] && [ -x "$WEBPRESSO_WP_BIN" ] || fail override-missing "WEBPRESSO_WP_BIN points to $WEBPRESSO_WP_BIN, which is not an executable file."
210
+ choose explicit-binary "$WEBPRESSO_WP_BIN"
211
+ elif [ -n "$WP_FORCE_JIT_PATH" ]; then
212
+ case $WP_FORCE_JIT_PATH in /*) ;; *) fail override-missing "WP_FORCE_JIT_PATH must be an absolute path (got $WP_FORCE_JIT_PATH)." ;; esac
213
+ is_source_shaped "$WP_FORCE_JIT_PATH" || fail override-missing "WP_FORCE_JIT_PATH points to $WP_FORCE_JIT_PATH, which is not a webpresso source checkout (executable bin/wp and src/cli/cli.ts)."
214
+ choose explicit-checkout "$WP_FORCE_JIT_PATH/bin/wp"
215
+ else
216
+ # Checkout first, across every ancestor: a .webpressorc.json nested inside
217
+ # a source checkout must not pin a foreign runtime there. Only when no
218
+ # ancestor is a checkout does the nearest consumer config decide.
219
+ dir=${PWD:-}
220
+ consumer_root=
221
+ while [ -n "$dir" ]; do
222
+ if is_source_shaped "$dir"; then
223
+ is_admitted_checkout "$dir" || fail checkout-not-admitted "$dir looks like a webpresso source checkout, but its repository is not listed in $admission_file; add the absolute path of its primary checkout there (one per line) to run its bin/wp, or set WP_FORCE_JIT_PATH=$dir for this call."
224
+ choose checkout "$dir/bin/wp"
225
+ break
226
+ fi
227
+ if [ -z "$consumer_root" ] && [ -e "$dir/.webpressorc.json" ]; then
228
+ consumer_root=$dir
229
+ fi
230
+ [ "$dir" != / ] || break
231
+ dir=${dir%/*}
232
+ [ -n "$dir" ] || dir=/
233
+ done
234
+ [ -n "$TARGET" ] || choose_release "$consumer_root"
235
+ fi
236
+
237
+ # Never exec a shim: this one (directly or through a symlink) or another
238
+ # byte-identical plugin copy would recurse instead of reaching a runtime.
239
+ if [ "$TARGET" = "$0" ] || [ "$TARGET" -ef "$0" ] || is_shim_file "$TARGET"; then
240
+ fail self-reference "$MODE resolved to a wp shim ($TARGET); point it at a wp runtime instead."
241
+ fi
242
+
243
+ case " $announced_modes " in
244
+ *" $MODE "*) printf 'wp-shim: %s %s\n' "$MODE" "$TARGET" >&2 ;;
245
+ esac
246
+
247
+ WP_SKIP_UPDATE_CHECK=${WP_SKIP_UPDATE_CHECK:-1}
248
+ export WP_SKIP_UPDATE_CHECK
249
+
250
+ exec "$TARGET" "$@"