@stll/oxlint-config 1.3.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +341 -28
  2. package/dist/cloud-setup-cli.cjs +20 -40
  3. package/dist/cloud-setup-cli.cjs.map +1 -1
  4. package/dist/cloud-setup-cli.mjs +21 -41
  5. package/dist/cloud-setup-cli.mjs.map +1 -1
  6. package/dist/consumer-compat-cli.cjs +52 -251
  7. package/dist/consumer-compat-cli.cjs.map +1 -1
  8. package/dist/consumer-compat-cli.mjs +7 -206
  9. package/dist/consumer-compat-cli.mjs.map +1 -1
  10. package/dist/consumer-node-support.cjs +497 -0
  11. package/dist/consumer-node-support.cjs.map +1 -0
  12. package/dist/consumer-node-support.mjs +381 -0
  13. package/dist/consumer-node-support.mjs.map +1 -0
  14. package/dist/dependabot-cli.cjs +91 -0
  15. package/dist/dependabot-cli.cjs.map +1 -0
  16. package/dist/dependabot-cli.d.cts +1 -0
  17. package/dist/dependabot-cli.d.mts +1 -0
  18. package/dist/dependabot-cli.mjs +91 -0
  19. package/dist/dependabot-cli.mjs.map +1 -0
  20. package/dist/dependabot.cjs +7 -0
  21. package/dist/dependabot.d.cts +118 -0
  22. package/dist/dependabot.d.mts +118 -0
  23. package/dist/dependabot.mjs +3 -0
  24. package/dist/generated-file.cjs +61 -0
  25. package/dist/generated-file.cjs.map +1 -0
  26. package/dist/generated-file.mjs +44 -0
  27. package/dist/generated-file.mjs.map +1 -0
  28. package/dist/git-tracked-output.cjs +2 -2
  29. package/dist/git-tracked-output.mjs +1 -1
  30. package/dist/jsonc.cjs +66 -0
  31. package/dist/jsonc.cjs.map +1 -0
  32. package/dist/jsonc.mjs +61 -0
  33. package/dist/jsonc.mjs.map +1 -0
  34. package/dist/publish-artifact-syntax.cjs +1309 -0
  35. package/dist/publish-artifact-syntax.cjs.map +1 -0
  36. package/dist/publish-artifact-syntax.mjs +1265 -0
  37. package/dist/publish-artifact-syntax.mjs.map +1 -0
  38. package/dist/publish-build-target-vite-dts-helper2.cjs +1 -1
  39. package/dist/publish-build-target-vite-dts-helper2.mjs +1 -1
  40. package/dist/publish-contract-cli.cjs +55 -14
  41. package/dist/publish-contract-cli.cjs.map +1 -1
  42. package/dist/publish-contract-cli.mjs +50 -9
  43. package/dist/publish-contract-cli.mjs.map +1 -1
  44. package/dist/quarantine-cli.cjs +66 -0
  45. package/dist/quarantine-cli.cjs.map +1 -0
  46. package/dist/quarantine-cli.d.cts +1 -0
  47. package/dist/quarantine-cli.d.mts +1 -0
  48. package/dist/quarantine-cli.mjs +66 -0
  49. package/dist/quarantine-cli.mjs.map +1 -0
  50. package/dist/quarantine-policy.cjs +10 -0
  51. package/dist/quarantine-policy.d.cts +27 -0
  52. package/dist/quarantine-policy.d.mts +27 -0
  53. package/dist/quarantine-policy.mjs +1 -0
  54. package/dist/quarantine-policy2.cjs +399 -0
  55. package/dist/quarantine-policy2.cjs.map +1 -0
  56. package/dist/quarantine-policy2.mjs +352 -0
  57. package/dist/quarantine-policy2.mjs.map +1 -0
  58. package/dist/toolchain-changed.cjs +16 -67
  59. package/dist/toolchain-changed.cjs.map +1 -1
  60. package/dist/toolchain-changed.mjs +19 -70
  61. package/dist/toolchain-changed.mjs.map +1 -1
  62. package/dist/toolchain-check-cli.cjs +1 -0
  63. package/dist/toolchain-check-cli.cjs.map +1 -1
  64. package/dist/toolchain-check-cli.mjs +1 -0
  65. package/dist/toolchain-check-cli.mjs.map +1 -1
  66. package/dist/toolchain-dependabot.cjs +529 -0
  67. package/dist/toolchain-dependabot.cjs.map +1 -0
  68. package/dist/toolchain-dependabot.mjs +485 -0
  69. package/dist/toolchain-dependabot.mjs.map +1 -0
  70. package/dist/toolchain-guard.cjs +1143 -432
  71. package/dist/toolchain-guard.cjs.map +1 -1
  72. package/dist/toolchain-guard.mjs +1132 -421
  73. package/dist/toolchain-guard.mjs.map +1 -1
  74. package/dist/toolchain-inputs.cjs +216 -38
  75. package/dist/toolchain-inputs.cjs.map +1 -1
  76. package/dist/toolchain-inputs.mjs +213 -41
  77. package/dist/toolchain-inputs.mjs.map +1 -1
  78. package/dist/toolchain-schema.cjs +0 -493
  79. package/dist/toolchain-schema.cjs.map +1 -1
  80. package/dist/toolchain-schema.mjs +3 -379
  81. package/dist/toolchain-schema.mjs.map +1 -1
  82. package/dist/typecheck-parity.cjs +4 -3
  83. package/dist/typecheck-parity.cjs.map +1 -1
  84. package/dist/typecheck-parity.mjs +4 -3
  85. package/dist/typecheck-parity.mjs.map +1 -1
  86. package/dist/typecheck-probe-cli.cjs +3 -3
  87. package/dist/typecheck-probe-cli.cjs.map +1 -1
  88. package/dist/typecheck-probe-cli.mjs +3 -3
  89. package/dist/typecheck-probe-cli.mjs.map +1 -1
  90. package/package.json +25 -1
  91. package/toolchain.json +11 -4
  92. package/dist/publish-contract.cjs +0 -431
  93. package/dist/publish-contract.cjs.map +0 -1
  94. package/dist/publish-contract.mjs +0 -396
  95. package/dist/publish-contract.mjs.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import path from "node:path";
