Compare commits

...
Sign in to create a new pull request.

21 commits

Author SHA1 Message Date
Daniel Han
2f7cc2d5b0 bun: pin version + auto-install + harden cache-corruption recovery 2026-05-19 04:40:09 +00:00
pre-commit-ci[bot]
ca0d2fdd3a [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-05-19 04:17:39 +00:00
Daniel Han
819ed20e98 audit: scan bun.lock alongside package-lock.json + Cargo.lock
Adds audit_bun_lockfile() that parses Bun's text lockfile format
(bun.lock, lockfileVersion 1) and applies the same supply-chain
checks already used on npm and cargo lockfiles:

  - non-registry-resolved-url   (git+/file:/tarball sources)
  - missing-integrity-hash      (no sha-prefixed tail on registry entry)
  - blocked-known-malicious     (BLOCKED_NPM_VERSIONS hit)
  - known-ioc-string            (IOC substring in raw body)
  - missing-lockfile            (path doesn't exist)
  - unreadable-lockfile         (chmod 000 / OSError)
  - malformed-lockfile          (JSONC parse failure)
  - unsupported-lockfile-version (anything != 1)

bun.lock is JSONC (valid JSON with trailing commas allowed); the
parser strips trailing commas via a single regex before json.loads.
Each package entry is a 4-element array:
  ["name@version", "<registry-url-or-empty>", {metadata}, "sha512-..."]
Default scope adds the three bun.lock paths committed alongside
the three package-lock.json paths. New repeatable --bun-lockfile
flag mirrors --npm-lockfile and --cargo-lockfile.

Verified locally:
  positive (3 npm + 3 bun + 1 cargo, real PR lockfiles): exit 0
  unreadable bun.lock (chmod 000): exit 1, [unreadable-lockfile]
  malformed bun.lock: exit 1, [malformed-lockfile]
  missing bun.lock:  exit 1, [missing-lockfile]
2026-05-19 04:06:21 +00:00
Daniel Han
9c0372d132 bun: restore bun-first / npm-fallback install (both frozen-lockfile)
PR 5479 dropped Bun support because no bun.lock was committed. With
the bun.lock files now committed for all three Studio install
surfaces (previous commit), Bun --frozen-lockfile becomes a
viable fast path again -- ~5-10x faster than npm ci in practice
(measured locally: frontend 1.4s vs 8s, oxc 35ms vs 371ms, studio
14ms vs 444ms).

Install logic per surface (studio/setup.sh, studio/setup.ps1,
build.sh):

  1. If bun.lock exists AND `bun` is on PATH:
       try `bun install --frozen-lockfile`
       verify critical binaries are present (tsc + vite for frontend,
       oxc-parser for oxc validator) -- workaround for the known
       bun-cache-corruption bug where install can exit 0 but leave
       binaries missing
       on validation failure: rm -rf node_modules + clear bun cache,
       fall through to npm ci
  2. Else (no bun.lock, no bun, or bun failed):
       `npm ci` against the committed package-lock.json

Both paths run lockfile-strict, so the install is byte-reproducible
from whichever lockfile the chosen package manager understands. The
build always runs through Node (`npm run build`) -- avoids bun
runtime quirks on some platforms.

Bun auto-install via `npm install -g bun` is NOT restored: a user
who wants the speed-up installs Bun themselves, and the npm ci path
remains the default-available install route. This matches the
upstream Studio docs' install instructions.
2026-05-19 04:03:37 +00:00
Daniel Han
dc2292a5a0 bun: commit bun.lock for the three Studio install surfaces
Adds bun.lock alongside the existing package-lock.json for:
  studio/                                              (Tauri CLI holder, 13 pkgs)
  studio/backend/core/data_recipe/oxc-validator/       (49 pkgs)
  studio/frontend/                                     (1090 pkgs)

Generated with `bun install --lockfile-only --save-text-lockfile`
against Bun 1.3.11. Each lockfile carries sha512 integrity entries
and full platform-conditional optionalDependencies coverage for
linux/darwin/win32 x64/arm64/musl variants.

Updates .gitignore to mirror the existing package-lock.json opt-in
pattern: a top-level `bun.lock` ignore with negations for the three
surfaces, plus removal of the legacy `bun.lock` entry in
studio/frontend/.gitignore.

This unlocks `bun install --frozen-lockfile` as the fast path in
setup.sh / setup.ps1 / build.sh (follow-up commit).
2026-05-19 04:01:14 +00:00
Daniel Han
35a30f1960 Merge remote-tracking branch 'origin/main' into pr5479-fix-audit-unreadable-lockfile 2026-05-19 02:50:59 +00:00
Daniel Han
1f397f45df audit: wrap lockfile read in try/except OSError
Opus reviewer 3 caught a crash path: when a lockfile is unreadable
(chmod 000, permission denied, is-a-directory, broken pipe, etc.)
the audit script bubbles up a raw Python traceback and exits 1 with
no Finding emitted. In CI, a transient permission or encoding error
is indistinguishable from a malicious lockfile -- the maintainer reads
the log and sees noise, not a structured diagnosis.

Wraps both path.read_text() calls (npm and cargo branches) in a
try/except OSError that appends a new Finding kind
"unreadable-lockfile" with the original errno detail, mirroring how
missing-lockfile is handled today. Backward compatible (additive);
all 24 existing audit fixtures still pass.

Verified by fixture: chmod 000 on a lockfile -> exit 1 with
[unreadable-lockfile] finding, no traceback.
2026-05-19 01:42:05 +00:00
danielhanchen
220ae1116f pyproject: restore intel-gpu extras dropped in main merge
The merge of main into ci/frozen-lockfile-installs in 888347d5 resolved
pyproject.toml against the pre-merge branch state, silently dropping the
intel-gpu xpu extras that landed on main in f7069b24 and were pinned in
e20bbeff (#5499) after this branch was created. None of the lockfile-pin
feature commits touch pyproject.toml; this restore reverts only the
unintended deletion and keeps the supply-chain hardening intact.

Restored extras:
  intelgputorch271 + intel-gpu-torch271
  intelgputorch291 + intel-gpu-torch291
  intelgputorch2110 + intel-gpu-torch2110
  intelgputorch2120 + intel-gpu-torch2120

Restored the triton-xpu 3.6.0 pin section inside intelgputorch210.
2026-05-18 11:20:39 +00:00
Daniel Han
888347d551
Merge branch 'main' into ci/frozen-lockfile-installs 2026-05-18 03:51:16 -07:00
Daniel Han
c244a9d0f5
Merge branch 'main' into ci/frozen-lockfile-installs 2026-05-17 01:16:12 -07:00
Daniel Han
7238504b47 ci: trim verbose rationale comments
Collapse 4-6 line "why this matters" blocks to 1-2 lines stating the
single load-bearing fact in lockfile_supply_chain_audit.py (missing-
lockfile finding rationale, CLI default-scoping rationale) and in
studio-tauri-smoke.yml (pre-install audit ordering, npm ci semantics,
frontend lifecycle-script context). No behaviour change.
2026-05-16 13:48:39 +00:00
Daniel Han
032eb880d9 ci(lockfile_supply_chain_audit): scope defaults to the no-args case
When a caller passes an explicit --npm-lockfile or --cargo-lockfile,
they are scoping the scan to the paths they listed; the script was
still silently grafting the other ecosystem's defaults on top, which
meant `--npm-lockfile X` would also audit DEFAULT_CARGO_LOCKFILES.
With the missing-lockfile Finding now emitted, that surfaced as a
false positive whenever a caller explicitly scoped only one
ecosystem. Default fallback is now reserved for the no-args CI
invocation, where every default path is expected to exist.
2026-05-16 13:48:39 +00:00
Daniel Han
b743e1b4ff ci: extend lockfile-strict surface to Tauri smoke + audit jobs
Brings the parallel CI paths into line with the lockfile-pinned
release path and tightens the supply-chain audit surface:

studio-tauri-smoke.yml: run lockfile_supply_chain_audit.py before
the Tauri CLI install, and install via `npm ci --prefix studio`
against the committed studio/package-lock.json (was a mutable
`npm install --save-dev` post-audit). This relocates the existing
pre-install lockfile supply-chain audit step; the step's name and
command are preserved verbatim so its purpose is unchanged, only
its position relative to the install. The earlier security
rationale about lifecycle scripts and the postinstall-dropper
class is preserved on the Frontend build step where it actually
applies (vite/esbuild lifecycle scripts run on the frontend
install); the Tauri CLI install step gets a new rationale tied to
`npm ci` semantics.

security-audit.yml:
  * add studio/package.json and studio/package-lock.json to the PR
    path filter so a Tauri CLI lockfile change cannot bypass the
    workflow,
  * extend OSV-Scanner, scan_npm_packages.py (with LOG3 and exit-
    code propagation), and the install-script diff to cover
    studio/package-lock.json,
  * add an npm audit step for the Tauri CLI holder project,
  * extend the npm-provenance-and-install-scripts job with
    --ignore-scripts installs + npm audit signatures for the
    oxc-validator and Tauri CLI holder projects; the existing
    frontend audit-signatures step is renamed to "(Studio
    frontend, informational)" purely for disambiguation against
    the two new sibling steps, with its log path rerouted through
    $GITHUB_WORKSPACE so a single artifact upload can collect all
    three logs,
  * update the lockfile-audit step summary to list the Tauri CLI
    holder lockfile,
  * fix the stale "Initially non-blocking" comment on the now-
    blocking npm scan-packages step.

build.sh and studio/setup.ps1 (oxc): pass --no-fund --no-audit to
npm ci for parity with the other call sites.

studio/setup.sh and studio/setup.ps1: restore the bun.lock
exclusion in the frontend staleness check so a leftover local
bun.lock from the migration does not trigger a spurious rebuild.

scripts/lockfile_supply_chain_audit.py: emit a HIGH-severity
missing-lockfile Finding when a requested lockfile does not
exist, so a deleted default cannot silently pass the audit. Uses
the script's own Finding accumulator pattern (sibling
scripts/scan_npm_packages.py implements the same intent via an
rc=2 hard-fail, its single-lockfile-per-invocation idiom; this
script aggregates multiple lockfiles so Finding is the natural
channel).

scripts/check_frontend_dep_removal.py: add studio/package.json
and studio/package-lock.json to EXPECTED_NOISE_FILES; the new
Tauri CLI holder manifests must not count as frontend dep usage.
2026-05-16 13:48:39 +00:00
Daniel Han
d43b0e3029 Merge remote-tracking branch 'origin/main' into 2026-05-16 12:50:46 +00:00
Daniel Han
cf39fb6957 ci: address 4x Opus reviewer findings on frozen-lockfile installs
Two blockers from the parallel Opus review batch:

1. The Tauri CLI install in release-desktop.yml was the last unfrozen
install path: `npm install --save-dev --prefix studio
@tauri-apps/cli@2.10.1 --no-fund --no-audit` pins the top-level
version but leaves the transitive closure floating, defeats the
pre-install lockfile audit (no lockfile to scan), and skips
integrity verification. Committed a minimal studio/package.json
(devDep @tauri-apps/cli@2.10.1) plus the resolved
studio/package-lock.json (12 packages: CLI + 11 platform-native
binaries, all with integrity hashes, lockfileVersion 3). Switched
the step to `npm ci --prefix studio` and added a pre-install
lockfile_supply_chain_audit.py step ahead of it so any tarball
postinstall is gated by the structural scan. Allowlisted
studio/package-lock.json .gitignore and added it to the
audit script's default scan set.

2. The bun branch was dead code in build.sh, studio/setup.sh, and
studio/setup.ps1: nowhere in the repo is a bun.lock committed,
and `bun install --frozen-lockfile` cannot migrate from
package-lock.json. With no lockfile, every entry to the bun
path either silently regenerates a bun.lock (under permissive
install modes -- a fresh attack surface) or fails outright (under
frozen-lockfile). Removed `npm install -g bun` bootstrap, the
`_try_bun_install` helper + cache-retry, every `if bun.lock &&
command -v bun` guard, and the now-unreachable
"fall back to npm" messaging. All three scripts now have a
single `npm ci` path. bun.lock skip entries in lint-ci.yml +
wheel-smoke.yml are kept as forward-compat sanity checks --
they assert bun.lock is NOT shipped / scanned, which is
stronger after this commit, not weaker.

Smoke-tested locally:
`npm ci --prefix studio` resolves 3 packages (CLI + 2 linux native
binaries), `npx --prefix studio tauri --version` prints
`tauri-cli 2.10.1` exactly.
`python3 scripts/lockfile_supply_chain_audit.py` scans 3 npm + 1
cargo lockfiles, 0 findings.
`bash -n build.sh`, `bash -n studio/setup.sh`, and a pwsh
scriptblock parse of studio/setup.ps1 all succeed.
2026-05-16 11:11:32 +00:00
Daniel Han
fd5f9be57d ci: run OXC tarball scan even when frontend scan exits nonzero
Reviewer found a real bug in 0ddfc10: the new two-scan step ran under
`set -o pipefail` (default `set -e` from the step shell), so a HIGH or
CRITICAL on the frontend lockfile would abort the step before the OXC
scan ran. Both reports are most useful exactly when one has already
failed.

Capture each rc via PIPESTATUS, run both scans unconditionally, write
both into the step summary, and only then propagate the worst rc.
2026-05-16 10:00:40 +00:00
Daniel Han
d1124c52a9 ci: address bot review on
Two issues codex flagged:

1. bun.lock gate (studio/frontend/.gitignore line 14 ignores bun.lock,
so it is never committed). bun install --frozen-lockfile cannot
migrate from package-lock.json, so without a bun.lock the bun path
always fails. setup.sh then misclassifies that as a corrupt cache,
clears the user's bun cache, and re-runs the same guaranteed-failing
command before falling back to npm. build.sh, studio/setup.sh, and
studio/setup.ps1 now only enter the bun path when bun.lock is
present; otherwise we go straight to npm ci.

2. OXC validator lockfile was outside the npm supply-chain scan
surface. lockfile_supply_chain_audit.py default, npm audit, OSV,
scan_npm_packages.py invocation, and the diff-for-new-install-scripts
step all now cover both lockfiles. security-audit.yml pull_request
paths filter triggers on changes to either. wheel-smoke checks the
built wheel ships the OXC lockfile too.

Verified:
python3 scripts/lockfile_supply_chain_audit.py
> OK: 0 findings across 2 npm + 1 cargo lockfile(s)
python3 scripts/scan_npm_packages.py --lockfile oxc-validator/...
> OK
2026-05-16 09:21:33 +00:00
Daniel Han
75a8129e3b ci: tighten install-path comments
Collapse the worked-example narrative to one-line WHYs. Code is
unchanged.
2026-05-16 06:51:22 +00:00
Daniel Han
2f1d2deb5c ci: commit oxc-validator lockfile so npm ci has something to install
Followup to 7bb1eb6 (npm install -> npm ci for the oxc-validator
runtime install in studio/setup.sh, studio/setup.ps1). That commit
worked locally because the lockfile already existed there from an
earlier `npm install`, but `npm ci` failed in CI because the
lockfile was never committed:

npm error EUSAGE
npm error The `npm ci` command can only install with an existing
npm error package-lock.json or npm-shrinkwrap.json with lockfileVersion >= 1.

Root cause: the project-root .gitignore has a bare `package-lock.json`
entry left over from a Python-template gitignore. The frontend
lockfile was force-added past it; the oxc-validator lockfile never
was. So a fresh actions/checkout did not have it.

Fix:
Force-commit studio/backend/core/data_recipe/oxc-validator/package-lock.json
(5 packages, lockfileVersion 3, integrity-pinned).
Replace the bare gitignore rule with explicit `!` exceptions for
the two committed npm-project lockfiles, with a comment explaining
why stray lockfiles in random Python subtrees are still ignored.

The pyproject.toml package-data glob `backend/core/data_recipe/oxc-validator/*.json`
already pulls the lockfile into the pip-installed wheel; the only
gap was that fresh git checkouts (CI) didn't have it.
2026-05-16 06:14:14 +00:00
Daniel Han
883f26a2a4 ci: pin install to lockfile in remaining install paths
Followup to 152fe8d. Three more sites still called naked
`bun install` / `npm install`, which honour caret ranges in
package.json and can pull a fresh minor/patch of a transitive
dep from the registry on the next run.

studio/setup.ps1 (4 sites): the Windows end-user installer.
bun install -> bun install --frozen-lockfile (both initial
and the cache-clear retry); the npm fallback and the OXC
validator npm install both -> npm ci. Error messages updated
to reference the new command.

studio/setup.sh: the OXC validator runtime install for the
Unix path was still naked `npm install`. Now `npm ci`.

github/workflows/release-desktop.yml: the desktop release
build's frontend install was still naked `npm install`. Now
`npm ci` so a published .app/.dmg/.AppImage/.msi can never
have shipped with a registry-resolved transitive that drifted
from the committed lockfile.

The pinned Tauri CLI install in the same workflow stays as
`npm install --save-dev @tauri-apps/cli@2.10.1` because that
line is intentionally adding a specific package to package.json,
not syncing from the lockfile.

Verified `npm ci --no-fund --no-audit --dry-run` exits 0 against
both the studio/frontend and studio/backend/core/data_recipe/
oxc-validator lockfiles.
2026-05-16 06:04:28 +00:00
Daniel Han
53c0d8c477 ci: pin install to lockfile in build.sh and studio/setup.sh
build.sh and studio/setup.sh both call naked `bun install` and
`npm install`. With caret ranges in package.json (the default for
most deps), those commands resolve a fresh minor/patch from the
registry if one exists, even though the lockfile pins specific
versions. An attacker who hijacks any transitive dep and publishes
a malicious patch release can have it pulled into the release build
or end-user install without anyone noticing.

Both paths now use lockfile-strict mode:
bun install -> bun install --frozen-lockfile
npm install -> npm ci

These install exactly what the committed lockfile pins, verify
cryptographic hashes, and fail fast on any drift between package.json
and the lockfile. The CI workflows that build the frontend already
use `npm ci`; this aligns the local build and end-user setup paths
with the same guarantee.

Verified `npm ci --no-fund --no-audit --dry-run` exits 0 against
the current studio/frontend lockfile (1042 packages, no drift).
2026-05-16 05:34:41 +00:00
17 changed files with 4111 additions and 171 deletions

View file

@ -361,14 +361,19 @@ jobs:
with:
node-version: 24
# Pre-install lockfile structural audit. Has to fire BEFORE
# `npm ci` so any tarball's `prepare` / `postinstall` cannot run
# ahead of the scan. Pure-Python read-only; safe everywhere.
- name: Lockfile supply-chain audit (pre-install scan)
shell: bash
run: python3 scripts/lockfile_supply_chain_audit.py
- name: Install pinned Tauri CLI
# Lifecycle scripts (esbuild native-binary postinstall, etc.) are
# required for `vite build`. The pre-install lockfile structural
# audit (lockfile_supply_chain_audit.py) is the practical defence
# against the npm postinstall-dropper class -- it fires BEFORE any
# tarball runs, on the injection pattern itself rather than an
# advisory-DB lookup.
run: npm install --save-dev --prefix studio @tauri-apps/cli@2.10.1 --no-fund --no-audit
# `npm ci` resolves @tauri-apps/cli and its 11 platform-specific
# optional native binaries from studio/package-lock.json --
# transitive versions are fully pinned, integrity hashes are
# verified, and the install is reproducible across re-runs.
run: npm ci --prefix studio --no-fund --no-audit
- name: Verify pinned Tauri CLI
shell: bash
@ -443,13 +448,11 @@ jobs:
- name: Install frontend dependencies
working-directory: studio/frontend
# Lifecycle scripts (esbuild native-binary postinstall, etc.) are
# required for `vite build`. The pre-install lockfile structural
# audit (lockfile_supply_chain_audit.py) is the practical defence
# against the npm postinstall-dropper class -- it fires BEFORE any
# tarball runs, on the injection pattern itself rather than an
# advisory-DB lookup.
run: npm install --no-fund --no-audit
# npm ci: release build installs exactly what the lockfile pins.
# Lifecycle scripts (esbuild native-binary postinstall) are required
# for vite build; lockfile_supply_chain_audit.py runs pre-install as
# the defence against the postinstall-dropper class.
run: npm ci --no-fund --no-audit
# ── Rust ──
- name: Install Rust stable

View file

@ -53,6 +53,10 @@ on:
- 'studio/backend/requirements/**'
- 'studio/frontend/package.json'
- 'studio/frontend/package-lock.json'
- 'studio/backend/core/data_recipe/oxc-validator/package.json'
- 'studio/backend/core/data_recipe/oxc-validator/package-lock.json'
- 'studio/package.json'
- 'studio/package-lock.json'
- 'studio/src-tauri/Cargo.toml'
- 'studio/src-tauri/Cargo.lock'
- 'pyproject.toml'
@ -278,7 +282,7 @@ jobs:
{
echo "## Lockfile supply-chain audit"
echo
echo "Scanned: studio/frontend/package-lock.json + studio/src-tauri/Cargo.lock"
echo "Scanned: studio/frontend/package-lock.json + studio/backend/core/data_recipe/oxc-validator/package-lock.json + studio/package-lock.json + studio/src-tauri/Cargo.lock"
echo
echo "No structural anomalies or known IOC strings."
} >> "$GITHUB_STEP_SUMMARY"
@ -307,6 +311,39 @@ jobs:
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
- name: npm audit (oxc-validator runtime)
# Same audit surface, separate npm project (oxc-parser, oxlint).
continue-on-error: true
working-directory: studio/backend/core/data_recipe/oxc-validator
run: |
set +e
npm audit --audit-level=high | tee "$GITHUB_WORKSPACE/logs-npm-audit-oxc.txt"
npm audit --json > "$GITHUB_WORKSPACE/logs-npm-audit-oxc.json" || true
{
echo "## npm audit (oxc-validator)"
echo
echo '```'
tail -200 "$GITHUB_WORKSPACE/logs-npm-audit-oxc.txt"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
- name: npm audit (Studio Tauri CLI holder)
# Same audit surface, third npm project (@tauri-apps/cli for the
# signed desktop release; lockfile lives at studio/package-lock.json).
continue-on-error: true
working-directory: studio
run: |
set +e
npm audit --audit-level=high | tee "$GITHUB_WORKSPACE/logs-npm-audit-studio.txt"
npm audit --json > "$GITHUB_WORKSPACE/logs-npm-audit-studio.json" || true
{
echo "## npm audit (Studio Tauri CLI holder)"
echo
echo '```'
tail -200 "$GITHUB_WORKSPACE/logs-npm-audit-studio.txt"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
# ─────────────────────────────────────────────────────────────
# cargo: Studio Tauri shell
# ─────────────────────────────────────────────────────────────
@ -348,6 +385,8 @@ jobs:
/tmp/osv-scanner --version
/tmp/osv-scanner scan source \
--lockfile=studio/frontend/package-lock.json \
--lockfile=studio/backend/core/data_recipe/oxc-validator/package-lock.json \
--lockfile=studio/package-lock.json \
--lockfile=studio/src-tauri/Cargo.lock \
--lockfile=requirements.txt:audit-reqs/unsloth-deps.txt \
--lockfile=requirements.txt:audit-reqs/studio.txt \
@ -911,26 +950,71 @@ jobs:
# full log and surface it in the step summary either way. It
# never runs `npm install`, never executes anything from a
# downloaded tarball, and only fetches from registry.npmjs.org.
# Initially non-blocking so the baseline can settle; drop
# continue-on-error once the baseline is clean for a week.
# This step is blocking: the final exit code is the worst rc
# across all three npm projects (no continue-on-error).
#
# Three separate npm projects share this scan surface; scan each.
# Capture exit codes via PIPESTATUS so a HIGH/CRITICAL on one
# lockfile does not skip the next scan (all reports are most
# useful exactly when one already failed).
run: |
set -o pipefail
set +e
LOG=logs-scan-npm.txt
python3 scripts/scan_npm_packages.py 2>&1 | tee "$LOG"
frontend_rc=${PIPESTATUS[0]}
LOG2=logs-scan-npm-oxc.txt
python3 scripts/scan_npm_packages.py \
--lockfile studio/backend/core/data_recipe/oxc-validator/package-lock.json \
2>&1 | tee "$LOG2"
oxc_rc=${PIPESTATUS[0]}
LOG3=logs-scan-npm-studio.txt
python3 scripts/scan_npm_packages.py \
--lockfile studio/package-lock.json \
2>&1 | tee "$LOG3"
studio_rc=${PIPESTATUS[0]}
{
echo "## scan_npm_packages"
echo "## scan_npm_packages (Studio frontend)"
echo
echo '### Findings (tail)'
echo '```'
tail -300 "$LOG"
echo '```'
echo
echo "## scan_npm_packages (oxc-validator)"
echo
echo '### Findings (tail)'
echo '```'
tail -300 "$LOG2"
echo '```'
echo
echo "## scan_npm_packages (Studio Tauri CLI holder)"
echo
echo '### Findings (tail)'
echo '```'
tail -300 "$LOG3"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
if [ "$frontend_rc" -ne 0 ]; then
exit "$frontend_rc"
fi
if [ "$oxc_rc" -ne 0 ]; then
exit "$oxc_rc"
fi
exit "$studio_rc"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: always()
with:
name: scan-npm-packages-log
path: logs-scan-npm.txt
path: |
logs-scan-npm.txt
logs-scan-npm-oxc.txt
logs-scan-npm-studio.txt
retention-days: 30
# ─────────────────────────────────────────────────────────────────────
@ -1077,7 +1161,7 @@ jobs:
working-directory: studio/frontend
run: npm ci --ignore-scripts
- name: npm audit signatures (informational)
- name: npm audit signatures (Studio frontend, informational)
# Surfaces unsigned / mis-signed packages from the npm
# transparency log. continue-on-error during baseline-build
# phase; promote to hard gate once the lockfile is fully
@ -1086,10 +1170,48 @@ jobs:
continue-on-error: true
run: |
set -o pipefail
LOG=logs-audit-signatures.txt
LOG="$GITHUB_WORKSPACE/logs-audit-signatures.txt"
npm audit signatures 2>&1 | tee "$LOG"
{
echo "## npm audit signatures"
echo "## npm audit signatures (Studio frontend)"
echo
echo '```'
tail -200 "$LOG"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
- name: Install oxc-validator deps (--ignore-scripts)
working-directory: studio/backend/core/data_recipe/oxc-validator
run: npm ci --ignore-scripts
- name: npm audit signatures (oxc-validator, informational)
working-directory: studio/backend/core/data_recipe/oxc-validator
continue-on-error: true
run: |
set -o pipefail
LOG="$GITHUB_WORKSPACE/logs-audit-signatures-oxc.txt"
npm audit signatures 2>&1 | tee "$LOG"
{
echo "## npm audit signatures (oxc-validator)"
echo
echo '```'
tail -200 "$LOG"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
- name: Install Studio Tauri CLI holder deps (--ignore-scripts)
working-directory: studio
run: npm ci --ignore-scripts
- name: npm audit signatures (Studio Tauri CLI holder, informational)
working-directory: studio
continue-on-error: true
run: |
set -o pipefail
LOG="$GITHUB_WORKSPACE/logs-audit-signatures-studio.txt"
npm audit signatures 2>&1 | tee "$LOG"
{
echo "## npm audit signatures (Studio Tauri CLI holder)"
echo
echo '```'
tail -200 "$LOG"
@ -1103,6 +1225,12 @@ jobs:
BASE_SHA="${{ github.event.pull_request.base.sha }}"
git show "$BASE_SHA:studio/frontend/package-lock.json" \
> /tmp/base-package-lock.json
# OXC lockfile may not exist on the base ref (newly added).
git show "$BASE_SHA:studio/backend/core/data_recipe/oxc-validator/package-lock.json" \
> /tmp/base-oxc-package-lock.json 2>/dev/null || echo '{}' > /tmp/base-oxc-package-lock.json
# Tauri CLI holder lockfile may not exist on the base ref (newly added).
git show "$BASE_SHA:studio/package-lock.json" \
> /tmp/base-studio-package-lock.json 2>/dev/null || echo '{}' > /tmp/base-studio-package-lock.json
- name: Diff for newly-added install-script deps
if: github.event_name == 'pull_request'
@ -1110,6 +1238,12 @@ jobs:
python3 scripts/check_new_install_scripts.py \
--base /tmp/base-package-lock.json \
--head studio/frontend/package-lock.json
python3 scripts/check_new_install_scripts.py \
--base /tmp/base-oxc-package-lock.json \
--head studio/backend/core/data_recipe/oxc-validator/package-lock.json
python3 scripts/check_new_install_scripts.py \
--base /tmp/base-studio-package-lock.json \
--head studio/package-lock.json
- name: Skip install-script diff (non-PR trigger)
if: github.event_name != 'pull_request'
@ -1121,6 +1255,9 @@ jobs:
if: always()
with:
name: npm-audit-signatures-log
path: studio/frontend/logs-audit-signatures.txt
path: |
logs-audit-signatures.txt
logs-audit-signatures-oxc.txt
logs-audit-signatures-studio.txt
if-no-files-found: ignore
retention-days: 30

View file

@ -60,14 +60,15 @@ jobs:
with:
workspaces: studio/src-tauri -> target
# Must run BEFORE any `npm ci` so a tarball's prepare/postinstall
# cannot execute ahead of the structural scan.
- name: Lockfile supply-chain audit (pre-install scan)
run: python3 scripts/lockfile_supply_chain_audit.py
- name: Install pinned Tauri CLI (matches release-desktop.yml)
# Lifecycle scripts (esbuild native-binary postinstall, etc.) are
# required for `vite build`. The pre-install lockfile structural
# audit (lockfile_supply_chain_audit.py) is the practical defence
# against the npm postinstall-dropper class -- it fires BEFORE any
# tarball runs, on the injection pattern itself rather than an
# advisory-DB lookup.
run: npm install --save-dev --prefix studio @tauri-apps/cli@2.10.1 --no-fund --no-audit
# `npm ci` resolves @tauri-apps/cli from studio/package-lock.json
# (transitives fully pinned, integrity hashes verified).
run: npm ci --prefix studio --no-fund --no-audit
- name: Verify pinned Tauri CLI version
run: |
@ -75,17 +76,11 @@ jobs:
echo "$out"
[ "$out" = "tauri-cli 2.10.1" ] || { echo "::error::expected tauri-cli 2.10.1, got $out"; exit 1; }
- name: Lockfile supply-chain audit (pre-install scan)
run: python3 scripts/lockfile_supply_chain_audit.py
- name: Frontend build (npm ci, vite)
working-directory: studio/frontend
# Lifecycle scripts (esbuild native-binary postinstall, etc.) are
# required for `vite build`. The pre-install lockfile structural
# audit (lockfile_supply_chain_audit.py) is the practical defence
# against the npm postinstall-dropper class -- it fires BEFORE any
# tarball runs, on the injection pattern itself rather than an
# advisory-DB lookup.
# Vite build needs esbuild's native-binary postinstall; the
# pre-install lockfile audit above is what gates that path
# against the npm postinstall-dropper class.
run: |
npm ci --no-fund --no-audit
npm run build

View file

@ -87,6 +87,7 @@ jobs:
n = z.namelist()
checks = {
"lockfile shipped": any(s.endswith("studio/frontend/package-lock.json") for s in n),
"oxc lockfile shipped": any(s.endswith("oxc-validator/package-lock.json") for s in n),
"frontend dist shipped": any(s.endswith("studio/frontend/dist/index.html") for s in n),
"no node_modules": not any("studio/frontend/node_modules/" in s for s in n),
"no bun.lock": not any(s.endswith("studio/frontend/bun.lock") for s in n),

10
.gitignore vendored
View file

@ -229,5 +229,15 @@ log.txt
setup_leo.sh
server.pid
*.log
# Ignore stray lockfiles; real npm projects opt back in below (npm ci needs them).
package-lock.json
!studio/frontend/package-lock.json
!studio/backend/core/data_recipe/oxc-validator/package-lock.json
!studio/package-lock.json
# Bun lockfiles: same opt-in policy as package-lock.json above.
# bun install --frozen-lockfile needs these committed for the same surfaces.
bun.lock
!studio/frontend/bun.lock
!studio/backend/core/data_recipe/oxc-validator/bun.lock
!studio/bun.lock
llama.cpp/

View file

@ -33,19 +33,36 @@ _restore_gitignores() {
}
trap _restore_gitignores EXIT
# Use bun for install if available (faster), fall back to npm.
# Frontend install: Bun --frozen-lockfile first (faster) if a
# committed bun.lock is present, npm ci as the always-available
# fallback. Both run lockfile-strict so the install is byte-
# reproducible from whichever lockfile the chosen package manager
# understands. Build always runs through Node -- avoids bun runtime
# quirks on some platforms. This is build.sh (release wheel build,
# typically CI); we do NOT auto-install bun here -- the calling
# environment should provide it explicitly. The cache-corruption
# recovery ladder mirrors studio/setup.sh.
_install_ok=false
if command -v bun &>/dev/null; then
if bun install; then
_install_ok=true
else
echo "⚠ bun install failed, falling back to npm"
if [ -f bun.lock ] && command -v bun &>/dev/null; then
_attempts=0
while [ "$_attempts" -lt 2 ] && [ "$_install_ok" != "true" ]; do
_attempts=$((_attempts + 1))
if bun install --frozen-lockfile --no-progress \
&& { [ -x node_modules/.bin/tsc ] || [ -f node_modules/.bin/tsc.exe ]; } \
&& { [ -x node_modules/.bin/vite ] || [ -f node_modules/.bin/vite.exe ]; }; then
_install_ok=true
break
fi
echo "⚠ bun --frozen-lockfile incomplete (try $_attempts); clearing cache + retrying"
rm -rf node_modules
fi
bun pm cache rm >/dev/null 2>&1 || true
done
fi
if [ "$_install_ok" != "true" ]; then
if ! npm install; then
echo "❌ ERROR: package install failed" >&2
echo "→ falling back to npm ci"
rm -rf node_modules
if ! npm ci --no-fund --no-audit; then
echo "❌ ERROR: npm ci failed" >&2
exit 1
fi
fi

View file

@ -49,6 +49,8 @@ EXPECTED_NOISE_FILES = {
"studio/frontend/package-lock.json",
"studio/backend/core/data_recipe/oxc-validator/package.json",
"studio/backend/core/data_recipe/oxc-validator/package-lock.json",
"studio/package.json",
"studio/package-lock.json",
}
# Only quoted-string occurrences in these file types can be module specifiers.

View file

@ -397,9 +397,35 @@ class Finding:
def audit_npm_lockfile(path: Path) -> list[Finding]:
findings: list[Finding] = []
if not path.exists():
# A missing requested lockfile is a config error, not a clean
# audit; surface it so a deleted default cannot pass silently.
findings.append(
Finding(
path = str(path),
package = "<root>",
kind = "missing-lockfile",
detail = (
"expected lockfile not found; refusing to silently "
"report a clean audit for a path that was not scanned"
),
)
)
return findings
raw = path.read_text(encoding = "utf-8")
try:
raw = path.read_text(encoding = "utf-8")
except OSError as exc:
# Permission denied, is-a-directory, broken-pipe etc. -- surface
# as a finding instead of crashing CI with a raw traceback.
findings.append(
Finding(
path = str(path),
package = "<root>",
kind = "unreadable-lockfile",
detail = f"could not read file: {exc}",
)
)
return findings
try:
lock = json.loads(raw)
except json.JSONDecodeError as exc:
@ -550,12 +576,201 @@ def _first_line_containing(text: str, needle: str) -> int | None:
_PACKAGE_HEADER = re.compile(r"^\[\[package\]\]\s*$")
# Bun's text lockfile (bun.lock, Bun >= 1.2) is JSONC -- valid JSON with
# trailing commas allowed. Strip them before json.loads.
_BUN_LOCK_TRAILING_COMMA_RE = re.compile(r",(\s*[}\]])")
def _parse_bun_lockfile_text(raw: str) -> dict:
"""Parse bun.lock JSONC text by stripping trailing commas."""
cleaned = _BUN_LOCK_TRAILING_COMMA_RE.sub(r"\1", raw)
return json.loads(cleaned)
def audit_bun_lockfile(path: Path) -> list[Finding]:
"""Pre-install audit for Bun's text lockfile (bun.lock, version 1).
Each package entry is a JSON array shaped like:
"pkg-name": [
"pkg-name@version",
"<registry-url-or-empty>",
{"optionalDependencies": ..., "bin": ..., "os": ..., ...},
"sha512-<base64>" # integrity, registry-resolved only
]
git/file/tarball-resolved entries carry the source URL in element 1
and lack the sha512 tail. We flag any non-default-registry source
and any registry entry without a sha512 tail.
"""
findings: list[Finding] = []
if not path.exists():
findings.append(
Finding(
path = str(path),
package = "<root>",
kind = "missing-lockfile",
detail = (
"expected lockfile not found; refusing to silently "
"report a clean audit for a path that was not scanned"
),
)
)
return findings
try:
raw = path.read_text(encoding = "utf-8")
except OSError as exc:
findings.append(
Finding(
path = str(path),
package = "<root>",
kind = "unreadable-lockfile",
detail = f"could not read file: {exc}",
)
)
return findings
try:
lock = _parse_bun_lockfile_text(raw)
except json.JSONDecodeError as exc:
findings.append(
Finding(
path = str(path),
package = "<root>",
kind = "malformed-lockfile",
detail = f"could not parse bun.lock as JSONC: {exc}",
)
)
return findings
lockfile_version = lock.get("lockfileVersion")
if lockfile_version != 1:
findings.append(
Finding(
path = str(path),
package = "<root>",
kind = "unsupported-lockfile-version",
detail = f"only bun lockfileVersion 1 audited; got {lockfile_version}",
)
)
packages = lock.get("packages") or {}
for pkg_name, entry in packages.items():
if not isinstance(entry, list) or len(entry) < 2:
continue
# entry[0] is "name@version"; entry[1] is the source URL when
# non-default-registry (e.g. "git+https://..." or "file:./..."),
# or "" when resolved against the default npm registry.
nv = entry[0] if isinstance(entry[0], str) else ""
# version is everything after the LAST '@' (handles scoped names
# like "@scope/name@1.2.3").
version = nv.rsplit("@", 1)[-1] if "@" in nv else ""
source = entry[1] if len(entry) > 1 and isinstance(entry[1], str) else ""
# 1. Source origin: empty string means default registry. Anything
# else (git+, file:, http://github.com/, tarball URL) is flagged
# the same way audit_npm_lockfile flags non-registry resolved URLs.
if source and not any(
source.startswith(p) for p in NPM_REGISTRY_PREFIXES_ALLOWED
):
findings.append(
Finding(
path = str(path),
package = pkg_name,
kind = "non-registry-resolved-url",
detail = (
f"source={source!r}; only {NPM_REGISTRY_PREFIX} "
"is permitted. Direct GitHub / git / file references "
"are the Shai-Hulud injection vector."
),
)
)
# 2. Integrity hash: registry-resolved entries carry a sha-prefixed
# tail element. Missing tail on a registry entry is a finding.
has_sha_tail = isinstance(entry[-1], str) and (
entry[-1].startswith("sha512-")
or entry[-1].startswith("sha256-")
or entry[-1].startswith("sha1-")
)
if not source and not has_sha_tail:
findings.append(
Finding(
path = str(path),
package = pkg_name,
kind = "missing-integrity-hash",
detail = (
"no sha-prefixed integrity tail in bun.lock entry; "
"bun cannot verify the tarball SHA against the "
"registry-published hash"
),
)
)
# 3. Blocked-malicious-version list.
blocked = BLOCKED_NPM_VERSIONS.get(pkg_name, set())
if version and version in blocked:
findings.append(
Finding(
path = str(path),
package = pkg_name,
kind = "blocked-known-malicious",
detail = (
f"{pkg_name}@{version} is on the BLOCKED_NPM_VERSIONS list"
),
)
)
# 4. IOC string scan against the raw file body (mirrors audit_npm_lockfile).
for ioc in NPM_IOC_STRINGS:
if ioc in raw:
line_no = _first_line_containing(raw, ioc)
findings.append(
Finding(
path = f"{path}:{line_no}" if line_no else str(path),
package = "<ioc-match>",
kind = "known-ioc-string",
detail = (
f"matched known IOC substring {ioc!r}; this is "
"a public indicator of a recent supply-chain "
"compromise. Refuse to install."
),
)
)
return findings
def audit_cargo_lockfile(path: Path) -> list[Finding]:
findings: list[Finding] = []
if not path.exists():
# See audit_npm_lockfile: missing lockfile is a finding.
findings.append(
Finding(
path = str(path),
package = "<root>",
kind = "missing-lockfile",
detail = (
"expected lockfile not found; refusing to silently "
"report a clean audit for a path that was not scanned"
),
)
)
return findings
raw = path.read_text(encoding = "utf-8")
try:
raw = path.read_text(encoding = "utf-8")
except OSError as exc:
findings.append(
Finding(
path = str(path),
package = "<root>",
kind = "unreadable-lockfile",
detail = f"could not read file: {exc}",
)
)
return findings
try:
import tomllib # type: ignore[import-not-found]
except ImportError:
@ -652,7 +867,16 @@ def audit_cargo_lockfile(path: Path) -> list[Finding]:
# ─────────────────────────────────────────────────────────────────────
DEFAULT_NPM_LOCKFILES = ("studio/frontend/package-lock.json",)
DEFAULT_NPM_LOCKFILES = (
"studio/frontend/package-lock.json",
"studio/backend/core/data_recipe/oxc-validator/package-lock.json",
"studio/package-lock.json",
)
DEFAULT_BUN_LOCKFILES = (
"studio/frontend/bun.lock",
"studio/backend/core/data_recipe/oxc-validator/bun.lock",
"studio/bun.lock",
)
DEFAULT_CARGO_LOCKFILES = ("studio/src-tauri/Cargo.lock",)
@ -671,7 +895,20 @@ def main(argv: list[str] | None = None) -> int:
default = None,
help = (
"Path to a package-lock.json (repeatable). "
"Default: studio/frontend/package-lock.json."
"Default: studio/frontend/package-lock.json, "
"studio/backend/core/data_recipe/oxc-validator/package-lock.json, "
"and studio/package-lock.json (Tauri CLI for desktop release)."
),
)
parser.add_argument(
"--bun-lockfile",
action = "append",
default = None,
help = (
"Path to a bun.lock (repeatable). "
"Default: studio/frontend/bun.lock, "
"studio/backend/core/data_recipe/oxc-validator/bun.lock, "
"and studio/bun.lock (Tauri CLI for desktop release)."
),
)
parser.add_argument(
@ -715,13 +952,29 @@ def main(argv: list[str] | None = None) -> int:
return 0
root = Path(args.root).resolve()
npm_paths = [root / p for p in (args.npm_lockfile or DEFAULT_NPM_LOCKFILES)]
cargo_paths = [root / p for p in (args.cargo_lockfile or DEFAULT_CARGO_LOCKFILES)]
# Explicit --npm-lockfile/--bun-lockfile/--cargo-lockfile scopes the
# scan to those paths; defaults apply only to the no-args CI invocation.
_user_explicit = (
args.npm_lockfile is not None
or args.bun_lockfile is not None
or args.cargo_lockfile is not None
)
if _user_explicit:
npm_paths = [root / p for p in (args.npm_lockfile or ())]
bun_paths = [root / p for p in (args.bun_lockfile or ())]
cargo_paths = [root / p for p in (args.cargo_lockfile or ())]
else:
npm_paths = [root / p for p in DEFAULT_NPM_LOCKFILES]
bun_paths = [root / p for p in DEFAULT_BUN_LOCKFILES]
cargo_paths = [root / p for p in DEFAULT_CARGO_LOCKFILES]
all_findings: list[Finding] = []
for p in npm_paths:
print(f"[lockfile-audit] npm: {p}", flush = True)
all_findings.extend(audit_npm_lockfile(p))
for p in bun_paths:
print(f"[lockfile-audit] bun: {p}", flush = True)
all_findings.extend(audit_bun_lockfile(p))
for p in cargo_paths:
print(f"[lockfile-audit] cargo: {p}", flush = True)
all_findings.extend(audit_cargo_lockfile(p))
@ -729,7 +982,8 @@ def main(argv: list[str] | None = None) -> int:
if not all_findings:
print(
f"[lockfile-audit] OK: 0 findings across "
f"{len(npm_paths)} npm + {len(cargo_paths)} cargo lockfile(s)",
f"{len(npm_paths)} npm + {len(bun_paths)} bun + "
f"{len(cargo_paths)} cargo lockfile(s)",
flush = True,
)
return 0

View file

@ -0,0 +1,110 @@
{
"lockfileVersion": 1,
"configVersion": 0,
"workspaces": {
"": {
"name": "unsloth-oxc-validator-runtime",
"dependencies": {
"oxc-parser": "^0.123.0",
"oxlint": "^1.51.0",
},
},
},
"packages": {
"@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="],
"@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="],
"@oxc-parser/binding-android-arm-eabi": ["@oxc-parser/binding-android-arm-eabi@0.123.0", "", { "os": "android", "cpu": "arm" }, "sha512-EHQ58z+6DbZWokMOKg5AB1KuwrXVgfbBLuuLFfzdc7bI5A4igvdvjKMhUv1VBV+0FABiUCOjNKUmMF7ugprwbQ=="],
"@oxc-parser/binding-android-arm64": ["@oxc-parser/binding-android-arm64@0.123.0", "", { "os": "android", "cpu": "arm64" }, "sha512-BK1E0zqNoHf38nTHjnGZ+olKHSKNHh65pChjY06yhaWYP8X7yNDqhQDA4neMPRqnPBgpN4/OW1oSMrdJgDi2aw=="],
"@oxc-parser/binding-darwin-arm64": ["@oxc-parser/binding-darwin-arm64@0.123.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-dkMPbtTbqU+cm+k4YGOBs4zAuq3Xu+wqjbGQvLAuVO7qHhNY4p5LBNudOmOoi0jxS8h1W6Jmlzv8MAKGpK+iDg=="],
"@oxc-parser/binding-darwin-x64": ["@oxc-parser/binding-darwin-x64@0.123.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-85pic0rCd59DGdM69jI9xE/Snb2KtrfiU48QigjJXjzxUOenGvH4SAFIjFpO/2ZnI3Kz50D8pht4jKN3t2022Q=="],
"@oxc-parser/binding-freebsd-x64": ["@oxc-parser/binding-freebsd-x64@0.123.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-mjEiW6z7JtaiHMK/8aJic1lfjkKpzFwK2XFNmm187BFbtDamjGVuKNr2TEyrFEYJyZc217wokR1wrYeZGBQo4Q=="],
"@oxc-parser/binding-linux-arm-gnueabihf": ["@oxc-parser/binding-linux-arm-gnueabihf@0.123.0", "", { "os": "linux", "cpu": "arm" }, "sha512-mYxigPtGt6SZfhNZBIJfuDM92cLo8XUW08WuKxzHvcmWu6xndLqwLp99Vg4uHke1AXicQEHU3Wri2X9bHF0Vlw=="],
"@oxc-parser/binding-linux-arm-musleabihf": ["@oxc-parser/binding-linux-arm-musleabihf@0.123.0", "", { "os": "linux", "cpu": "arm" }, "sha512-ttWirDC9eUBn0R4Tzz3aeDaLrx9drPdNiLJ8MXeDBFxd6cwLfTIC27qjsdfGpn942tkVIZY3sjWAnvbwDDjX7g=="],
"@oxc-parser/binding-linux-arm64-gnu": ["@oxc-parser/binding-linux-arm64-gnu@0.123.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-apAHyoMNRYT+2G98Y14caZmsr5LD9PsWpGI7nXmSwK26LGiQneCU6HvHQ+d+AX+RJ5TTWZtEb2RD7OLqAC0cYQ=="],
"@oxc-parser/binding-linux-arm64-musl": ["@oxc-parser/binding-linux-arm64-musl@0.123.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-3r99Qa4egjO/iXUBxTlN6Ddt1YkLifG6olzvj8gkoKEK2U/MOW7mQfXRyBmuoMgmZ7O4vk41gO3d21c6VcN3yQ=="],
"@oxc-parser/binding-linux-ppc64-gnu": ["@oxc-parser/binding-linux-ppc64-gnu@0.123.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Hr/Z24kUE4pjJs346g80WDwjyJGrxiw6hExJuOiME/76ZFz68y5L11UzprRkW9FN4HxBB7tLZ/fytczV2fEsiA=="],
"@oxc-parser/binding-linux-riscv64-gnu": ["@oxc-parser/binding-linux-riscv64-gnu@0.123.0", "", { "os": "linux", "cpu": "none" }, "sha512-sxjbhs+8WXeuoLnZ2rBmQ96gPdq3SCmz24reIltsKLUt1EDMgdaQsr7RqwBphw3QAImkMtlPQfAWDWwZyo0xDg=="],
"@oxc-parser/binding-linux-riscv64-musl": ["@oxc-parser/binding-linux-riscv64-musl@0.123.0", "", { "os": "linux", "cpu": "none" }, "sha512-d6xHHhqldA/W+VC7v8uHs24zM69Ad3HnHQ45h+uuBhCsbZx3d0E0wL2K3uJ5mYKTR6UPMFk9VMXcHWwvg1PRZQ=="],
"@oxc-parser/binding-linux-s390x-gnu": ["@oxc-parser/binding-linux-s390x-gnu@0.123.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-+di9A5wJQlv0VodyhADjJ2rC4geyHY+uhJDl3TFjMgYhhlgLZchi9uHD5mfiUEDWHt1x7/eU2u1ge3LLazZmFw=="],
"@oxc-parser/binding-linux-x64-gnu": ["@oxc-parser/binding-linux-x64-gnu@0.123.0", "", { "os": "linux", "cpu": "x64" }, "sha512-sh7pw2g/u6LE1TaRRQsV9Kv9+1y+CywaaNwWWP+3bnEPk/L692oTG0hmEviUlawI8v3OGC+AhbjtAD+HXWQAkg=="],
"@oxc-parser/binding-linux-x64-musl": ["@oxc-parser/binding-linux-x64-musl@0.123.0", "", { "os": "linux", "cpu": "x64" }, "sha512-S+LoD8PiJ639JwIqK1knIeqAyYkeCbLHtAgfapszKX0yVCaYP+aer8dJxL25de9qcDjvYWVrYCkuDZzHmOl2Xw=="],
"@oxc-parser/binding-openharmony-arm64": ["@oxc-parser/binding-openharmony-arm64@0.123.0", "", { "os": "none", "cpu": "arm64" }, "sha512-/65vryK11q1I+k+7ukDlwZOxUFCLYsoZBZPGZHyet5bIP5e3D8mV3uCuvpWZ9Hoe6vUZFw/nAfCrX59MeuJPgw=="],
"@oxc-parser/binding-wasm32-wasi": ["@oxc-parser/binding-wasm32-wasi@0.123.0", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.1.2" }, "cpu": "none" }, "sha512-y4OsMGQiAbZzj2Rq0LEfvhR48rQDvbvqsl/dPdn4tdf+z3H79nZuR+lQ/+KUGjD30vpVGem138sBWHFj9UR+Vg=="],
"@oxc-parser/binding-win32-arm64-msvc": ["@oxc-parser/binding-win32-arm64-msvc@0.123.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-9lBqI6AXAkjYavkdpizNU3Q51uoVYfp9FJPx19hnCEdPku1jSgzSnvgmCvhCue0GziIvIvIdWgZ41wXQ3EOoBw=="],
"@oxc-parser/binding-win32-ia32-msvc": ["@oxc-parser/binding-win32-ia32-msvc@0.123.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-zJbqBHwSUB7CyvAONy9ewGtQwcQj+ylOhYGETvUPp3KIYx7lolj4Gayof7iA22SU5eMSjO5COL0c8wYhmn9agA=="],
"@oxc-parser/binding-win32-x64-msvc": ["@oxc-parser/binding-win32-x64-msvc@0.123.0", "", { "os": "win32", "cpu": "x64" }, "sha512-q7RZvglQvGo3RX5ljtcGSabu2B2c0oDU/6xC3sBMhsV5KRo0PvyxLdordbEN31NTfuZu4Sgl86C76cAURZIHWA=="],
"@oxc-project/types": ["@oxc-project/types@0.123.0", "", {}, "sha512-YtECP/y8Mj1lSHiUWGSRzy/C6teUKlS87dEfuVKT09LgQbUsBW1rNg+MiJ4buGu3yuADV60gbIvo9/HplA56Ew=="],
"@oxlint/binding-android-arm-eabi": ["@oxlint/binding-android-arm-eabi@1.64.0", "", { "os": "android", "cpu": "arm" }, "sha512-2r6Nq3XXGLHEXKkSj8JtmJ6N4gDw431DPFOg0ZoJHlNjnG6HVMm/ksQ10m0HJ8WBvwgMe1L50UHPaYZutCRPCw=="],
"@oxlint/binding-android-arm64": ["@oxlint/binding-android-arm64@1.64.0", "", { "os": "android", "cpu": "arm64" }, "sha512-ePJMpePgg7fBv+L/hVx1xXRU5/5gd5m0obLA6hPEfLXF3GjpR8idIDbY1dhQYhyz1ms2wdTccSboo6KEd2Oxtg=="],
"@oxlint/binding-darwin-arm64": ["@oxlint/binding-darwin-arm64@1.64.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-U4DMLQd10gJLuoSTLSGbfv3bGjTlUNsScm9Dgb8wwBqmCzidf1pE1pXV4doGNxqwH3KtVng1AGTINA0NvkGLvQ=="],
"@oxlint/binding-darwin-x64": ["@oxlint/binding-darwin-x64@1.64.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-GoRIL48QWm4/TAvjN8pB1nAG+1/uqc9EdnWT9zqHeb6wsmjZtywj8VRe5aGW47Fdb64YtLOsdLqVxOvQuz98Wg=="],
"@oxlint/binding-freebsd-x64": ["@oxlint/binding-freebsd-x64@1.64.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-5dFkv4tkg7PxJJGS9/OjrJwjhuHczrd3OQOkRE0wHcLM+ncUnULtzEPWjqGOxTXxZnLWcB91bGiIznx89TVXyQ=="],
"@oxlint/binding-linux-arm-gnueabihf": ["@oxlint/binding-linux-arm-gnueabihf@1.64.0", "", { "os": "linux", "cpu": "arm" }, "sha512-jsBqMLl/uOL5+Kq/+BtK9FrmiNGUbx8SiyZXv+WlUxA45KuwcLu9BfiSIL3I3DBDgWM3yZizDITnTK9BcqNBQg=="],
"@oxlint/binding-linux-arm-musleabihf": ["@oxlint/binding-linux-arm-musleabihf@1.64.0", "", { "os": "linux", "cpu": "arm" }, "sha512-1lrj8At/Uuc9GhjrVFBQo0NEjfBrTkzpmtHIGAhNnIXqn1CAyGL+qrztUsXb2GIluJrpl9Q7qRLJOb/NqydacQ=="],
"@oxlint/binding-linux-arm64-gnu": ["@oxlint/binding-linux-arm64-gnu@1.64.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-HpSQbubwh03mMhAdy2BYtad/fsY8vDFHDAb6bUwuCYg2VD3xCQgn6ArKcO0oZyLCheacKTv4PrF3Mfu5hgoE2g=="],
"@oxlint/binding-linux-arm64-musl": ["@oxlint/binding-linux-arm64-musl@1.64.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-00QQ0h0Y7u0G69BgiH3+ky2aaq/QvkDL6DYok8htIuJHxybiux5aQ8jwmg8qIk9wha6UagUP2BAwAzbemcJbpg=="],
"@oxlint/binding-linux-ppc64-gnu": ["@oxlint/binding-linux-ppc64-gnu@1.64.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-2GaimTV6EMW+s5HS0An3oGbQme3BgHswvfVdGk3EB57Xe9+/gyT+Qd7lNVzb3rtir52vbIPzXfaYArzs5b5zcw=="],
"@oxlint/binding-linux-riscv64-gnu": ["@oxlint/binding-linux-riscv64-gnu@1.64.0", "", { "os": "linux", "cpu": "none" }, "sha512-H46AtFb9wypjoVwGdlxrm0DsD809NGmtiK9HiyPKTxkSte2YjhC4S+00rOIrwCaxcyPiGid3Y3OMXp5KMAkGZw=="],
"@oxlint/binding-linux-riscv64-musl": ["@oxlint/binding-linux-riscv64-musl@1.64.0", "", { "os": "linux", "cpu": "none" }, "sha512-HEgsidjjvvyzdg82icYkuFCf7REDV7B9JFwbIMbVwrKLBY0MrXX+bku3POn/hduZ2yW91IyVDUMq0Bf02KwXQw=="],
"@oxlint/binding-linux-s390x-gnu": ["@oxlint/binding-linux-s390x-gnu@1.64.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-Axvm8qryotmKN00P5w4JapaSjvP2LOSbdbBJiX+2SuHd3QzhW7TUc8skqgw+ahQZ5DmzEYeHCqauvW8f32Ns6Q=="],
"@oxlint/binding-linux-x64-gnu": ["@oxlint/binding-linux-x64-gnu@1.64.0", "", { "os": "linux", "cpu": "x64" }, "sha512-cR60vSd7+m+KRZ3GQGfDxWwahW5RMXg0qlGvAluZr0fTUYvw0H9N9AXAF/M/PMqgytyqvVNmBAkJG9l7U30Y1g=="],
"@oxlint/binding-linux-x64-musl": ["@oxlint/binding-linux-x64-musl@1.64.0", "", { "os": "linux", "cpu": "x64" }, "sha512-2u/aPZ9pEg7HnvZPDsHxUGNnrpr4qaHi+mCgLgpt+LYRzPrS4Px4wPfkIdRdr2GvKnaYyt+XSlto0Vm5sbStTg=="],
"@oxlint/binding-openharmony-arm64": ["@oxlint/binding-openharmony-arm64@1.64.0", "", { "os": "none", "cpu": "arm64" }, "sha512-kfhkGfCdoXLSxEkrhDlJrvBYajGmq+ma4EMc53dsOWTq+rIBOlI0vTBmpZNnM5oH2LY/K/w1HAK+UQEgjgpVUg=="],
"@oxlint/binding-win32-arm64-msvc": ["@oxlint/binding-win32-arm64-msvc@1.64.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-r/cNKBFieONoVu2bb1KkVouq9W+edDUgHumXJGphCRRj+U0xaD4nanrw8ZOqo0IsutPkEM4vCcGBpak6x5aXMg=="],
"@oxlint/binding-win32-ia32-msvc": ["@oxlint/binding-win32-ia32-msvc@1.64.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-tUw0xUUwEFVZbpJoeCblkv8SJA4Xz3CdXCJbAnBsiNLyxDrk2tLcxEAS6M73Q7hHHDg3OtwI8vZVK3t5RJt4Gw=="],
"@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.64.0", "", { "os": "win32", "cpu": "x64" }, "sha512-9CBR+LO0JVST87fNTzzNxS5I29jIUO5gxT9i9+M3SDHHALElj9sY1Prf12tad3vIRC6OD7Ehtvvh+sn13vSwHw=="],
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="],
"oxc-parser": ["oxc-parser@0.123.0", "", { "dependencies": { "@oxc-project/types": "^0.123.0" }, "optionalDependencies": { "@oxc-parser/binding-android-arm-eabi": "0.123.0", "@oxc-parser/binding-android-arm64": "0.123.0", "@oxc-parser/binding-darwin-arm64": "0.123.0", "@oxc-parser/binding-darwin-x64": "0.123.0", "@oxc-parser/binding-freebsd-x64": "0.123.0", "@oxc-parser/binding-linux-arm-gnueabihf": "0.123.0", "@oxc-parser/binding-linux-arm-musleabihf": "0.123.0", "@oxc-parser/binding-linux-arm64-gnu": "0.123.0", "@oxc-parser/binding-linux-arm64-musl": "0.123.0", "@oxc-parser/binding-linux-ppc64-gnu": "0.123.0", "@oxc-parser/binding-linux-riscv64-gnu": "0.123.0", "@oxc-parser/binding-linux-riscv64-musl": "0.123.0", "@oxc-parser/binding-linux-s390x-gnu": "0.123.0", "@oxc-parser/binding-linux-x64-gnu": "0.123.0", "@oxc-parser/binding-linux-x64-musl": "0.123.0", "@oxc-parser/binding-openharmony-arm64": "0.123.0", "@oxc-parser/binding-wasm32-wasi": "0.123.0", "@oxc-parser/binding-win32-arm64-msvc": "0.123.0", "@oxc-parser/binding-win32-ia32-msvc": "0.123.0", "@oxc-parser/binding-win32-x64-msvc": "0.123.0" } }, "sha512-F6ak0tFc01ZGbl5KxvLDQ2K005Z086mp3ByCQBDhUjqXLkapGUkMuJSsYixncdEpkLlcRDcruHR71LD339ADUA=="],
"oxlint": ["oxlint@1.64.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.64.0", "@oxlint/binding-android-arm64": "1.64.0", "@oxlint/binding-darwin-arm64": "1.64.0", "@oxlint/binding-darwin-x64": "1.64.0", "@oxlint/binding-freebsd-x64": "1.64.0", "@oxlint/binding-linux-arm-gnueabihf": "1.64.0", "@oxlint/binding-linux-arm-musleabihf": "1.64.0", "@oxlint/binding-linux-arm64-gnu": "1.64.0", "@oxlint/binding-linux-arm64-musl": "1.64.0", "@oxlint/binding-linux-ppc64-gnu": "1.64.0", "@oxlint/binding-linux-riscv64-gnu": "1.64.0", "@oxlint/binding-linux-riscv64-musl": "1.64.0", "@oxlint/binding-linux-s390x-gnu": "1.64.0", "@oxlint/binding-linux-x64-gnu": "1.64.0", "@oxlint/binding-linux-x64-musl": "1.64.0", "@oxlint/binding-openharmony-arm64": "1.64.0", "@oxlint/binding-win32-arm64-msvc": "1.64.0", "@oxlint/binding-win32-ia32-msvc": "1.64.0", "@oxlint/binding-win32-x64-msvc": "1.64.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.22.1" }, "optionalPeers": ["oxlint-tsgolint"], "bin": "bin/oxlint" }, "sha512-Star3SNpWPeWFPw7kRXIhXUSn6fdiAl25q15CQzH/9WaOtG6e9CWTc25vNZOCr4PE1yEP1GtKJKIKglhj3OmEQ=="],
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
}
}

View file

@ -0,0 +1,799 @@
{
"name": "unsloth-oxc-validator-runtime",
"version": "0.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "unsloth-oxc-validator-runtime",
"version": "0.0.1",
"dependencies": {
"oxc-parser": "^0.123.0",
"oxlint": "^1.51.0"
}
},
"node_modules/@emnapi/core": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@emnapi/wasi-threads": "1.2.1",
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/runtime": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/wasi-threads": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@napi-rs/wasm-runtime": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
"integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==",
"license": "MIT",
"optional": true,
"dependencies": {
"@tybys/wasm-util": "^0.10.1"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
},
"peerDependencies": {
"@emnapi/core": "^1.7.1",
"@emnapi/runtime": "^1.7.1"
}
},
"node_modules/@oxc-parser/binding-android-arm-eabi": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.123.0.tgz",
"integrity": "sha512-EHQ58z+6DbZWokMOKg5AB1KuwrXVgfbBLuuLFfzdc7bI5A4igvdvjKMhUv1VBV+0FABiUCOjNKUmMF7ugprwbQ==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-android-arm64": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.123.0.tgz",
"integrity": "sha512-BK1E0zqNoHf38nTHjnGZ+olKHSKNHh65pChjY06yhaWYP8X7yNDqhQDA4neMPRqnPBgpN4/OW1oSMrdJgDi2aw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-darwin-arm64": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.123.0.tgz",
"integrity": "sha512-dkMPbtTbqU+cm+k4YGOBs4zAuq3Xu+wqjbGQvLAuVO7qHhNY4p5LBNudOmOoi0jxS8h1W6Jmlzv8MAKGpK+iDg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-darwin-x64": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.123.0.tgz",
"integrity": "sha512-85pic0rCd59DGdM69jI9xE/Snb2KtrfiU48QigjJXjzxUOenGvH4SAFIjFpO/2ZnI3Kz50D8pht4jKN3t2022Q==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-freebsd-x64": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.123.0.tgz",
"integrity": "sha512-mjEiW6z7JtaiHMK/8aJic1lfjkKpzFwK2XFNmm187BFbtDamjGVuKNr2TEyrFEYJyZc217wokR1wrYeZGBQo4Q==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-linux-arm-gnueabihf": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.123.0.tgz",
"integrity": "sha512-mYxigPtGt6SZfhNZBIJfuDM92cLo8XUW08WuKxzHvcmWu6xndLqwLp99Vg4uHke1AXicQEHU3Wri2X9bHF0Vlw==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-linux-arm-musleabihf": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.123.0.tgz",
"integrity": "sha512-ttWirDC9eUBn0R4Tzz3aeDaLrx9drPdNiLJ8MXeDBFxd6cwLfTIC27qjsdfGpn942tkVIZY3sjWAnvbwDDjX7g==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-linux-arm64-gnu": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.123.0.tgz",
"integrity": "sha512-apAHyoMNRYT+2G98Y14caZmsr5LD9PsWpGI7nXmSwK26LGiQneCU6HvHQ+d+AX+RJ5TTWZtEb2RD7OLqAC0cYQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-linux-arm64-musl": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.123.0.tgz",
"integrity": "sha512-3r99Qa4egjO/iXUBxTlN6Ddt1YkLifG6olzvj8gkoKEK2U/MOW7mQfXRyBmuoMgmZ7O4vk41gO3d21c6VcN3yQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-linux-ppc64-gnu": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.123.0.tgz",
"integrity": "sha512-Hr/Z24kUE4pjJs346g80WDwjyJGrxiw6hExJuOiME/76ZFz68y5L11UzprRkW9FN4HxBB7tLZ/fytczV2fEsiA==",
"cpu": [
"ppc64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-linux-riscv64-gnu": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.123.0.tgz",
"integrity": "sha512-sxjbhs+8WXeuoLnZ2rBmQ96gPdq3SCmz24reIltsKLUt1EDMgdaQsr7RqwBphw3QAImkMtlPQfAWDWwZyo0xDg==",
"cpu": [
"riscv64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-linux-riscv64-musl": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.123.0.tgz",
"integrity": "sha512-d6xHHhqldA/W+VC7v8uHs24zM69Ad3HnHQ45h+uuBhCsbZx3d0E0wL2K3uJ5mYKTR6UPMFk9VMXcHWwvg1PRZQ==",
"cpu": [
"riscv64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-linux-s390x-gnu": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.123.0.tgz",
"integrity": "sha512-+di9A5wJQlv0VodyhADjJ2rC4geyHY+uhJDl3TFjMgYhhlgLZchi9uHD5mfiUEDWHt1x7/eU2u1ge3LLazZmFw==",
"cpu": [
"s390x"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-linux-x64-gnu": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.123.0.tgz",
"integrity": "sha512-sh7pw2g/u6LE1TaRRQsV9Kv9+1y+CywaaNwWWP+3bnEPk/L692oTG0hmEviUlawI8v3OGC+AhbjtAD+HXWQAkg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-linux-x64-musl": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.123.0.tgz",
"integrity": "sha512-S+LoD8PiJ639JwIqK1knIeqAyYkeCbLHtAgfapszKX0yVCaYP+aer8dJxL25de9qcDjvYWVrYCkuDZzHmOl2Xw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-openharmony-arm64": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.123.0.tgz",
"integrity": "sha512-/65vryK11q1I+k+7ukDlwZOxUFCLYsoZBZPGZHyet5bIP5e3D8mV3uCuvpWZ9Hoe6vUZFw/nAfCrX59MeuJPgw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-wasm32-wasi": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.123.0.tgz",
"integrity": "sha512-y4OsMGQiAbZzj2Rq0LEfvhR48rQDvbvqsl/dPdn4tdf+z3H79nZuR+lQ/+KUGjD30vpVGem138sBWHFj9UR+Vg==",
"cpu": [
"wasm32"
],
"license": "MIT",
"optional": true,
"dependencies": {
"@napi-rs/wasm-runtime": "^1.1.2"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@oxc-parser/binding-win32-arm64-msvc": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.123.0.tgz",
"integrity": "sha512-9lBqI6AXAkjYavkdpizNU3Q51uoVYfp9FJPx19hnCEdPku1jSgzSnvgmCvhCue0GziIvIvIdWgZ41wXQ3EOoBw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-win32-ia32-msvc": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.123.0.tgz",
"integrity": "sha512-zJbqBHwSUB7CyvAONy9ewGtQwcQj+ylOhYGETvUPp3KIYx7lolj4Gayof7iA22SU5eMSjO5COL0c8wYhmn9agA==",
"cpu": [
"ia32"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-win32-x64-msvc": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.123.0.tgz",
"integrity": "sha512-q7RZvglQvGo3RX5ljtcGSabu2B2c0oDU/6xC3sBMhsV5KRo0PvyxLdordbEN31NTfuZu4Sgl86C76cAURZIHWA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-project/types": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.123.0.tgz",
"integrity": "sha512-YtECP/y8Mj1lSHiUWGSRzy/C6teUKlS87dEfuVKT09LgQbUsBW1rNg+MiJ4buGu3yuADV60gbIvo9/HplA56Ew==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/Boshen"
}
},
"node_modules/@oxlint/binding-android-arm-eabi": {
"version": "1.64.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.64.0.tgz",
"integrity": "sha512-2r6Nq3XXGLHEXKkSj8JtmJ6N4gDw431DPFOg0ZoJHlNjnG6HVMm/ksQ10m0HJ8WBvwgMe1L50UHPaYZutCRPCw==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-android-arm64": {
"version": "1.64.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.64.0.tgz",
"integrity": "sha512-ePJMpePgg7fBv+L/hVx1xXRU5/5gd5m0obLA6hPEfLXF3GjpR8idIDbY1dhQYhyz1ms2wdTccSboo6KEd2Oxtg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-darwin-arm64": {
"version": "1.64.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.64.0.tgz",
"integrity": "sha512-U4DMLQd10gJLuoSTLSGbfv3bGjTlUNsScm9Dgb8wwBqmCzidf1pE1pXV4doGNxqwH3KtVng1AGTINA0NvkGLvQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-darwin-x64": {
"version": "1.64.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.64.0.tgz",
"integrity": "sha512-GoRIL48QWm4/TAvjN8pB1nAG+1/uqc9EdnWT9zqHeb6wsmjZtywj8VRe5aGW47Fdb64YtLOsdLqVxOvQuz98Wg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-freebsd-x64": {
"version": "1.64.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.64.0.tgz",
"integrity": "sha512-5dFkv4tkg7PxJJGS9/OjrJwjhuHczrd3OQOkRE0wHcLM+ncUnULtzEPWjqGOxTXxZnLWcB91bGiIznx89TVXyQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-linux-arm-gnueabihf": {
"version": "1.64.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.64.0.tgz",
"integrity": "sha512-jsBqMLl/uOL5+Kq/+BtK9FrmiNGUbx8SiyZXv+WlUxA45KuwcLu9BfiSIL3I3DBDgWM3yZizDITnTK9BcqNBQg==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-linux-arm-musleabihf": {
"version": "1.64.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.64.0.tgz",
"integrity": "sha512-1lrj8At/Uuc9GhjrVFBQo0NEjfBrTkzpmtHIGAhNnIXqn1CAyGL+qrztUsXb2GIluJrpl9Q7qRLJOb/NqydacQ==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-linux-arm64-gnu": {
"version": "1.64.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.64.0.tgz",
"integrity": "sha512-HpSQbubwh03mMhAdy2BYtad/fsY8vDFHDAb6bUwuCYg2VD3xCQgn6ArKcO0oZyLCheacKTv4PrF3Mfu5hgoE2g==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-linux-arm64-musl": {
"version": "1.64.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.64.0.tgz",
"integrity": "sha512-00QQ0h0Y7u0G69BgiH3+ky2aaq/QvkDL6DYok8htIuJHxybiux5aQ8jwmg8qIk9wha6UagUP2BAwAzbemcJbpg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-linux-ppc64-gnu": {
"version": "1.64.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.64.0.tgz",
"integrity": "sha512-2GaimTV6EMW+s5HS0An3oGbQme3BgHswvfVdGk3EB57Xe9+/gyT+Qd7lNVzb3rtir52vbIPzXfaYArzs5b5zcw==",
"cpu": [
"ppc64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-linux-riscv64-gnu": {
"version": "1.64.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.64.0.tgz",
"integrity": "sha512-H46AtFb9wypjoVwGdlxrm0DsD809NGmtiK9HiyPKTxkSte2YjhC4S+00rOIrwCaxcyPiGid3Y3OMXp5KMAkGZw==",
"cpu": [
"riscv64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-linux-riscv64-musl": {
"version": "1.64.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.64.0.tgz",
"integrity": "sha512-HEgsidjjvvyzdg82icYkuFCf7REDV7B9JFwbIMbVwrKLBY0MrXX+bku3POn/hduZ2yW91IyVDUMq0Bf02KwXQw==",
"cpu": [
"riscv64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-linux-s390x-gnu": {
"version": "1.64.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.64.0.tgz",
"integrity": "sha512-Axvm8qryotmKN00P5w4JapaSjvP2LOSbdbBJiX+2SuHd3QzhW7TUc8skqgw+ahQZ5DmzEYeHCqauvW8f32Ns6Q==",
"cpu": [
"s390x"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-linux-x64-gnu": {
"version": "1.64.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.64.0.tgz",
"integrity": "sha512-cR60vSd7+m+KRZ3GQGfDxWwahW5RMXg0qlGvAluZr0fTUYvw0H9N9AXAF/M/PMqgytyqvVNmBAkJG9l7U30Y1g==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-linux-x64-musl": {
"version": "1.64.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.64.0.tgz",
"integrity": "sha512-2u/aPZ9pEg7HnvZPDsHxUGNnrpr4qaHi+mCgLgpt+LYRzPrS4Px4wPfkIdRdr2GvKnaYyt+XSlto0Vm5sbStTg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-openharmony-arm64": {
"version": "1.64.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.64.0.tgz",
"integrity": "sha512-kfhkGfCdoXLSxEkrhDlJrvBYajGmq+ma4EMc53dsOWTq+rIBOlI0vTBmpZNnM5oH2LY/K/w1HAK+UQEgjgpVUg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-win32-arm64-msvc": {
"version": "1.64.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.64.0.tgz",
"integrity": "sha512-r/cNKBFieONoVu2bb1KkVouq9W+edDUgHumXJGphCRRj+U0xaD4nanrw8ZOqo0IsutPkEM4vCcGBpak6x5aXMg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-win32-ia32-msvc": {
"version": "1.64.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.64.0.tgz",
"integrity": "sha512-tUw0xUUwEFVZbpJoeCblkv8SJA4Xz3CdXCJbAnBsiNLyxDrk2tLcxEAS6M73Q7hHHDg3OtwI8vZVK3t5RJt4Gw==",
"cpu": [
"ia32"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-win32-x64-msvc": {
"version": "1.64.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.64.0.tgz",
"integrity": "sha512-9CBR+LO0JVST87fNTzzNxS5I29jIUO5gxT9i9+M3SDHHALElj9sY1Prf12tad3vIRC6OD7Ehtvvh+sn13vSwHw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@tybys/wasm-util": {
"version": "0.10.2",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
"integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==",
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/oxc-parser": {
"version": "0.123.0",
"resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.123.0.tgz",
"integrity": "sha512-F6ak0tFc01ZGbl5KxvLDQ2K005Z086mp3ByCQBDhUjqXLkapGUkMuJSsYixncdEpkLlcRDcruHR71LD339ADUA==",
"license": "MIT",
"dependencies": {
"@oxc-project/types": "^0.123.0"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/sponsors/Boshen"
},
"optionalDependencies": {
"@oxc-parser/binding-android-arm-eabi": "0.123.0",
"@oxc-parser/binding-android-arm64": "0.123.0",
"@oxc-parser/binding-darwin-arm64": "0.123.0",
"@oxc-parser/binding-darwin-x64": "0.123.0",
"@oxc-parser/binding-freebsd-x64": "0.123.0",
"@oxc-parser/binding-linux-arm-gnueabihf": "0.123.0",
"@oxc-parser/binding-linux-arm-musleabihf": "0.123.0",
"@oxc-parser/binding-linux-arm64-gnu": "0.123.0",
"@oxc-parser/binding-linux-arm64-musl": "0.123.0",
"@oxc-parser/binding-linux-ppc64-gnu": "0.123.0",
"@oxc-parser/binding-linux-riscv64-gnu": "0.123.0",
"@oxc-parser/binding-linux-riscv64-musl": "0.123.0",
"@oxc-parser/binding-linux-s390x-gnu": "0.123.0",
"@oxc-parser/binding-linux-x64-gnu": "0.123.0",
"@oxc-parser/binding-linux-x64-musl": "0.123.0",
"@oxc-parser/binding-openharmony-arm64": "0.123.0",
"@oxc-parser/binding-wasm32-wasi": "0.123.0",
"@oxc-parser/binding-win32-arm64-msvc": "0.123.0",
"@oxc-parser/binding-win32-ia32-msvc": "0.123.0",
"@oxc-parser/binding-win32-x64-msvc": "0.123.0"
}
},
"node_modules/oxlint": {
"version": "1.64.0",
"resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.64.0.tgz",
"integrity": "sha512-Star3SNpWPeWFPw7kRXIhXUSn6fdiAl25q15CQzH/9WaOtG6e9CWTc25vNZOCr4PE1yEP1GtKJKIKglhj3OmEQ==",
"license": "MIT",
"bin": {
"oxlint": "bin/oxlint"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/sponsors/Boshen"
},
"optionalDependencies": {
"@oxlint/binding-android-arm-eabi": "1.64.0",
"@oxlint/binding-android-arm64": "1.64.0",
"@oxlint/binding-darwin-arm64": "1.64.0",
"@oxlint/binding-darwin-x64": "1.64.0",
"@oxlint/binding-freebsd-x64": "1.64.0",
"@oxlint/binding-linux-arm-gnueabihf": "1.64.0",
"@oxlint/binding-linux-arm-musleabihf": "1.64.0",
"@oxlint/binding-linux-arm64-gnu": "1.64.0",
"@oxlint/binding-linux-arm64-musl": "1.64.0",
"@oxlint/binding-linux-ppc64-gnu": "1.64.0",
"@oxlint/binding-linux-riscv64-gnu": "1.64.0",
"@oxlint/binding-linux-riscv64-musl": "1.64.0",
"@oxlint/binding-linux-s390x-gnu": "1.64.0",
"@oxlint/binding-linux-x64-gnu": "1.64.0",
"@oxlint/binding-linux-x64-musl": "1.64.0",
"@oxlint/binding-openharmony-arm64": "1.64.0",
"@oxlint/binding-win32-arm64-msvc": "1.64.0",
"@oxlint/binding-win32-ia32-msvc": "1.64.0",
"@oxlint/binding-win32-x64-msvc": "1.64.0"
},
"peerDependencies": {
"oxlint-tsgolint": ">=0.22.1"
},
"peerDependenciesMeta": {
"oxlint-tsgolint": {
"optional": true
}
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD",
"optional": true
}
}
}

37
studio/bun.lock Normal file
View file

@ -0,0 +1,37 @@
{
"lockfileVersion": 1,
"configVersion": 0,
"workspaces": {
"": {
"name": "unsloth-studio-tauri-cli",
"devDependencies": {
"@tauri-apps/cli": "2.10.1",
},
},
},
"packages": {
"@tauri-apps/cli": ["@tauri-apps/cli@2.10.1", "", { "optionalDependencies": { "@tauri-apps/cli-darwin-arm64": "2.10.1", "@tauri-apps/cli-darwin-x64": "2.10.1", "@tauri-apps/cli-linux-arm-gnueabihf": "2.10.1", "@tauri-apps/cli-linux-arm64-gnu": "2.10.1", "@tauri-apps/cli-linux-arm64-musl": "2.10.1", "@tauri-apps/cli-linux-riscv64-gnu": "2.10.1", "@tauri-apps/cli-linux-x64-gnu": "2.10.1", "@tauri-apps/cli-linux-x64-musl": "2.10.1", "@tauri-apps/cli-win32-arm64-msvc": "2.10.1", "@tauri-apps/cli-win32-ia32-msvc": "2.10.1", "@tauri-apps/cli-win32-x64-msvc": "2.10.1" }, "bin": { "tauri": "tauri.js" } }, "sha512-jQNGF/5quwORdZSSLtTluyKQ+o6SMa/AUICfhf4egCGFdMHqWssApVgYSbg+jmrZoc8e1DscNvjTnXtlHLS11g=="],
"@tauri-apps/cli-darwin-arm64": ["@tauri-apps/cli-darwin-arm64@2.10.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Z2OjCXiZ+fbYZy7PmP3WRnOpM9+Fy+oonKDEmUE6MwN4IGaYqgceTjwHucc/kEEYZos5GICve35f7ZiizgqEnQ=="],
"@tauri-apps/cli-darwin-x64": ["@tauri-apps/cli-darwin-x64@2.10.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-V/irQVvjPMGOTQqNj55PnQPVuH4VJP8vZCN7ajnj+ZS8Kom1tEM2hR3qbbIRoS3dBKs5mbG8yg1WC+97dq17Pw=="],
"@tauri-apps/cli-linux-arm-gnueabihf": ["@tauri-apps/cli-linux-arm-gnueabihf@2.10.1", "", { "os": "linux", "cpu": "arm" }, "sha512-Hyzwsb4VnCWKGfTw+wSt15Z2pLw2f0JdFBfq2vHBOBhvg7oi6uhKiF87hmbXOBXUZaGkyRDkCHsdzJcIfoJC2w=="],
"@tauri-apps/cli-linux-arm64-gnu": ["@tauri-apps/cli-linux-arm64-gnu@2.10.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-OyOYs2t5GkBIvyWjA1+h4CZxTcdz1OZPCWAPz5DYEfB0cnWHERTnQ/SLayQzncrT0kwRoSfSz9KxenkyJoTelA=="],
"@tauri-apps/cli-linux-arm64-musl": ["@tauri-apps/cli-linux-arm64-musl@2.10.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-MIj78PDDGjkg3NqGptDOGgfXks7SYJwhiMh8SBoZS+vfdz7yP5jN18bNaLnDhsVIPARcAhE1TlsZe/8Yxo2zqg=="],
"@tauri-apps/cli-linux-riscv64-gnu": ["@tauri-apps/cli-linux-riscv64-gnu@2.10.1", "", { "os": "linux", "cpu": "none" }, "sha512-X0lvOVUg8PCVaoEtEAnpxmnkwlE1gcMDTqfhbefICKDnOTJ5Est3qL0SrWxizDackIOKBcvtpejrSiVpuJI1kw=="],
"@tauri-apps/cli-linux-x64-gnu": ["@tauri-apps/cli-linux-x64-gnu@2.10.1", "", { "os": "linux", "cpu": "x64" }, "sha512-2/12bEzsJS9fAKybxgicCDFxYD1WEI9kO+tlDwX5znWG2GwMBaiWcmhGlZ8fi+DMe9CXlcVarMTYc0L3REIRxw=="],
"@tauri-apps/cli-linux-x64-musl": ["@tauri-apps/cli-linux-x64-musl@2.10.1", "", { "os": "linux", "cpu": "x64" }, "sha512-Y8J0ZzswPz50UcGOFuXGEMrxbjwKSPgXftx5qnkuMs2rmwQB5ssvLb6tn54wDSYxe7S6vlLob9vt0VKuNOaCIQ=="],
"@tauri-apps/cli-win32-arm64-msvc": ["@tauri-apps/cli-win32-arm64-msvc@2.10.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-iSt5B86jHYAPJa/IlYw++SXtFPGnWtFJriHn7X0NFBVunF6zu9+/zOn8OgqIWSl8RgzhLGXQEEtGBdR4wzpVgg=="],
"@tauri-apps/cli-win32-ia32-msvc": ["@tauri-apps/cli-win32-ia32-msvc@2.10.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-gXyxgEzsFegmnWywYU5pEBURkcFN/Oo45EAwvZrHMh+zUSEAvO5E8TXsgPADYm31d1u7OQU3O3HsYfVBf2moHw=="],
"@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.10.1", "", { "os": "win32", "cpu": "x64" }, "sha512-6Cn7YpPFwzChy0ERz6djKEmUehWrYlM+xTaNzGPgZocw3BD7OfwfWHKVWxXzdjEW2KfKkHddfdxK1XXTYqBRLg=="],
}
}

View file

@ -11,7 +11,6 @@ pnpm-debug.log*
lerna-debug.log*
node_modules
bun.lock
dist
dist-ssr
test/

2273
studio/frontend/bun.lock Normal file

File diff suppressed because it is too large Load diff

233
studio/package-lock.json generated Normal file
View file

@ -0,0 +1,233 @@
{
"name": "unsloth-studio-tauri-cli",
"version": "0.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "unsloth-studio-tauri-cli",
"version": "0.0.0",
"license": "AGPL-3.0-only",
"devDependencies": {
"@tauri-apps/cli": "2.10.1"
}
},
"node_modules/@tauri-apps/cli": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.10.1.tgz",
"integrity": "sha512-jQNGF/5quwORdZSSLtTluyKQ+o6SMa/AUICfhf4egCGFdMHqWssApVgYSbg+jmrZoc8e1DscNvjTnXtlHLS11g==",
"dev": true,
"license": "Apache-2.0 OR MIT",
"bin": {
"tauri": "tauri.js"
},
"engines": {
"node": ">= 10"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/tauri"
},
"optionalDependencies": {
"@tauri-apps/cli-darwin-arm64": "2.10.1",
"@tauri-apps/cli-darwin-x64": "2.10.1",
"@tauri-apps/cli-linux-arm-gnueabihf": "2.10.1",
"@tauri-apps/cli-linux-arm64-gnu": "2.10.1",
"@tauri-apps/cli-linux-arm64-musl": "2.10.1",
"@tauri-apps/cli-linux-riscv64-gnu": "2.10.1",
"@tauri-apps/cli-linux-x64-gnu": "2.10.1",
"@tauri-apps/cli-linux-x64-musl": "2.10.1",
"@tauri-apps/cli-win32-arm64-msvc": "2.10.1",
"@tauri-apps/cli-win32-ia32-msvc": "2.10.1",
"@tauri-apps/cli-win32-x64-msvc": "2.10.1"
}
},
"node_modules/@tauri-apps/cli-darwin-arm64": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.10.1.tgz",
"integrity": "sha512-Z2OjCXiZ+fbYZy7PmP3WRnOpM9+Fy+oonKDEmUE6MwN4IGaYqgceTjwHucc/kEEYZos5GICve35f7ZiizgqEnQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-darwin-x64": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.10.1.tgz",
"integrity": "sha512-V/irQVvjPMGOTQqNj55PnQPVuH4VJP8vZCN7ajnj+ZS8Kom1tEM2hR3qbbIRoS3dBKs5mbG8yg1WC+97dq17Pw==",
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-arm-gnueabihf": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.10.1.tgz",
"integrity": "sha512-Hyzwsb4VnCWKGfTw+wSt15Z2pLw2f0JdFBfq2vHBOBhvg7oi6uhKiF87hmbXOBXUZaGkyRDkCHsdzJcIfoJC2w==",
"cpu": [
"arm"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-arm64-gnu": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.10.1.tgz",
"integrity": "sha512-OyOYs2t5GkBIvyWjA1+h4CZxTcdz1OZPCWAPz5DYEfB0cnWHERTnQ/SLayQzncrT0kwRoSfSz9KxenkyJoTelA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-arm64-musl": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.10.1.tgz",
"integrity": "sha512-MIj78PDDGjkg3NqGptDOGgfXks7SYJwhiMh8SBoZS+vfdz7yP5jN18bNaLnDhsVIPARcAhE1TlsZe/8Yxo2zqg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-riscv64-gnu": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.10.1.tgz",
"integrity": "sha512-X0lvOVUg8PCVaoEtEAnpxmnkwlE1gcMDTqfhbefICKDnOTJ5Est3qL0SrWxizDackIOKBcvtpejrSiVpuJI1kw==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-x64-gnu": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.10.1.tgz",
"integrity": "sha512-2/12bEzsJS9fAKybxgicCDFxYD1WEI9kO+tlDwX5znWG2GwMBaiWcmhGlZ8fi+DMe9CXlcVarMTYc0L3REIRxw==",
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-x64-musl": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.10.1.tgz",
"integrity": "sha512-Y8J0ZzswPz50UcGOFuXGEMrxbjwKSPgXftx5qnkuMs2rmwQB5ssvLb6tn54wDSYxe7S6vlLob9vt0VKuNOaCIQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-win32-arm64-msvc": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.10.1.tgz",
"integrity": "sha512-iSt5B86jHYAPJa/IlYw++SXtFPGnWtFJriHn7X0NFBVunF6zu9+/zOn8OgqIWSl8RgzhLGXQEEtGBdR4wzpVgg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-win32-ia32-msvc": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.10.1.tgz",
"integrity": "sha512-gXyxgEzsFegmnWywYU5pEBURkcFN/Oo45EAwvZrHMh+zUSEAvO5E8TXsgPADYm31d1u7OQU3O3HsYfVBf2moHw==",
"cpu": [
"ia32"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-win32-x64-msvc": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.10.1.tgz",
"integrity": "sha512-6Cn7YpPFwzChy0ERz6djKEmUehWrYlM+xTaNzGPgZocw3BD7OfwfWHKVWxXzdjEW2KfKkHddfdxK1XXTYqBRLg==",
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
}
}
}

10
studio/package.json Normal file
View file

@ -0,0 +1,10 @@
{
"name": "unsloth-studio-tauri-cli",
"version": "0.0.0",
"private": true,
"description": "Lockfile holder for @tauri-apps/cli used by the desktop release workflow. Not a real npm package; `npm ci --prefix studio` resolves the pinned Tauri CLI from this directory's package-lock.json.",
"license": "AGPL-3.0-only",
"devDependencies": {
"@tauri-apps/cli": "2.10.1"
}
}

View file

@ -36,6 +36,12 @@ $DefaultLlamaSource = "https://github.com/ggml-org/llama.cpp"
$DefaultLlamaTag = "latest"
$DefaultLlamaForceCompileRef = "master"
# Bun pin. Tracks the version used to generate the committed bun.lock
# files. Auto-installed on absence; force-reinstalled only as a
# cache-corruption recovery step (we don't overwrite a user's existing
# bun unless their install is producing corrupt output).
$Script:BunPinVersion = "1.3.11"
# Verbose can be enabled either by CLI flag or by UNSLOTH_VERBOSE=1.
$script:UnslothVerbose = ($env:UNSLOTH_VERBOSE -eq '1')
foreach ($a in $args) {
@ -1147,22 +1153,31 @@ if ($IsPipInstall) {
step "node" "$(node -v) | npm $(npm -v)"
# ── bun (optional, faster package installs) ──
# Installed via npm — Node is already guaranteed above. Works on all platforms.
# ── Bun (optional, used for faster lockfile-strict installs) ──
# Auto-install only when missing. We do NOT overwrite a user's
# existing bun unless their install is producing corrupt output
# (handled inside the frontend install block below as a cache-
# recovery step). Pin tracks the version used to generate the
# committed bun.lock files.
if (-not (Get-Command bun -ErrorAction SilentlyContinue)) {
substep "installing bun (faster frontend package installs)..."
substep "installing bun@$Script:BunPinVersion (pinned)..."
$prevEAP_bun = $ErrorActionPreference
$ErrorActionPreference = "Continue"
Invoke-SetupCommand { npm install -g bun } | Out-Null
Invoke-SetupCommand { npm install -g "bun@$Script:BunPinVersion" } | Out-Null
$ErrorActionPreference = $prevEAP_bun
Refresh-Environment
if (Get-Command bun -ErrorAction SilentlyContinue) {
substep "bun installed ($(bun --version))"
substep "bun $(bun --version) installed"
} else {
substep "bun install skipped (npm will be used instead)"
substep "bun install skipped (npm ci will be used instead)"
}
} else {
substep "bun already installed ($(bun --version))"
$bunVer = (bun --version 2>$null)
if ($bunVer -eq $Script:BunPinVersion) {
substep "bun $bunVer at pin"
} else {
substep "bun $bunVer present (pin is $Script:BunPinVersion; not overwriting user install)"
}
}
}
@ -1321,65 +1336,76 @@ if ($NeedFrontendBuild -and -not $IsPipInstall) {
$WalkDir = Split-Path $WalkDir -Parent
}
# Use bun if available (faster install), fall back to npm.
# Bun is used only as package manager; Node runs the actual build (Vite 8).
# Frontend install: Bun --frozen-lockfile first (faster, typically
# 5-10x) when a committed bun.lock is present, npm ci as the
# always-available fallback. Both run in lockfile-strict mode so
# the install is byte-reproducible from whichever lockfile the
# chosen package manager understands. The build always runs through
# Node (npm run build) -- avoids bun runtime quirks on some
# platforms.
#
# Bun's package cache can occasionally store metadata-only entries
# that pass exit 0 but leave binaries (tsc, vite) missing. We
# verify both binaries after install; on failure we clear the cache
# and let npm ci take over rather than retrying bun, since npm ci
# is the more defensive path under cache corruption.
$prevEAP_npm = $ErrorActionPreference
$ErrorActionPreference = "Continue"
Push-Location $FrontendDir
$UseBun = $null -ne (Get-Command bun -ErrorAction SilentlyContinue)
# bun's package cache can become corrupt -- packages get stored with only
# metadata but no actual content (bin/, lib/). When this happens bun install
# exits 0 but leaves binaries missing. We validate after install and clear
# the cache + retry once before falling back to npm.
# Cache-corruption recovery ladder. Bun's package cache can store
# metadata-only entries that pass `bun install` exit 0 but leave
# binaries (tsc, vite) missing. We verify both binaries after each
# attempt and escalate:
# try 1: bun install --frozen-lockfile
# try 2: remove node_modules + bun pm cache rm, retry
# try 3: force-reinstall bun@$Script:BunPinVersion (a corrupted
# bun binary itself can produce empty payloads), retry
# final: npm ci (always-available safety net)
$InstallOk = $false
$UseBun = (Test-Path "bun.lock" -PathType Leaf) -and ($null -ne (Get-Command bun -ErrorAction SilentlyContinue))
if ($UseBun) {
Write-Host " Using bun for package install (faster)" -ForegroundColor DarkGray
$bunExit = Invoke-SetupCommand { bun install }
# On Windows, .bin/ entries vary by package manager:
# npm → tsc, tsc.cmd, tsc.ps1
# bun → tsc.exe, tsc.bunx
$hasTsc = (Test-Path "node_modules\.bin\tsc") -or (Test-Path "node_modules\.bin\tsc.cmd") -or (Test-Path "node_modules\.bin\tsc.exe") -or (Test-Path "node_modules\.bin\tsc.bunx")
$hasVite = (Test-Path "node_modules\.bin\vite") -or (Test-Path "node_modules\.bin\vite.cmd") -or (Test-Path "node_modules\.bin\vite.exe") -or (Test-Path "node_modules\.bin\vite.bunx")
if ($bunExit -eq 0 -and $hasTsc -and $hasVite) {
# bun install succeeded and critical binaries are present
} elseif ($bunExit -eq 0) {
Write-Host " bun install exited 0 but critical binaries are missing, clearing cache and retrying..." -ForegroundColor Yellow
if (Test-Path "node_modules") {
Remove-Item "node_modules" -Recurse -Force -ErrorAction SilentlyContinue
}
Invoke-SetupCommand { bun pm cache rm } | Out-Null
$bunExit = Invoke-SetupCommand { bun install }
$bunVer = (bun --version 2>$null)
Write-Host " Using bun $bunVer --frozen-lockfile (faster)" -ForegroundColor DarkGray
$bunAttempt = 0
while ($bunAttempt -lt 3 -and -not $InstallOk) {
$bunAttempt++
$bunExit = Invoke-SetupCommand { bun install --frozen-lockfile --no-progress }
# On Windows, .bin/ entries vary by package manager:
# npm -> tsc, tsc.cmd, tsc.ps1
# bun -> tsc.exe, tsc.bunx
$hasTsc = (Test-Path "node_modules\.bin\tsc") -or (Test-Path "node_modules\.bin\tsc.cmd") -or (Test-Path "node_modules\.bin\tsc.exe") -or (Test-Path "node_modules\.bin\tsc.bunx")
$hasVite = (Test-Path "node_modules\.bin\vite") -or (Test-Path "node_modules\.bin\vite.cmd") -or (Test-Path "node_modules\.bin\vite.exe") -or (Test-Path "node_modules\.bin\vite.bunx")
if ($bunExit -ne 0 -or -not $hasTsc -or -not $hasVite) {
Write-Host " bun retry failed, falling back to npm" -ForegroundColor Yellow
if (Test-Path "node_modules") {
Remove-Item "node_modules" -Recurse -Force -ErrorAction SilentlyContinue
}
$UseBun = $false
if ($bunExit -eq 0 -and $hasTsc -and $hasVite) {
$InstallOk = $true
break
}
} else {
substep "bun install failed (exit $bunExit), falling back to npm" "Yellow"
if (Test-Path "node_modules") {
Remove-Item "node_modules" -Recurse -Force -ErrorAction SilentlyContinue
if (Test-Path "node_modules") { Remove-Item "node_modules" -Recurse -Force -ErrorAction SilentlyContinue }
if ($bunAttempt -eq 1) {
substep "bun install incomplete (try 1); clearing cache + retrying" "Yellow"
Invoke-SetupCommand { bun pm cache rm } | Out-Null
} elseif ($bunAttempt -eq 2) {
substep "bun install still incomplete (try 2); reinstalling bun@$Script:BunPinVersion + retrying" "Yellow"
Invoke-SetupCommand { bun pm cache rm } | Out-Null
Invoke-SetupCommand { npm install -g "bun@$Script:BunPinVersion" } | Out-Null
Refresh-Environment
}
$UseBun = $false
}
}
if (-not $UseBun) {
$npmExit = Invoke-SetupCommand { npm install }
if (-not $InstallOk) {
substep "falling back to npm ci" "Yellow"
if (Test-Path "node_modules") { Remove-Item "node_modules" -Recurse -Force -ErrorAction SilentlyContinue }
$npmExit = Invoke-SetupCommand { npm ci }
if ($npmExit -ne 0) {
Pop-Location
$ErrorActionPreference = $prevEAP_npm
foreach ($gi in $HiddenGitignores) { Rename-Item -Path "$gi._twbuild" -NewName (Split-Path $gi -Leaf) -Force -ErrorAction SilentlyContinue }
Write-Host "[ERROR] npm install failed (exit code $npmExit)" -ForegroundColor Red
Write-Host " Try running 'npm install' manually in frontend/ to see errors" -ForegroundColor Yellow
Write-Host "[ERROR] npm ci failed (exit code $npmExit)" -ForegroundColor Red
Write-Host " Try running 'npm ci' manually in frontend/ to see errors" -ForegroundColor Yellow
exit 1
}
}
# Always use npm to run the build (Node runtime — avoids bun Windows runtime issues)
$buildExit = Invoke-SetupCommand { npm run build }
if ($buildExit -ne 0) {
Pop-Location
@ -1411,12 +1437,26 @@ if (Test-Path $OxcValidatorDir) {
$prevEAP_oxc = $ErrorActionPreference
$ErrorActionPreference = "Continue"
Push-Location $OxcValidatorDir
$oxcInstallExit = Invoke-SetupCommand { npm install }
if ($oxcInstallExit -ne 0) {
Pop-Location
$ErrorActionPreference = $prevEAP_oxc
Write-Host "[ERROR] OXC validator npm install failed (exit code $oxcInstallExit)" -ForegroundColor Red
exit 1
# Same Bun-first / npm-fallback pattern as the frontend install
# above; both lockfile-strict.
$OxcInstallOk = $false
$UseBunOxc = (Test-Path "bun.lock" -PathType Leaf) -and ($null -ne (Get-Command bun -ErrorAction SilentlyContinue))
if ($UseBunOxc) {
$bunOxcExit = Invoke-SetupCommand { bun install --frozen-lockfile --no-progress }
if ($bunOxcExit -eq 0 -and (Test-Path "node_modules\oxc-parser" -PathType Container)) {
$OxcInstallOk = $true
} else {
if (Test-Path "node_modules") { Remove-Item "node_modules" -Recurse -Force -ErrorAction SilentlyContinue }
}
}
if (-not $OxcInstallOk) {
$oxcInstallExit = Invoke-SetupCommand { npm ci --no-fund --no-audit }
if ($oxcInstallExit -ne 0) {
Pop-Location
$ErrorActionPreference = $prevEAP_oxc
Write-Host "[ERROR] OXC validator npm ci failed (exit code $oxcInstallExit)" -ForegroundColor Red
exit 1
}
}
Pop-Location
$ErrorActionPreference = $prevEAP_oxc

View file

@ -42,6 +42,12 @@ _DEFAULT_LLAMA_SOURCE="https://github.com/ggml-org/llama.cpp"
_DEFAULT_LLAMA_TAG="latest"
_DEFAULT_LLAMA_FORCE_COMPILE_REF="master"
# Bun pin. Tracks the version used to generate the committed bun.lock
# files. Auto-installed on absence; force-reinstalled only as a
# cache-corruption recovery step (we don't overwrite a user's existing
# bun unless their install is producing corrupt output).
_BUN_PIN_VERSION="1.3.11"
# ── Colors (same palette as startup_banner / install_python_stack) ──
if [ -n "${NO_COLOR:-}" ]; then
C_TITLE= C_DIM= C_OK= C_WARN= C_ERR= C_RST=
@ -295,18 +301,29 @@ fi
step "node" "$(node -v) | npm $(npm -v)"
verbose_substep "node check: NEED_NODE=$NEED_NODE NODE_OK=${NODE_OK:-unknown} NPM_MAJOR=${NPM_MAJOR:-unknown}"
# ── Install bun (optional, faster package installs) ──
# Uses npm to install bun globally -- Node is already guaranteed above,
# avoids platform-specific installers, PATH issues, and admin requirements.
if ! command -v bun &>/dev/null; then
substep "installing bun..."
if run_maybe_quiet npm install -g bun && command -v bun &>/dev/null; then
substep "bun installed ($(bun --version))"
else
substep "bun install skipped (npm will be used instead)"
# ── Bun (optional, used for faster lockfile-strict installs) ──
# Auto-install only when missing. We do NOT overwrite a user's existing
# bun unless their install is producing corrupt output (handled inside
# the frontend install loop below as a cache-recovery step).
_bun_at_pin() {
command -v bun >/dev/null 2>&1 || return 1
[ "$(bun --version 2>/dev/null)" = "$_BUN_PIN_VERSION" ]
}
_install_pinned_bun() {
substep "installing bun@$_BUN_PIN_VERSION (pinned)"
if run_maybe_quiet npm install -g "bun@$_BUN_PIN_VERSION" && command -v bun &>/dev/null; then
substep "bun $(bun --version) installed"
return 0
fi
substep "bun install skipped (npm ci will be used instead)" "$C_WARN"
return 1
}
if ! command -v bun &>/dev/null; then
_install_pinned_bun || true
elif _bun_at_pin; then
verbose_substep "bun $(bun --version) at pin"
else
substep "bun already installed ($(bun --version))"
substep "bun $(bun --version) present (pin is $_BUN_PIN_VERSION; not overwriting user install)"
fi
# ── Build frontend ──
@ -329,59 +346,49 @@ _restore_gitignores() {
}
trap _restore_gitignores EXIT
# Use bun for install if available (faster), fall back to npm.
# Build always uses npm (Node runtime -- avoids bun runtime issues on some platforms).
# NOTE: We intentionally avoid run_quiet for the bun install attempt because
# run_quiet calls exit on failure, which would kill the script before the npm
# fallback can run. Instead we capture output manually and only show it on failure.
# Frontend install: Bun --frozen-lockfile first (faster, typically
# 5-10x) when a committed bun.lock is present, npm ci as the
# always-available fallback. Both run in lockfile-strict mode so the
# install is byte-reproducible from whichever lockfile the chosen
# package manager understands. The build always runs through Node
# (npm run build) -- avoids bun runtime quirks on some platforms.
#
# IMPORTANT: bun's package cache can become corrupt -- packages get stored
# with only metadata (package.json, README) but no actual content (bin/,
# lib/). When this happens bun install exits 0 but leaves binaries missing.
# We verify critical binaries after install. If missing, we clear the cache
# and retry once before falling back to npm.
_try_bun_install() {
local _log _exit_code=0
_log=$(mktemp)
bun install >"$_log" 2>&1 || _exit_code=$?
# bun may create .exe shims on Windows (Git Bash / MSYS2) instead of plain scripts
if [ "$_exit_code" -eq 0 ] \
&& { [ -x node_modules/.bin/tsc ] || [ -f node_modules/.bin/tsc.exe ] || [ -f node_modules/.bin/tsc.bunx ]; } \
&& { [ -x node_modules/.bin/vite ] || [ -f node_modules/.bin/vite.exe ] || [ -f node_modules/.bin/vite.bunx ]; }; then
rm -f "$_log"
return 0
fi
# Either bun install failed or it exited 0 but left packages missing
if [ "$_exit_code" -ne 0 ]; then
echo " bun install failed (exit code $_exit_code):"
else
echo " bun install exited 0 but critical binaries are missing:"
fi
sed 's/^/ | /' "$_log" >&2
rm -f "$_log"
rm -rf node_modules
return 1
}
# Cache-corruption recovery ladder. Bun's package cache can store
# metadata-only entries that pass `bun install` exit 0 but leave
# binaries (tsc, vite) missing. We verify both binaries after each
# attempt and escalate:
# try 1: bun install --frozen-lockfile
# try 2: rm -rf node_modules + bun pm cache rm, retry
# try 3: force-reinstall bun@$_BUN_PIN_VERSION (a corrupted bun
# binary itself can produce empty payloads), retry
# final: npm ci (always-available safety net)
_bun_install_ok=false
if command -v bun &>/dev/null; then
substep "using bun for package install (faster)"
if _try_bun_install; then
_bun_install_ok=true
else
# First attempt failed, likely due to corrupt cache entries.
# Clear the cache and retry once.
echo " Clearing bun cache and retrying..."
run_maybe_quiet bun pm cache rm || true
if _try_bun_install; then
if [ -f bun.lock ] && command -v bun &>/dev/null; then
substep "using bun $(bun --version) --frozen-lockfile (faster)"
_bun_attempts=0
while [ "$_bun_attempts" -lt 3 ] && [ "$_bun_install_ok" = false ]; do
_bun_attempts=$((_bun_attempts + 1))
if run_quiet_no_exit "bun install --frozen-lockfile (try $_bun_attempts)" bun install --frozen-lockfile --no-progress \
&& { [ -x node_modules/.bin/tsc ] || [ -f node_modules/.bin/tsc.exe ] || [ -f node_modules/.bin/tsc.bunx ]; } \
&& { [ -x node_modules/.bin/vite ] || [ -f node_modules/.bin/vite.exe ] || [ -f node_modules/.bin/vite.bunx ]; }; then
_bun_install_ok=true
break
fi
fi
rm -rf node_modules
if [ "$_bun_attempts" -eq 1 ]; then
substep "bun install incomplete (try 1); clearing cache + retrying" "$C_WARN"
run_maybe_quiet bun pm cache rm || true
elif [ "$_bun_attempts" -eq 2 ]; then
substep "bun install still incomplete (try 2); reinstalling bun@$_BUN_PIN_VERSION + retrying" "$C_WARN"
run_maybe_quiet bun pm cache rm || true
_install_pinned_bun || true
fi
done
fi
if [ "$_bun_install_ok" = false ]; then
run_quiet_no_exit "npm install" npm install --no-fund --no-audit --loglevel=error
substep "falling back to npm ci"
rm -rf node_modules
run_quiet_no_exit "npm ci" npm ci --no-fund --no-audit --loglevel=error
_npm_install_rc=$?
if [ "$_npm_install_rc" -ne 0 ]; then
exit "$_npm_install_rc"
@ -406,12 +413,25 @@ cd "$SCRIPT_DIR"
fi # end frontend build check
# ── oxc-validator runtime ──
# Same Bun-first / npm-fallback pattern as the frontend install above;
# both lockfile-strict.
if [ -d "$SCRIPT_DIR/backend/core/data_recipe/oxc-validator" ] && command -v npm &>/dev/null; then
cd "$SCRIPT_DIR/backend/core/data_recipe/oxc-validator"
run_quiet_no_exit "npm install (oxc validator runtime)" npm install --no-fund --no-audit --loglevel=error
_oxc_install_rc=$?
if [ "$_oxc_install_rc" -ne 0 ]; then
exit "$_oxc_install_rc"
_oxc_bun_ok=false
if [ -f bun.lock ] && command -v bun &>/dev/null; then
if run_quiet_no_exit "bun install --frozen-lockfile (oxc validator)" bun install --frozen-lockfile --no-progress \
&& [ -d node_modules/oxc-parser ]; then
_oxc_bun_ok=true
else
rm -rf node_modules
fi
fi
if [ "$_oxc_bun_ok" = false ]; then
run_quiet_no_exit "npm ci (oxc validator runtime)" npm ci --no-fund --no-audit --loglevel=error
_oxc_install_rc=$?
if [ "$_oxc_install_rc" -ne 0 ]; then
exit "$_oxc_install_rc"
fi
fi
cd "$SCRIPT_DIR"
fi