@stll/oxlint-config 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +254 -17
  2. package/dist/cloud-setup-cli.cjs +18 -38
  3. package/dist/cloud-setup-cli.cjs.map +1 -1
  4. package/dist/cloud-setup-cli.mjs +21 -41
  5. package/dist/cloud-setup-cli.mjs.map +1 -1
  6. package/dist/consumer-compat-cli.cjs +50 -249
  7. package/dist/consumer-compat-cli.cjs.map +1 -1
  8. package/dist/consumer-compat-cli.mjs +6 -205
  9. package/dist/consumer-compat-cli.mjs.map +1 -1
  10. package/dist/consumer-node-support.cjs +497 -0
  11. package/dist/consumer-node-support.cjs.map +1 -0
  12. package/dist/consumer-node-support.mjs +381 -0
  13. package/dist/consumer-node-support.mjs.map +1 -0
  14. package/dist/dependabot-cli.cjs +67 -0
  15. package/dist/dependabot-cli.cjs.map +1 -0
  16. package/dist/dependabot-cli.d.cts +1 -0
  17. package/dist/dependabot-cli.d.mts +1 -0
  18. package/dist/dependabot-cli.mjs +67 -0
  19. package/dist/dependabot-cli.mjs.map +1 -0
  20. package/dist/dependabot.cjs +5 -0
  21. package/dist/dependabot.d.cts +108 -0
  22. package/dist/dependabot.d.mts +108 -0
  23. package/dist/dependabot.mjs +3 -0
  24. package/dist/generated-file.cjs +61 -0
  25. package/dist/generated-file.cjs.map +1 -0
  26. package/dist/generated-file.mjs +44 -0
  27. package/dist/generated-file.mjs.map +1 -0
  28. package/dist/publish-artifact-syntax.cjs +1309 -0
  29. package/dist/publish-artifact-syntax.cjs.map +1 -0
  30. package/dist/publish-artifact-syntax.mjs +1265 -0
  31. package/dist/publish-artifact-syntax.mjs.map +1 -0
  32. package/dist/publish-contract-cli.cjs +53 -12
  33. package/dist/publish-contract-cli.cjs.map +1 -1
  34. package/dist/publish-contract-cli.mjs +49 -8
  35. package/dist/publish-contract-cli.mjs.map +1 -1
  36. package/dist/toolchain-changed.cjs +5 -1
  37. package/dist/toolchain-changed.cjs.map +1 -1
  38. package/dist/toolchain-changed.mjs +6 -2
  39. package/dist/toolchain-changed.mjs.map +1 -1
  40. package/dist/toolchain-dependabot.cjs +455 -0
  41. package/dist/toolchain-dependabot.cjs.map +1 -0
  42. package/dist/toolchain-dependabot.mjs +423 -0
  43. package/dist/toolchain-dependabot.mjs.map +1 -0
  44. package/dist/toolchain-guard.cjs +1096 -424
  45. package/dist/toolchain-guard.cjs.map +1 -1
  46. package/dist/toolchain-guard.mjs +1085 -413
  47. package/dist/toolchain-guard.mjs.map +1 -1
  48. package/dist/toolchain-inputs.cjs +122 -22
  49. package/dist/toolchain-inputs.cjs.map +1 -1
  50. package/dist/toolchain-inputs.mjs +117 -23
  51. package/dist/toolchain-inputs.mjs.map +1 -1
  52. package/dist/toolchain-schema.cjs +0 -493
  53. package/dist/toolchain-schema.cjs.map +1 -1
  54. package/dist/toolchain-schema.mjs +3 -379
  55. package/dist/toolchain-schema.mjs.map +1 -1
  56. package/dist/typecheck-parity.cjs +1 -1
  57. package/dist/typecheck-probe-cli.cjs +1 -1
  58. package/dist/typecheck-probe-cli.cjs.map +1 -1
  59. package/dist/typecheck-probe-cli.mjs +1 -1
  60. package/dist/typecheck-probe-cli.mjs.map +1 -1
  61. package/package.json +13 -1
  62. package/dist/publish-contract.cjs +0 -431
  63. package/dist/publish-contract.cjs.map +0 -1
  64. package/dist/publish-contract.mjs +0 -396
  65. package/dist/publish-contract.mjs.map +0 -1
package/README.md CHANGED
@@ -257,12 +257,15 @@ Peer ranges declare support: they must include the policy compiler release and t
257
257
  compatibility release when the selected layout uses it. Ranges do not contribute
258
258
  to the installed compiler inventory.
259
259
 
260
- JavaScript update roots come from the repository root, declared workspace membership,
260
+ JavaScript update roots include the repository root, declared workspace membership,
261
261
  and directories with their own `bun.lock`, `package-lock.json`, `yarn.lock`, or
262
- `pnpm-lock.yaml`. Other package manifests do not create update roots; directory names
263
- have no special meaning. Workspace members share their owner's update entry.
262
+ `pnpm-lock.yaml`. Independent manifests with dependencies, an explicit package manager,
263
+ or dependency-bearing workspace members also create update roots. Metadata-only
264
+ fixtures do not. Package-manager selection follows the nearest declaration or lock;
265
+ workspace members share their owner's update entry. Bundler `Gemfile`/`Gemfile.lock`
266
+ and Composer `composer.json`/`composer.lock` declare roots at their own directories.
264
267
 
265
- `node-engine` and `cloud-setup-drift` are mandatory: opt-outs cannot bypass the
268
+ `node-engine`, `workflow-trust` and `cloud-setup-drift` are mandatory: opt-outs cannot bypass the
266
269
  shared Node support range or generated cloud setup requirements.
267
270
 
268
271
  Opt-outs apply to the entire repository for that rule. Unknown or duplicate
@@ -288,6 +291,37 @@ discovering or building referenced projects. Missing, unreadable, and zero-input
288
291
  selected configs fail the check. `--help` prints usage and exits successfully;
289
292
  unknown arguments fail.
290
293
 
294
+ ## Generate dependency update configuration
295
+
296
+ Run `bunx --no-install stll-dependabot` from the repository root. It reads tracked
297
+ configuration and writes canonical `.github/dependabot.yml`, preserving an existing
298
+ `.github/dependabot.yaml` spelling, including when the indexed output has been
299
+ deleted from the working tree. Conflicting indexed or existing spellings are
300
+ rejected, and unrelated unreadable inputs still stop generation. Repeated runs
301
+ produce identical bytes; linked output files and directories are rejected.
302
+
303
+ For programmatic generation, the `@stll/oxlint-config/dependabot` subpath exports
304
+ `generateDependabotConfig({ files, policy })` and `parseToolchainPolicy`:
305
+
306
+ ```ts
307
+ import {
308
+ generateDependabotConfig,
309
+ parseToolchainPolicy,
310
+ } from "@stll/oxlint-config/dependabot";
311
+ import sharedPolicy from "@stll/oxlint-config/toolchain.json";
312
+
313
+ const yaml = generateDependabotConfig({
314
+ files: trackedConfiguration,
315
+ policy: parseToolchainPolicy(sharedPolicy).dependabot,
316
+ });
317
+ ```
318
+
319
+ The root lint-config entry does not load the tooling parser dependencies
320
+ `yaml`, `picomatch` or `smol-toml`; they belong to the tooling subpath and CLIs.
321
+ `files` maps existing tracked repository-relative configuration paths to their text.
322
+ Deleted tracked manifests are absent from the generator snapshot. Use the
323
+ CLI when a tracked snapshot is not already available; no source imports are needed.
324
+
291
325
  ## Shared cloud setup
292
326
 
293
327
  Add an explicit declaration to tracked root `stll-toolchain.json`:
@@ -305,6 +339,7 @@ Add an explicit declaration to tracked root `stll-toolchain.json`:
305
339
  },