2
+ import { parse } from "smol-toml";
2
3
  import { createHash } from "node:crypto";
3
4
  import { subset, valid, validRange } from "semver";
4
- import { parse as parse$1 } from "smol-toml";
5
5
  import { isDeepStrictEqual } from "node:util";
6
6
  import { parseDocument } from "yaml";
7
7
  import picomatch from "picomatch";
@@ -375,6 +375,21 @@ const cloudSetupArtifacts = {
375
375
  binaries: { "rustup-init": "rustup-init" }
376
376
  }
377
377
  },
378
+ gitleaks: {
379
+ version: "8.30.1",
380
+ amd64: {
381
+ url: "https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_x64.tar.gz",
382
+ sha256: "551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb",
383
+ archive: "tar.gz",
384
+ binaries: { gitleaks: "gitleaks" }
385
+ },
386
+ arm64: {
387
+ url: "https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_arm64.tar.gz",
388
+ sha256: "e4a487ee7ccd7d3a7f7ec08657610aa3606637dab924210b3aee62570fb4b080",
389
+ archive: "tar.gz",
390
+ binaries: { gitleaks: "gitleaks" }
391
+ }
392
+ },
378
393
  "cargo-nextest": {
379
394
  version: "0.9.148",
380
395
  amd64: {
@@ -442,7 +457,8 @@ const cloudSetupArtifactVersions = {
442
457
  "rustup-init": [cloudSetupArtifacts["rustup-init"]],
443
458
  "cargo-nextest": [cloudSetupArtifacts["cargo-nextest"]],
444
459
  "wasm-bindgen-cli": wasmBindgenArtifactReleases,
445
- uv: [cloudSetupArtifacts.uv]
460
+ uv: [cloudSetupArtifacts.uv],
461
+ gitleaks: [cloudSetupArtifacts.gitleaks]
446
462
  };
447
463
  /** A new version requires an explicit, verified catalog entry; never build from source. */
448
464
  const getCloudSetupArtifact = (tool, version) => {
@@ -497,11 +513,20 @@ const parseRust = (input) => {
497
513
  if (!object(input) || Object.keys(input).some((key) => ![
498
514
  "toolchain",
499
515
  "components",
500
- "targets"
501
- ].includes(key))) throw new Error("cloud.rust accepts only toolchain, components and targets");
516
+ "targets",
517
+ "additionalToolchains"
518
+ ].includes(key))) throw new Error("cloud.rust accepts only toolchain, components, targets and additionalToolchains");
502
519
  const toolchain = input["toolchain"];
503
520
  if (toolchain !== void 0 && !exactPin(toolchain)) throw new Error("cloud.rust.toolchain must be an exact stable release");
521
+ const additional = input["additionalToolchains"];
522
+ if (additional !== void 0 && !Array.isArray(additional)) throw new Error("cloud.rust.additionalToolchains requires exact stable releases");
523
+ const additionalToolchains = additional === void 0 ? [] : Array.from(additional, (pin) => {
524
+ if (!exactPin(pin)) throw new Error("cloud.rust.additionalToolchains requires exact stable releases");
525
+ return pin;
526
+ });
527
+ if (new Set(additionalToolchains).size !== additionalToolchains.length) throw new Error("cloud.rust.additionalToolchains contains duplicates");
504
528
  return {
529
+ additionalToolchains,
505
530
  ...toolchain === void 0 ? {} : { toolchain },
506
531
  components: parseCloudRustNames(input["components"], "components"),
507
532
  targets: parseCloudRustNames(input["targets"], "targets")
@@ -672,7 +697,12 @@ const postgresStart = String.raw`
672
697
  pg_pid="$(head -n 1 "$pg_data/postmaster.pid")"
673
698
  service_process "$pg_pid" "$pg_bin/postgres"
674
699
  [[ "$(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"
675
- DATABASE_URL="postgresql://stll_cloud:$pg_password@127.0.0.1:55432/cloud_test"
700
+ POSTGRES_HOST='127.0.0.1:55432'
701
+ POSTGRES_USER='stll_cloud'
702
+ POSTGRES_PASSWORD="$pg_password"
703
+ POSTGRES_DB_NAME='cloud_test'
704
+ POSTGRES_ADDITIONAL_CONNECTION_DATA=''
705
+ DATABASE_URL="postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/$POSTGRES_DB_NAME$POSTGRES_ADDITIONAL_CONNECTION_DATA"
676
706
  printf 'postgres: %s\n' "$pg_state"
677
707
  `;
678
708
  const valkeyStart = String.raw`
@@ -817,7 +847,7 @@ const resolveCloudToolchains = ({ cloud, files, rust, rustExceptions }) => {
817
847
  if (source !== void 0 && selector !== void 0) throw new Error("cloud Rust requires one root rust-toolchain.toml or rust-toolchain file");
818
848
  let declared;
819
849
  if (source !== void 0 || selector?.trimStart().startsWith("[")) {
820
- const parsed = parse$1(source ?? selector ?? "");
850
+ const parsed = parse(source ?? selector ?? "");
821
851
  if (!record$2(parsed["toolchain"])) throw new Error("cloud Rust requires [toolchain]");
822
852
  declared = parsed["toolchain"];
823
853
  if (Object.keys(declared).some((key) => ![
@@ -835,6 +865,11 @@ const resolveCloudToolchains = ({ cloud, files, rust, rustExceptions }) => {
835
865
  if (channel !== rust && !rustExceptions.some(({ pin }) => pin === channel)) throw new Error(`cloud Rust must use shared Rust ${rust} or a recorded rustExceptions pin`);
836
866
  if (cloud.rust?.toolchain !== void 0 && cloud.rust.toolchain !== channel) throw new Error("cloud.rust.toolchain conflicts with root Rust toolchain");
837
867
  }
868
+ const additionalToolchains = cloud.rust?.additionalToolchains ?? [];
869
+ for (const pin of additionalToolchains) {
870
+ if (pin === channel) throw new Error("cloud.rust.additionalToolchains must not repeat the primary toolchain");
871
+ if (pin !== rust && !rustExceptions.some(({ pin: authorized }) => authorized === pin)) throw new Error(`cloud.rust.additionalToolchains ${pin} requires an authorized rustExceptions pin`);
872
+ }
838
873
  const components = [.../* @__PURE__ */ new Set([...parseCloudRustNames(declared?.["components"], "components"), ...cloud.rust?.components ?? []])].sort();
839
874
  const targets = [.../* @__PURE__ */ new Set([...parseCloudRustNames(declared?.["targets"], "targets"), ...cloud.rust?.targets ?? []])].sort();
840
875
  const resolveTool = (tool, version) => {
@@ -847,7 +882,7 @@ const resolveCloudToolchains = ({ cloud, files, rust, rustExceptions }) => {
847
882
  if (tool === "wasm-bindgen-cli") {
848
883
  const lock = files["Cargo.lock"];
849
884
  if (lock === void 0) throw new Error("wasm-bindgen-cli requires tracked root Cargo.lock");
850
- const packages = parse$1(lock)["package"];
885
+ const packages = parse(lock)["package"];
851
886
  if (!Array.isArray(packages)) throw new Error("Cargo.lock requires package entries for wasm-bindgen-cli");
852
887
  const versions = new Set(packages.flatMap((entry) => record$2(entry) && entry["name"] === "wasm-bindgen" ? [entry["version"]] : []));
853
888
  if (versions.size !== 1 || !versions.has(version)) throw new Error(`cloud.tools.wasm-bindgen-cli ${version} must equal the unique wasm-bindgen crate version in Cargo.lock (found ${[...versions].join(", ") || "none"})`);
@@ -865,6 +900,7 @@ const resolveCloudToolchains = ({ cloud, files, rust, rustExceptions }) => {
865
900
  return {
866
901
  rust: rustEnabled && typeof channel === "string" ? {
867
902
  channel,
903
+ additionalToolchains: [...additionalToolchains].sort(),
868
904
  components,
869
905
  targets,
870
906
  installer: cloudSetupArtifacts["rustup-init"]
@@ -901,7 +937,7 @@ rust_run() {
901
937
  *) fail 'Invalid Rust execution mode' ;;
902
938
  esac
903
939
  }
904
- rust_ready() {
940
+ primary_rust_ready() {
905
941
  local directory
906
942
  for directory in "$RUSTUP_HOME" "$CARGO_HOME"; do
907
943
  [[ -d "$directory" && ! -L "$directory" && "$(stat -c '%u' "$directory")" == "$OUTPUT_UID" ]] || return 1
@@ -915,13 +951,19 @@ rust_ready() {
915
951
  @VERIFY_TARGETS@
916
952
  return 0
917
953
  }
954
+ rust_ready() {
955
+ local additional_version
956
+ primary_rust_ready || return 1
957
+ @VERIFY_ADDITIONAL_TOOLCHAINS@
958
+ return 0
959
+ }
918
960
  verify_rust() { rust_ready || fail 'Pinned Rust, components or targets are unavailable; run install'; }
919
961
  install_rust() (
920
962
  if ! command -v cc >/dev/null || ! command -v ld >/dev/null || [[ ! -f /usr/include/stdlib.h ]]; then
921
963
  apt_refresh_for build-essential
922
964
  apt_install build-essential
923
965
  fi
924
- local artifact_url artifact_sha
966
+ local artifact_url artifact_sha additional_version
925
967
  # Subshell scope retains scratch for EXIT cleanup after a failed command.
926
968
  scratch="$(mktemp -d)"
927
969
  trap 'rm -rf -- "$scratch"' EXIT
@@ -940,9 +982,10 @@ install_rust() (
940
982
  rust_run network "$scratch/rustup-init" -y --no-modify-path --default-toolchain none --profile minimal
941
983
  fi
942
984
  rust_run local "$CARGO_HOME/bin/rustup" set auto-self-update disable
943
- if ! rust_ready; then
985
+ if ! primary_rust_ready; then
944
986
  rust_run network "$CARGO_HOME/bin/rustup" toolchain install @RUST@ --profile minimal --no-self-update @RUST_ARGS@
945
987
  fi
988
+ @INSTALL_ADDITIONAL_TOOLCHAINS@
946
989
  rust_run local "$CARGO_HOME/bin/rustup" default @RUST@
947
990
  verify_rust
948
991
  for tool in rustup cargo rustc rustdoc @RUST_PROXIES@; do
@@ -953,7 +996,7 @@ install_rust() (
953
996
  fi
954
997
  done
955
998
  )
956
- `.replaceAll("@RUSTUP@", rust.installer.version).replaceAll("@RUST@", rust.channel).replace("@RUSTUP_ARTIFACT@", artifactSelection(rust.installer)).replace("@RUST_ARGS@", args).replace("@RUST_PROXIES@", Object.entries(cloudRustComponentBinaries).flatMap(([component, binaries]) => rust.components.includes(component) ? Array.from(binaries) : []).join(" ")).replace("@VERIFY_COMPONENTS@", rust.components.map((name) => `rust_run local "$CARGO_HOME/bin/rustup" component list --toolchain ${quote(rust.channel)} --installed | awk -v name=${quote(name)} -v host="$host" '$1 == name || $1 == name "-" host { found=1 } END { exit !found }' || return 1`).join("\n ")).replace("@VERIFY_TARGETS@", rust.targets.map((name) => `rust_run local "$CARGO_HOME/bin/rustup" target list --toolchain ${quote(rust.channel)} --installed | awk -v name=${quote(name)} '$1 == name { found=1 } END { exit !found }' || return 1`).join("\n "));
999
+ `.replace("@VERIFY_ADDITIONAL_TOOLCHAINS@", rust.additionalToolchains.map((pin) => `additional_version="$(rust_run local "$CARGO_HOME/bin/rustc" +${pin} --version)" || return 1\n [[ "$additional_version" == 'rustc ${pin} '* ]] || return 1`).join("\n ")).replace("@INSTALL_ADDITIONAL_TOOLCHAINS@", rust.additionalToolchains.map((pin) => `if ! additional_version="$(rust_run local "$CARGO_HOME/bin/rustc" +${pin} --version)" || [[ "$additional_version" != 'rustc ${pin} '* ]]; then\n rust_run network "$CARGO_HOME/bin/rustup" toolchain install ${pin} --profile minimal --no-self-update\n fi`).join("\n ")).replaceAll("@RUSTUP@", rust.installer.version).replaceAll("@RUST@", rust.channel).replace("@RUSTUP_ARTIFACT@", artifactSelection(rust.installer)).replace("@RUST_ARGS@", args).replace("@RUST_PROXIES@", Object.entries(cloudRustComponentBinaries).flatMap(([component, binaries]) => rust.components.includes(component) ? Array.from(binaries) : []).join(" ")).replace("@VERIFY_COMPONENTS@", rust.components.map((name) => `rust_run local "$CARGO_HOME/bin/rustup" component list --toolchain ${quote(rust.channel)} --installed | awk -v name=${quote(name)} -v host="$host" '$1 == name || $1 == name "-" host { found=1 } END { exit !found }' || return 1`).join("\n ")).replace("@VERIFY_TARGETS@", rust.targets.map((name) => `rust_run local "$CARGO_HOME/bin/rustup" target list --toolchain ${quote(rust.channel)} --installed | awk -v name=${quote(name)} '$1 == name { found=1 } END { exit !found }' || return 1`).join("\n "));
957
1000
  };
958
1001
  const packageToolInstall = ({ tool, release }) => {
959
1002
  const name = tool.replaceAll("-", "_");
@@ -976,7 +1019,7 @@ install_${name}() {
976
1019
  const artifactToolInstall = ({ tool, release }) => {
977
1020
  const directory = `/opt/stll-cloud/tools/${tool}/${release.version}`;
978
1021
  const command = tool === "wasm-bindgen-cli" ? "wasm-bindgen" : tool;
979
- const marker = `${tool === "cargo-nextest" ? "cargo-nextest" : command} ${release.version}`;
1022
+ const marker = `${tool === "gitleaks" ? "gitleaks version" : command} ${release.version}`;
980
1023
  return {
981
1024
  name: tool.replaceAll("-", "_"),
982
1025
  text: `verify_${tool.replaceAll("-", "_")}() {
@@ -1055,9 +1098,9 @@ const script = String.raw`#!/usr/bin/env bash
1055
1098
  # Supported platform: Ubuntu 24.04 (noble), amd64/arm64, root or passwordless sudo.
1056
1099
  set -euo pipefail
1057
1100
 
1058
- usage() { echo 'Usage: cloud-setup.sh install|start' >&2; exit 2; }
1101
+ usage() { echo 'Usage: cloud-setup.sh install|start|session' >&2; exit 2; }
1059
1102
  [[ "$#" == 1 ]] || usage
1060
- case "$1" in install|start) MODE="$1" ;; *) usage ;; esac
1103
+ case "$1" in install|start|session) MODE="$1" ;; *) usage ;; esac
1061
1104
  fail() { echo "cloud-setup: $*" >&2; exit 1; }
1062
1105
  trap 'echo "cloud-setup: command failed at line $LINENO" >&2' ERR
1063
1106
  # Each phase logs its start and duration so a stalled phase is visible in the task log.
@@ -1113,19 +1156,35 @@ esac
1113
1156
 
1114
1157
  @NETWORK@
1115
1158
 
1159
+ node_ready() {
1160
+ local node_version npm_version npx_version
1161
+ [[ -x "$NODE_DIR/bin/node" && -x "$NODE_DIR/bin/npm" && -x "$NODE_DIR/bin/npx" ]] || return 1
1162
+ node_version="$("$NODE_DIR/bin/node" --version)" || return 1
1163
+ [[ "$node_version" == "v$NODE_VERSION" ]] || return 1
1164
+ npm_version="$("$NODE_DIR/bin/npm" --version)" || return 1
1165
+ [[ "$npm_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || return 1
1166
+ npx_version="$("$NODE_DIR/bin/npx" --version)" || return 1
1167
+ [[ "$npx_version" == "$npm_version" ]] || return 1
1168
+ }
1116
1169
  runtime_ready() {
1117
- [[ -x "$NODE_DIR/bin/node" && "$($NODE_DIR/bin/node --version)" == "v$NODE_VERSION" ]] || fail 'Pinned Node is unavailable; run install'
1170
+ node_ready || fail 'Pinned Node, npm or npx is unavailable; run install'
1118
1171
  [[ -x "$BUN_DIR/bun" && "$($BUN_DIR/bun --version)" == "$BUN_VERSION" ]] || fail 'Pinned Bun is unavailable; run install'
1119
1172
  }
1120
- install_runtimes() {
1173
+ install_runtimes() (
1174
+ if [[ ! -s /etc/ssl/certs/ca-certificates.crt ]]; then
1175
+ command -v update-ca-certificates >/dev/null || fail 'System CA certificate bundle is missing; restore ca-certificates in the base image offline before install'
1176
+ update-ca-certificates || fail 'System CA certificate bundle could not be repaired offline; restore ca-certificates in the base image before install'
1177
+ fi
1178
+ [[ -s /etc/ssl/certs/ca-certificates.crt ]] || fail 'System CA certificate bundle is unavailable after offline repair; restore ca-certificates in the base image before install'
1121
1179
  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 || ! ldconfig -p | awk '$1 == "libatomic.so.1" { found=1 } END { exit !found }'; then
1122
1180
  apt_refresh_for ca-certificates curl unzip xz-utils openssl gnupg util-linux libatomic1
1123
1181
  DEBIAN_FRONTEND=noninteractive apt_install ca-certificates curl unzip xz-utils openssl gnupg util-linux libatomic1
1124
1182
  fi
1125
- local scratch node_archive bun_archive bun_folder
1183
+ local node_archive bun_archive bun_folder
1184
+ # Subshell scope retains scratch for EXIT cleanup after a failed command.
1126
1185
  scratch="$(mktemp -d)"
1127
- trap 'rm -rf -- "$scratch"' RETURN
1128
- if [[ ! -x "$NODE_DIR/bin/node" ]] || [[ "$($NODE_DIR/bin/node --version)" != "v$NODE_VERSION" ]]; then
1186
+ trap 'rm -rf -- "$scratch"' EXIT
1187
+ if ! node_ready; then
1129
1188
  node_archive="node-v$NODE_VERSION-linux-$NODE_ARCH.tar.xz"
1130
1189
  download_file "https://nodejs.org/dist/v$NODE_VERSION/$node_archive" "$scratch/$node_archive" 120
1131
1190
  download_file "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt" "$scratch/node-sums" 45
@@ -1148,9 +1207,7 @@ install_runtimes() {
1148
1207
  ln -sfnT -- "$NODE_DIR/bin/npm" /usr/local/bin/npm
1149
1208
  ln -sfnT -- "$NODE_DIR/bin/npx" /usr/local/bin/npx
1150
1209
  ln -sfnT -- "$BUN_DIR/bun" /usr/local/bin/bun
1151
- rm -rf -- "$scratch"
1152
- trap - RETURN
1153
- }
1210
+ )
1154
1211
 
1155
1212
  dependency_install() {
1156
1213
  local directory download_hosts
@@ -1196,6 +1253,8 @@ process.stdout.write([...hosts].sort().join(","));
1196
1253
  @INPUT_VALIDATION@
1197
1254
 
1198
1255
  @ENV_FUNCTIONS@
1256
+ @ENVIRONMENT_VALUES@
1257
+ @SESSION_ENVIRONMENT@
1199
1258
 
1200
1259
  @PREPARE_FUNCTION@
1201
1260
 
@@ -1208,14 +1267,38 @@ if [[ "$MODE" == install ]]; then
1208
1267
  @PREPARE_STEP@
1209
1268
  exit 0
1210
1269
  fi
1211
- step runtime-check runtime_ready
1212
- step tool-check verify_tools
1213
- step shell install_shell_environment
1214
- step shell-check verify_shell_environment
1215
- @ENV_VALIDATE@
1216
- step state prepare_state
1217
- step service-start start_services
1218
- @ENV_WRITE@
1270
+ @ENV_INITIALIZE@
1271
+ start_environment() {
1272
+ step runtime-check runtime_ready
1273
+ step tool-check verify_tools
1274
+ step shell install_shell_environment
1275
+ step shell-check verify_shell_environment
1276
+ @ENV_VALIDATE@
1277
+ step state prepare_state
1278
+ step service-start start_services
1279
+ @ENV_WRITE@
1280
+ }
1281
+ if [[ "$MODE" == session ]]; then
1282
+ start_environment >&2
1283
+ else
1284
+ start_environment
1285
+ fi
1286
+ @SESSION_ENVIRONMENT_CALL@
1287
+ `;
1288
+ const environmentValuesFunction = String.raw`
1289
+ environment_values() {
1290
+ local emit="$1"
1291
+ "$emit" NODE_ENV test
1292
+ if [[ -n "$DATABASE_URL" ]]; then
1293
+ "$emit" DATABASE_URL "$DATABASE_URL"
1294
+ "$emit" POSTGRES_HOST "$POSTGRES_HOST"
1295
+ "$emit" POSTGRES_USER "$POSTGRES_USER"
1296
+ "$emit" POSTGRES_PASSWORD "$POSTGRES_PASSWORD"
1297
+ "$emit" POSTGRES_DB_NAME "$POSTGRES_DB_NAME"
1298
+ "$emit" POSTGRES_ADDITIONAL_CONNECTION_DATA "$POSTGRES_ADDITIONAL_CONNECTION_DATA"
1299
+ fi
1300
+ if [[ -n "$REDIS_URL" ]]; then "$emit" REDIS_URL "$REDIS_URL"; fi
1301
+ }
1219
1302
  `;
1220
1303
  const environmentFunctions = String.raw`
1221
1304
  validate_environment() {
@@ -1231,20 +1314,26 @@ validate_environment() {
1231
1314
  [[ "$first_line" == "$ENV_MARKER" ]] || fail 'Refusing to overwrite an unowned environment file'
1232
1315
  fi
1233
1316
  }
1317
+ environment_file_value() { printf '%s=%s\n' "$1" "$2"; }
1234
1318
  write_environment() {
1235
1319
  validate_environment
1236
1320
  local env_path="$REPO_ROOT/$ENV_FILE" temporary
1237
1321
  temporary="$(mktemp "$(dirname -- "$env_path")/.stll-cloud-env.XXXXXX")"
1238
1322
  chmod 600 "$temporary"
1239
1323
  {
1240
- printf '%s\n' "$ENV_MARKER" 'NODE_ENV=test'
1241
- if [[ -n "$DATABASE_URL" ]]; then printf 'DATABASE_URL=%s\n' "$DATABASE_URL"; fi
1242
- if [[ -n "$REDIS_URL" ]]; then printf 'REDIS_URL=%s\n' "$REDIS_URL"; fi
1324
+ printf '%s\n' "$ENV_MARKER"
1325
+ environment_values environment_file_value
1243
1326
  } > "$temporary"
1244
1327
  chown "$OUTPUT_UID:$OUTPUT_GID" "$temporary"
1245
1328
  mv -f -- "$temporary" "$env_path"
1246
1329
  }
1247
1330
  `;
1331
+ const sessionEnvironmentFunction = String.raw`
1332
+ environment_shell_value() { printf 'export %s=%q\n' "$1" "$2"; }
1333
+ emit_session_environment() {
1334
+ environment_values environment_shell_value
1335
+ }
1336
+ `;
1248
1337
  const shellQuote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
1249
1338
  const renderPrepare = (prepare, files) => {
1250
1339
  if (prepare === void 0) return "";
@@ -1298,7 +1387,11 @@ const generateCloudSetup = ({ policy, cloud, nodeVersion, files = {}, rustExcept
1298
1387
  ["@NODE@", exactNode],
1299
1388
  ["@ENV_ASSIGNMENTS@", selected.envFile === void 0 ? "" : `ENV_FILE=${shellQuote(selected.envFile)}\nENV_MARKER=${shellQuote(cloudEnvironmentMarker)}`],
1300
1389
  ["@ENV_FUNCTIONS@", selected.envFile === void 0 ? "" : environmentFunctions],
1301
- ["@ENV_VALIDATE@", selected.envFile === void 0 ? "" : "DATABASE_URL=''\nREDIS_URL=''\nvalidate_environment"],
1390
+ ["@ENVIRONMENT_VALUES@", selected.services.length === 0 ? "" : environmentValuesFunction],
1391
+ ["@ENV_INITIALIZE@", selected.services.length === 0 ? "" : "DATABASE_URL=''\nPOSTGRES_HOST=''\nPOSTGRES_USER=''\nPOSTGRES_PASSWORD=''\nPOSTGRES_DB_NAME=''\nPOSTGRES_ADDITIONAL_CONNECTION_DATA=''\nREDIS_URL=''"],
1392
+ ["@ENV_VALIDATE@", selected.envFile === void 0 ? "" : "validate_environment"],
1393
+ ["@SESSION_ENVIRONMENT@", selected.services.length === 0 ? "" : sessionEnvironmentFunction],
1394
+ ["@SESSION_ENVIRONMENT_CALL@", selected.services.length === 0 ? "" : "if [[ \"$MODE\" == session ]]; then emit_session_environment; fi"],
1302
1395
  ["@ENV_WRITE@", selected.envFile === void 0 ? "" : "write_environment"],
1303
1396
  ["@PREPARE_FUNCTION@", renderPrepare(selected.prepare, files)],
1304
1397
  ["@PREPARE_STEP@", selected.prepare === void 0 ? "" : " step prepare prepare_sources"],
@@ -1314,16 +1407,16 @@ const generateCloudSetup = ({ policy, cloud, nodeVersion, files = {}, rustExcept
1314
1407
  valkey: policy.valkey
1315
1408
  })]
1316
1409
  ]);
1317
- return script.replace(/@[A-Z0-9_]+@/g, (token) => {
1410
+ return `${script.replace(/@[A-Z0-9_]+@/g, (token) => {
1318
1411
  const value = replacements.get(token);
1319
1412
  if (value === void 0) throw new Error(`Missing cloud setup field: ${token}`);
1320
1413
  return value;
1321
- });
1414
+ }).replace(/[ \t]+$/gm, "").trimEnd()}\n`;
1322
1415
  };
1323
1416
  //#endregion
1324
1417
  //#region src/cloud-setup-claude.ts
1325
1418
  const cloudClaudeSettingsPath = ".claude/settings.json";
1326
- const sessionStartHook = (script) => ({
1419
+ const releasedSessionStartHook = (script) => ({
1327
1420
  matcher: "",
1328
1421
  hooks: [{
1329
1422
  type: "command",
@@ -1340,8 +1433,75 @@ const sessionStartHook = (script) => ({
1340
1433
  fi`
1341
1434
  }]
1342
1435
  });
1436
+ const sessionStartHook = (script) => ({
1437
+ matcher: "",
1438
+ hooks: [{
1439
+ type: "command",
1440
+ command: `if [ "\${CLAUDE_CODE_REMOTE:-}" = "true" ]; then
1441
+ cloud_setup_incomplete() {
1442
+ printf '%s\\n' 'Cloud setup could not establish a verified session environment. From the repository root, run bash .agents/cloud-setup.sh install and then bash .agents/cloud-setup.sh start. Do not assume pinned tools or services are ready until start succeeds.'
1443
+ printf '%s\\n' "cloud-setup: $1" >&2
1444
+ exit 0
1445
+ }
1446
+ if [ -n "\${CLAUDE_ENV_FILE:-}" ]; then
1447
+ cloud_session_exports="$(${script} session)" || cloud_setup_incomplete 'startup failed; run install'
1448
+ else
1449
+ ${script} start >&2 || cloud_setup_incomplete 'startup failed; run install'
1450
+ exit 0
1451
+ fi
1452
+ if [ -n "\${CLAUDE_ENV_FILE:-}" ]; then
1453
+ if [ ! -s ${cloudShellProfilePath} ]; then
1454
+ cloud_setup_incomplete 'cloud shell profile is unavailable; run install'
1455
+ fi
1456
+ cloud_shell_exports="$(cat ${cloudShellProfilePath})" || cloud_setup_incomplete 'cloud shell profile could not be read; run install'
1457
+ [ -n "$cloud_shell_exports" ] || cloud_setup_incomplete 'cloud shell profile is unavailable; run install'
1458
+ if [ -e "$CLAUDE_ENV_FILE" ] || [ -L "$CLAUDE_ENV_FILE" ]; then
1459
+ [ -f "$CLAUDE_ENV_FILE" ] && [ ! -L "$CLAUDE_ENV_FILE" ] || cloud_setup_incomplete 'session environment must be a regular file; check its path and rerun start'
1460
+ fi
1461
+ cloud_env_temporary="$(umask 077; mktemp "\${CLAUDE_ENV_FILE}.XXXXXX")" || cloud_setup_incomplete 'session environment could not be prepared; check its write access and rerun start'
1462
+ if [ -e "$CLAUDE_ENV_FILE" ] || [ -L "$CLAUDE_ENV_FILE" ]; then
1463
+ cat "$CLAUDE_ENV_FILE" > "$cloud_env_temporary" || { rm -f -- "$cloud_env_temporary"; cloud_setup_incomplete 'session environment could not be read; check its access and rerun start'; }
1464
+ fi
1465
+ if [ -n "$cloud_session_exports" ]; then
1466
+ cloud_session_exports="$cloud_session_exports
1467
+ $cloud_shell_exports"
1468
+ else
1469
+ cloud_session_exports="$cloud_shell_exports"
1470
+ fi
1471
+ printf '\\n%s\\n' "$cloud_session_exports" >> "$cloud_env_temporary" || { rm -f -- "$cloud_env_temporary"; cloud_setup_incomplete 'session environment could not be prepared; check its write access and rerun start'; }
1472
+ mv -f -- "$cloud_env_temporary" "$CLAUDE_ENV_FILE" || { rm -f -- "$cloud_env_temporary"; cloud_setup_incomplete 'session environment could not be persisted; check its write access and rerun start'; }
1473
+ fi
1474
+ fi`
1475
+ }]
1476
+ });
1477
+ const currentMainSessionStartHook = (script) => ({
1478
+ matcher: "",
1479
+ hooks: [{
1480
+ type: "command",
1481
+ command: `if [ "\${CLAUDE_CODE_REMOTE:-}" = "true" ]; then
1482
+ cloud_setup_incomplete() {
1483
+ printf '%s\\n' 'Cloud setup could not establish a verified session environment. From the repository root, run bash .agents/cloud-setup.sh install and then bash .agents/cloud-setup.sh start. Do not assume pinned tools or services are ready until start succeeds.'
1484
+ printf '%s\\n' "cloud-setup: $1" >&2
1485
+ exit 0
1486
+ }
1487
+ ${script} start >&2 || cloud_setup_incomplete 'startup failed; run install'
1488
+ if [ -n "\${CLAUDE_ENV_FILE:-}" ]; then
1489
+ if [ ! -s ${cloudShellProfilePath} ]; then
1490
+ cloud_setup_incomplete 'cloud shell profile is unavailable; run install'
1491
+ fi
1492
+ cloud_shell_exports="$(cat ${cloudShellProfilePath})" || cloud_setup_incomplete 'cloud shell profile could not be read; run install'
1493
+ [ -n "$cloud_shell_exports" ] || cloud_setup_incomplete 'cloud shell profile is unavailable; run install'
1494
+ printf '\\n%s\\n' "$cloud_shell_exports" >> "$CLAUDE_ENV_FILE" || cloud_setup_incomplete 'session environment could not be persisted; check its write access and rerun start'
1495
+ fi
1496
+ fi`
1497
+ }]
1498
+ });
1343
1499
  const cloudSessionStartHook = sessionStartHook(`bash "$CLAUDE_PROJECT_DIR/${cloudSetupPath}"`);
1344
- const previousCloudSessionStartHooks = [sessionStartHook(`bash ${cloudSetupPath}`)];
1500
+ const previousCloudSessionStartHooks = [
1501
+ releasedSessionStartHook(`bash ${cloudSetupPath}`),
1502
+ releasedSessionStartHook(`bash "$CLAUDE_PROJECT_DIR/${cloudSetupPath}"`),
1503
+ currentMainSessionStartHook(`bash "$CLAUDE_PROJECT_DIR/${cloudSetupPath}"`)
1504
+ ];
1345
1505
  const cloudAttribution = {
1346
1506
  commit: "",
1347
1507
  pr: ""
@@ -1575,6 +1735,18 @@ const javascriptDependencyLockfiles = [
1575
1735
  "yarn.lock",
1576
1736
  "pnpm-lock.yaml"
1577
1737
  ];
1738
+ const dependabotManifestEcosystems = {
1739
+ Gemfile: "bundler",
1740
+ "Gemfile.lock": "bundler",
1741
+ "composer.json": "composer",
1742
+ "composer.lock": "composer"
1743
+ };
1744
+ /** Share ecosystem inventory with snapshot selection; no dependency graph reads are needed. */
1745
+ const dependabotManifestEcosystem = (file) => {
1746
+ if (excludedInputPath(file)) return void 0;
1747
+ const basename = file.split("/").at(-1);
1748
+ for (const [name, ecosystem] of Object.entries(dependabotManifestEcosystems)) if (basename === name) return ecosystem;
1749
+ };
1578
1750
  /** Match project, environment, local, and fragment filenames recognized by mise. */
1579
1751
  const isMiseConfigPath = (file) => !excludedInputPath(file) && (/(?:^|\/)\.?mise(?:\.[^./][^/]*)?\.toml$/.test(file) || /(?:^|\/)(?:\.?mise|\.config\/mise)\/config(?:\.[^./][^/]*)?\.toml$/.test(file) || /(?:^|\/)(?:\.?mise|\.config\/mise)\/conf\.d\/[^./][^/]*\.toml$/.test(file));
1580
1752
  /** GitHub action metadata may live at the repository root or in any directory. */
@@ -1610,13 +1782,13 @@ const isDockerDefinitionPath = (file) => {
1610
1782
  const isShellScript = (file, text = "") => !excludedInputPath(file) && (/\.(?:sh|bash|zsh)$/.test(file) || /^#![ \t]*(?:\/(?:[^ \t\r\n]+\/)*(?:bash|dash|ksh|zsh|sh)|\/usr\/bin\/env[ \t]+(?:-S[ \t]+)?(?:bash|dash|ksh|zsh|sh))(?=[ \t\r\n]|$)/.test(text));
1611
1783
  /** Lockfiles establish ecosystem presence without reading their dependency graphs. */
1612
1784
  const toolchainInputKind = (file, text = "") => {
1613
- if (file === ".agents/cloud-setup.sh" || file === ".claude/settings.json" || file === "Cargo.lock") return "config";
1785
+ if (file === "bunfig.toml" || file === ".agents/cloud-setup.sh" || file === ".claude/settings.json" || file === "Cargo.lock") return "config";
1614
1786
  if (githubAutomationFileKind(file) !== void 0) return "config";
1615
1787
  if (excludedInputPath(file)) return void 0;
1616
- if (pythonDependencyManifestKind(file) === "pipfile-lock" || javascriptDependencyLockfiles.some((name) => file.split("/").at(-1) === name) || /(?:^|\/)uv\.lock$/.test(file)) return "presence";
1788
+ if (dependabotManifestEcosystem(file) !== void 0 || pythonDependencyManifestKind(file) === "pipfile-lock" || javascriptDependencyLockfiles.some((name) => file.split("/").at(-1) === name) || /(?:^|\/)uv\.lock$/.test(file)) return "presence";
1617
1789
  if (githubAutomationFileKind(file) !== void 0 || isShellScript(file, text) || /(?:^|\/)ci-service-images\.json$/.test(file) || 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";
1618
1790
  };
1619
1791
  //#endregion
1620
- export { nodeSelectorMatches as C, nodePolicyValid as S, parseCloudSetup as T, hasCloudAttribution as _, isPythonDependencyManifest as a, cloudSetupPath as b, pythonDependencyManifestKind as c, isComposeDefinitionPath as d, isKubernetesDefinitionPath as f, cloudSessionStartFix as g, cloudClaudeSettingsPath as h, isMiseConfigPath as i, toolchainInputKind as l, cloudAttributionFix as m, isDependabotGithubActionsPath as n, isShellScript as o, workspaceContains as p, isDockerDefinitionPath as r, javascriptDependencyLockfiles as s, githubAutomationFileKind as t, containerDocumentImages as u, hasCloudSessionStartHook as v, nodeSupportRangeMatches as w, generateCloudSetup as x, mergeCloudClaudeSettings as y };
1792
+ export { nodePolicyValid as C, parseCloudSetup as E, generateCloudSetup as S, nodeSupportRangeMatches as T, cloudSessionStartFix as _, isMiseConfigPath as a, mergeCloudClaudeSettings as b, javascriptDependencyLockfiles as c, containerDocumentImages as d, isComposeDefinitionPath as f, cloudClaudeSettingsPath as g, cloudAttributionFix as h, isDockerDefinitionPath as i, pythonDependencyManifestKind as l, workspaceContains as m, githubAutomationFileKind as n, isPythonDependencyManifest as o, isKubernetesDefinitionPath as p, isDependabotGithubActionsPath as r, isShellScript as s, dependabotManifestEcosystem as t, toolchainInputKind as u, hasCloudAttribution as v, nodeSelectorMatches as w, cloudSetupPath as x, hasCloudSessionStartHook as y };
1621
1793
 
1622
1794
  //# sourceMappingURL=toolchain-inputs.mjs.map