@stll/oxlint-config 1.0.2 → 1.2.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 (49) hide show
  1. package/README.md +97 -4
  2. package/dist/cloud-setup-cli.cjs +62 -21
  3. package/dist/cloud-setup-cli.cjs.map +1 -1
  4. package/dist/cloud-setup-cli.mjs +63 -22
  5. package/dist/cloud-setup-cli.mjs.map +1 -1
  6. package/dist/consumer-compat-cli.cjs +7 -6
  7. package/dist/consumer-compat-cli.cjs.map +1 -1
  8. package/dist/consumer-compat-cli.mjs +7 -6
  9. package/dist/consumer-compat-cli.mjs.map +1 -1
  10. package/dist/git-tracked-output.cjs.map +1 -1
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/publish-build-target-vite-dts-helper.mjs +1 -2
  13. package/dist/publish-build-target-vite-dts-helper2.cjs +5 -5
  14. package/dist/publish-build-target-vite-dts-helper2.cjs.map +1 -1
  15. package/dist/publish-contract-cli.cjs +17 -16
  16. package/dist/publish-contract-cli.cjs.map +1 -1
  17. package/dist/publish-contract-cli.mjs +6 -5
  18. package/dist/publish-contract-cli.mjs.map +1 -1
  19. package/dist/publish-contract.cjs +1 -1
  20. package/dist/publish-contract.cjs.map +1 -1
  21. package/dist/publish-contract.mjs +1 -1
  22. package/dist/rolldown-runtime.cjs +1 -1
  23. package/dist/toolchain-changed.cjs +25 -15
  24. package/dist/toolchain-changed.cjs.map +1 -1
  25. package/dist/toolchain-changed.mjs +27 -17
  26. package/dist/toolchain-changed.mjs.map +1 -1
  27. package/dist/toolchain-check-cli.cjs.map +1 -1
  28. package/dist/toolchain-check-cli.mjs.map +1 -1
  29. package/dist/toolchain-guard.cjs +72 -48
  30. package/dist/toolchain-guard.cjs.map +1 -1
  31. package/dist/toolchain-guard.mjs +79 -55
  32. package/dist/toolchain-guard.mjs.map +1 -1
  33. package/dist/toolchain-inputs.cjs +803 -40
  34. package/dist/toolchain-inputs.cjs.map +1 -1
  35. package/dist/toolchain-inputs.mjs +768 -41
  36. package/dist/toolchain-inputs.mjs.map +1 -1
  37. package/dist/toolchain-schema.cjs +1 -1
  38. package/dist/toolchain-schema.cjs.map +1 -1
  39. package/dist/toolchain-schema.mjs +2 -2
  40. package/dist/typecheck-parity-cli.cjs.map +1 -1
  41. package/dist/typecheck-parity-cli.mjs.map +1 -1
  42. package/dist/typecheck-parity.cjs +8 -7
  43. package/dist/typecheck-parity.cjs.map +1 -1
  44. package/dist/typecheck-parity.mjs +6 -5
  45. package/dist/typecheck-parity.mjs.map +1 -1
  46. package/dist/typecheck-probe-cli.cjs +1 -1
  47. package/dist/typecheck-probe-cli.cjs.map +1 -1
  48. package/dist/typecheck-probe-cli.mjs +1 -1
  49. package/package.json +6 -6
package/README.md CHANGED
@@ -298,7 +298,17 @@ Add an explicit declaration to tracked root `stll-toolchain.json`:
298
298
  "cloud": {
299
299
  "services": ["postgres", "valkey"],
300
300
  "install": "bun install --frozen-lockfile",
301
- "envFile": ".env.cloud"
301
+ "envFile": ".env.cloud",
302
+ "rust": {
303
+ "components": ["clippy", "rustfmt"],
304
+ "targets": ["wasm32-unknown-unknown"]
305
+ },
306
+ "tools": {
307
+ "cargo-nextest": "0.9.148",
308
+ "wasm-bindgen-cli": "0.2.126",
309
+ "uv": "0.13.1",
310
+ "time": "1.9"
311
+ }
302
312
  }
303
313
  }
