@stll/oxlint-config 0.12.2 → 0.14.1

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.
Files changed (82) hide show
  1. package/README.md +234 -1
  2. package/dist/cloud-setup-cli.cjs +6 -4
  3. package/dist/cloud-setup-cli.cjs.map +1 -1
  4. package/dist/cloud-setup-cli.mjs +3 -1
  5. package/dist/cloud-setup-cli.mjs.map +1 -1
  6. package/dist/compiler-packages.cjs +28 -0
  7. package/dist/compiler-packages.cjs.map +1 -0
  8. package/dist/compiler-packages.mjs +17 -0
  9. package/dist/compiler-packages.mjs.map +1 -0
  10. package/dist/consumer-compat-cli.cjs +1091 -0
  11. package/dist/consumer-compat-cli.cjs.map +1 -0
  12. package/dist/consumer-compat-cli.d.cts +1 -0
  13. package/dist/consumer-compat-cli.d.mts +1 -0
  14. package/dist/consumer-compat-cli.mjs +1089 -0
  15. package/dist/consumer-compat-cli.mjs.map +1 -0
  16. package/dist/publish-build-target-nuxt-helper.cjs +71 -0
  17. package/dist/publish-build-target-nuxt-helper.cjs.map +1 -0
  18. package/dist/publish-build-target-nuxt-helper.d.cts +10 -0
  19. package/dist/publish-build-target-nuxt-helper.d.mts +10 -0
  20. package/dist/publish-build-target-nuxt-helper.mjs +68 -0
  21. package/dist/publish-build-target-nuxt-helper.mjs.map +1 -0
  22. package/dist/publish-build-target-vite-dts-helper.cjs +3 -0
  23. package/dist/publish-build-target-vite-dts-helper.d.cts +24 -0
  24. package/dist/publish-build-target-vite-dts-helper.d.mts +24 -0
  25. package/dist/publish-build-target-vite-dts-helper.mjs +2 -0
  26. package/dist/publish-build-target-vite-dts-helper2.cjs +372 -0
  27. package/dist/publish-build-target-vite-dts-helper2.cjs.map +1 -0
  28. package/dist/publish-build-target-vite-dts-helper2.mjs +359 -0
  29. package/dist/publish-build-target-vite-dts-helper2.mjs.map +1 -0
  30. package/dist/publish-contract-cli.cjs +983 -0
  31. package/dist/publish-contract-cli.cjs.map +1 -0
  32. package/dist/publish-contract-cli.d.cts +1 -0
  33. package/dist/publish-contract-cli.d.mts +1 -0
  34. package/dist/publish-contract-cli.mjs +981 -0
  35. package/dist/publish-contract-cli.mjs.map +1 -0
  36. package/dist/publish-contract.cjs +431 -0
  37. package/dist/publish-contract.cjs.map +1 -0
  38. package/dist/publish-contract.mjs +396 -0
  39. package/dist/publish-contract.mjs.map +1 -0
  40. package/dist/rolldown-runtime.cjs +28 -0
  41. package/dist/toolchain-changed-cli.cjs +34 -0
  42. package/dist/toolchain-changed-cli.cjs.map +1 -0
  43. package/dist/toolchain-changed-cli.d.cts +1 -0
  44. package/dist/toolchain-changed-cli.d.mts +1 -0
  45. package/dist/toolchain-changed-cli.mjs +34 -0
  46. package/dist/toolchain-changed-cli.mjs.map +1 -0
  47. package/dist/toolchain-changed.cjs +1470 -0
  48. package/dist/toolchain-changed.cjs.map +1 -0
  49. package/dist/toolchain-changed.mjs +1463 -0
  50. package/dist/toolchain-changed.mjs.map +1 -0
  51. package/dist/toolchain-check-cli.cjs +2 -1
  52. package/dist/toolchain-check-cli.cjs.map +1 -1
  53. package/dist/toolchain-check-cli.mjs +2 -1
  54. package/dist/toolchain-check-cli.mjs.map +1 -1
  55. package/dist/toolchain-guard.cjs +360 -1161
  56. package/dist/toolchain-guard.cjs.map +1 -1
  57. package/dist/toolchain-guard.mjs +338 -1100
  58. package/dist/toolchain-guard.mjs.map +1 -1
  59. package/dist/toolchain-inputs.cjs +926 -0
  60. package/dist/toolchain-inputs.cjs.map +1 -0
  61. package/dist/toolchain-inputs.mjs +816 -0
  62. package/dist/toolchain-inputs.mjs.map +1 -0
  63. package/dist/toolchain-schema.cjs +702 -0
  64. package/dist/toolchain-schema.cjs.map +1 -0
  65. package/dist/toolchain-schema.mjs +580 -0
  66. package/dist/toolchain-schema.mjs.map +1 -0
  67. package/dist/typecheck-parity-cli.cjs +38 -898
  68. package/dist/typecheck-parity-cli.cjs.map +1 -1
  69. package/dist/typecheck-parity-cli.mjs +39 -899
  70. package/dist/typecheck-parity-cli.mjs.map +1 -1
  71. package/dist/typecheck-parity.cjs +905 -0
  72. package/dist/typecheck-parity.cjs.map +1 -0
  73. package/dist/typecheck-parity.mjs +894 -0
  74. package/dist/typecheck-parity.mjs.map +1 -0
  75. package/dist/typecheck-probe-cli.cjs +176 -0
  76. package/dist/typecheck-probe-cli.cjs.map +1 -0
  77. package/dist/typecheck-probe-cli.d.cts +1 -0
  78. package/dist/typecheck-probe-cli.d.mts +1 -0
  79. package/dist/typecheck-probe-cli.mjs +174 -0
  80. package/dist/typecheck-probe-cli.mjs.map +1 -0
  81. package/package.json +43 -5
  82. package/toolchain.json +9 -1