306
340
  "tools": {
307
341
  "cargo-nextest": "0.9.148",
342
+ "gitleaks": "8.30.1",
308
343
  "wasm-bindgen-cli": "0.2.126",
309
344
  "uv": "0.13.1",
310
345
  "time": "1.9"
@@ -336,10 +371,17 @@ the minimal profile and a pinned, checksum-verified rustup installer. Rust-enabl
336
371
  setup also provisions Ubuntu's `build-essential` package for native compilation
337
372
  and linking.
338
373
 
374
+ `cloud.rust.additionalToolchains` explicitly selects extra exact compiler releases.
375
+ Each must match the shared pin or a valid `rustExceptions` entry and differ from the
376
+ primary compiler. Extras are installed once with the minimal profile and verified
377
+ offline at session start. An exception alone authorizes a version; it does not
378
+ provision it. This setting installs neither Hawk nor extra compiler components.
379
+ Generated scripts contain no trailing whitespace and end with one newline.
380
+
339
381
  `cloud.tools` is a closed object: the supported keys are `cargo-nextest`,
340
- `wasm-bindgen-cli`, `uv`, and `time`. Each value must be an exact release supported by
382
+ `wasm-bindgen-cli`, `gitleaks`, `uv`, and `time`. Each value must be an exact release supported by
341
383
  the installed tooling package. Unknown tools and unsupported versions fail
342
- generation. `cargo-nextest`, `wasm-bindgen-cli`, and `uv` use versioned upstream Linux binaries with committed
384
+ generation. `cargo-nextest`, `wasm-bindgen-cli`, `gitleaks`, and `uv` use versioned upstream Linux binaries with committed
343
385
  SHA256 pins for amd64 and arm64; setup does not build them with `cargo install`.
344
386
  `time` installs GNU time (`/usr/bin/time`, needed for `/usr/bin/time -v`) from
345
387
  Ubuntu's `time` package with bounded apt; its value is the upstream release
@@ -370,8 +412,8 @@ The command writes `.agents/cloud-setup.sh` with executable permissions and
370
412
  merges a `SessionStart` hook into `.claude/settings.json`. The hook runs
371
413
  `bash "$CLAUDE_PROJECT_DIR/.agents/cloud-setup.sh" start` only when
372
414
  `CLAUDE_CODE_REMOTE=true`, including sessions that reuse cached filesystems and
373
- sessions started from a repository subdirectory. Generation replaces the
374
- relative-path hook written by 1.1.0 in place. After successful startup, it appends the
415
+ sessions started from a repository subdirectory. Generation replaces the exact hooks written by 1.1.0 and 1.3.1 in place.
416
+ After successful startup, it appends the
375
417
  managed shell profile's literal exports to `CLAUDE_ENV_FILE` when available, so
376
418
  subsequent Claude commands select pinned runtimes and tools before preinstalled
377
419
  versions while retaining their inherited `PATH`. The settings also set
@@ -379,8 +421,12 @@ versions while retaining their inherited `PATH`. The settings also set
379
421
  attribution to commits or pull requests. Existing environment-file
380
422
  content, unrelated settings, and hooks are preserved. Invalid JSON, disabled
381
423
  hooks, conflicting cloud startup entries, or a different `attribution` value stop
382
- generation with a repair instruction. Commit both generated files.
424
+ generation with a repair instruction. Startup or environment persistence failures
425
+ produce actionable session context and a stderr reason; SessionStart cannot block
426
+ a session. Successful startup is silent on stdout. Commit both generated files.
383
427
  Generation refuses symlink parents or destinations and hard-linked destinations.
428
+ Generation recreates its deleted tracked output files and requires unrelated
429
+ tracked inputs to remain readable.
384
430
  `stll-toolchain-check` rejects missing, drifted, or undeclared tracked scripts,
385
431
  missing or conflicting startup hooks, and missing or different attribution
386
432
  settings with mandatory `cloud-setup-drift`.
@@ -392,6 +438,11 @@ prints usage without reading repository configuration.
392
438
  | Networked install | `bash .agents/cloud-setup.sh install` |
393
439
  | Offline session startup | `bash .agents/cloud-setup.sh start` |
394
440
 
441
+ Runtime installation repairs and verifies a missing or empty system CA bundle
442
+ offline before any apt refresh or download, including when the certificate package
443
+ is already current. If the certificate updater is unavailable or cannot restore
444
+ trust, restore the base image offline before rerunning installation.
445
+
395
446
  Both commands can be rerun. Missing or unknown subcommands print usage and
396
447
  exit 2; there is no implicit lifecycle mode. The script supports Ubuntu 24.04
397
448
  with root access or passwordless sudo and fails when required capabilities
@@ -432,14 +483,14 @@ or the configured package hosts; a redirected download names its original host.
432
483
 
433
484
  Allow these direct download hosts for the generated installer:
434
485
 
435
- | Host | Downloads |
436
- | ---------------------- | ------------------------------------------------------------------------ |
437
- | `nodejs.org` | Node archives and SHA256 manifests |
438
- | `github.com` | Bun archives and checksums; pinned nextest, wasm-bindgen and uv releases |
439
- | `static.rust-lang.org` | Pinned rustup-init, Rust manifests, toolchains, components and targets |
440
- | `www.postgresql.org` | PostgreSQL repository signing key |
441
- | `apt.postgresql.org` | PostgreSQL packages and repository metadata |
442
- | `download.valkey.io` | Pinned Valkey binary packages |
486
+ | Host | Downloads |
487
+ | ---------------------- | ---------------------------------------------------------------------------------- |
488
+ | `nodejs.org` | Node archives and SHA256 manifests |
489
+ | `github.com` | Bun archives and checksums; pinned nextest, wasm-bindgen, gitleaks and uv releases |
490
+ | `static.rust-lang.org` | Pinned rustup-init, Rust manifests, toolchains, components and targets |
491
+ | `www.postgresql.org` | PostgreSQL repository signing key |
492
+ | `apt.postgresql.org` | PostgreSQL packages and repository metadata |
493
+ | `download.valkey.io` | Pinned Valkey binary packages |
443
494
 
444
495
  GitHub release downloads redirect to `release-assets.githubusercontent.com`;
445
496
  allow that host as well. This includes Bun and the optional prebuilt tools.
@@ -463,6 +514,66 @@ resolved JavaScript targets, and entry points. `stll-publish-contract --write`
463
514
  records an explicit contract decision as a reviewable diff; it also validates
464
515
  consumer support and cannot accept development-only requirements.
465
516
 
517
+ Use `stll-publish-contract --artifact path/to/package.tgz` to check an existing
518
+ release archive after the workspace metadata checks. It validates the static
519
+ literal import closure within the packed package and compares the archive's
520
+ manifest with its declared public workspace package's committed contract and
521
+ resolved build target. The command never packs
522
+ or executes the archive, accepts exactly one archive, and cannot combine
523
+ `--artifact` with `--write`. Unknown or private package artifacts fail. Bare external
524
+ package dependencies are outside this local archive check. Dynamic imports require
525
+ literal specifiers; computed values cannot exclude the module loader namespace.
526
+ The reader follows literal ESM imports/re-exports, CommonJS
527
+ require/resolve calls and named, destructured or namespace `createRequire` bindings based on
528
+ `import.meta.url` or `__filename`. Asset references must be regular packed files;
529
+ non-JavaScript ESM assets are not parsed as scripts. Recursive import mappings
530
+ are rejected, including expanding wildcard aliases.
531
+ Node resolves collected specifiers against a temporary archive filesystem:
532
+ `createRequire(importer).resolve` for CommonJS and `import.meta.resolve` with the
533
+ importer's URL for ESM, using their respective default conditions. The worker
534
+ reads manifests and file existence without loading package code. Only manifests
535
+ need contents; other members use inert placeholders, and selected targets must
536
+ still be regular packed members. External import-map dependencies use inert
537
+ metadata outside the packed package boundary. Unknown external mapping forms
538
+ fail explicitly. Temporary files and the worker are removed after validation.
539
+ Regular-file validation and script traversal are tracked separately: an asset
540
+ later reached through CommonJS is parsed regardless of archive member order.
541
+ Exported declarations use the same scope collection as ordinary declarations.
542
+ Loader references are allowed only within recognized static declarations and
543
+ calls; inline module factories and unsupported namespace references fail closed.
544
+ Known loader calls require one literal specifier. Unresolved loader aliases,
545
+ computed bases, unsupported propagation and conflicting bindings report an
546
+ `Unresolvable dynamic loader` diagnostic instead of omitting dependencies.
547
+ In CommonJS scopes, declarations, parameters and assignments that shadow wrapper
548
+ loader names or inherited loader aliases must resolve to a recognized static
549
+ binding. Unknown bindings fail closed; uninitialized wrapper `var` declarations
550
+ retain the existing loader binding.
551
+ Imports from `node:module` or `module` must declare recognized factory or namespace
552
+ bindings, including string-named `createRequire` imports. Unsupported named imports,
553
+ side-effect imports, re-exports and dynamic namespace imports fail closed. Derived
554
+ aliases retain this policy in ESM scopes. A `__filename` factory base must resolve
555
+ to the unshadowed CommonJS wrapper binding; ESM factories use `import.meta.url`.
556
+ Loader bindings, factory results, module namespaces and their aliases cannot be
557
+ exported through ESM declarations/defaults/re-exports or CommonJS exports. They
558
+ produce an `unsupported loader propagation` diagnostic; analysis does not infer
559
+ loader behavior across module boundaries. Ordinary exported functions may still
560
+ make recognized static local loader calls.
561
+
562
+ This check proves the import closure of ordinary authored code in our own
563
+ packages within the supported static forms. It is not a security boundary against
564
+ deliberately obfuscated code. Unrecognized loader forms are rejected rather than
565
+ inferred; the check does not interpret dynamic code or prove arbitrary JavaScript
566
+ data flow. Known escape hatches are rejected: CommonJS wrapper `arguments`,
567
+ `eval`, `Function` construction/calls, `module.constructor`, `module.require`,
568
+ `process.mainModule`, global `require` properties, and `require.cache`,
569
+ `require.extensions` or `require.main` access. The strict metadata-read policy
570
+ also rejects aliases such as `const a = require.main`, which would otherwise
571
+ propagate an unclassified loader-capable object.
572
+
573
+ Wildcard exports exclude directory entries and treat only JavaScript-like
574
+ matches as scripts. Licenses and other extensionless wildcard matches remain
575
+ opaque; an import that reaches extensionless code still traverses its closure.
576
+
466
577
  The consumer policy pins Node 22.23.3, npm 12.2.0, pnpm 12.9.1, and TypeScript
467
578
  6.0.3 independently of the development toolchain. Published engines and TypeScript
468
579
  peers must support the consumer versions; ranges may include newer versions.
@@ -542,6 +653,9 @@ static contract; no older Node-to-ECMAScript mapping is inferred. Browser target
542
653
  are unsupported here: declare an explicit ECMAScript year. Types-only contracts reject any packed JavaScript. Nested
543
654
  package manifests and explicit module extensions determine parsing mode.
544
655
  Linked syntax inputs and unsafe archive paths fail before staging.
656
+ Invalid JavaScript encountered during import-closure analysis throws a `SyntaxError`
657
+ that names the packed member and retains the original parser error as its cause.
658
+ This can precede the declared-target and native Node checks.
545
659
 
546
660
  The reusable consumer job declares its exact scope in `stll-toolchain.json`:
547
661
 
@@ -623,6 +737,118 @@ exits successfully even when history is unreadable, reporting `changed: true`
623
737
  and `status: "unreadable"`. Use `changed` to force the full CI depth on both
624
738
  pull requests and merge-queue runs. An unreadable snapshot never skips checks.
625
739
 
740
+ Run repository-installed tools only after the workflow's existing trust decision.
741
+ The mandatory `workflow-trust` rule applies to PR workflows declaring a
742
+ `trust-check` job. Repository execution requires a direct dependency and a
743
+ condition requiring its `trusted == 'true'` output for every PR trigger.
744
+ Before that boundary, only literal output, bounded comparisons, `true`, `false`,
745
+ `exit`, bounded Git `rev-parse`/`merge-base` object queries, and GET-only `gh api`
746
+ reads are accepted. The trust job's structural `if`/`elif` comparisons use the
747
+ same checks; output may go only to `GITHUB_OUTPUT` or `/dev/null`. Variables must
748
+ be quoted, and command names and `printf` formats must be literal.
749
+
750
+ The following catalog is the production allowlist. Command kinds select the
751
+ corresponding bounded argument validator; a listed command does not authorize
752
+ arbitrary arguments. Git requires the stated number of explicit object refs.
753
+ Bootstrap action inputs are restricted to the listed keys; Bun requires a literal
754
+ version rather than a repository version file.
755
+
756
+ <!-- workflow-trust-bootstrap-catalog:start -->
757
+
758
+ ```json
759
+ {
760
+ "commands": {
761
+ "test": "comparison",
762
+ "[": "comparison",
763
+ "[[": "comparison",
764
+ "true": "status",
765
+ "false": "status",
766
+ "exit": "exit",
767
+ "echo": "output",
768
+ "printf": "format",
769
+ "git": "git",
770
+ "gh": "github"
771
+ },
772
+ "git": {
773
+ "rev-parse": {
774
+ "flags": ["--short", "--verify"],
775
+ "references": 1
776
+ },
777
+ "merge-base": {
778
+ "flags": ["--all", "--is-ancestor"],
779
+ "references": 2
780
+ }
781
+ },
782
+ "github": ["api"],
783
+ "actions": {
784
+ "actions/checkout": [
785
+ "fetch-depth",
786
+ "persist-credentials",
787
+ "ref",
788
+ "repository",
789
+ "submodules"
790
+ ],
791
+ "oven-sh/setup-bun": ["bun-version", "no-cache"]
792
+ }
793
+ }
794
+ ```
795
+
796
+ <!-- workflow-trust-bootstrap-catalog:end -->
797
+
798
+ Job containers and nonempty services require the job-level trust gate because
799
+ they start before steps and supply executable code. A step condition cannot
800
+ authorize their startup. Digest pinning alone does not approve an image for
801
+ pre-trust execution; the guard has no implicit image approval catalog.
802
+ Before trust, step actions must use a full SHA and match the explicit bootstrap
803
+ catalog: `actions/checkout` or `oven-sh/setup-bun`, with
804
+ bounded selector inputs. Other actions and unsupported inputs require the
805
+ job-level gate; a step condition cannot authorize them. Every job-level reusable workflow
806
+ call requires the job-level trust condition, including pinned calls without inputs.
807
+
808
+ Interpreters, sourced files, executable paths, package managers (including
809
+ metadata and script-disabled install modes), launchers, custom shells, and
810
+ unknown commands or syntax require trust. File readers, file-presence tests,
811
+ `jq`/`yq`, Git working-tree/history commands, and `actions/setup-node` require the
812
+ job-level gate. Unsupported options and query programs also require the gate. For example, gate the entire
813
+ detector job, including dependency installation:
814
+
815
+ ```yaml
816
+ check-changes:
817
+ needs: [trust-check]
818
+ if: >-
819
+ always() && !cancelled()
820
+ && ((github.event_name != 'pull_request'
821
+ && github.event_name != 'pull_request_target')
822
+ || needs.trust-check.outputs.trusted == 'true')
823
+ runs-on: ubuntu-latest
824
+ steps:
825
+ # Check out, set up Bun, install dependencies, then run the shared detector.
826
+ - run: bunx --no-install stll-toolchain-changed --since "$BASE_REF"
827
+ ```
828
+
829
+ Actions interpolation is checked before shell quoting or comments. Only the
830
+ bounded identity expressions `github.sha`, `github.run_id`, `github.run_attempt`
831
+ and `github.repository` may appear directly in pre-trust scripts. Other contexts,
832
+ functions and matrix values require environment indirection or the trust gate;
833
+ the guard does not infer that an expression is safe from its shell quotes.
834
+ Pass contributor-controlled metadata through a supported data environment key
835
+ (`PR_TITLE`, `HEAD_REF`, `ISSUE_BODY` or `COMMENT_BODY`) and quote its expansion:
836
+
837
+ ```yaml
838
+ - env:
839
+ PR_TITLE: ${{ github.event.pull_request.title }}
840
+ run: echo "$PR_TITLE"
841
+ ```
842
+
843
+ The trust job must classify each PR trigger used by the workflow. Main/manual
844
+ jobs remain runnable when the PR-only trust job is skipped. Job or step
845
+ conditions may prove that execution is limited to non-PR events; event string
846
+ comparisons follow Actions case-insensitive equality. The guard
847
+ proves only supported static metadata commands and boolean conditions; unknown
848
+ commands and conditions cannot authorize execution. It does not validate the
849
+ trust job's authorization policy.
850
+ Workflows with no declared trust job retain their existing fork-CI policy.
851
+
626
852
  The comparison includes Bun declarations, Node selectors and support ranges,
627
853
  resolved TypeScript compiler versions (including workspace and compatibility
628
854
  aliases), oxlint, oxlint-tsgolint, oxfmt, and shared tooling pins. Lockfile
@@ -701,3 +927,14 @@ another exact stable release. Each record contains exactly `pin`, `reason`,
701
927
  `tracking` (a GitHub issue URL) and `expires` (a future `YYYY-MM-DD` date).
702
928
  Channels, expired records and `rust-version` rule opt-outs fail validation.
703
929
  See the [root policy guide](../../README.md) for a complete declaration.
930
+
931
+ `stll-toolchain-check` requires tracked JavaScript and TypeScript files selected by type-aware lint to belong to declared TypeScript projects. See the [Node scripts project recipe](https://github.com/stella/tooling/blob/main/README.md) for scripts outside the library project. The check inventories Oxlint-selected files and expands compiler configurations; it does not run lint or type checking.
932
+
933
+ The project-ownership guard models declared package-manager scripts, including
934
+ literal filtered Bun build bodies and their script aliases with the selected
935
+ workspace as the working directory. It inventories supported linter invocations;
936
+ it never executes build bodies. Unrecognized linter launchers and script forms
937
+ fail closed. This is an ownership check for ordinary authored package scripts,
938
+ not a security boundary against deliberately obfuscated code or lint invocations
939
+ hidden inside arbitrary program bodies. Unsupported forms require an explicit
940
+ supported script invocation or a separately tested extension to the model.
@@ -3,44 +3,15 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const require_toolchain_inputs = require("./toolchain-inputs.cjs");
4
4
  const require_toolchain_guard = require("./toolchain-guard.cjs");
5
5
  const require_toolchain_schema = require("./toolchain-schema.cjs");
6
+ const require_generated_file = require("./generated-file.cjs");
6
7
  let node_fs = require("node:fs");
7
8
  let node_path = require("node:path");
8
9
  let node_fs_promises = require("node:fs/promises");
9
10
  //#region src/cloud-setup-cli.ts
10
11
  const usage = "Usage: stll-cloud-setup (run from repository root)\n stll-cloud-setup --help";
11
- const optionalStat = async (path) => {
12
- try {
13
- return await (0, node_fs_promises.lstat)(path);
14
- } catch (error) {
15
- if (error instanceof Error && "code" in error && error.code === "ENOENT") return void 0;
16
- throw error;
17
- }
18
- };
19
- const checkOutput = async (destination) => {
20
- const parent = (0, node_path.dirname)(destination);
21
- const parentStat = await optionalStat(parent);
22
- if (parentStat !== void 0 && (parentStat.isSymbolicLink() || !parentStat.isDirectory())) throw new Error("cloud setup parent must be a real repository directory");
23
- const destinationStat = await optionalStat(destination);
24
- if (destinationStat !== void 0 && (destinationStat.isSymbolicLink() || !destinationStat.isFile() || destinationStat.nlink !== 1)) throw new Error("cloud setup destination must be a single-link regular repository file");
25
- return destinationStat;
26
- };
27
- const writeOutput = async ({ destination, text, mode }) => {
28
- await checkOutput(destination);
29
- await (0, node_fs_promises.mkdir)((0, node_path.dirname)(destination), { recursive: true });
30
- const output = await (0, node_fs_promises.open)(destination, node_fs.constants.O_WRONLY | node_fs.constants.O_CREAT | node_fs.constants.O_NOFOLLOW, mode);
31
- try {
32
- const opened = await output.stat();
33
- if (!opened.isFile() || opened.nlink !== 1) throw new Error("cloud setup destination must be a single-link regular repository file");
34
- await output.truncate(0);
35
- await output.writeFile(text, "utf8");
36
- await output.chmod(mode);
37
- } finally {
38
- await output.close();
39
- }
40
- };
41
12
  const readClaudeSettings = async (destination) => {
42
13
  try {
43
- if (await checkOutput(destination) === void 0) return void 0;
14
+ if (await require_generated_file.checkGeneratedOutput(destination) === void 0) return void 0;
44
15
  const input = await (0, node_fs_promises.open)(destination, node_fs.constants.O_RDONLY | node_fs.constants.O_NOFOLLOW);
45
16
  try {
46
17
  const opened = await input.stat();
@@ -65,7 +36,20 @@ const main = async () => {
65
36
  if (args.length !== 0) throw new Error(usage);
66
37
  const root = await (0, node_fs_promises.realpath)(process.cwd());
67
38
  const { files, diagnostics } = require_toolchain_guard.readToolchainInputs(root);
68
- if (diagnostics.length > 0) throw new Error(diagnostics.map(({ path, message }) => `${path}: ${message}${path === ".claude/settings.json" ? `; ${require_toolchain_inputs.cloudSessionStartFix}` : ""}`).join("\n"));
39
+ const destination = (0, node_path.join)(root, require_toolchain_inputs.cloudSetupPath);
40
+ const settingsDestination = (0, node_path.join)(root, require_toolchain_inputs.cloudClaudeSettingsPath);
41
+ const settings = await readClaudeSettings(settingsDestination);
42
+ const remainingDiagnostics = await require_generated_file.generationDiagnostics({
43
+ diagnostics,
44
+ outputs: [{
45
+ relative: require_toolchain_inputs.cloudSetupPath,
46
+ destination
47
+ }, {
48
+ relative: require_toolchain_inputs.cloudClaudeSettingsPath,
49
+ destination: settingsDestination
50
+ }]
51
+ });
52
+ if (remainingDiagnostics.length > 0) throw new Error(remainingDiagnostics.map(({ path, message }) => `${path}: ${message}${path === ".claude/settings.json" ? `; ${require_toolchain_inputs.cloudSessionStartFix}` : ""}`).join("\n"));
69
53
  const text = files["stll-toolchain.json"];
70
54
  if (text === void 0) throw new Error("requires tracked stll-toolchain.json");
71
55
  const { cloud, rustExceptions } = require_toolchain_guard.parseToolchainConfiguration(JSON.parse(text));
@@ -80,17 +64,13 @@ const main = async () => {
80
64
  files,
81
65
  rustExceptions
82
66
  });
83
- const destination = (0, node_path.join)(root, require_toolchain_inputs.cloudSetupPath);
84
- await checkOutput(destination);
85
- const settingsDestination = (0, node_path.join)(root, require_toolchain_inputs.cloudClaudeSettingsPath);
86
- const settings = await readClaudeSettings(settingsDestination);
87
67
  const mergedSettings = require_toolchain_inputs.mergeCloudClaudeSettings(settings?.text);
88
- await writeOutput({
68
+ await require_generated_file.writeGeneratedFile({
89
69
  destination,
90
70
  text: script,
91
71
  mode: 493
92
72
  });
93
- if (mergedSettings !== settings?.text) await writeOutput({
73
+ if (mergedSettings !== settings?.text) await require_generated_file.writeGeneratedFile({
94
74
  destination: settingsDestination,
95
75
  text: mergedSettings,
96
76
  mode: settings?.mode ?? 420
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-setup-cli.cjs","names":["lstat","dirname","mkdir","open","constants","cloudClaudeSettingsPath","cloudSessionStartFix","realpath","readToolchainInputs","parseToolchainConfiguration","parseToolchainPolicy","readFile","generateCloudSetup","join","cloudSetupPath","mergeCloudClaudeSettings"],"sources":["../src/cloud-setup-cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { constants } from \"node:fs\";\nimport { lstat, mkdir, open, readFile, realpath } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\nimport { cloudSetupPath, generateCloudSetup } from \"./cloud-setup\";\nimport {\n cloudClaudeSettingsPath,\n cloudSessionStartFix,\n mergeCloudClaudeSettings,\n} from \"./cloud-setup-claude\";\nimport {\n parseToolchainConfiguration,\n readToolchainInputs,\n} from \"./toolchain-guard\";\nimport { parseToolchainPolicy } from \"./toolchain-schema\";\n\nconst usage =\n \"Usage: stll-cloud-setup (run from repository root)\\n stll-cloud-setup --help\";\nconst optionalStat = async (path: string) => {\n try {\n return await lstat(path);\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\")\n return undefined;\n throw error;\n }\n};\n\nconst checkOutput = async (destination: string) => {\n const parent = dirname(destination);\n const parentStat = await optionalStat(parent);\n if (\n parentStat !== undefined &&\n (parentStat.isSymbolicLink() || !parentStat.isDirectory())\n )\n throw new Error(\"cloud setup parent must be a real repository directory\");\n const destinationStat = await optionalStat(destination);\n if (\n destinationStat !== undefined &&\n (destinationStat.isSymbolicLink() ||\n !destinationStat.isFile() ||\n destinationStat.nlink !== 1)\n )\n throw new Error(\n \"cloud setup destination must be a single-link regular repository file\",\n );\n return destinationStat;\n};\n\ntype WriteOutputOptions = {\n destination: string;\n text: string;\n mode: number;\n};\n\nconst writeOutput = async ({ destination, text, mode }: WriteOutputOptions) => {\n await checkOutput(destination);\n await mkdir(dirname(destination), { recursive: true });\n const output = await open(\n destination,\n constants.O_WRONLY | constants.O_CREAT | constants.O_NOFOLLOW,\n mode,\n );\n try {\n const opened = await output.stat();\n if (!opened.isFile() || opened.nlink !== 1)\n throw new Error(\n \"cloud setup destination must be a single-link regular repository file\",\n );\n await output.truncate(0);\n await output.writeFile(text, \"utf8\");\n await output.chmod(mode);\n } finally {\n await output.close();\n }\n};\n\nconst readClaudeSettings = async (destination: string) => {\n try {\n if ((await checkOutput(destination)) === undefined) return undefined;\n const input = await open(\n destination,\n constants.O_RDONLY | constants.O_NOFOLLOW,\n );\n try {\n const opened = await input.stat();\n if (!opened.isFile() || opened.nlink !== 1)\n throw new Error(\n \"settings must be a single-link regular repository file\",\n );\n return { text: await input.readFile(\"utf8\"), mode: opened.mode & 0o777 };\n } finally {\n await input.close();\n }\n } catch (error) {\n throw new Error(\n `cannot safely merge ${cloudClaudeSettingsPath}: ${error instanceof Error ? error.message : String(error)}; ${cloudSessionStartFix}`,\n );\n }\n};\n\nconst main = async () => {\n const args = process.argv.slice(2);\n if (args.length === 1 && args.at(0) === \"--help\") {\n process.stdout.write(`${usage}\\n`);\n return;\n }\n if (args.length !== 0) throw new Error(usage);\n const root = await realpath(process.cwd());\n const { files, diagnostics } = readToolchainInputs(root);\n if (diagnostics.length > 0)\n throw new Error(\n diagnostics\n .map(\n ({ path, message }) =>\n `${path}: ${message}${path === cloudClaudeSettingsPath ? `; ${cloudSessionStartFix}` : \"\"}`,\n )\n .join(\"\\n\"),\n );\n const text = files[\"stll-toolchain.json\"];\n if (text === undefined)\n throw new Error(\"requires tracked stll-toolchain.json\");\n const { cloud, rustExceptions } = parseToolchainConfiguration(\n JSON.parse(text),\n );\n if (cloud === undefined)\n throw new Error(\"requires an explicit cloud declaration\");\n const nodeVersion = files[\".node-version\"];\n if (nodeVersion === undefined)\n throw new Error(\"requires tracked root .node-version\");\n const policy = parseToolchainPolicy(\n JSON.parse(\n await readFile(new URL(\"../toolchain.json\", import.meta.url), \"utf8\"),\n ),\n );\n const script = generateCloudSetup({\n policy,\n cloud,\n nodeVersion: nodeVersion.trim(),\n files,\n rustExceptions,\n });\n const destination = join(root, cloudSetupPath);\n await checkOutput(destination);\n const settingsDestination = join(root, cloudClaudeSettingsPath);\n const settings = await readClaudeSettings(settingsDestination);\n const mergedSettings = mergeCloudClaudeSettings(settings?.text);\n await writeOutput({ destination, text: script, mode: 0o755 });\n if (mergedSettings !== settings?.text)\n await writeOutput({\n destination: settingsDestination,\n text: mergedSettings,\n mode: settings?.mode ?? 0o644,\n });\n process.stdout.write(\n `Generated ${cloudSetupPath} and ${cloudClaudeSettingsPath}\\n`,\n );\n};\n\nmain().catch((error: unknown) => {\n process.stderr.write(\n `cloud-setup: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exitCode = 1;\n});\n"],"mappings":";;;;;;;;;AAiBA,MAAM,QACJ;AACF,MAAM,eAAe,OAAO,SAAiB;CAC3C,IAAI;EACF,OAAO,OAAA,GAAMA,iBAAAA,MAAAA,CAAM,IAAI;CACzB,SAAS,OAAO;EACd,IAAI,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,UAC9D,OAAO,KAAA;EACT,MAAM;CACR;AACF;AAEA,MAAM,cAAc,OAAO,gBAAwB;CACjD,MAAM,UAAA,GAASC,UAAAA,QAAAA,CAAQ,WAAW;CAClC,MAAM,aAAa,MAAM,aAAa,MAAM;CAC5C,IACE,eAAe,KAAA,MACd,WAAW,eAAe,KAAK,CAAC,WAAW,YAAY,IAExD,MAAM,IAAI,MAAM,wDAAwD;CAC1E,MAAM,kBAAkB,MAAM,aAAa,WAAW;CACtD,IACE,oBAAoB,KAAA,MACnB,gBAAgB,eAAe,KAC9B,CAAC,gBAAgB,OAAO,KACxB,gBAAgB,UAAU,IAE5B,MAAM,IAAI,MACR,uEACF;CACF,OAAO;AACT;AAQA,MAAM,cAAc,OAAO,EAAE,aAAa,MAAM,WAA+B;CAC7E,MAAM,YAAY,WAAW;CAC7B,OAAA,GAAMC,iBAAAA,MAAAA,EAAAA,GAAMD,UAAAA,QAAAA,CAAQ,WAAW,GAAG,EAAE,WAAW,KAAK,CAAC;CACrD,MAAM,SAAS,OAAA,GAAME,iBAAAA,KAAAA,CACnB,aACAC,QAAAA,UAAU,WAAWA,QAAAA,UAAU,UAAUA,QAAAA,UAAU,YACnD,IACF;CACA,IAAI;EACF,MAAM,SAAS,MAAM,OAAO,KAAK;EACjC,IAAI,CAAC,OAAO,OAAO,KAAK,OAAO,UAAU,GACvC,MAAM,IAAI,MACR,uEACF;EACF,MAAM,OAAO,SAAS,CAAC;EACvB,MAAM,OAAO,UAAU,MAAM,MAAM;EACnC,MAAM,OAAO,MAAM,IAAI;CACzB,UAAU;EACR,MAAM,OAAO,MAAM;CACrB;AACF;AAEA,MAAM,qBAAqB,OAAO,gBAAwB;CACxD,IAAI;EACF,IAAK,MAAM,YAAY,WAAW,MAAO,KAAA,GAAW,OAAO,KAAA;EAC3D,MAAM,QAAQ,OAAA,GAAMD,iBAAAA,KAAAA,CAClB,aACAC,QAAAA,UAAU,WAAWA,QAAAA,UAAU,UACjC;EACA,IAAI;GACF,MAAM,SAAS,MAAM,MAAM,KAAK;GAChC,IAAI,CAAC,OAAO,OAAO,KAAK,OAAO,UAAU,GACvC,MAAM,IAAI,MACR,wDACF;GACF,OAAO;IAAE,MAAM,MAAM,MAAM,SAAS,MAAM;IAAG,MAAM,OAAO,OAAO;GAAM;EACzE,UAAU;GACR,MAAM,MAAM,MAAM;EACpB;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MACR,uBAAuBC,yBAAAA,wBAAwB,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,IAAIC,yBAAAA,sBAChH;CACF;AACF;AAEA,MAAM,OAAO,YAAY;CACvB,MAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;CACjC,IAAI,KAAK,WAAW,KAAK,KAAK,GAAG,CAAC,MAAM,UAAU;EAChD,QAAQ,OAAO,MAAM,GAAG,MAAM,GAAG;EACjC;CACF;CACA,IAAI,KAAK,WAAW,GAAG,MAAM,IAAI,MAAM,KAAK;CAC5C,MAAM,OAAO,OAAA,GAAMC,iBAAAA,SAAAA,CAAS,QAAQ,IAAI,CAAC;CACzC,MAAM,EAAE,OAAO,gBAAgBC,wBAAAA,oBAAoB,IAAI;CACvD,IAAI,YAAY,SAAS,GACvB,MAAM,IAAI,MACR,YACG,KACE,EAAE,MAAM,cACP,GAAG,KAAK,IAAI,UAAU,SAAA,0BAAmC,KAAKF,yBAAAA,yBAAyB,IAC3F,CAAC,CACA,KAAK,IAAI,CACd;CACF,MAAM,OAAO,MAAM;CACnB,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,MAAM,sCAAsC;CACxD,MAAM,EAAE,OAAO,mBAAmBG,wBAAAA,4BAChC,KAAK,MAAM,IAAI,CACjB;CACA,IAAI,UAAU,KAAA,GACZ,MAAM,IAAI,MAAM,wCAAwC;CAC1D,MAAM,cAAc,MAAM;CAC1B,IAAI,gBAAgB,KAAA,GAClB,MAAM,IAAI,MAAM,qCAAqC;CACvD,MAAM,SAASC,yBAAAA,qBACb,KAAK,MACH,OAAA,GAAMC,iBAAAA,SAAAA,CAAS,IAAI,IAAI,qBAAA,QAAA,KAAA,CAAA,CAAA,cAAA,UAAA,CAAA,CAAA,IAAoC,GAAG,MAAM,CACtE,CACF;CACA,MAAM,SAASC,yBAAAA,mBAAmB;EAChC;EACA;EACA,aAAa,YAAY,KAAK;EAC9B;EACA;CACF,CAAC;CACD,MAAM,eAAA,GAAcC,UAAAA,KAAAA,CAAK,MAAMC,yBAAAA,cAAc;CAC7C,MAAM,YAAY,WAAW;CAC7B,MAAM,uBAAA,GAAsBD,UAAAA,KAAAA,CAAK,MAAMR,yBAAAA,uBAAuB;CAC9D,MAAM,WAAW,MAAM,mBAAmB,mBAAmB;CAC7D,MAAM,iBAAiBU,yBAAAA,yBAAyB,UAAU,IAAI;CAC9D,MAAM,YAAY;EAAE;EAAa,MAAM;EAAQ,MAAM;CAAM,CAAC;CAC5D,IAAI,mBAAmB,UAAU,MAC/B,MAAM,YAAY;EAChB,aAAa;EACb,MAAM;EACN,MAAM,UAAU,QAAQ;CAC1B,CAAC;CACH,QAAQ,OAAO,MACb,aAAaD,yBAAAA,eAAe,OAAOT,yBAAAA,wBAAwB,GAC7D;AACF;AAEA,KAAK,CAAC,CAAC,OAAO,UAAmB;CAC/B,QAAQ,OAAO,MACb,gBAAgB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,GACzE;CACA,QAAQ,WAAW;AACrB,CAAC"}
1
+ {"version":3,"file":"cloud-setup-cli.cjs","names":["checkGeneratedOutput","open","constants","cloudClaudeSettingsPath","cloudSessionStartFix","realpath","readToolchainInputs","join","cloudSetupPath","generationDiagnostics","parseToolchainConfiguration","parseToolchainPolicy","readFile","generateCloudSetup","mergeCloudClaudeSettings","writeGeneratedFile"],"sources":["../src/cloud-setup-cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { constants } from \"node:fs\";\nimport { open, readFile, realpath } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { cloudSetupPath, generateCloudSetup } from \"./cloud-setup\";\nimport {\n cloudClaudeSettingsPath,\n cloudSessionStartFix,\n mergeCloudClaudeSettings,\n} from \"./cloud-setup-claude\";\nimport {\n checkGeneratedOutput,\n generationDiagnostics,\n writeGeneratedFile,\n} from \"./generated-file\";\nimport {\n parseToolchainConfiguration,\n readToolchainInputs,\n} from \"./toolchain-guard\";\nimport { parseToolchainPolicy } from \"./toolchain-schema\";\n\nconst usage =\n \"Usage: stll-cloud-setup (run from repository root)\\n stll-cloud-setup --help\";\nconst readClaudeSettings = async (destination: string) => {\n try {\n if ((await checkGeneratedOutput(destination)) === undefined)\n return undefined;\n const input = await open(\n destination,\n constants.O_RDONLY | constants.O_NOFOLLOW,\n );\n try {\n const opened = await input.stat();\n if (!opened.isFile() || opened.nlink !== 1)\n throw new Error(\n \"settings must be a single-link regular repository file\",\n );\n return { text: await input.readFile(\"utf8\"), mode: opened.mode & 0o777 };\n } finally {\n await input.close();\n }\n } catch (error) {\n throw new Error(\n `cannot safely merge ${cloudClaudeSettingsPath}: ${error instanceof Error ? error.message : String(error)}; ${cloudSessionStartFix}`,\n );\n }\n};\n\nconst main = async () => {\n const args = process.argv.slice(2);\n if (args.length === 1 && args.at(0) === \"--help\") {\n process.stdout.write(`${usage}\\n`);\n return;\n }\n if (args.length !== 0) throw new Error(usage);\n const root = await realpath(process.cwd());\n const { files, diagnostics } = readToolchainInputs(root);\n const destination = join(root, cloudSetupPath);\n const settingsDestination = join(root, cloudClaudeSettingsPath);\n // Validate settings separately to retain actionable merge diagnostics.\n const settings = await readClaudeSettings(settingsDestination);\n const remainingDiagnostics = await generationDiagnostics({\n diagnostics,\n outputs: [\n { relative: cloudSetupPath, destination },\n { relative: cloudClaudeSettingsPath, destination: settingsDestination },\n ],\n });\n if (remainingDiagnostics.length > 0)\n throw new Error(\n remainingDiagnostics\n .map(\n ({ path, message }) =>\n `${path}: ${message}${path === cloudClaudeSettingsPath ? `; ${cloudSessionStartFix}` : \"\"}`,\n )\n .join(\"\\n\"),\n );\n const text = files[\"stll-toolchain.json\"];\n if (text === undefined)\n throw new Error(\"requires tracked stll-toolchain.json\");\n const { cloud, rustExceptions } = parseToolchainConfiguration(\n JSON.parse(text),\n );\n if (cloud === undefined)\n throw new Error(\"requires an explicit cloud declaration\");\n const nodeVersion = files[\".node-version\"];\n if (nodeVersion === undefined)\n throw new Error(\"requires tracked root .node-version\");\n const policy = parseToolchainPolicy(\n JSON.parse(\n await readFile(new URL(\"../toolchain.json\", import.meta.url), \"utf8\"),\n ),\n );\n const script = generateCloudSetup({\n policy,\n cloud,\n nodeVersion: nodeVersion.trim(),\n files,\n rustExceptions,\n });\n const mergedSettings = mergeCloudClaudeSettings(settings?.text);\n await writeGeneratedFile({ destination, text: script, mode: 0o755 });\n if (mergedSettings !== settings?.text)\n await writeGeneratedFile({\n destination: settingsDestination,\n text: mergedSettings,\n mode: settings?.mode ?? 0o644,\n });\n process.stdout.write(\n `Generated ${cloudSetupPath} and ${cloudClaudeSettingsPath}\\n`,\n );\n};\n\nmain().catch((error: unknown) => {\n process.stderr.write(\n `cloud-setup: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exitCode = 1;\n});\n"],"mappings":"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"}
@@ -1,45 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import { b as cloudSetupPath, g as cloudSessionStartFix, h as cloudClaudeSettingsPath, x as generateCloudSetup, y as mergeCloudClaudeSettings } from "./toolchain-inputs.mjs";
2
+ import { S as generateCloudSetup, _ as cloudSessionStartFix, b as mergeCloudClaudeSettings, g as cloudClaudeSettingsPath, x as cloudSetupPath } from "./toolchain-inputs.mjs";
3
3
  import { n as parseToolchainConfiguration, r as readToolchainInputs } from "./toolchain-guard.mjs";
4
4
  import { t as parseToolchainPolicy } from "./toolchain-schema.mjs";
5
+ import { n as generationDiagnostics, r as writeGeneratedFile, t as checkGeneratedOutput } from "./generated-file.mjs";
5
6
  import { constants } from "node:fs";
6
- import { dirname, join } from "node:path";
7
- import { lstat, mkdir, open, readFile, realpath } from "node:fs/promises";
7
+ import { join } from "node:path";
8
+ import { open, readFile, realpath } from "node:fs/promises";
8
9
  //#region src/cloud-setup-cli.ts
9
10
  const usage = "Usage: stll-cloud-setup (run from repository root)\n stll-cloud-setup --help";
10
- const optionalStat = async (path) => {
11
- try {
12
- return await lstat(path);
13
- } catch (error) {
14
- if (error instanceof Error && "code" in error && error.code === "ENOENT") return void 0;
15
- throw error;
16
- }
17
- };
18
- const checkOutput = async (destination) => {
19
- const parent = dirname(destination);
20
- const parentStat = await optionalStat(parent);
21
- if (parentStat !== void 0 && (parentStat.isSymbolicLink() || !parentStat.isDirectory())) throw new Error("cloud setup parent must be a real repository directory");
22
- const destinationStat = await optionalStat(destination);
23
- if (destinationStat !== void 0 && (destinationStat.isSymbolicLink() || !destinationStat.isFile() || destinationStat.nlink !== 1)) throw new Error("cloud setup destination must be a single-link regular repository file");
24
- return destinationStat;
25
- };
26
- const writeOutput = async ({ destination, text, mode }) => {
27
- await checkOutput(destination);
28
- await mkdir(dirname(destination), { recursive: true });
29
- const output = await open(destination, constants.O_WRONLY | constants.O_CREAT | constants.O_NOFOLLOW, mode);
30
- try {
31
- const opened = await output.stat();
32
- if (!opened.isFile() || opened.nlink !== 1) throw new Error("cloud setup destination must be a single-link regular repository file");
33
- await output.truncate(0);
34
- await output.writeFile(text, "utf8");
35
- await output.chmod(mode);
36
- } finally {
37
- await output.close();
38
- }
39
- };
40
11
  const readClaudeSettings = async (destination) => {
41
12
  try {
42
- if (await checkOutput(destination) === void 0) return void 0;
13
+ if (await checkGeneratedOutput(destination) === void 0) return void 0;
43
14
  const input = await open(destination, constants.O_RDONLY | constants.O_NOFOLLOW);
44
15
  try {
45
16
  const opened = await input.stat();
@@ -64,7 +35,20 @@ const main = async () => {
64
35
  if (args.length !== 0) throw new Error(usage);
65
36
  const root = await realpath(process.cwd());
66
37
  const { files, diagnostics } = readToolchainInputs(root);
67
- if (diagnostics.length > 0) throw new Error(diagnostics.map(({ path, message }) => `${path}: ${message}${path === ".claude/settings.json" ? `; ${cloudSessionStartFix}` : ""}`).join("\n"));
38
+ const destination = join(root, cloudSetupPath);
39
+ const settingsDestination = join(root, cloudClaudeSettingsPath);
40
+ const settings = await readClaudeSettings(settingsDestination);
41
+ const remainingDiagnostics = await generationDiagnostics({
42
+ diagnostics,
43
+ outputs: [{
44
+ relative: cloudSetupPath,
45
+ destination
46
+ }, {
47
+ relative: cloudClaudeSettingsPath,
48
+ destination: settingsDestination
49
+ }]
50
+ });
51
+ if (remainingDiagnostics.length > 0) throw new Error(remainingDiagnostics.map(({ path, message }) => `${path}: ${message}${path === ".claude/settings.json" ? `; ${cloudSessionStartFix}` : ""}`).join("\n"));
68
52
  const text = files["stll-toolchain.json"];
69
53
  if (text === void 0) throw new Error("requires tracked stll-toolchain.json");
70
54
  const { cloud, rustExceptions } = parseToolchainConfiguration(JSON.parse(text));
@@ -79,17 +63,13 @@ const main = async () => {
79
63
  files,
80
64
  rustExceptions
81
65
  });
82
- const destination = join(root, cloudSetupPath);
83
- await checkOutput(destination);
84
- const settingsDestination = join(root, cloudClaudeSettingsPath);
85
- const settings = await readClaudeSettings(settingsDestination);
86
66
  const mergedSettings = mergeCloudClaudeSettings(settings?.text);
87
- await writeOutput({
67
+ await writeGeneratedFile({
88
68
  destination,
89
69
  text: script,
90
70
  mode: 493
91
71
  });
92
- if (mergedSettings !== settings?.text) await writeOutput({
72
+ if (mergedSettings !== settings?.text) await writeGeneratedFile({
93
73
  destination: settingsDestination,
94
74
  text: mergedSettings,
95
75
  mode: settings?.mode ?? 420
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-setup-cli.mjs","names":[],"sources":["../src/cloud-setup-cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { constants } from \"node:fs\";\nimport { lstat, mkdir, open, readFile, realpath } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\nimport { cloudSetupPath, generateCloudSetup } from \"./cloud-setup\";\nimport {\n cloudClaudeSettingsPath,\n cloudSessionStartFix,\n mergeCloudClaudeSettings,\n} from \"./cloud-setup-claude\";\nimport {\n parseToolchainConfiguration,\n readToolchainInputs,\n} from \"./toolchain-guard\";\nimport { parseToolchainPolicy } from \"./toolchain-schema\";\n\nconst usage =\n \"Usage: stll-cloud-setup (run from repository root)\\n stll-cloud-setup --help\";\nconst optionalStat = async (path: string) => {\n try {\n return await lstat(path);\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\")\n return undefined;\n throw error;\n }\n};\n\nconst checkOutput = async (destination: string) => {\n const parent = dirname(destination);\n const parentStat = await optionalStat(parent);\n if (\n parentStat !== undefined &&\n (parentStat.isSymbolicLink() || !parentStat.isDirectory())\n )\n throw new Error(\"cloud setup parent must be a real repository directory\");\n const destinationStat = await optionalStat(destination);\n if (\n destinationStat !== undefined &&\n (destinationStat.isSymbolicLink() ||\n !destinationStat.isFile() ||\n destinationStat.nlink !== 1)\n )\n throw new Error(\n \"cloud setup destination must be a single-link regular repository file\",\n );\n return destinationStat;\n};\n\ntype WriteOutputOptions = {\n destination: string;\n text: string;\n mode: number;\n};\n\nconst writeOutput = async ({ destination, text, mode }: WriteOutputOptions) => {\n await checkOutput(destination);\n await mkdir(dirname(destination), { recursive: true });\n const output = await open(\n destination,\n constants.O_WRONLY | constants.O_CREAT | constants.O_NOFOLLOW,\n mode,\n );\n try {\n const opened = await output.stat();\n if (!opened.isFile() || opened.nlink !== 1)\n throw new Error(\n \"cloud setup destination must be a single-link regular repository file\",\n );\n await output.truncate(0);\n await output.writeFile(text, \"utf8\");\n await output.chmod(mode);\n } finally {\n await output.close();\n }\n};\n\nconst readClaudeSettings = async (destination: string) => {\n try {\n if ((await checkOutput(destination)) === undefined) return undefined;\n const input = await open(\n destination,\n constants.O_RDONLY | constants.O_NOFOLLOW,\n );\n try {\n const opened = await input.stat();\n if (!opened.isFile() || opened.nlink !== 1)\n throw new Error(\n \"settings must be a single-link regular repository file\",\n );\n return { text: await input.readFile(\"utf8\"), mode: opened.mode & 0o777 };\n } finally {\n await input.close();\n }\n } catch (error) {\n throw new Error(\n `cannot safely merge ${cloudClaudeSettingsPath}: ${error instanceof Error ? error.message : String(error)}; ${cloudSessionStartFix}`,\n );\n }\n};\n\nconst main = async () => {\n const args = process.argv.slice(2);\n if (args.length === 1 && args.at(0) === \"--help\") {\n process.stdout.write(`${usage}\\n`);\n return;\n }\n if (args.length !== 0) throw new Error(usage);\n const root = await realpath(process.cwd());\n const { files, diagnostics } = readToolchainInputs(root);\n if (diagnostics.length > 0)\n throw new Error(\n diagnostics\n .map(\n ({ path, message }) =>\n `${path}: ${message}${path === cloudClaudeSettingsPath ? `; ${cloudSessionStartFix}` : \"\"}`,\n )\n .join(\"\\n\"),\n );\n const text = files[\"stll-toolchain.json\"];\n if (text === undefined)\n throw new Error(\"requires tracked stll-toolchain.json\");\n const { cloud, rustExceptions } = parseToolchainConfiguration(\n JSON.parse(text),\n );\n if (cloud === undefined)\n throw new Error(\"requires an explicit cloud declaration\");\n const nodeVersion = files[\".node-version\"];\n if (nodeVersion === undefined)\n throw new Error(\"requires tracked root .node-version\");\n const policy = parseToolchainPolicy(\n JSON.parse(\n await readFile(new URL(\"../toolchain.json\", import.meta.url), \"utf8\"),\n ),\n );\n const script = generateCloudSetup({\n policy,\n cloud,\n nodeVersion: nodeVersion.trim(),\n files,\n rustExceptions,\n });\n const destination = join(root, cloudSetupPath);\n await checkOutput(destination);\n const settingsDestination = join(root, cloudClaudeSettingsPath);\n const settings = await readClaudeSettings(settingsDestination);\n const mergedSettings = mergeCloudClaudeSettings(settings?.text);\n await writeOutput({ destination, text: script, mode: 0o755 });\n if (mergedSettings !== settings?.text)\n await writeOutput({\n destination: settingsDestination,\n text: mergedSettings,\n mode: settings?.mode ?? 0o644,\n });\n process.stdout.write(\n `Generated ${cloudSetupPath} and ${cloudClaudeSettingsPath}\\n`,\n );\n};\n\nmain().catch((error: unknown) => {\n process.stderr.write(\n `cloud-setup: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exitCode = 1;\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"cloud-setup-cli.mjs","names":[],"sources":["../src/cloud-setup-cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { constants } from \"node:fs\";\nimport { open, readFile, realpath } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { cloudSetupPath, generateCloudSetup } from \"./cloud-setup\";\nimport {\n cloudClaudeSettingsPath,\n cloudSessionStartFix,\n mergeCloudClaudeSettings,\n} from \"./cloud-setup-claude\";\nimport {\n checkGeneratedOutput,\n generationDiagnostics,\n writeGeneratedFile,\n} from \"./generated-file\";\nimport {\n parseToolchainConfiguration,\n readToolchainInputs,\n} from \"./toolchain-guard\";\nimport { parseToolchainPolicy } from \"./toolchain-schema\";\n\nconst usage =\n \"Usage: stll-cloud-setup (run from repository root)\\n stll-cloud-setup --help\";\nconst readClaudeSettings = async (destination: string) => {\n try {\n if ((await checkGeneratedOutput(destination)) === undefined)\n return undefined;\n const input = await open(\n destination,\n constants.O_RDONLY | constants.O_NOFOLLOW,\n );\n try {\n const opened = await input.stat();\n if (!opened.isFile() || opened.nlink !== 1)\n throw new Error(\n \"settings must be a single-link regular repository file\",\n );\n return { text: await input.readFile(\"utf8\"), mode: opened.mode & 0o777 };\n } finally {\n await input.close();\n }\n } catch (error) {\n throw new Error(\n `cannot safely merge ${cloudClaudeSettingsPath}: ${error instanceof Error ? error.message : String(error)}; ${cloudSessionStartFix}`,\n );\n }\n};\n\nconst main = async () => {\n const args = process.argv.slice(2);\n if (args.length === 1 && args.at(0) === \"--help\") {\n process.stdout.write(`${usage}\\n`);\n return;\n }\n if (args.length !== 0) throw new Error(usage);\n const root = await realpath(process.cwd());\n const { files, diagnostics } = readToolchainInputs(root);\n const destination = join(root, cloudSetupPath);\n const settingsDestination = join(root, cloudClaudeSettingsPath);\n // Validate settings separately to retain actionable merge diagnostics.\n const settings = await readClaudeSettings(settingsDestination);\n const remainingDiagnostics = await generationDiagnostics({\n diagnostics,\n outputs: [\n { relative: cloudSetupPath, destination },\n { relative: cloudClaudeSettingsPath, destination: settingsDestination },\n ],\n });\n if (remainingDiagnostics.length > 0)\n throw new Error(\n remainingDiagnostics\n .map(\n ({ path, message }) =>\n `${path}: ${message}${path === cloudClaudeSettingsPath ? `; ${cloudSessionStartFix}` : \"\"}`,\n )\n .join(\"\\n\"),\n );\n const text = files[\"stll-toolchain.json\"];\n if (text === undefined)\n throw new Error(\"requires tracked stll-toolchain.json\");\n const { cloud, rustExceptions } = parseToolchainConfiguration(\n JSON.parse(text),\n );\n if (cloud === undefined)\n throw new Error(\"requires an explicit cloud declaration\");\n const nodeVersion = files[\".node-version\"];\n if (nodeVersion === undefined)\n throw new Error(\"requires tracked root .node-version\");\n const policy = parseToolchainPolicy(\n JSON.parse(\n await readFile(new URL(\"../toolchain.json\", import.meta.url), \"utf8\"),\n ),\n );\n const script = generateCloudSetup({\n policy,\n cloud,\n nodeVersion: nodeVersion.trim(),\n files,\n rustExceptions,\n });\n const mergedSettings = mergeCloudClaudeSettings(settings?.text);\n await writeGeneratedFile({ destination, text: script, mode: 0o755 });\n if (mergedSettings !== settings?.text)\n await writeGeneratedFile({\n destination: settingsDestination,\n text: mergedSettings,\n mode: settings?.mode ?? 0o644,\n });\n process.stdout.write(\n `Generated ${cloudSetupPath} and ${cloudClaudeSettingsPath}\\n`,\n );\n};\n\nmain().catch((error: unknown) => {\n process.stderr.write(\n `cloud-setup: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exitCode = 1;\n});\n"],"mappings":";;;;;;;;;AAsBA,MAAM,QACJ;AACF,MAAM,qBAAqB,OAAO,gBAAwB;CACxD,IAAI;EACF,IAAK,MAAM,qBAAqB,WAAW,MAAO,KAAA,GAChD,OAAO,KAAA;EACT,MAAM,QAAQ,MAAM,KAClB,aACA,UAAU,WAAW,UAAU,UACjC;EACA,IAAI;GACF,MAAM,SAAS,MAAM,MAAM,KAAK;GAChC,IAAI,CAAC,OAAO,OAAO,KAAK,OAAO,UAAU,GACvC,MAAM,IAAI,MACR,wDACF;GACF,OAAO;IAAE,MAAM,MAAM,MAAM,SAAS,MAAM;IAAG,MAAM,OAAO,OAAO;GAAM;EACzE,UAAU;GACR,MAAM,MAAM,MAAM;EACpB;CACF,SAAS,OAAO;EACd,MAAM,IAAI,MACR,uBAAuB,wBAAwB,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,IAAI,sBAChH;CACF;AACF;AAEA,MAAM,OAAO,YAAY;CACvB,MAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;CACjC,IAAI,KAAK,WAAW,KAAK,KAAK,GAAG,CAAC,MAAM,UAAU;EAChD,QAAQ,OAAO,MAAM,GAAG,MAAM,GAAG;EACjC;CACF;CACA,IAAI,KAAK,WAAW,GAAG,MAAM,IAAI,MAAM,KAAK;CAC5C,MAAM,OAAO,MAAM,SAAS,QAAQ,IAAI,CAAC;CACzC,MAAM,EAAE,OAAO,gBAAgB,oBAAoB,IAAI;CACvD,MAAM,cAAc,KAAK,MAAM,cAAc;CAC7C,MAAM,sBAAsB,KAAK,MAAM,uBAAuB;CAE9D,MAAM,WAAW,MAAM,mBAAmB,mBAAmB;CAC7D,MAAM,uBAAuB,MAAM,sBAAsB;EACvD;EACA,SAAS,CACP;GAAE,UAAU;GAAgB;EAAY,GACxC;GAAE,UAAU;GAAyB,aAAa;EAAoB,CACxE;CACF,CAAC;CACD,IAAI,qBAAqB,SAAS,GAChC,MAAM,IAAI,MACR,qBACG,KACE,EAAE,MAAM,cACP,GAAG,KAAK,IAAI,UAAU,SAAA,0BAAmC,KAAK,yBAAyB,IAC3F,CAAC,CACA,KAAK,IAAI,CACd;CACF,MAAM,OAAO,MAAM;CACnB,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,MAAM,sCAAsC;CACxD,MAAM,EAAE,OAAO,mBAAmB,4BAChC,KAAK,MAAM,IAAI,CACjB;CACA,IAAI,UAAU,KAAA,GACZ,MAAM,IAAI,MAAM,wCAAwC;CAC1D,MAAM,cAAc,MAAM;CAC1B,IAAI,gBAAgB,KAAA,GAClB,MAAM,IAAI,MAAM,qCAAqC;CACvD,MAAM,SAAS,qBACb,KAAK,MACH,MAAM,SAAS,IAAI,IAAI,qBAAqB,YAAY,GAAG,GAAG,MAAM,CACtE,CACF;CACA,MAAM,SAAS,mBAAmB;EAChC;EACA;EACA,aAAa,YAAY,KAAK;EAC9B;EACA;CACF,CAAC;CACD,MAAM,iBAAiB,yBAAyB,UAAU,IAAI;CAC9D,MAAM,mBAAmB;EAAE;EAAa,MAAM;EAAQ,MAAM;CAAM,CAAC;CACnE,IAAI,mBAAmB,UAAU,MAC/B,MAAM,mBAAmB;EACvB,aAAa;EACb,MAAM;EACN,MAAM,UAAU,QAAQ;CAC1B,CAAC;CACH,QAAQ,OAAO,MACb,aAAa,eAAe,OAAO,wBAAwB,GAC7D;AACF;AAEA,KAAK,CAAC,CAAC,OAAO,UAAmB;CAC/B,QAAQ,OAAO,MACb,gBAAgB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,GACzE;CACA,QAAQ,WAAW;AACrB,CAAC"}