304
314
  ```
@@ -314,6 +324,31 @@ repository-relative path; its existing parent must be safe at runtime. Track an
314
324
  bunx --no-install stll-cloud-setup
315
325
  ```
316
326
 
327
+ Rust is optional. A tracked root `rust-toolchain.toml` enables it automatically;
328
+ otherwise declare `cloud.rust` to enable it. `cloud.rust.toolchain`, when given,
329
+ must be an exact stable Rust patch. The root toolchain file supplies the pin when
330
+ present; without either pin, the installed shared policy supplies it. Conflicting
331
+ pins fail generation. The selected pin must match the shared policy or a recorded
332
+ `rustExceptions` pin. `components` and `targets` select additional rustup
333
+ components and compilation targets; the root toolchain file also contributes
334
+ its selections. A declared root profile must be `minimal`. Rust installation uses
335
+ the minimal profile and a pinned, checksum-verified rustup installer. Rust-enabled
336
+ setup also provisions Ubuntu's `build-essential` package for native compilation
337
+ and linking.
338
+
339
+ `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
341
+ 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
343
+ SHA256 pins for amd64 and arm64; setup does not build them with `cargo install`.
344
+ `time` installs GNU time (`/usr/bin/time`, needed for `/usr/bin/time -v`) from
345
+ Ubuntu's `time` package with bounded apt; its value is the upstream release
346
+ (`1.9` on Ubuntu 24.04). GNU time reports its version as `UNKNOWN`, so setup
347
+ verifies the binary's GNU identity and the installed package release. A present,
348
+ matching package is reused without network access.
349
+ `wasm-bindgen-cli` requires the matching `wasm-bindgen` crate version in tracked
350
+ root `Cargo.lock`. Regenerate after updating the lockfile or any tool pin.
351
+
317
352
  Generation uses the installed policy's Bun, Node, PostgreSQL, and Valkey pins.
318
353
  PostgreSQL uses the PGDG major-version packages; minor updates within the pinned
319
354
  major are accepted for the disposable database. Before adding PGDG, provisioning
@@ -323,8 +358,23 @@ binary DEBs for the declared release and architecture, verifies the policy's
323
358
  SHA256 pins, and installs those local files with apt. It does not resolve Valkey
324
359
  from a mutable package index.
325
360
  The command writes `.agents/cloud-setup.sh` with executable permissions and
326
- refuses symlink parents or destinations and hard-linked destinations. `stll-toolchain-check` rejects missing,
327
- drifted, or undeclared tracked scripts with mandatory `cloud-setup-drift`.
361
+ merges a `SessionStart` hook into `.claude/settings.json`. The hook runs
362
+ `bash "$CLAUDE_PROJECT_DIR/.agents/cloud-setup.sh" start` only when
363
+ `CLAUDE_CODE_REMOTE=true`, including sessions that reuse cached filesystems and
364
+ sessions started from a repository subdirectory. Generation replaces the
365
+ relative-path hook written by 1.1.0 in place. After successful startup, it appends the
366
+ managed shell profile's literal exports to `CLAUDE_ENV_FILE` when available, so
367
+ subsequent Claude commands select pinned runtimes and tools before preinstalled
368
+ versions while retaining their inherited `PATH`. The settings also set
369
+ `"attribution": {"commit": "", "pr": ""}`, so cloud sessions add no tool
370
+ attribution to commits or pull requests. Existing environment-file
371
+ content, unrelated settings, and hooks are preserved. Invalid JSON, disabled
372
+ hooks, conflicting cloud startup entries, or a different `attribution` value stop
373
+ generation with a repair instruction. Commit both generated files.
374
+ Generation refuses symlink parents or destinations and hard-linked destinations.
375
+ `stll-toolchain-check` rejects missing, drifted, or undeclared tracked scripts,
376
+ missing or conflicting startup hooks, and missing or different attribution
377
+ settings with mandatory `cloud-setup-drift`.
328
378
  Regenerate after changing the policy or declaration. `stll-cloud-setup --help`
329
379
  prints usage without reading repository configuration.
330
380
 
@@ -337,11 +387,19 @@ Both commands can be rerun. Missing or unknown subcommands print usage and
337
387
  exit 2; there is no implicit lifecycle mode. The script supports Ubuntu 24.04
338
388
  with root access or passwordless sudo and fails when required capabilities
339
389
  or pinned installations are unavailable. `install` provisions runtimes,
340
- dependencies, and declared services; `start` restores local services after
390
+ dependencies, and declared services; `start` refreshes the managed shell environment
391
+ and restores local services after
341
392
  cached environments lose running processes. Services bind only to localhost.
342
393
  System provisioning runs as root; frozen dependencies run as the invoking user
343
394
  with that user's home and a private, user-owned Bun cache. Package installation
344
395
  does not permit automatic downgrades.
396
+ Runtime commands and declared tools are exposed through `/usr/local/bin`, so a
397
+ fresh shell can use Bun, Node, Rust, and the tools without sourcing `.cargo/env`
398
+ or changing `PATH`. Rust proxies select the provisioned system toolchain.
399
+ Installation also writes `/etc/profile.d/stll-cloud.sh` and sources it from the
400
+ invoking user's Bash startup files. The profile puts pinned Node and Bun ahead
401
+ of inherited runtime directories while preserving unrelated paths and existing
402
+ startup settings. Rerunning installation does not duplicate these source entries.
345
403
  If a newer Valkey package is already installed, provisioning fails rather than
346
404
  downgrading it; use an environment compatible with the pinned release.
347
405
  The environment file contains `NODE_ENV=test` and declared local connection
@@ -353,6 +411,41 @@ Environments may cache filesystems without preserving service processes.
353
411
  Run `start` at every session start, including sessions that reuse installed files.
354
412
  The supported OS, privileges, and pinned runtime requirements are checked explicitly.
355
413
 
414
+ ### Network hosts
415
+
416
+ Downloads use a 20-second connection timeout, three retries, and a total deadline
417
+ (45 seconds for manifests and keys; 120 seconds for binary archives). APT uses
418
+ 20-second Acquire timeouts, three retries, and a 180-second command deadline.
419
+ Rust provisioning commands have a 180-second deadline. Frozen dependency
420
+ installation uses a 20-second Bun HTTP idle timeout and a 240-second overall
421
+ deadline, including lifecycle scripts. Timeout failures name the download host
422
+ or the configured package hosts; a redirected download names its original host.
423
+
424
+ Allow these direct download hosts for the generated installer:
425
+
426
+ | Host | Downloads |
427
+ | ---------------------- | ------------------------------------------------------------------------ |
428
+ | `nodejs.org` | Node archives and SHA256 manifests |
429
+ | `github.com` | Bun archives and checksums; pinned nextest, wasm-bindgen and uv releases |
430
+ | `static.rust-lang.org` | Pinned rustup-init, Rust manifests, toolchains, components and targets |
431
+ | `www.postgresql.org` | PostgreSQL repository signing key |
432
+ | `apt.postgresql.org` | PostgreSQL packages and repository metadata |
433
+ | `download.valkey.io` | Pinned Valkey binary packages |
434
+
435
+ GitHub release downloads redirect to `release-assets.githubusercontent.com`;
436
+ allow that host as well. This includes Bun and the optional prebuilt tools.
437
+ Rust provisioning uses `static.rust-lang.org` for both distribution and update endpoints.
438
+
439
+ System prerequisites and the optional `time` package use the image's configured
440
+ Ubuntu apt repositories:
441
+ standard Ubuntu 24.04 images use `archive.ubuntu.com`, `security.ubuntu.com`,
442
+ or `ports.ubuntu.com`, depending on architecture. Images configured with a
443
+ mirror need that mirror's host instead. Frozen Bun installation also needs
444
+ the package registry (normally `registry.npmjs.org`) and any download hosts
445
+ referenced by that repository's lockfile or dependency lifecycle scripts.
446
+ Those repository-specific hosts are not chosen by this generator. `start`
447
+ performs no downloads.
448
+
356
449
  ## Published package contracts
357
450
 
358
451
  Run `stll-publish-contract` in PR CI. Every published root or declared workspace
@@ -16,6 +16,46 @@ const optionalStat = async (path) => {
16
16
  throw error;
17
17
  }
18
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
+ const readClaudeSettings = async (destination) => {
42
+ try {
43
+ if (await checkOutput(destination) === void 0) return void 0;
44
+ const input = await (0, node_fs_promises.open)(destination, node_fs.constants.O_RDONLY | node_fs.constants.O_NOFOLLOW);
45
+ try {
46
+ const opened = await input.stat();
47
+ if (!opened.isFile() || opened.nlink !== 1) throw new Error("settings must be a single-link regular repository file");
48
+ return {
49
+ text: await input.readFile("utf8"),
50
+ mode: opened.mode & 511
51
+ };
52
+ } finally {
53
+ await input.close();
54
+ }
55
+ } catch (error) {
56
+ throw new Error(`cannot safely merge ${require_toolchain_inputs.cloudClaudeSettingsPath}: ${error instanceof Error ? error.message : String(error)}; ${require_toolchain_inputs.cloudSessionStartFix}`);
57
+ }
58
+ };
19
59
  const main = async () => {
20
60
  const args = process.argv.slice(2);
21
61
  if (args.length === 1 && args.at(0) === "--help") {
@@ -25,36 +65,37 @@ const main = async () => {
25
65
  if (args.length !== 0) throw new Error(usage);
26
66
  const root = await (0, node_fs_promises.realpath)(process.cwd());
27
67
  const { files, diagnostics } = require_toolchain_guard.readToolchainInputs(root);
28
- if (diagnostics.length > 0) throw new Error(diagnostics.map(({ path, message }) => `${path}: ${message}`).join("\n"));
68
+ if (diagnostics.length > 0) throw new Error(diagnostics.map(({ path, message }) => `${path}: ${message}${path === ".claude/settings.json" ? `; ${require_toolchain_inputs.cloudSessionStartFix}` : ""}`).join("\n"));
29
69
  const text = files["stll-toolchain.json"];
30
70
  if (text === void 0) throw new Error("requires tracked stll-toolchain.json");
31
- const { cloud } = require_toolchain_guard.parseToolchainConfiguration(JSON.parse(text));
71
+ const { cloud, rustExceptions } = require_toolchain_guard.parseToolchainConfiguration(JSON.parse(text));
32
72
  if (cloud === void 0) throw new Error("requires an explicit cloud declaration");
33
73
  const nodeVersion = files[".node-version"];
34
74
  if (nodeVersion === void 0) throw new Error("requires tracked root .node-version");
75
+ const policy = require_toolchain_schema.parseToolchainPolicy(JSON.parse(await (0, node_fs_promises.readFile)(new URL("../toolchain.json", require("url").pathToFileURL(__filename).href), "utf8")));
35
76
  const script = require_toolchain_inputs.generateCloudSetup({
36
- policy: require_toolchain_schema.parseToolchainPolicy(JSON.parse(await (0, node_fs_promises.readFile)(new URL("../toolchain.json", require("url").pathToFileURL(__filename).href), "utf8"))),
77
+ policy,
37
78
  cloud,
38
- nodeVersion: nodeVersion.trim()
79
+ nodeVersion: nodeVersion.trim(),
80
+ files,
81
+ rustExceptions
39
82
  });
40
83
  const destination = (0, node_path.join)(root, require_toolchain_inputs.cloudSetupPath);
41
- const parent = (0, node_path.dirname)(destination);
42
- const parentStat = await optionalStat(parent);
43
- if (parentStat === void 0) await (0, node_fs_promises.mkdir)(parent);
44
- else if (parentStat.isSymbolicLink() || !parentStat.isDirectory()) throw new Error("cloud setup parent must be a real repository directory");
45
- const destinationStat = await optionalStat(destination);
46
- 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");
47
- const output = await (0, node_fs_promises.open)(destination, node_fs.constants.O_WRONLY | node_fs.constants.O_CREAT | node_fs.constants.O_NOFOLLOW, 493);
48
- try {
49
- const opened = await output.stat();
50
- if (!opened.isFile() || opened.nlink !== 1) throw new Error("cloud setup destination must be a single-link regular repository file");
51
- await output.truncate(0);
52
- await output.writeFile(script, "utf8");
53
- await output.chmod(493);
54
- } finally {
55
- await output.close();
56
- }
57
- process.stdout.write(`Generated ${require_toolchain_inputs.cloudSetupPath}\n`);
84
+ await checkOutput(destination);
85
+ const settingsDestination = (0, node_path.join)(root, require_toolchain_inputs.cloudClaudeSettingsPath);
86
+ const settings = await readClaudeSettings(settingsDestination);
87
+ const mergedSettings = require_toolchain_inputs.mergeCloudClaudeSettings(settings?.text);
88
+ await writeOutput({
89
+ destination,
90
+ text: script,
91
+ mode: 493
92
+ });
93
+ if (mergedSettings !== settings?.text) await writeOutput({
94
+ destination: settingsDestination,
95
+ text: mergedSettings,
96
+ mode: settings?.mode ?? 420
97
+ });
98
+ process.stdout.write(`Generated ${require_toolchain_inputs.cloudSetupPath} and ${require_toolchain_inputs.cloudClaudeSettingsPath}\n`);
58
99
  };
59
100
  main().catch((error) => {
60
101
  process.stderr.write(`cloud-setup: ${error instanceof Error ? error.message : String(error)}\n`);
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-setup-cli.cjs","names":["readToolchainInputs","parseToolchainConfiguration","generateCloudSetup","parseToolchainPolicy","cloudSetupPath","constants"],"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 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 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.map(({ path, message }) => `${path}: ${message}`).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 } = parseToolchainConfiguration(JSON.parse(text));\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 });\n const destination = join(root, cloudSetupPath);\n const parent = dirname(destination);\n const parentStat = await optionalStat(parent);\n if (parentStat === undefined) await mkdir(parent);\n else if (parentStat.isSymbolicLink() || !parentStat.isDirectory())\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 const output = await open(\n destination,\n constants.O_WRONLY | constants.O_CREAT | constants.O_NOFOLLOW,\n 0o755,\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(script, \"utf8\");\n await output.chmod(0o755);\n } finally {\n await output.close();\n }\n process.stdout.write(`Generated ${cloudSetupPath}\\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":";;;;;;;;;AAYA,MAAM,QACJ;AACF,MAAM,eAAe,OAAO,SAAiB;CAC3C,IAAI;EACF,OAAO,OAAA,GAAA,iBAAA,MAAA,CAAY,IAAI;CACzB,SAAS,OAAO;EACd,IAAI,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,UAC9D,OAAO,KAAA;EACT,MAAM;CACR;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,GAAA,iBAAA,SAAA,CAAe,QAAQ,IAAI,CAAC;CACzC,MAAM,EAAE,OAAO,gBAAgBA,wBAAAA,oBAAoB,IAAI;CACvD,IAAI,YAAY,SAAS,GACvB,MAAM,IAAI,MACR,YAAY,KAAK,EAAE,MAAM,cAAc,GAAG,KAAK,IAAI,SAAS,CAAC,CAAC,KAAK,IAAI,CACzE;CACF,MAAM,OAAO,MAAM;CACnB,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,MAAM,sCAAsC;CACxD,MAAM,EAAE,UAAUC,wBAAAA,4BAA4B,KAAK,MAAM,IAAI,CAAC;CAC9D,IAAI,UAAU,KAAA,GACZ,MAAM,IAAI,MAAM,wCAAwC;CAC1D,MAAM,cAAc,MAAM;CAC1B,IAAI,gBAAgB,KAAA,GAClB,MAAM,IAAI,MAAM,qCAAqC;CAMvD,MAAM,SAASC,yBAAAA,mBAAmB;EAChC,QANaC,yBAAAA,qBACb,KAAK,MACH,OAAA,GAAA,iBAAA,SAAA,CAAe,IAAI,IAAI,qBAAA,QAAA,KAAA,CAAA,CAAA,cAAA,UAAA,CAAA,CAAA,IAAoC,GAAG,MAAM,CACtE,CAGK;EACL;EACA,aAAa,YAAY,KAAK;CAChC,CAAC;CACD,MAAM,eAAA,GAAA,UAAA,KAAA,CAAmB,MAAMC,yBAAAA,cAAc;CAC7C,MAAM,UAAA,GAAA,UAAA,QAAA,CAAiB,WAAW;CAClC,MAAM,aAAa,MAAM,aAAa,MAAM;CAC5C,IAAI,eAAe,KAAA,GAAW,OAAA,GAAA,iBAAA,MAAA,CAAY,MAAM;MAC3C,IAAI,WAAW,eAAe,KAAK,CAAC,WAAW,YAAY,GAC9D,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,MAAM,SAAS,OAAA,GAAA,iBAAA,KAAA,CACb,aACAC,QAAAA,UAAU,WAAWA,QAAAA,UAAU,UAAUA,QAAAA,UAAU,YACnD,GACF;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,QAAQ,MAAM;EACrC,MAAM,OAAO,MAAM,GAAK;CAC1B,UAAU;EACR,MAAM,OAAO,MAAM;CACrB;CACA,QAAQ,OAAO,MAAM,aAAaD,yBAAAA,eAAe,GAAG;AACtD;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":["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":"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"}
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { h as generateCloudSetup, m as cloudSetupPath } from "./toolchain-inputs.mjs";
2
+ import { b as cloudSetupPath, g as cloudSessionStartFix, h as cloudClaudeSettingsPath, x as generateCloudSetup, y as mergeCloudClaudeSettings } 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
5
  import { constants } from "node:fs";