@@ -0,0 +1,816 @@
1
+ import path from "node:path";
2
+ import { subset, valid, validRange } from "semver";
3
+ import picomatch from "picomatch";
4
+ //#region src/cloud-setup-install.ts
5
+ const packageInstall = String.raw`
6
+ # Prevent package maintainer scripts from starting unmanaged system services.
7
+ apt_install() (
8
+ # EXIT runs after function locals expire; this subshell owns the trap state.
9
+ backup=''
10
+ policy_state=untouched
11
+ [[ ! -L /var/lock/stll-cloud-apt.lock ]] || fail 'Invalid package install lock'
12
+ exec 8>/var/lock/stll-cloud-apt.lock
13
+ flock -x -w 120 8 || fail 'Another package installation is active'
14
+ [[ ! -L /usr/sbin/policy-rc.d ]] || fail 'Unsupported service-start policy symlink'
15
+ [[ ! -e /usr/sbin/policy-rc.d || -f /usr/sbin/policy-rc.d ]] || fail 'Unsupported service-start policy file'
16
+ backup="$(mktemp -d)"
17
+ cleanup_package_policy() {
18
+ local status=$?
19
+ trap - EXIT HUP INT TERM
20
+ if [[ "$policy_state" == installed ]]; then
21
+ if [[ -f "$backup/original" ]]; then
22
+ if ! cp -p "$backup/original" /usr/sbin/policy-rc.d; then status=1; fi
23
+ else
24
+ if ! rm -f /usr/sbin/policy-rc.d; then status=1; fi
25
+ fi
26
+ fi
27
+ rm -rf -- "$backup"
28
+ exit "$status"
29
+ }
30
+ trap cleanup_package_policy EXIT
31
+ trap 'exit 129' HUP
32
+ trap 'exit 130' INT
33
+ trap 'exit 143' TERM
34
+ if [[ -e /usr/sbin/policy-rc.d ]]; then cp -p /usr/sbin/policy-rc.d "$backup/original"; fi
35
+ policy_state=installed
36
+ printf '%s\n' '#!/bin/sh' 'exit 101' > /usr/sbin/policy-rc.d
37
+ chmod 755 /usr/sbin/policy-rc.d
38
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "$@"
39
+ )
40
+
41
+ `;
42
+ const postgresSourceLine = "deb [signed-by=/usr/share/keyrings/stll-pgdg.asc] https://apt.postgresql.org/pub/repos/apt noble-pgdg main";
43
+ const postgresSigningFingerprint = "B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8";
44
+ const postgresSources = String.raw`
45
+ pgdg_key_fingerprint() {
46
+ gpg --batch --show-keys --with-colons "$1" | awk -F: '$1 == "pub" { primary = 1 } $1 == "sub" { primary = 0 } $1 == "fpr" && primary { print toupper($10); primary = 0 }'
47
+ }
48
+ pgdg_sources_ready() {
49
+ local managed="$1" file mode records suite keyring override fingerprint selected_keyring=''
50
+ shift
51
+ PGDG_SOURCE_STATE=missing
52
+ [[ ! -L "$managed" ]] || fail "Managed PGDG source must not be a symlink: $managed"
53
+ for file in "$@"; do
54
+ [[ -e "$file" ]] || continue
55
+ [[ -f "$file" ]] || fail "APT source must be a regular file: $file"
56
+ case "$file" in *.sources) mode=sources ;; *) mode=list ;; esac
57
+ records="$(awk -v mode="$mode" '
58
+ function trim(value) { gsub(/^[[:space:]]+|[[:space:]]+$/, "", value); return value }
59
+ function pgdg_uri(uri) {
60
+ uri = tolower(uri)
61
+ if (uri !~ /^[a-z][a-z0-9+.-]*:\/\//) return 0
62
+ sub(/^[a-z][a-z0-9+.-]*:\/\//, "", uri)
63
+ sub(/[\/?#].*$/, "", uri)
64
+ sub(/^.*@/, "", uri)
65
+ sub(/:.*$/, "", uri)
66
+ return uri == "apt.postgresql.org" || uri == "apt.postgresql.org."
67
+ }
68
+ function contains_pgdg(value, tokens, count, token_index) {
69
+ count = split(value, tokens, /[[:space:]]+/)
70
+ for (token_index = 1; token_index <= count; token_index++) if (pgdg_uri(tokens[token_index])) return 1
71
+ return 0
72
+ }
73
+ function unsafe_field(name) {
74
+ return name ~ /^(trusted|allow-insecure|allow-weak|allow-downgrade-to-insecure)$/
75
+ }
76
+ function disabled_override(value) {
77
+ value = tolower(trim(value))
78
+ gsub(/^["\047]|["\047]$/, "", value)
79
+ return value ~ /^(no|false|without|off|disable|0)$/
80
+ }
81
+ function stanza(override_index) {
82
+ override = 0
83
+ for (override_index = 1; override_index <= override_count; override_index++) {
84
+ if (!disabled_override(override_values[override_index])) override = 1
85
+ delete override_values[override_index]
86
+ }
87
+ if (tolower(trim(enabled)) != "no" && pgdg) print (trim(suites) == "" ? "-" : trim(suites)) "\t" (trim(keyring) == "" ? "-" : trim(keyring)) "\t" (override ? "unsafe" : "safe")
88
+ enabled = ""; pgdg = 0; field = ""; suites = ""; keyring = ""; override = 0; override_count = 0
89
+ }
90
+ /^[[:space:]]*#/ { if (mode == "list" || $0 ~ /^#/) next }
91
+ {
92
+ line = $0
93
+ if (mode == "list") {
94
+ sub(/[[:space:]]*#.*$/, "", line)
95
+ if (line !~ /^[[:space:]]*deb(-src)?[[:space:]]/ || !contains_pgdg(line)) next
96
+ keyring = ""; override = 0
97
+ if (match(line, /\[[^]]*\]/)) {
98
+ options = substr(line, RSTART + 1, RLENGTH - 2)
99
+ count = split(options, tokens, /[[:space:]]+/)
100
+ for (token_index = 1; token_index <= count; token_index++) {
101
+ if (tolower(tokens[token_index]) ~ /^signed-by=/) { keyring = tokens[token_index]; sub(/^[^=]*=/, "", keyring) }
102
+ option_name = tolower(tokens[token_index]); sub(/=.*/, "", option_name)
103
+ option_value = tokens[token_index]; sub(/^[^=]*=/, "", option_value)
104
+ if (unsafe_field(option_name) && !disabled_override(option_value)) override = 1
105
+ }
106
+ sub(/\[[^]]*\]/, "", line)
107
+ }
108
+ count = split(line, tokens, /[[:space:]]+/)
109
+ for (token_index = 1; token_index <= count; token_index++) if (pgdg_uri(tokens[token_index])) print (tokens[token_index + 1] == "" ? "-" : tokens[token_index + 1]) "\t" (keyring == "" ? "-" : keyring) "\t" (override ? "unsafe" : "safe")
110
+ next
111
+ }
112
+ if (line ~ /^[[:space:]]*$/) { stanza(); next }
113
+ if (line ~ /^[[:space:]]/) {
114
+ if (field == "uris" && contains_pgdg(line)) pgdg = 1
115
+ if (field == "enabled") enabled = enabled " " line
116
+ if (field == "suites") suites = suites " " line
117
+ if (field == "signed-by") keyring = keyring " " line
118
+ if (unsafe_field(field)) override_values[override_count] = override_values[override_count] " " line
119
+ next
120
+ }
121
+ field = tolower(line)
122
+ sub(/:.*/, "", field)
123
+ value = line
124
+ sub(/^[^:]*:[[:space:]]*/, "", value)
125
+ if (field == "uris" && contains_pgdg(value)) pgdg = 1
126
+ if (field == "enabled") enabled = value
127
+ if (field == "suites") suites = value
128
+ if (field == "signed-by") keyring = value
129
+ if (unsafe_field(field)) override_values[++override_count] = value
130
+ }
131
+ END { if (mode == "sources") stanza() }
132
+ ' "$file")" || fail "Cannot inspect APT source: $file"
133
+ if [[ "$file" == "$managed" && -z "$records" ]]; then
134
+ fail "Managed PGDG source has no active PGDG entry: $file"
135
+ fi
136
+ while IFS=$'\t' read -r suite keyring override; do
137
+ [[ -n "$suite" ]] || continue
138
+ [[ "$override" == safe ]] || fail "PGDG source enables an unsafe trust override: $file"
139
+ [[ "$suite" == noble-pgdg && "$keyring" == /* && ! "$keyring" =~ [[:space:],] ]] || fail "PGDG source requires noble-pgdg and one Signed-By keyring: $file"
140
+ [[ -z "$selected_keyring" || "$selected_keyring" == "$keyring" ]] || fail "Conflicting PGDG Signed-By keyrings: $file"
141
+ fingerprint="$(pgdg_key_fingerprint "$keyring")" || fail "Cannot verify PGDG Signed-By keyring: $file"
142
+ [[ "$fingerprint" == @PGDG_FINGERPRINT@ ]] || fail "PGDG Signed-By fingerprint mismatch: $file"
143
+ selected_keyring="$keyring"
144
+ PGDG_SOURCE_STATE=reuse
145
+ done <<< "$records"
146
+ done
147
+ }
148
+ `;
149
+ const postgresInstall = String.raw`
150
+ postgres_ready() {
151
+ local binary
152
+ for binary in postgres initdb pg_ctl psql createdb; do
153
+ [[ -x "/usr/lib/postgresql/@POSTGRES@/bin/$binary" ]] || return 1
154
+ done
155
+ /usr/lib/postgresql/@POSTGRES@/bin/postgres --version | grep -Eq '^postgres \(PostgreSQL\) @POSTGRES@\.'
156
+ }
157
+ if ! postgres_ready; then
158
+ pgdg_sources_ready /etc/apt/sources.list.d/stll-pgdg.list /etc/apt/sources.list /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources
159
+ if [[ "$PGDG_SOURCE_STATE" == missing ]]; then
160
+ local key key_fingerprint
161
+ key="$(mktemp)"
162
+ curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 https://www.postgresql.org/media/keys/ACCC4CF8.asc -o "$key"
163
+ key_fingerprint="$(pgdg_key_fingerprint "$key")"
164
+ [[ "$key_fingerprint" == @PGDG_FINGERPRINT@ ]] || fail 'PostgreSQL signing key mismatch'
165
+ install -d -m 755 /usr/share/keyrings
166
+ install -m 644 "$key" /usr/share/keyrings/stll-pgdg.asc
167
+ rm -- "$key"
168
+ printf '%s\n' '@PGDG_SOURCE@' > /etc/apt/sources.list.d/stll-pgdg.list
169
+ fi
170
+ apt-get update
171
+ apt_install postgresql-@POSTGRES@ postgresql-client-@POSTGRES@
172
+ fi
173
+ postgres_ready || fail 'PostgreSQL toolchain is incomplete or differs from the policy major'
174
+ `;
175
+ const valkeyInstall = String.raw`
176
+ if ! command -v valkey-server >/dev/null || [[ "$(valkey-server --version | sed -nE 's/.*v=([0-9.]+).*/\1/p')" != '@VALKEY@' ]] || ! command -v valkey-cli >/dev/null || [[ "$(valkey-cli --version)" != 'valkey-cli @VALKEY@' ]]; then
177
+ local scratch server_url server_sha tools_url tools_sha package
178
+ scratch="$(mktemp -d)"
179
+ chmod 755 "$scratch"
180
+ case "$ARCH" in
181
+ amd64) server_url='@AMD64_SERVER_URL@'; server_sha='@AMD64_SERVER_SHA@'; tools_url='@AMD64_TOOLS_URL@'; tools_sha='@AMD64_TOOLS_SHA@' ;;
182
+ arm64) server_url='@ARM64_SERVER_URL@'; server_sha='@ARM64_SERVER_SHA@'; tools_url='@ARM64_TOOLS_URL@'; tools_sha='@ARM64_TOOLS_SHA@' ;;
183
+ *) fail 'Unsupported Valkey architecture' ;;
184
+ esac
185
+ curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 "$server_url" -o "$scratch/valkey-server.deb"
186
+ curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 "$tools_url" -o "$scratch/valkey-tools.deb"
187
+ (cd "$scratch"; printf '%s %s\n' "$server_sha" valkey-server.deb "$tools_sha" valkey-tools.deb | sha256sum --strict -c -)
188
+ for package in valkey-server valkey-tools; do
189
+ [[ "$(dpkg-deb -f "$scratch/$package.deb" Package)" == "$package" ]] || fail 'Valkey artifact package mismatch'
190
+ [[ "$(dpkg-deb -f "$scratch/$package.deb" Version)" == '@VALKEY@-1.noble' ]] || fail 'Valkey artifact version mismatch'
191
+ [[ "$(dpkg-deb -f "$scratch/$package.deb" Architecture)" == "$ARCH" ]] || fail 'Valkey artifact architecture mismatch'
192
+ done
193
+ apt-get update
194
+ apt_install "$scratch/valkey-server.deb" "$scratch/valkey-tools.deb"
195
+ rm -rf -- "$scratch"
196
+ fi
197
+ [[ "$(valkey-server --version | sed -nE 's/.*v=([0-9.]+).*/\1/p')" == '@VALKEY@' ]] || fail 'Valkey version differs from policy'
198
+ [[ "$(valkey-cli --version)" == 'valkey-cli @VALKEY@' ]] || fail 'Valkey client version differs from policy'
199
+ `;
200
+ const state = String.raw`
201
+ service_account_ready() {
202
+ local uid gid shell
203
+ uid="$(id -u stll-cloud)" || fail 'Service user is unavailable; run install'
204
+ gid="$(id -g stll-cloud)"
205
+ shell="$(getent passwd stll-cloud | cut -d: -f7)"
206
+ [[ "$uid" =~ ^[1-9][0-9]*$ && "$gid" =~ ^[1-9][0-9]*$ && "$shell" == /usr/sbin/nologin ]] || fail 'Service user must be unprivileged with no login shell'
207
+ }
208
+ prepare_state() {
209
+ service_account_ready
210
+ [[ ! -L /var/lib/stll-cloud ]] || fail 'Service state root must not be a symlink'
211
+ if [[ -e /var/lib/stll-cloud ]]; then
212
+ [[ -d /var/lib/stll-cloud && "$(stat -c '%u:%a' /var/lib/stll-cloud)" == 0:755 ]] || fail 'Invalid service state root'
213
+ else
214
+ install -d -m 755 -o root -g root /var/lib/stll-cloud
215
+ fi
216
+ STATE="/var/lib/stll-cloud/$(printf '%s' "$REPO_ROOT" | sha256sum | cut -d' ' -f1)"
217
+ [[ ! -L "$STATE" ]] || fail 'Service state must not be a symlink'
218
+ if [[ -e "$STATE" ]]; then
219
+ [[ -d "$STATE" && "$(stat -c '%u:%a' "$STATE")" == 0:710 && "$(stat -c '%g' "$STATE")" == "$(id -g stll-cloud)" ]] || fail 'Invalid service state ownership'
220
+ else
221
+ install -d -m 710 -o root -g stll-cloud "$STATE"
222
+ fi
223
+ [[ ! -L "$STATE/lock" ]] || fail 'Service lock must not be a symlink'
224
+ exec 9>"$STATE/lock"
225
+ flock -x -w 10 9 || fail 'Another setup process owns the service state'
226
+ }
227
+ `;
228
+ const renderCloudServiceInstall = ({ services, postgres, valkey, valkeyArtifacts }) => {
229
+ const parts = services.map((service) => {
230
+ switch (service) {
231
+ case "postgres": return postgresInstall.replaceAll("@POSTGRES@", postgres).replaceAll("@PGDG_SOURCE@", postgresSourceLine).replaceAll("@PGDG_FINGERPRINT@", postgresSigningFingerprint);
232
+ case "valkey": {
233
+ const replacements = /* @__PURE__ */ new Map([
234
+ ["@VALKEY@", valkey],
235
+ ["@AMD64_SERVER_URL@", valkeyArtifacts.amd64.server.url],
236
+ ["@AMD64_SERVER_SHA@", valkeyArtifacts.amd64.server.sha256],
237
+ ["@AMD64_TOOLS_URL@", valkeyArtifacts.amd64.tools.url],
238
+ ["@AMD64_TOOLS_SHA@", valkeyArtifacts.amd64.tools.sha256],
239
+ ["@ARM64_SERVER_URL@", valkeyArtifacts.arm64.server.url],
240
+ ["@ARM64_SERVER_SHA@", valkeyArtifacts.arm64.server.sha256],
241
+ ["@ARM64_TOOLS_URL@", valkeyArtifacts.arm64.tools.url],
242
+ ["@ARM64_TOOLS_SHA@", valkeyArtifacts.arm64.tools.sha256]
243
+ ]);
244
+ return valkeyInstall.replace(/@[A-Z0-9_]+@/g, (token) => {
245
+ const value = replacements.get(token);
246
+ if (value === void 0) throw new Error(`Missing Valkey artifact field: ${token}`);
247
+ return value;
248
+ });
249
+ }
250
+ default: throw new Error(`Unsupported service: ${String(service)}`);
251
+ }
252
+ });
253
+ const user = services.length === 0 ? "" : " if ! id stll-cloud >/dev/null 2>&1; then useradd --system --user-group --no-create-home --shell /usr/sbin/nologin stll-cloud; fi\n service_account_ready\n";
254
+ return `${packageInstall}\n${services.includes("postgres") ? postgresSources.replaceAll("@PGDG_FINGERPRINT@", postgresSigningFingerprint) : ""}\n${services.length === 0 ? "prepare_state() { :; }\n" : state}\ninstall_services() {\n${user}${parts.join("")}\n :\n}\n`;
255
+ };
256
+ //#endregion
257
+ //#region src/cloud-setup-schema.ts
258
+ const cloudServices = ["postgres", "valkey"];
259
+ const cloudInstallCommand = "bun install --frozen-lockfile";
260
+ const cloudService = (value) => cloudServices.some((service) => value === service);
261
+ /** Parse an explicit declaration without assuming services for undeclared repositories. */
262
+ const parseCloudSetup = (input) => {
263
+ if (input === void 0) return void 0;
264
+ if (typeof input !== "object" || input === null || Array.isArray(input)) throw new Error("cloud must be an object");
265
+ for (const key of Object.keys(input)) if (key !== "services" && key !== "install" && key !== "envFile") throw new Error(`unsupported cloud key: ${key}`);
266
+ const selected = "services" in input ? input.services : [];
267
+ if (!Array.isArray(selected)) throw new Error("cloud.services must be an array when provided");
268
+ if (!("install" in input) || input.install !== "bun install --frozen-lockfile") throw new Error(`cloud.install must be exactly ${cloudInstallCommand}`);
269
+ const seen = /* @__PURE__ */ new Set();
270
+ const services = Array.from(selected, (service) => {
271
+ if (!cloudService(service)) throw new Error("cloud.services contains an unsupported service");
272
+ if (seen.has(service)) throw new Error(`cloud.services contains duplicate service: ${service}`);
273
+ seen.add(service);
274
+ return service;
275
+ });
276
+ if (services.length === 0) {
277
+ if ("envFile" in input) throw new Error("cloud.envFile is not allowed without services");
278
+ return {
279
+ services,
280
+ install: cloudInstallCommand
281
+ };
282
+ }
283
+ if (!("envFile" in input) || typeof input.envFile !== "string") throw new Error("cloud.envFile must be a canonical repository-relative path");
284
+ const envFile = input.envFile;
285
+ if (envFile.startsWith("/") || /^[A-Za-z]:/.test(envFile) || envFile.includes("\\") || /\p{Cc}/u.test(envFile) || envFile.split("/").some((part) => part === "" || part === "." || part === "..")) throw new Error("cloud.envFile must be a canonical repository-relative path");
286
+ return {
287
+ services,
288
+ install: cloudInstallCommand,
289
+ envFile
290
+ };
291
+ };
292
+ //#endregion
293
+ //#region src/cloud-setup-services.ts
294
+ const common = String.raw`
295
+ service_file() {
296
+ local file="$1" owner="$2"
297
+ [[ ! -L "$file" ]] || fail "Service state must not contain symbolic links"
298
+ if [[ -e "$file" ]]; then
299
+ [[ -f "$file" && "$(stat -c '%u:%a' "$file")" == "$owner:600" ]] || fail "Invalid service state file"
300
+ fi
301
+ }
302
+ service_directory() {
303
+ local directory="$1"
304
+ [[ ! -L "$directory" ]] || fail "Service directory must not be a symbolic link"
305
+ if [[ -e "$directory" ]]; then
306
+ [[ -d "$directory" && "$(stat -c '%u:%a' "$directory")" == "$SERVICE_UID:700" ]] || fail "Invalid service directory"
307
+ else
308
+ root install -d -m 700 -o stll-cloud -g stll-cloud "$directory"
309
+ fi
310
+ }
311
+ service_password() {
312
+ local file="$1"
313
+ service_file "$file" 0
314
+ if [[ ! -e "$file" ]]; then
315
+ (umask 077; openssl rand -hex 32 > "$file")
316
+ root chown root:root "$file"
317
+ fi
318
+ [[ "$(cat "$file")" =~ ^[0-9a-f]{64}$ ]] || fail "Invalid service credential state"
319
+ }
320
+ service_process_matches() {
321
+ local pid="$1" executable="$2" owner actual expected
322
+ [[ "$pid" =~ ^[1-9][0-9]*$ && -d "/proc/$pid" ]] || return 1
323
+ owner="$(stat -c '%u' "/proc/$pid" 2>/dev/null)" || return 2
324
+ [[ "$owner" =~ ^[0-9]+$ ]] || return 2
325
+ [[ "$owner" == "$SERVICE_UID" ]] || return 1
326
+ actual="$(readlink -f "/proc/$pid/exe" 2>/dev/null)" || return 2
327
+ expected="$(readlink -f "$executable")" || return 2
328
+ [[ -n "$actual" && -n "$expected" && "$actual" != *' (deleted)' ]] || return 2
329
+ [[ "$actual" == "$expected" ]]
330
+ }
331
+ service_process() {
332
+ service_process_matches "$1" "$2" || fail "Managed service process identity differs from the recorded state"
333
+ }
334
+ service_prune_pid() {
335
+ local file="$1" executable="$2" service="$3" pid identity
336
+ [[ -e "$file" ]] || return 0
337
+ pid="$(head -n 1 "$file")"
338
+ # PostgreSQL encodes a standalone backend with a negative PID.
339
+ if [[ "$service" == postgres ]]; then pid="$(printf '%s' "$pid" | sed 's/^-//')"; fi
340
+ if service_process_matches "$pid" "$executable"; then return 0; else identity=$?; fi
341
+ [[ "$identity" != 2 ]] || fail "Cannot verify recorded live service process; retaining its PID file"
342
+ # Let PostgreSQL check orphan shared memory before removing a dead-PID lock.
343
+ if [[ "$service" == postgres && "$pid" =~ ^[1-9][0-9]*$ && ! -d "/proc/$pid" ]]; then return 0; fi
344
+ if [[ "$service" == postgres ]]; then
345
+ [[ "$pid" =~ ^[1-9][0-9]*$ && "$SERVICE_UID" != 0 ]] || fail "Cannot safely recover PostgreSQL PID state"
346
+ # The root controller is not our nonroot postgres. Preserve the remaining
347
+ # lock rows so PostgreSQL's EPERM/ancestor path still checks orphan memory.
348
+ root sed -i "1s/.*/$$/" "$file"
349
+ return 0
350
+ fi
351
+ # Restored state can name an unrelated live process; never signal that PID.
352
+ root rm -- "$file"
353
+ }
354
+ `;
355
+ const postgresStart = String.raw`
356
+ local pg_bin='/usr/lib/postgresql/@POSTGRES@/bin' pg_data="$STATE/postgres" pg_password pg_details pg_pid pg_version
357
+ service_password "$STATE/postgres.password"
358
+ pg_password="$(cat "$STATE/postgres.password")"
359
+ service_directory "$pg_data"
360
+ for file in PG_VERSION postmaster.pid postgresql.conf pg_hba.conf pg_ident.conf postgresql.auto.conf; do
361
+ service_file "$pg_data/$file" "$SERVICE_UID"
362
+ done
363
+ if [[ ! -e "$pg_data/PG_VERSION" ]]; then
364
+ [[ -z "$(ls -A "$pg_data")" ]] || fail "PostgreSQL data directory is not initialized"
365
+ service_file "$STATE/postgres.init-password" "$SERVICE_UID"
366
+ (umask 077; printf '%s\n' "$pg_password" > "$STATE/postgres.init-password")
367
+ root chown stll-cloud:stll-cloud "$STATE/postgres.init-password"
368
+ service_run env PGHOST=127.0.0.1 PGHOSTADDR=127.0.0.1 "$pg_bin/initdb" -D "$pg_data" --username=stll_cloud --auth-host=scram-sha-256 --auth-local=scram-sha-256 --pwfile="$STATE/postgres.init-password" --encoding=UTF8 --locale=C --set=unix_socket_directories= >/dev/null || fail "PostgreSQL initialization failed"
369
+ root rm "$STATE/postgres.init-password"
370
+ fi
371
+ [[ -f "$pg_data/PG_VERSION" && "$(cat "$pg_data/PG_VERSION")" == '@POSTGRES@' ]] || fail "PostgreSQL data has a different major version"
372
+ service_prune_pid "$pg_data/postmaster.pid" "$pg_bin/postgres" postgres
373
+ if ! service_run env PGHOST=127.0.0.1 PGHOSTADDR=127.0.0.1 "$pg_bin/pg_ctl" -D "$pg_data" status >/dev/null 2>&1; then
374
+ service_run env PGHOST=127.0.0.1 PGHOSTADDR=127.0.0.1 "$pg_bin/pg_ctl" -D "$pg_data" -l "$pg_data/cloud.log" -o "-h 127.0.0.1 -p 55432 -c unix_socket_directories=" -t 10 -w start >/dev/null || fail "PostgreSQL start failed; port 55432 must be available"
375
+ fi
376
+ pg_details="$(env -u PGSERVICE -u PGSERVICEFILE -u PGOPTIONS PGHOSTADDR=127.0.0.1 PGPASSWORD="$pg_password" PGCONNECT_TIMEOUT=2 "$pg_bin/psql" -X -h 127.0.0.1 -p 55432 -U stll_cloud -d postgres -At -v ON_ERROR_STOP=1 -c "SELECT current_setting('data_directory'), current_setting('listen_addresses'), current_setting('port'), current_setting('server_version_num'), current_setting('unix_socket_directories')")" || fail "PostgreSQL authenticated readiness failed"
377
+ pg_version="$(printf '%s\n' "$pg_details" | cut -d '|' -f4)"
378
+ [[ "$pg_details" == "$pg_data|127.0.0.1|55432|$pg_version|" && "$pg_version" =~ ^[0-9]{6}$ ]] || fail "PostgreSQL endpoint does not match managed data and listener"
379
+ (( pg_version / 10000 == @POSTGRES@ )) || fail "PostgreSQL endpoint has a different major version"
380
+ pg_pid="$(head -n 1 "$pg_data/postmaster.pid")"
381
+ service_process "$pg_pid" "$pg_bin/postgres"
382
+ [[ "$(env -u PGSERVICE -u PGSERVICEFILE -u PGOPTIONS PGHOSTADDR=127.0.0.1 PGPASSWORD="$pg_password" PGCONNECT_TIMEOUT=2 "$pg_bin/psql" -X -h 127.0.0.1 -p 55432 -U stll_cloud -d postgres -At -v ON_ERROR_STOP=1 -c "SELECT count(*) FROM pg_database WHERE datname = 'cloud_test'")" == 1 ]] || env -u PGSERVICE -u PGSERVICEFILE -u PGOPTIONS PGHOSTADDR=127.0.0.1 PGPASSWORD="$pg_password" PGCONNECT_TIMEOUT=2 "$pg_bin/createdb" -h 127.0.0.1 -p 55432 -U stll_cloud cloud_test || fail "PostgreSQL test database creation failed"
383
+ DATABASE_URL="postgresql://stll_cloud:$pg_password@127.0.0.1:55432/cloud_test"
384
+ `;
385
+ const valkeyStart = String.raw`
386
+ local vk_data="$STATE/valkey" vk_password vk_config vk_info vk_pid vk_expected ready=0
387
+ service_password "$STATE/valkey.password"
388
+ vk_password="$(cat "$STATE/valkey.password")"
389
+ service_directory "$vk_data"
390
+ vk_config="$vk_data/valkey.conf"
391
+ service_file "$vk_config" "$SERVICE_UID"
392
+ for file in valkey.pid valkey.log dump.rdb; do
393
+ [[ ! -L "$vk_data/$file" ]] || fail "Valkey state must not contain symbolic links"
394
+ if [[ -e "$vk_data/$file" ]]; then
395
+ [[ -f "$vk_data/$file" && "$(stat -c '%u' "$vk_data/$file")" == "$SERVICE_UID" ]] || fail "Invalid Valkey state file"
396
+ fi
397
+ done
398
+ vk_expected="$(printf '%s\n' 'bind 127.0.0.1' 'port 56379' 'protected-mode yes' 'daemonize yes' "requirepass $vk_password" "dir $vk_data" "pidfile $vk_data/valkey.pid" "logfile $vk_data/valkey.log" 'save ""' 'appendonly no')"
399
+ if [[ ! -e "$vk_config" ]]; then
400
+ (umask 077; printf '%s\n' "$vk_expected" > "$vk_config")
401
+ root chown stll-cloud:stll-cloud "$vk_config"
402
+ fi
403
+ [[ "$(cat "$vk_config")" == "$vk_expected" ]] || fail "Valkey configuration differs from managed settings"
404
+ service_prune_pid "$vk_data/valkey.pid" "$(command -v valkey-server)" valkey
405
+ if [[ ! -e "$vk_data/valkey.pid" ]]; then
406
+ service_run valkey-server "$vk_config" || fail "Valkey start failed; port 56379 must be available"
407
+ fi
408
+ for ((attempt=0; attempt<20; attempt++)); do
409
+ if vk_info="$(VALKEYCLI_AUTH="$vk_password" timeout 0.2 valkey-cli -h 127.0.0.1 -p 56379 --raw INFO server 2>/dev/null)"; then
410
+ if [[ "$vk_info" == *$'valkey_version:@VALKEY@\r\n'* && "$vk_info" == *"config_file:$vk_config"$'\r\n'* ]]; then ready=1; break; fi
411
+ fi
412
+ sleep 0.25
413
+ done
414
+ [[ "$ready" == 1 ]] || fail "Valkey authenticated readiness or managed version/configuration check failed"
415
+ [[ -f "$vk_data/valkey.pid" ]] || fail "Valkey managed process identifier is unavailable"
416
+ vk_pid="$(cat "$vk_data/valkey.pid")"
417
+ service_process "$vk_pid" "$(command -v valkey-server)"
418
+ [[ "$vk_info" == *"process_id:$vk_pid"$'\r\n'* ]] || fail "Valkey endpoint has a different process identifier"
419
+ [[ "$(VALKEYCLI_AUTH="$vk_password" timeout 2 valkey-cli -h 127.0.0.1 -p 56379 --raw CONFIG GET bind)" == $'bind\n127.0.0.1' ]] || fail "Valkey endpoint must bind only loopback"
420
+ [[ "$(VALKEYCLI_AUTH="$vk_password" timeout 2 valkey-cli -h 127.0.0.1 -p 56379 --raw CONFIG GET dir)" == "$(printf 'dir\n%s' "$vk_data")" ]] || fail "Valkey endpoint has a different data directory"
421
+ REDIS_URL="redis://:$vk_password@127.0.0.1:56379/0"
422
+ `;
423
+ /** Render only declared services; installation and environment-file ownership stay with the caller. */
424
+ const renderCloudServiceStart = ({ services, postgres, valkey }) => {
425
+ if (!/^[1-9][0-9]*$/.test(postgres) || !/^\d+\.\d+\.\d+$/.test(valkey)) throw new Error("Invalid cloud service runtime policy");
426
+ if (services.length === 0) return "start_services() { :; }\n";
427
+ const bodies = services.map((service) => {
428
+ switch (service) {
429
+ case "postgres": return postgresStart.replaceAll("@POSTGRES@", postgres);
430
+ case "valkey": return valkeyStart.replaceAll("@VALKEY@", valkey);
431
+ default: throw new Error(`Unsupported cloud service: ${String(service)}`);
432
+ }
433
+ });
434
+ return `${common}\nstart_services() {\n local SERVICE_UID file attempt\n SERVICE_UID="$(id -u stll-cloud)"\n [[ -d "$STATE" && ! -L "$STATE" && "$(stat -c '%u' "$STATE")" == 0 ]] || fail "Invalid cloud service state directory"\n${bodies.join("")}\n}\n`;
435
+ };
436
+ //#endregion
437
+ //#region src/toolchain-node.ts
438
+ const numericComponent = "(?:0|[1-9]\\d*)";
439
+ const policyPattern = new RegExp(`^${numericComponent}\\.x$`);
440
+ const selectorPattern = new RegExp(`^(${numericComponent})(?:\\.(?:x|${numericComponent}(?:\\.${numericComponent})?))?$`);
441
+ /** A Node policy selects the complete stable release series of one major. */
442
+ const nodePolicyValid = (value) => policyPattern.test(value) && validRange(value) !== null;
443
+ /** Static selectors may narrow that series, but cannot float across majors. */
444
+ const nodeSelectorMatches = (value, policy) => {
445
+ if (!nodePolicyValid(policy) || typeof value !== "string") return false;
446
+ const major = selectorPattern.exec(value)?.[1];
447
+ return major !== void 0 && major === policy.slice(0, -2) && validRange(value) !== null;
448
+ };
449
+ /** Engines must support every stable release in the approved major series. */
450
+ const nodeSupportRangeMatches = (policy, range) => {
451
+ if (!nodePolicyValid(policy)) return false;
452
+ try {
453
+ return subset(policy, range);
454
+ } catch {
455
+ return false;
456
+ }
457
+ };
458
+ //#endregion
459
+ //#region src/cloud-setup.ts
460
+ const cloudSetupPath = ".agents/cloud-setup.sh";
461
+ const cloudEnvironmentMarker = "# Generated by stll-cloud-setup; disposable local test services.";
462
+ const script = String.raw`#!/usr/bin/env bash
463
+ # Generated by stll-cloud-setup. Regenerate with stll-cloud-setup.
464
+ # Networked installation and offline service startup are separate lifecycle steps.
465
+ # Cached filesystems may not preserve processes; run start at every session start.
466
+ # Supported platform: Ubuntu 24.04 (noble), amd64/arm64, root or passwordless sudo.
467
+ set -euo pipefail
468
+
469
+ usage() { echo 'Usage: cloud-setup.sh install|start' >&2; exit 2; }
470
+ [[ "$#" == 1 ]] || usage
471
+ case "$1" in install|start) MODE="$1" ;; *) usage ;; esac
472
+ fail() { echo "cloud-setup: $*" >&2; exit 1; }
473
+ trap 'echo "cloud-setup: command failed at line $LINENO" >&2' ERR
474
+ [[ -f /etc/os-release ]] || fail 'Ubuntu 24.04 is required'
475
+ . /etc/os-release
476
+ [[ "$ID" == ubuntu && "$VERSION_ID" == 24.04 && "$VERSION_CODENAME" == noble ]] || fail 'Only Ubuntu 24.04 (noble) is supported'
477
+ SCRIPT="$(realpath -- "$0")"
478
+ REPO_ROOT="$(cd -- "$(dirname -- "$SCRIPT")/.." && pwd -P)"
479
+ [[ "$SCRIPT" == "$REPO_ROOT/.agents/cloud-setup.sh" ]] || fail 'Run the generated repository script'
480
+ if [[ "$(id -u)" != 0 ]]; then
481
+ command -v sudo >/dev/null && sudo -n true || fail 'Root or passwordless sudo is required'
482
+ exec sudo -n -- bash "$SCRIPT" "$MODE"
483
+ fi
484
+ OUTPUT_UID="$(printenv SUDO_UID || id -u)"
485
+ [[ "$OUTPUT_UID" =~ ^[0-9]+$ ]] || fail 'Invalid invoking user'
486
+ OUTPUT_GID="$(getent passwd "$OUTPUT_UID" | cut -d: -f4)"
487
+ [[ "$OUTPUT_GID" =~ ^[0-9]+$ ]] || fail 'Invoking user is unavailable'
488
+ OUTPUT_USER="$(getent passwd "$OUTPUT_UID" | cut -d: -f1)"
489
+ OUTPUT_HOME="$(getent passwd "$OUTPUT_UID" | cut -d: -f6)"
490
+ [[ -n "$OUTPUT_USER" && "$OUTPUT_HOME" == /* && -d "$OUTPUT_HOME" ]] || fail 'Invoking user requires an existing home directory'
491
+ root() { "$@"; }
492
+ service_run() { runuser -u stll-cloud -- "$@" 9>&-; }
493
+ BUN_VERSION='@BUN@'
494
+ NODE_VERSION='@NODE@'
495
+ NODE_DIR="/opt/stll-cloud/node/$NODE_VERSION"
496
+ BUN_DIR="/opt/stll-cloud/bun/$BUN_VERSION"
497
+ export PATH="$NODE_DIR/bin:$BUN_DIR:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
498
+ BUN_CACHE="/var/cache/stll-cloud/bun/$OUTPUT_UID"
499
+ @ENV_ASSIGNMENTS@
500
+ [[ -f "$REPO_ROOT/.node-version" && ! -L "$REPO_ROOT/.node-version" ]] || fail 'A regular root .node-version is required'
501
+ [[ "$(awk 'NF { if (++count > 1 || NF != 1) exit 1; value=$1 } END { if (count != 1) exit 1; print value }' "$REPO_ROOT/.node-version")" == "$NODE_VERSION" ]] || fail 'Node selector changed; regenerate cloud-setup.sh'
502
+ cd -- "$REPO_ROOT"
503
+ case "$(uname -m)" in
504
+ x86_64) ARCH=amd64; NODE_ARCH=x64; BUN_ARCH=x64 ;;
505
+ aarch64|arm64) ARCH=arm64; NODE_ARCH=arm64; BUN_ARCH=aarch64 ;;
506
+ *) fail 'Only amd64 and arm64 are supported' ;;
507
+ esac
508
+
509
+ runtime_ready() {
510
+ [[ -x "$NODE_DIR/bin/node" && "$($NODE_DIR/bin/node --version)" == "v$NODE_VERSION" ]] || fail 'Pinned Node is unavailable; run install'
511
+ [[ -x "$BUN_DIR/bun" && "$($BUN_DIR/bun --version)" == "$BUN_VERSION" ]] || fail 'Pinned Bun is unavailable; run install'
512
+ }
513
+ install_runtimes() {
514
+ if ! command -v curl >/dev/null || ! command -v unzip >/dev/null || ! command -v xz >/dev/null || ! command -v openssl >/dev/null || ! command -v gpg >/dev/null; then
515
+ apt-get update
516
+ DEBIAN_FRONTEND=noninteractive apt_install ca-certificates curl unzip xz-utils openssl gnupg util-linux
517
+ fi
518
+ local scratch node_archive bun_archive bun_folder
519
+ scratch="$(mktemp -d)"
520
+ trap 'rm -rf -- "$scratch"' RETURN
521
+ if [[ ! -x "$NODE_DIR/bin/node" ]] || [[ "$($NODE_DIR/bin/node --version)" != "v$NODE_VERSION" ]]; then
522
+ node_archive="node-v$NODE_VERSION-linux-$NODE_ARCH.tar.xz"
523
+ curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 "https://nodejs.org/dist/v$NODE_VERSION/$node_archive" -o "$scratch/$node_archive"
524
+ curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt" -o "$scratch/node-sums"
525
+ (cd "$scratch"; awk -v name="$node_archive" '$2 == name { print; count++ } END { if (count != 1) exit 1 }' node-sums | sha256sum --strict -c -)
526
+ install -d -m 755 "$NODE_DIR"
527
+ tar -xJf "$scratch/$node_archive" --strip-components=1 -C "$NODE_DIR"
528
+ fi
529
+ if [[ ! -x "$BUN_DIR/bun" ]] || [[ "$($BUN_DIR/bun --version)" != "$BUN_VERSION" ]]; then
530
+ bun_folder="bun-linux-$BUN_ARCH"
531
+ bun_archive="$bun_folder.zip"
532
+ curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 "https://github.com/oven-sh/bun/releases/download/bun-v$BUN_VERSION/$bun_archive" -o "$scratch/$bun_archive"
533
+ curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 "https://github.com/oven-sh/bun/releases/download/bun-v$BUN_VERSION/SHASUMS256.txt" -o "$scratch/bun-sums"
534
+ (cd "$scratch"; awk -v name="$bun_archive" '$2 == name || $2 == "*" name { print $1 " " name; count++ } END { if (count != 1) exit 1 }' bun-sums | sha256sum --strict -c -)
535
+ unzip -q "$scratch/$bun_archive" -d "$scratch/bun"
536
+ install -d -m 755 "$BUN_DIR"
537
+ install -m 755 "$scratch/bun/$bun_folder/bun" "$BUN_DIR/bun"
538
+ fi
539
+ runtime_ready
540
+ ln -sfnT -- "$NODE_DIR/bin/node" /usr/local/bin/node
541
+ ln -sfnT -- "$NODE_DIR/bin/npm" /usr/local/bin/npm
542
+ ln -sfnT -- "$NODE_DIR/bin/npx" /usr/local/bin/npx
543
+ ln -sfnT -- "$BUN_DIR/bun" /usr/local/bin/bun
544
+ rm -rf -- "$scratch"
545
+ trap - RETURN
546
+ }
547
+
548
+ dependency_install() {
549
+ local directory
550
+ for directory in /var/cache/stll-cloud /var/cache/stll-cloud/bun; do
551
+ [[ ! -L "$directory" ]] || fail 'Dependency cache parent must not be a symlink'
552
+ if [[ -e "$directory" ]]; then
553
+ [[ -d "$directory" && "$(stat -c '%u' "$directory")" == 0 ]] || fail 'Dependency cache parent must be root-owned'
554
+ fi
555
+ install -d -m 755 -o root -g root "$directory"
556
+ done
557
+ [[ ! -L "$BUN_CACHE" ]] || fail 'Dependency cache must not be a symlink'
558
+ if [[ -e "$BUN_CACHE" ]]; then
559
+ [[ -d "$BUN_CACHE" && "$(stat -c '%u' "$BUN_CACHE")" == "$OUTPUT_UID" ]] || fail 'Dependency cache ownership differs from the invoking user'
560
+ fi
561
+ install -d -m 700 -o "$OUTPUT_UID" -g "$OUTPUT_GID" "$BUN_CACHE"
562
+ runuser -u "$OUTPUT_USER" -- env HOME="$OUTPUT_HOME" BUN_INSTALL_CACHE_DIR="$BUN_CACHE" PATH="$PATH" "$BUN_DIR/bun" install --frozen-lockfile
563
+ }
564
+
565
+ @SERVICE_INSTALL@
566
+ @SERVICE_START@
567
+
568
+ @ENV_FUNCTIONS@
569
+
570
+ if [[ "$MODE" == install ]]; then
571
+ install_runtimes
572
+ install_services
573
+ dependency_install
574
+ exit 0
575
+ fi
576
+ runtime_ready
577
+ @ENV_VALIDATE@
578
+ prepare_state
579
+ start_services
580
+ @ENV_WRITE@
581
+ `;
582
+ const environmentFunctions = String.raw`
583
+ validate_environment() {
584
+ local part target="$REPO_ROOT" env_path="$REPO_ROOT/$ENV_FILE" first_line temporary
585
+ while IFS= read -r part; do
586
+ target="$target/$part"
587
+ [[ ! -L "$target" ]] || fail 'Environment path must not contain symbolic links'
588
+ done < <(printf '%s\n' "$ENV_FILE" | tr '/' '\n')
589
+ [[ -d "$(dirname -- "$env_path")" ]] || fail 'Environment parent directory is missing'
590
+ if [[ -e "$env_path" ]]; then
591
+ [[ -f "$env_path" ]] || fail 'Environment target must be a regular file'
592
+ IFS= read -r first_line < "$env_path" || fail 'Refusing to overwrite an unowned environment file'
593
+ [[ "$first_line" == "$ENV_MARKER" ]] || fail 'Refusing to overwrite an unowned environment file'
594
+ fi
595
+ }
596
+ write_environment() {
597
+ validate_environment
598
+ local env_path="$REPO_ROOT/$ENV_FILE" temporary
599
+ temporary="$(mktemp "$(dirname -- "$env_path")/.stll-cloud-env.XXXXXX")"
600
+ chmod 600 "$temporary"
601
+ {
602
+ printf '%s\n' "$ENV_MARKER" 'NODE_ENV=test'
603
+ if [[ -n "$DATABASE_URL" ]]; then printf 'DATABASE_URL=%s\n' "$DATABASE_URL"; fi
604
+ if [[ -n "$REDIS_URL" ]]; then printf 'REDIS_URL=%s\n' "$REDIS_URL"; fi
605
+ } > "$temporary"
606
+ chown "$OUTPUT_UID:$OUTPUT_GID" "$temporary"
607
+ mv -f -- "$temporary" "$env_path"
608
+ }
609
+ `;
610
+ const shellQuote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
611
+ const generateCloudSetup = ({ policy, cloud, nodeVersion }) => {
612
+ const selected = parseCloudSetup(cloud);
613
+ if (selected === void 0) throw new Error("cloud declaration is required");
614
+ const exactNode = nodeVersion.trim();
615
+ if (valid(exactNode) !== exactNode || !nodeSelectorMatches(exactNode, policy.node)) throw new Error("cloud requires a tracked exact .node-version within the shared Node series");
616
+ const services = cloudServices.filter((service) => selected.services.includes(service));
617
+ const replacements = /* @__PURE__ */ new Map([
618
+ ["@BUN@", policy.bun],
619
+ ["@NODE@", exactNode],
620
+ ["@ENV_ASSIGNMENTS@", selected.envFile === void 0 ? "" : `ENV_FILE=${shellQuote(selected.envFile)}\nENV_MARKER=${shellQuote(cloudEnvironmentMarker)}`],
621
+ ["@ENV_FUNCTIONS@", selected.envFile === void 0 ? "" : environmentFunctions],
622
+ ["@ENV_VALIDATE@", selected.envFile === void 0 ? "" : "DATABASE_URL=''\nREDIS_URL=''\nvalidate_environment"],
623
+ ["@ENV_WRITE@", selected.envFile === void 0 ? "" : "write_environment"],
624
+ ["@SERVICE_INSTALL@", renderCloudServiceInstall({
625
+ services,
626
+ postgres: policy.postgres,
627
+ valkey: policy.valkey,
628
+ valkeyArtifacts: policy.valkeyArtifacts
629
+ })],
630
+ ["@SERVICE_START@", renderCloudServiceStart({
631
+ services,
632
+ postgres: policy.postgres,
633
+ valkey: policy.valkey
634
+ })]
635
+ ]);
636
+ return script.replace(/@[A-Z0-9_]+@/g, (token) => {
637
+ const value = replacements.get(token);
638
+ if (value === void 0) throw new Error(`Missing cloud setup field: ${token}`);
639
+ return value;
640
+ });
641
+ };
642
+ //#endregion
643
+ //#region src/toolchain-workspaces.ts
644
+ /** Match manifest directories with the same positive and negative workspace patterns. */
645
+ const workspaceContains = ({ directory, file, patterns, rootMembership }) => {
646
+ const relative = path.posix.relative(directory, path.posix.dirname(file));
647
+ if (relative === "" && rootMembership === "implicit") return true;
648
+ if (relative === ".." || relative.startsWith("../")) return false;
649
+ if (!Array.isArray(patterns)) return false;
650
+ const normalize = (pattern) => path.posix.normalize(pattern);
651
+ const selected = patterns.filter((value) => typeof value === "string");
652
+ return picomatch(selected.filter((pattern) => !pattern.startsWith("!")).map(normalize))(relative) && !picomatch(selected.filter((pattern) => pattern.startsWith("!")).map((pattern) => normalize(pattern.slice(1))))(relative);
653
+ };
654
+ //#endregion
655
+ //#region src/toolchain-container-inputs.ts
656
+ const record = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
657
+ /** Filename class recognized by Dependabot's Docker Compose fetcher. */
658
+ const isComposeDefinitionPath = (file) => /^\.?(?:docker-)?compose(?:-[\w]+)?(?:\.[\w-]+)?\.ya?ml$/i.test(file.split("/").at(-1) ?? "");
659
+ /** Candidate YAML files are classified by their document shape after reading. */
660
+ const isKubernetesDefinitionPath = (file) => /^[^.].*\.ya?ml$/i.test(file.split("/").at(-1) ?? "");
661
+ const kubernetesPodSpecPaths = {
662
+ Pod: ["spec"],
663
+ Deployment: [
664
+ "spec",
665
+ "template",
666
+ "spec"
667
+ ],
668
+ DaemonSet: [
669
+ "spec",
670
+ "template",
671
+ "spec"
672
+ ],
673
+ StatefulSet: [
674
+ "spec",
675
+ "template",
676
+ "spec"
677
+ ],
678
+ ReplicaSet: [
679
+ "spec",
680
+ "template",
681
+ "spec"
682
+ ],
683
+ ReplicationController: [
684
+ "spec",
685
+ "template",
686
+ "spec"
687
+ ],
688
+ Job: [
689
+ "spec",
690
+ "template",
691
+ "spec"
692
+ ],
693
+ CronJob: [
694
+ "spec",
695
+ "jobTemplate",
696
+ "spec",
697
+ "template",
698
+ "spec"
699
+ ]
700
+ };
701
+ /** Shared semantic image enumeration for runtime checks and update-root generation. */
702
+ const containerDocumentImages = (document, file) => {
703
+ if (!record(document)) return void 0;
704
+ const services = document["services"];
705
+ if (isComposeDefinitionPath(file) && record(services)) {
706
+ const images = [];
707
+ for (const [name, service] of Object.entries(services)) if (record(service) && service["image"] !== void 0) images.push({
708
+ image: service["image"],
709
+ path: [
710
+ "services",
711
+ name,
712
+ "image"
713
+ ]
714
+ });
715
+ return {
716
+ ecosystem: "docker-compose",
717
+ images
718
+ };
719
+ }
720
+ if (typeof document["apiVersion"] !== "string" || typeof document["kind"] !== "string") return void 0;
721
+ const images = [];
722
+ const visited = /* @__PURE__ */ new Set();
723
+ const walkResource = (resource, prefix) => {
724
+ if (visited.has(resource)) return;
725
+ visited.add(resource);
726
+ if (typeof resource["apiVersion"] !== "string" || typeof resource["kind"] !== "string") return;
727
+ if (resource["kind"] === "List" && Array.isArray(resource["items"])) {
728
+ for (const [index, item] of resource["items"].entries()) if (record(item)) walkResource(item, [
729
+ ...prefix,
730
+ "items",
731
+ index
732
+ ]);
733
+ return;
734
+ }
735
+ const specPath = Object.entries(kubernetesPodSpecPaths).find(([kind]) => kind === resource["kind"])?.[1];
736
+ if (specPath === void 0) return;
737
+ let spec = resource;
738
+ for (const key of specPath) spec = record(spec) ? spec[key] : void 0;
739
+ if (!record(spec)) return;
740
+ for (const key of [
741
+ "containers",
742
+ "initContainers",
743
+ "ephemeralContainers"
744
+ ]) {
745
+ const containers = spec[key];
746
+ if (!Array.isArray(containers)) continue;
747
+ for (const [index, container] of containers.entries()) if (record(container) && container["image"] !== void 0) images.push({
748
+ image: container["image"],
749
+ path: [
750
+ ...prefix,
751
+ ...specPath,
752
+ key,
753
+ index,
754
+ "image"
755
+ ]
756
+ });
757
+ }
758
+ };
759
+ walkResource(document, []);
760
+ return images.length === 0 ? void 0 : {
761
+ ecosystem: "docker",
762
+ images
763
+ };
764
+ };
765
+ //#endregion
766
+ //#region src/toolchain-inputs.ts
767
+ const excludedInputPath = (file) => file === "" || file.split("/").some((part) => part === "node_modules" || part === "vendor");
768
+ const javascriptDependencyLockfiles = [
769
+ "bun.lock",
770
+ "package-lock.json",
771
+ "yarn.lock",
772
+ "pnpm-lock.yaml"
773
+ ];
774
+ /** Match project, environment, local, and fragment filenames recognized by mise. */
775
+ const isMiseConfigPath = (file) => !excludedInputPath(file) && (/(?:^|\/)\.?mise(?:\.[^./][^/]*)?\.toml$/.test(file) || /(?:^|\/)(?:\.?mise|\.config\/mise)\/config(?:\.[^./][^/]*)?\.toml$/.test(file) || /(?:^|\/)(?:\.?mise|\.config\/mise)\/conf\.d\/[^./][^/]*\.toml$/.test(file));
776
+ /** GitHub action metadata may live at the repository root or in any directory. */
777
+ const githubAutomationFileKind = (file) => {
778
+ if (file === "") return void 0;
779
+ if (/^\.github\/workflows\/[^/]+\.ya?ml$/.test(file)) return "workflow";
780
+ if (/(?:^|\/)action\.ya?ml$/.test(file)) return "action";
781
+ };
782
+ /** Dependabot updates workflows and repository-root action metadata. */
783
+ const isDependabotGithubActionsPath = (file) => {
784
+ const kind = githubAutomationFileKind(file);
785
+ return kind === "workflow" || kind === "action" && !file.includes("/");
786
+ };
787
+ /** Use one filename discriminator for reader selection and Python ecosystem decisions. */
788
+ const pythonDependencyManifestKind = (file) => {
789
+ if (excludedInputPath(file)) return void 0;
790
+ const name = file.split("/").at(-1);
791
+ if (name === "pyproject.toml") return "project";
792
+ if (name === "Pipfile") return "pipfile";
793
+ if (name === "Pipfile.lock") return "pipfile-lock";
794
+ if (name === "setup.py" || name === "setup.cfg") return "setup";
795
+ if (name !== void 0 && /^requirements[^/]*\.(?:txt|in)$/.test(name)) return "requirements";
796
+ };
797
+ const isPythonDependencyManifest = (file) => pythonDependencyManifestKind(file) !== void 0;
798
+ /** Docker definition names use dot-delimited variants, excluding source and documentation. */
799
+ const isDockerDefinitionPath = (file) => {
800
+ if (excludedInputPath(file)) return false;
801
+ const name = file.split("/").at(-1) ?? "";
802
+ if (/\.(?:[cm]?[jt]sx?|mdx?|rst|txt|html?|py|rs|go|java|sh|c|cc|cpp|cxx|h|hpp|cs|rb|php|swift|kt|css|scss)$/i.test(name)) return false;
803
+ return /^(?:[^./]+\.)*(?:dockerfile|containerfile)(?:\.[^./]+)*$/i.test(name);
804
+ };
805
+ /** Lockfiles establish ecosystem presence without reading their dependency graphs. */
806
+ const toolchainInputKind = (file) => {
807
+ if (file === ".agents/cloud-setup.sh") return "config";
808
+ if (githubAutomationFileKind(file) !== void 0) return "config";
809
+ if (excludedInputPath(file)) return void 0;
810
+ if (pythonDependencyManifestKind(file) === "pipfile-lock" || javascriptDependencyLockfiles.some((name) => file.split("/").at(-1) === name) || /(?:^|\/)uv\.lock$/.test(file)) return "presence";
811
+ if (githubAutomationFileKind(file) !== void 0 || isMiseConfigPath(file) || isPythonDependencyManifest(file) || isDockerDefinitionPath(file) || isComposeDefinitionPath(file) || isKubernetesDefinitionPath(file) || /(?:^|\/)(?:package\.json|consumer-compat\.json|pnpm-workspace\.yaml|stll-toolchain\.json|\.bun-version|\.node-version|\.nvmrc|\.python-version|rust-toolchain(?:\.toml)?|\.tool-versions|\.?mise\.toml|uv\.toml|\.uv\.toml|\.github\/dependabot\.ya?ml|Cargo\.toml)$/.test(file)) return "config";
812
+ };
813
+ //#endregion
814
+ export { nodeSupportRangeMatches as _, isPythonDependencyManifest as a, toolchainInputKind as c, isKubernetesDefinitionPath as d, workspaceContains as f, nodeSelectorMatches as g, nodePolicyValid as h, isMiseConfigPath as i, containerDocumentImages as l, generateCloudSetup as m, isDependabotGithubActionsPath as n, javascriptDependencyLockfiles as o, cloudSetupPath as p, isDockerDefinitionPath as r, pythonDependencyManifestKind as s, githubAutomationFileKind as t, isComposeDefinitionPath as u, parseCloudSetup as v };
815
+
816
+ //# sourceMappingURL=toolchain-inputs.mjs.map