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