@@ -15,6 +15,46 @@ const optionalStat = async (path) => {
15
15
  throw error;
16
16
  }
17
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
+ const readClaudeSettings = async (destination) => {
41
+ try {
42
+ if (await checkOutput(destination) === void 0) return void 0;
43
+ const input = await open(destination, constants.O_RDONLY | constants.O_NOFOLLOW);
44
+ try {
45
+ const opened = await input.stat();
46
+ if (!opened.isFile() || opened.nlink !== 1) throw new Error("settings must be a single-link regular repository file");
47
+ return {
48
+ text: await input.readFile("utf8"),
49
+ mode: opened.mode & 511
50
+ };
51
+ } finally {
52
+ await input.close();
53
+ }
54
+ } catch (error) {
55
+ throw new Error(`cannot safely merge ${cloudClaudeSettingsPath}: ${error instanceof Error ? error.message : String(error)}; ${cloudSessionStartFix}`);
56
+ }
57
+ };
18
58
  const main = async () => {
19
59
  const args = process.argv.slice(2);
20
60
  if (args.length === 1 && args.at(0) === "--help") {
@@ -24,36 +64,37 @@ const main = async () => {
24
64
  if (args.length !== 0) throw new Error(usage);
25
65
  const root = await realpath(process.cwd());
26
66
  const { files, diagnostics } = readToolchainInputs(root);
27
- if (diagnostics.length > 0) throw new Error(diagnostics.map(({ path, message }) => `${path}: ${message}`).join("\n"));
67
+ if (diagnostics.length > 0) throw new Error(diagnostics.map(({ path, message }) => `${path}: ${message}${path === ".claude/settings.json" ? `; ${cloudSessionStartFix}` : ""}`).join("\n"));
28
68
  const text = files["stll-toolchain.json"];
29
69
  if (text === void 0) throw new Error("requires tracked stll-toolchain.json");
30
- const { cloud } = parseToolchainConfiguration(JSON.parse(text));
70
+ const { cloud, rustExceptions } = parseToolchainConfiguration(JSON.parse(text));
31
71
  if (cloud === void 0) throw new Error("requires an explicit cloud declaration");
32
72
  const nodeVersion = files[".node-version"];
33
73
  if (nodeVersion === void 0) throw new Error("requires tracked root .node-version");
74
+ const policy = parseToolchainPolicy(JSON.parse(await readFile(new URL("../toolchain.json", import.meta.url), "utf8")));
34
75
  const script = generateCloudSetup({
35
- policy: parseToolchainPolicy(JSON.parse(await readFile(new URL("../toolchain.json", import.meta.url), "utf8"))),
76
+ policy,
36
77
  cloud,
37
- nodeVersion: nodeVersion.trim()
78
+ nodeVersion: nodeVersion.trim(),
79
+ files,
80
+ rustExceptions
38
81
  });
39
82
  const destination = join(root, cloudSetupPath);
40
- const parent = dirname(destination);
41
- const parentStat = await optionalStat(parent);
42
- if (parentStat === void 0) await mkdir(parent);
43
- else if (parentStat.isSymbolicLink() || !parentStat.isDirectory()) throw new Error("cloud setup parent must be a real repository directory");
44
- const destinationStat = await optionalStat(destination);
45
- 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");
46
- const output = await open(destination, constants.O_WRONLY | constants.O_CREAT | constants.O_NOFOLLOW, 493);
47
- try {
48
- const opened = await output.stat();
49
- if (!opened.isFile() || opened.nlink !== 1) throw new Error("cloud setup destination must be a single-link regular repository file");
50
- await output.truncate(0);
51
- await output.writeFile(script, "utf8");
52
- await output.chmod(493);
53
- } finally {
54
- await output.close();
55
- }
56
- process.stdout.write(`Generated ${cloudSetupPath}\n`);
83
+ await checkOutput(destination);
84
+ const settingsDestination = join(root, cloudClaudeSettingsPath);
85
+ const settings = await readClaudeSettings(settingsDestination);
86
+ const mergedSettings = mergeCloudClaudeSettings(settings?.text);
87
+ await writeOutput({
88
+ destination,
89
+ text: script,
90
+ mode: 493
91
+ });
92
+ if (mergedSettings !== settings?.text) await writeOutput({
93
+ destination: settingsDestination,
94
+ text: mergedSettings,
95
+ mode: settings?.mode ?? 420
96
+ });
97
+ process.stdout.write(`Generated ${cloudSetupPath} and ${cloudClaudeSettingsPath}\n`);
57
98
  };
58
99
  main().catch((error) => {
59
100
  process.stderr.write(`cloud-setup: ${error instanceof Error ? error.message : String(error)}\n`);
@@ -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 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 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.map(({ path, message }) => `${path}: ${message}`).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 } = parseToolchainConfiguration(JSON.parse(text));\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 });\n const destination = join(root, cloudSetupPath);\n const parent = dirname(destination);\n const parentStat = await optionalStat(parent);\n if (parentStat === undefined) await mkdir(parent);\n else if (parentStat.isSymbolicLink() || !parentStat.isDirectory())\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 const output = await open(\n destination,\n constants.O_WRONLY | constants.O_CREAT | constants.O_NOFOLLOW,\n 0o755,\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(script, \"utf8\");\n await output.chmod(0o755);\n } finally {\n await output.close();\n }\n process.stdout.write(`Generated ${cloudSetupPath}\\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":";;;;;;;;AAYA,MAAM,QACJ;AACF,MAAM,eAAe,OAAO,SAAiB;CAC3C,IAAI;EACF,OAAO,MAAM,MAAM,IAAI;CACzB,SAAS,OAAO;EACd,IAAI,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,UAC9D,OAAO,KAAA;EACT,MAAM;CACR;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,IAAI,YAAY,SAAS,GACvB,MAAM,IAAI,MACR,YAAY,KAAK,EAAE,MAAM,cAAc,GAAG,KAAK,IAAI,SAAS,CAAC,CAAC,KAAK,IAAI,CACzE;CACF,MAAM,OAAO,MAAM;CACnB,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,MAAM,sCAAsC;CACxD,MAAM,EAAE,UAAU,4BAA4B,KAAK,MAAM,IAAI,CAAC;CAC9D,IAAI,UAAU,KAAA,GACZ,MAAM,IAAI,MAAM,wCAAwC;CAC1D,MAAM,cAAc,MAAM;CAC1B,IAAI,gBAAgB,KAAA,GAClB,MAAM,IAAI,MAAM,qCAAqC;CAMvD,MAAM,SAAS,mBAAmB;EAChC,QANa,qBACb,KAAK,MACH,MAAM,SAAS,IAAI,IAAI,qBAAqB,OAAO,KAAK,GAAG,GAAG,MAAM,CACtE,CAGK;EACL;EACA,aAAa,YAAY,KAAK;CAChC,CAAC;CACD,MAAM,cAAc,KAAK,MAAM,cAAc;CAC7C,MAAM,SAAS,QAAQ,WAAW;CAClC,MAAM,aAAa,MAAM,aAAa,MAAM;CAC5C,IAAI,eAAe,KAAA,GAAW,MAAM,MAAM,MAAM;MAC3C,IAAI,WAAW,eAAe,KAAK,CAAC,WAAW,YAAY,GAC9D,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,MAAM,SAAS,MAAM,KACnB,aACA,UAAU,WAAW,UAAU,UAAU,UAAU,YACnD,GACF;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,QAAQ,MAAM;EACrC,MAAM,OAAO,MAAM,GAAK;CAC1B,UAAU;EACR,MAAM,OAAO,MAAM;CACrB;CACA,QAAQ,OAAO,MAAM,aAAa,eAAe,GAAG;AACtD;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.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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"}
@@ -6,14 +6,14 @@ const require_publish_contract = require("./publish-contract.cjs");
6
6
  let node_child_process = require("node:child_process");
7
7
  let node_path = require("node:path");
8
8
  node_path = require_rolldown_runtime.__toESM(node_path, 1);
9
+ let node_crypto = require("node:crypto");
9
10
  let semver = require("semver");
10
- let node_os = require("node:os");
11
- let yaml = require("yaml");
12
11
  let node_util = require("node:util");
12
+ let yaml = require("yaml");
13
+ let node_os = require("node:os");
13
14
  let node_fs_promises = require("node:fs/promises");
14
15
  let node_module = require("node:module");
15
16
  let acorn = require("acorn");
16
- let node_crypto = require("node:crypto");
17
17
  //#region src/consumer-build-approvals.ts
18
18
  const consumerBuildApprovals = async ({ manifest, lock, manager, directory }) => {
19
19
  if (!require_toolchain_schema.consumerRecord(lock)) throw new Error("invalid consumer dependency lock");
@@ -325,7 +325,7 @@ const checkPackedJavaScriptWithNode = async ({ files, target, node, executable }
325
325
  };
326
326
  /** Read bytes without extracting archive-controlled paths or links. */
327
327
  const readPackedSyntaxFiles = async (archive) => {
328
- const { stdout: listing } = await command("tar", ["-tzf", archive], { maxBuffer: 16 * 1024 * 1024 });
328
+ const { stdout: listing } = await command("tar", ["-tzf", archive], { maxBuffer: 16777216 });
329
329
  const files = /* @__PURE__ */ new Map();
330
330
  const members = archiveMembers(listing.split("\n").filter((entry) => entry !== ""));
331
331
  const read = async (file) => {
@@ -340,7 +340,7 @@ const readPackedSyntaxFiles = async (archive) => {
340
340
  "-xOf",
341
341
  archive,
342
342
  file
343
- ], { maxBuffer: 16 * 1024 * 1024 });
343
+ ], { maxBuffer: 16777216 });
344
344
  files.set(file, stdout);
345
345
  };
346
346
  for (const file of members) if (javascript.test(file) || node_path.default.posix.basename(file) === "package.json") await read(file);
@@ -770,9 +770,10 @@ const installConsumerFixtureDependencies = async (options) => {
770
770
  const manifest = JSON.parse(await (0, node_fs_promises.readFile)(manifestFile, "utf8"));
771
771
  if (!require_toolchain_schema.consumerRecord(manifest)) throw new Error("invalid consumer fixture manifest");
772
772
  const lockSource = await (0, node_fs_promises.readFile)(node_path.default.join(options.directory, options.manager === "npm" ? "package-lock.json" : "pnpm-lock.yaml"), "utf8");
773
+ const lock = options.manager === "npm" ? JSON.parse(lockSource) : (0, yaml.parse)(lockSource);
773
774
  const { approvals, rebuildTargets } = await consumerBuildApprovals({
774
775
  manifest,
775
- lock: options.manager === "npm" ? JSON.parse(lockSource) : (0, yaml.parse)(lockSource),
776
+ lock,
776
777
  manager: options.manager,
777
778
  directory: options.directory
778
779
  });