@rogatio/cli 6.16.0 → 6.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/node/index.js +573 -209
  2. package/package.json +3 -3
@@ -602,6 +602,25 @@ function migrateOwned(input) {
602
602
  }
603
603
  return { ok: true, project, notices };
604
604
  }
605
+ function normalizeLogicalPath(value) {
606
+ if (typeof value !== "string" || value.length === 0) return null;
607
+ if (value.includes("\\") || value.includes("%") || hasControl(value)) {
608
+ return null;
609
+ }
610
+ if (value.startsWith("/") || value.endsWith("/") || value.includes("//")) {
611
+ return null;
612
+ }
613
+ const parts = value.split("/");
614
+ if (parts.some(
615
+ (part) => part.length === 0 || part === "." || part === ".." || part.includes(":") || /[*?[\]]/.test(part)
616
+ )) {
617
+ return null;
618
+ }
619
+ return parts.join("/");
620
+ }
621
+ function isValidMockLogicalPath(value) {
622
+ return normalizeLogicalPath(value) !== null;
623
+ }
605
624
  function compileUrlRegex(value) {
606
625
  if (typeof value !== "string" || value.length === 0 || value.length > LIMITS.maxUrlRegexLength)
607
626
  return null;
@@ -1062,6 +1081,109 @@ function hasLoneSurrogate(value) {
1062
1081
  }
1063
1082
  return false;
1064
1083
  }
1084
+ function countCapturingGroups2(urlRegex) {
1085
+ let count = 0;
1086
+ let index = 0;
1087
+ const length = urlRegex.length;
1088
+ while (index < length) {
1089
+ const char = urlRegex[index];
1090
+ if (char === "\\") {
1091
+ index += 2;
1092
+ continue;
1093
+ }
1094
+ if (char === "(") {
1095
+ if (urlRegex[index + 1] === "?") {
1096
+ index = skipBalancedGroup2(urlRegex, index);
1097
+ continue;
1098
+ }
1099
+ count += 1;
1100
+ }
1101
+ index += 1;
1102
+ }
1103
+ return count;
1104
+ }
1105
+ function skipBalancedGroup2(source, start) {
1106
+ let depth = 0;
1107
+ let index = start;
1108
+ const length = source.length;
1109
+ while (index < length) {
1110
+ const char = source[index];
1111
+ if (char === "\\") {
1112
+ index += 2;
1113
+ continue;
1114
+ }
1115
+ if (char === "(") {
1116
+ depth += 1;
1117
+ } else if (char === ")") {
1118
+ depth -= 1;
1119
+ if (depth === 0) return index;
1120
+ }
1121
+ index += 1;
1122
+ }
1123
+ return length;
1124
+ }
1125
+ function validateRedirectDestination(destination, urlRegex) {
1126
+ const issues = [];
1127
+ if (typeof destination !== "string" || destination.length === 0) {
1128
+ issues.push({
1129
+ code: "schema.required",
1130
+ message: "Redirect destination must be a non-empty string."
1131
+ });
1132
+ return issues;
1133
+ }
1134
+ if (destination.length > LIMITS.maxRedirectDestinationLength) {
1135
+ issues.push({
1136
+ code: "schema.out-of-range",
1137
+ message: `Redirect destination must be at most ${LIMITS.maxRedirectDestinationLength} characters.`
1138
+ });
1139
+ return issues;
1140
+ }
1141
+ let url;
1142
+ try {
1143
+ url = new URL(destination);
1144
+ } catch {
1145
+ issues.push({
1146
+ code: "schema.invalid-format",
1147
+ message: "Redirect destination must be an absolute URL."
1148
+ });
1149
+ return issues;
1150
+ }
1151
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
1152
+ issues.push({
1153
+ code: "schema.invalid-value",
1154
+ message: "Redirect destination must use the http or https scheme."
1155
+ });
1156
+ return issues;
1157
+ }
1158
+ if (url.username.length > 0 || url.password.length > 0) {
1159
+ issues.push({
1160
+ code: "schema.invalid-value",
1161
+ message: "Redirect destination must not contain credentials."
1162
+ });
1163
+ return issues;
1164
+ }
1165
+ if (url.hostname.length === 0 || url.hostname.includes("*")) {
1166
+ issues.push({
1167
+ code: "schema.invalid-format",
1168
+ message: "Redirect destination must have a valid host."
1169
+ });
1170
+ return issues;
1171
+ }
1172
+ const groups = countCapturingGroups2(urlRegex);
1173
+ const backreference = /\\([1-9])/g;
1174
+ let match = backreference.exec(destination);
1175
+ while (match !== null) {
1176
+ const referenced = Number(match[1]);
1177
+ if (referenced > groups) {
1178
+ issues.push({
1179
+ code: "schema.invalid-value",
1180
+ message: `Redirect destination references capture group ${referenced} but the URL pattern defines ${groups}.`
1181
+ });
1182
+ }
1183
+ match = backreference.exec(destination);
1184
+ }
1185
+ return issues;
1186
+ }
1065
1187
  var ajv = new Ajv2020({
1066
1188
  allErrors: true,
1067
1189
  coerceTypes: false,
@@ -1686,22 +1808,6 @@ function semanticIssues(project) {
1686
1808
  }
1687
1809
  return issues;
1688
1810
  }
1689
- function isValidMockLogicalPath(value) {
1690
- if (value.length === 0) return false;
1691
- if (value.includes("\\") || value.includes("%") || hasControl(value)) {
1692
- return false;
1693
- }
1694
- if (value.startsWith("/") || value.endsWith("/") || value.includes("//")) {
1695
- return false;
1696
- }
1697
- const parts = value.split("/");
1698
- if (parts.some(
1699
- (part) => part.length === 0 || part === "." || part === ".." || part.includes(":") || /[*?[\]]/.test(part)
1700
- )) {
1701
- return false;
1702
- }
1703
- return true;
1704
- }
1705
1811
  function captureValidationIssues(value, urlRegex, instancePath) {
1706
1812
  return validateCaptureTemplate(value, urlRegex).map((issue) => ({
1707
1813
  instancePath,
@@ -1714,109 +1820,6 @@ function captureValidationIssues(value, urlRegex, instancePath) {
1714
1820
  }
1715
1821
  }));
1716
1822
  }
1717
- function skipBalancedGroup2(source, start) {
1718
- let depth = 0;
1719
- let index = start;
1720
- const length = source.length;
1721
- while (index < length) {
1722
- const char = source[index];
1723
- if (char === "\\") {
1724
- index += 2;
1725
- continue;
1726
- }
1727
- if (char === "(") {
1728
- depth += 1;
1729
- } else if (char === ")") {
1730
- depth -= 1;
1731
- if (depth === 0) return index;
1732
- }
1733
- index += 1;
1734
- }
1735
- return length;
1736
- }
1737
- function countCapturingGroups2(urlRegex) {
1738
- let count = 0;
1739
- let index = 0;
1740
- const length = urlRegex.length;
1741
- while (index < length) {
1742
- const char = urlRegex[index];
1743
- if (char === "\\") {
1744
- index += 2;
1745
- continue;
1746
- }
1747
- if (char === "(") {
1748
- if (urlRegex[index + 1] === "?") {
1749
- index = skipBalancedGroup2(urlRegex, index);
1750
- continue;
1751
- }
1752
- count += 1;
1753
- }
1754
- index += 1;
1755
- }
1756
- return count;
1757
- }
1758
- function validateRedirectDestination(destination, urlRegex) {
1759
- const issues = [];
1760
- if (typeof destination !== "string" || destination.length === 0) {
1761
- issues.push({
1762
- code: "schema.required",
1763
- message: "Redirect destination must be a non-empty string."
1764
- });
1765
- return issues;
1766
- }
1767
- if (destination.length > LIMITS.maxRedirectDestinationLength) {
1768
- issues.push({
1769
- code: "schema.out-of-range",
1770
- message: `Redirect destination must be at most ${LIMITS.maxRedirectDestinationLength} characters.`
1771
- });
1772
- return issues;
1773
- }
1774
- let url;
1775
- try {
1776
- url = new URL(destination);
1777
- } catch {
1778
- issues.push({
1779
- code: "schema.invalid-format",
1780
- message: "Redirect destination must be an absolute URL."
1781
- });
1782
- return issues;
1783
- }
1784
- if (url.protocol !== "http:" && url.protocol !== "https:") {
1785
- issues.push({
1786
- code: "schema.invalid-value",
1787
- message: "Redirect destination must use the http or https scheme."
1788
- });
1789
- return issues;
1790
- }
1791
- if (url.username.length > 0 || url.password.length > 0) {
1792
- issues.push({
1793
- code: "schema.invalid-value",
1794
- message: "Redirect destination must not contain credentials."
1795
- });
1796
- return issues;
1797
- }
1798
- if (url.hostname.length === 0 || url.hostname.includes("*")) {
1799
- issues.push({
1800
- code: "schema.invalid-format",
1801
- message: "Redirect destination must have a valid host."
1802
- });
1803
- return issues;
1804
- }
1805
- const groups = countCapturingGroups2(urlRegex);
1806
- const backreference = /\\([1-9])/g;
1807
- let match = backreference.exec(destination);
1808
- while (match !== null) {
1809
- const referenced = Number(match[1]);
1810
- if (referenced > groups) {
1811
- issues.push({
1812
- code: "schema.invalid-value",
1813
- message: `Redirect destination references capture group ${referenced} but the URL pattern defines ${groups}.`
1814
- });
1815
- }
1816
- match = backreference.exec(destination);
1817
- }
1818
- return issues;
1819
- }
1820
1823
  function validateProjectDetailed(value) {
1821
1824
  const snapshot2 = snapshotOwnData(value);
1822
1825
  if (!snapshot2.valid) {
@@ -2263,11 +2266,23 @@ import { close, fstat, read } from "node:fs";
2263
2266
  import { createRequire } from "node:module";
2264
2267
  import { createServer, request as httpRequest } from "node:http";
2265
2268
  import { join as join5 } from "node:path";
2266
- import { mkdir as mkdir2, readFile as readFile2, rename, rm, stat, writeFile as writeFile2 } from "node:fs/promises";
2269
+ import { createPrivateKey, sign, verify, X509Certificate as X509Certificate3 } from "node:crypto";
2270
+ import { constants as fsConstants } from "node:fs";
2271
+ import {
2272
+ lstat,
2273
+ mkdir as mkdir2,
2274
+ open as open2,
2275
+ readFile as readFile2,
2276
+ rename,
2277
+ rm,
2278
+ stat
2279
+ } from "node:fs/promises";
2267
2280
  import { basename, dirname as dirname2, isAbsolute as isAbsolute2, join as join2, relative as relative2 } from "node:path";
2281
+ import { X509Certificate } from "node:crypto";
2268
2282
  import { generateKeyPairSync, webcrypto } from "node:crypto";
2269
2283
  import { spawn, spawnSync } from "node:child_process";
2270
2284
  import { accessSync, constants as constants2, unlinkSync, writeFileSync } from "node:fs";
2285
+ import { mkdtemp, open as open3, rm as rm2 } from "node:fs/promises";
2271
2286
  import { tmpdir } from "node:os";
2272
2287
  import { dirname as dirname3, join as join3 } from "node:path";
2273
2288
  import { join as posixJoin } from "node:path/posix";
@@ -2281,7 +2296,7 @@ import { tmpdir as tmpdir2 } from "node:os";
2281
2296
  import { join as join4 } from "node:path";
2282
2297
  import { join as posixJoin3 } from "node:path/posix";
2283
2298
  import { constants as constants5 } from "node:fs";
2284
- import { open as open2, realpath as realpath2 } from "node:fs/promises";
2299
+ import { open as open4, realpath as realpath2 } from "node:fs/promises";
2285
2300
  import { isAbsolute as isAbsolute3 } from "node:path";
2286
2301
  import { Buffer as Buffer2 } from "node:buffer";
2287
2302
  import { existsSync as existsSync2 } from "node:fs";
@@ -20057,7 +20072,7 @@ var require_x509_cjs = __commonJS({
20057
20072
  var _X509Certificate_signature;
20058
20073
  var _X509Certificate_extensions;
20059
20074
  var _X509Certificate_publicKey;
20060
- var X509Certificate3 = class extends PemData {
20075
+ var X509Certificate5 = class extends PemData {
20061
20076
  get publicKey() {
20062
20077
  if (!tslib.__classPrivateFieldGet(this, _X509Certificate_publicKey, "f")) {
20063
20078
  tslib.__classPrivateFieldSet(this, _X509Certificate_publicKey, new PublicKey(this.asn.tbsCertificate.subjectPublicKeyInfo, this.parseOptions), "f");
@@ -20300,7 +20315,7 @@ var require_x509_cjs = __commonJS({
20300
20315
  }
20301
20316
  };
20302
20317
  _X509Certificate_tbs = /* @__PURE__ */ new WeakMap(), _X509Certificate_serialNumber = /* @__PURE__ */ new WeakMap(), _X509Certificate_subjectName = /* @__PURE__ */ new WeakMap(), _X509Certificate_subject = /* @__PURE__ */ new WeakMap(), _X509Certificate_issuerName = /* @__PURE__ */ new WeakMap(), _X509Certificate_issuer = /* @__PURE__ */ new WeakMap(), _X509Certificate_notBefore = /* @__PURE__ */ new WeakMap(), _X509Certificate_notAfter = /* @__PURE__ */ new WeakMap(), _X509Certificate_signatureAlgorithm = /* @__PURE__ */ new WeakMap(), _X509Certificate_signature = /* @__PURE__ */ new WeakMap(), _X509Certificate_extensions = /* @__PURE__ */ new WeakMap(), _X509Certificate_publicKey = /* @__PURE__ */ new WeakMap();
20303
- X509Certificate3.NAME = "Certificate";
20318
+ X509Certificate5.NAME = "Certificate";
20304
20319
  var _X509Certificates_options;
20305
20320
  var X509Certificates = class extends Array {
20306
20321
  constructor(param, options) {
@@ -20308,7 +20323,7 @@ var require_x509_cjs = __commonJS({
20308
20323
  _X509Certificates_options.set(this, void 0);
20309
20324
  if (PemData.isAsnEncoded(param)) {
20310
20325
  this.import(param, options);
20311
- } else if (param instanceof X509Certificate3) {
20326
+ } else if (param instanceof X509Certificate5) {
20312
20327
  this.push(param);
20313
20328
  } else if (Array.isArray(param)) {
20314
20329
  for (const item of param) {
@@ -20346,7 +20361,7 @@ var require_x509_cjs = __commonJS({
20346
20361
  const certificates = [];
20347
20362
  for (const item of signedData.certificates || []) {
20348
20363
  if (item.certificate) {
20349
- certificates.push(new X509Certificate3(item.certificate, options));
20364
+ certificates.push(new X509Certificate5(item.certificate, options));
20350
20365
  }
20351
20366
  }
20352
20367
  this.clear();
@@ -20538,7 +20553,7 @@ var require_x509_cjs = __commonJS({
20538
20553
  throw Error("Cannot convert ASN.1 signature value to WebCrypto format");
20539
20554
  }
20540
20555
  asnX509.signatureValue = asnSignature;
20541
- return new X509Certificate3(asn1Schema.AsnConvert.serialize(asnX509));
20556
+ return new X509Certificate5(asn1Schema.AsnConvert.serialize(asnX509));
20542
20557
  }
20543
20558
  };
20544
20559
  var _X509CrlEntry_serialNumber;
@@ -20759,7 +20774,7 @@ var require_x509_cjs = __commonJS({
20759
20774
  let publicKey;
20760
20775
  const paramsKey = params.publicKey;
20761
20776
  try {
20762
- if (paramsKey instanceof X509Certificate3) {
20777
+ if (paramsKey instanceof X509Certificate5) {
20763
20778
  keyAlgorithm = {
20764
20779
  ...paramsKey.publicKey.algorithm,
20765
20780
  ...paramsKey.signatureAlgorithm
@@ -20963,7 +20978,7 @@ var require_x509_cjs = __commonJS({
20963
20978
  exports.TextObject = TextObject;
20964
20979
  exports.UPN = UPN;
20965
20980
  exports.URL = URL2;
20966
- exports.X509Certificate = X509Certificate3;
20981
+ exports.X509Certificate = X509Certificate5;
20967
20982
  exports.X509CertificateGenerator = X509CertificateGenerator2;
20968
20983
  exports.X509Certificates = X509Certificates;
20969
20984
  exports.X509ChainBuilder = X509ChainBuilder;
@@ -21892,22 +21907,6 @@ function runtimeError(code) {
21892
21907
  function failure(code) {
21893
21908
  return { ok: false, error: runtimeError(code) };
21894
21909
  }
21895
- function normalizeLogicalPath(value) {
21896
- if (typeof value !== "string" || value.length === 0) return null;
21897
- if (value.includes("\\") || value.includes("%") || hasControl(value)) {
21898
- return null;
21899
- }
21900
- if (value.startsWith("/") || value.endsWith("/") || value.includes("//")) {
21901
- return null;
21902
- }
21903
- const parts = value.split("/");
21904
- if (parts.some(
21905
- (part) => part.length === 0 || part === "." || part === ".." || part.includes(":") || /[*?[\]]/.test(part)
21906
- )) {
21907
- return null;
21908
- }
21909
- return parts.join("/");
21910
- }
21911
21910
  var MAX_ARRAY_LENGTH = 4096;
21912
21911
  var MAX_OBJECT_PROPERTIES = 256;
21913
21912
  function snapshotOwnData3(value, ancestors = /* @__PURE__ */ new WeakSet()) {
@@ -22443,6 +22442,42 @@ function describeExtensionIdMismatch(expectedExtensionId, allowedOrigins) {
22443
22442
  `Run: ${command}`
22444
22443
  ].join("\n");
22445
22444
  }
22445
+ var MAX_CERTIFICATE_PEM_LENGTH = 16 * 1024;
22446
+ var BEGIN_CERTIFICATE = "-----BEGIN CERTIFICATE-----";
22447
+ var END_CERTIFICATE = "-----END CERTIFICATE-----";
22448
+ var BODY_LINE = /^[A-Za-z0-9+/]{1,64}={0,2}$/;
22449
+ function isInstallableCaCertificate(pem) {
22450
+ if (typeof pem !== "string" || pem.length === 0 || pem.length > MAX_CERTIFICATE_PEM_LENGTH || pem.includes("\0")) {
22451
+ return false;
22452
+ }
22453
+ const normalized = pem.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
22454
+ const lines = normalized.split("\n");
22455
+ if (lines[lines.length - 1] === "") lines.pop();
22456
+ if (lines.some((line) => line.trim() === "EOF")) return false;
22457
+ if (lines.length < 3 || lines[0] !== BEGIN_CERTIFICATE || lines[lines.length - 1] !== END_CERTIFICATE) {
22458
+ return false;
22459
+ }
22460
+ if (lines.filter((line) => line === BEGIN_CERTIFICATE).length !== 1) {
22461
+ return false;
22462
+ }
22463
+ if (lines.filter((line) => line === END_CERTIFICATE).length !== 1) {
22464
+ return false;
22465
+ }
22466
+ const body = lines.slice(1, -1);
22467
+ for (let index = 0; index < body.length; index += 1) {
22468
+ const line = body[index] ?? "";
22469
+ if (!BODY_LINE.test(line)) return false;
22470
+ if (line.includes("=") && index !== body.length - 1) return false;
22471
+ }
22472
+ if (body.join("").length % 4 !== 0) return false;
22473
+ try {
22474
+ const certificate = new X509Certificate(`${lines.join("\n")}
22475
+ `);
22476
+ return certificate.ca;
22477
+ } catch {
22478
+ return false;
22479
+ }
22480
+ }
22446
22481
  var import_reflect_metadata = __toESM(require_Reflect(), 1);
22447
22482
  var import_x509 = __toESM(require_x509_cjs(), 1);
22448
22483
  var crypto2 = webcrypto.subtle;
@@ -22565,12 +22600,100 @@ function isWellFormedManifest(value) {
22565
22600
  const m = value;
22566
22601
  return typeof m.name === "string" && typeof m.path === "string" && m.type === "stdio" && Array.isArray(m.allowed_origins) && m.allowed_origins.every((o) => typeof o === "string");
22567
22602
  }
22568
- async function writeFileAtomic(path2, data) {
22603
+ function noFollowFlag() {
22604
+ const flag = fsConstants.O_NOFOLLOW;
22605
+ return typeof flag === "number" ? flag : 0;
22606
+ }
22607
+ async function writeFileAtomic(path2, data, mode) {
22569
22608
  const dir = dirname2(path2);
22570
22609
  await mkdir2(dir, { recursive: true });
22571
22610
  const tmp = join2(dir, `.${basename(path2)}.${process.pid}.tmp`);
22572
- await writeFile2(tmp, data, "utf8");
22573
- await rename(tmp, path2);
22611
+ const flags = fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL | noFollowFlag();
22612
+ let created = false;
22613
+ let handle;
22614
+ try {
22615
+ handle = await open2(tmp, flags, 384);
22616
+ created = true;
22617
+ await handle.writeFile(data, "utf8");
22618
+ await handle.chmod(mode ?? 420);
22619
+ await handle.close();
22620
+ handle = void 0;
22621
+ await rename(tmp, path2);
22622
+ } catch (error) {
22623
+ await handle?.close().catch(() => void 0);
22624
+ if (created) {
22625
+ try {
22626
+ await rm(tmp, { force: true });
22627
+ } catch {
22628
+ }
22629
+ }
22630
+ throw error;
22631
+ }
22632
+ }
22633
+ var CA_POSSESSION_PAYLOAD = Buffer.from("rogatio-ca-possession");
22634
+ var ROGATIO_REQUEST_BODY_CA_SUBJECT = "CN=CN=Rogatio Request-Body CA";
22635
+ function caCertificateMatchesPrivateKey(certPem, keyPem) {
22636
+ try {
22637
+ if (!isInstallableCaCertificate(certPem)) return false;
22638
+ const certificate = new X509Certificate3(certPem);
22639
+ if (!certificate.ca) return false;
22640
+ const privateKey = createPrivateKey(keyPem);
22641
+ if (privateKey.type !== "private") return false;
22642
+ const signature = sign("sha256", CA_POSSESSION_PAYLOAD, privateKey);
22643
+ return verify(
22644
+ "sha256",
22645
+ CA_POSSESSION_PAYLOAD,
22646
+ certificate.publicKey,
22647
+ signature
22648
+ );
22649
+ } catch {
22650
+ return false;
22651
+ }
22652
+ }
22653
+ async function readNoFollowRegularFile(path2) {
22654
+ let handle;
22655
+ try {
22656
+ const info = await lstat(path2);
22657
+ if (!info.isFile()) return void 0;
22658
+ handle = await open2(path2, fsConstants.O_RDONLY | noFollowFlag());
22659
+ const opened = await handle.stat();
22660
+ if (!opened.isFile()) return void 0;
22661
+ return await handle.readFile({ encoding: "utf8" });
22662
+ } catch {
22663
+ return void 0;
22664
+ } finally {
22665
+ await handle?.close().catch(() => void 0);
22666
+ }
22667
+ }
22668
+ async function readPreviousRogatioCa(caCertFile) {
22669
+ const pem = await readNoFollowRegularFile(caCertFile);
22670
+ if (pem === void 0) return void 0;
22671
+ try {
22672
+ const certificate = new X509Certificate3(pem);
22673
+ if (!certificate.ca) return void 0;
22674
+ if (certificate.subject !== ROGATIO_REQUEST_BODY_CA_SUBJECT) {
22675
+ return void 0;
22676
+ }
22677
+ const fingerprint = sha1Fingerprint(pem);
22678
+ if (!fingerprint) return void 0;
22679
+ return { fingerprint, pem };
22680
+ } catch {
22681
+ return void 0;
22682
+ }
22683
+ }
22684
+ async function discardPreviousCa(caPreviousCertFile) {
22685
+ try {
22686
+ await rm(caPreviousCertFile, { force: true });
22687
+ } catch {
22688
+ }
22689
+ }
22690
+ function sha1Fingerprint(certPem) {
22691
+ try {
22692
+ const hex = new X509Certificate3(certPem).fingerprint.replaceAll(":", "").toLowerCase();
22693
+ return /^[0-9a-f]{40}$/.test(hex) ? hex : void 0;
22694
+ } catch {
22695
+ return void 0;
22696
+ }
22574
22697
  }
22575
22698
  async function existsFile(path2) {
22576
22699
  try {
@@ -22602,10 +22725,21 @@ function createRequestBodyTrustController(options = {}) {
22602
22725
  const detect = options.detectCapabilities ?? detectTrustCapabilities;
22603
22726
  const caTrustInstaller = options.caTrustInstaller;
22604
22727
  const caTrustRemover = options.caTrustRemover;
22728
+ const caTrustAnchorRemover = options.caTrustAnchorRemover;
22729
+ let anchorRemovalWarning;
22730
+ let rememberedFingerprint;
22731
+ let replacingPrevious = false;
22732
+ let reusedExisting = false;
22733
+ let previousKeyPem;
22734
+ let previousPubPem;
22605
22735
  const caCertFile = join2(
22606
22736
  installRoot,
22607
22737
  options.caCertFileName ?? ".rogatio-ca.crt"
22608
22738
  );
22739
+ const caPreviousCertFile = join2(
22740
+ dirname2(caCertFile),
22741
+ ".rogatio-ca.previous.crt"
22742
+ );
22609
22743
  const manifestPath = () => join2(manifestDir, `${hostName}.json`);
22610
22744
  let installerCalled = false;
22611
22745
  async function install(extensionId) {
@@ -22662,9 +22796,7 @@ function createRequestBodyTrustController(options = {}) {
22662
22796
  const trustResult = await trust();
22663
22797
  if (!trustResult.ok) {
22664
22798
  try {
22665
- await rm(caKeyFile, { force: true });
22666
- await rm(caPubFile, { force: true });
22667
- await rm(caCertFile, { force: true });
22799
+ await rollbackReplacedCa();
22668
22800
  } catch {
22669
22801
  }
22670
22802
  try {
@@ -22674,7 +22806,65 @@ function createRequestBodyTrustController(options = {}) {
22674
22806
  installerCalled = false;
22675
22807
  return trustResult;
22676
22808
  }
22677
- return { ok: true, state: "installed" };
22809
+ return trustResult.reasons ? { ok: true, state: "installed", reasons: trustResult.reasons } : { ok: true, state: "installed" };
22810
+ }
22811
+ async function reusableCaMaterial() {
22812
+ try {
22813
+ const [keyStat, certStat] = await Promise.all([
22814
+ lstat(caKeyFile),
22815
+ lstat(caCertFile)
22816
+ ]);
22817
+ if (!keyStat.isFile() || !certStat.isFile()) return false;
22818
+ if (process.platform !== "win32") {
22819
+ if ((keyStat.mode & 511) !== 384) return false;
22820
+ const uid = process.getuid?.();
22821
+ if (uid !== void 0 && keyStat.uid !== uid) return false;
22822
+ }
22823
+ const [certPem, keyPem] = await Promise.all([
22824
+ readFile2(caCertFile, "utf8"),
22825
+ readFile2(caKeyFile, "utf8")
22826
+ ]);
22827
+ return caCertificateMatchesPrivateKey(certPem, keyPem);
22828
+ } catch {
22829
+ return false;
22830
+ }
22831
+ }
22832
+ async function retireAnchor(anchor) {
22833
+ if (!caTrustAnchorRemover) return;
22834
+ try {
22835
+ await caTrustAnchorRemover(anchor.fingerprint, anchor.pem);
22836
+ } catch {
22837
+ anchorRemovalWarning = "previous-anchor-not-removed";
22838
+ console.error("[rogatio] previous CA trust anchor was not removed");
22839
+ }
22840
+ }
22841
+ async function removeLiveCaFiles() {
22842
+ await rm(caKeyFile, { force: true });
22843
+ await rm(caPubFile, { force: true });
22844
+ await rm(caCertFile, { force: true });
22845
+ }
22846
+ async function rollbackReplacedCa() {
22847
+ if (reusedExisting) return;
22848
+ if (!replacingPrevious) {
22849
+ await removeLiveCaFiles();
22850
+ return;
22851
+ }
22852
+ const saved = await readPreviousRogatioCa(caPreviousCertFile);
22853
+ const live = await readPreviousRogatioCa(caCertFile);
22854
+ const copyKept = rememberedFingerprint !== void 0 && saved?.fingerprint === rememberedFingerprint;
22855
+ const liveDiffers = live?.fingerprint !== rememberedFingerprint;
22856
+ if (copyKept && liveDiffers) {
22857
+ await removeLiveCaFiles();
22858
+ return;
22859
+ }
22860
+ if (copyKept && !liveDiffers) {
22861
+ if (previousKeyPem !== void 0) {
22862
+ await writeFileAtomic(caKeyFile, previousKeyPem, 384);
22863
+ }
22864
+ if (previousPubPem !== void 0) {
22865
+ await writeFileAtomic(caPubFile, previousPubPem);
22866
+ }
22867
+ }
22678
22868
  }
22679
22869
  async function runCaTrust() {
22680
22870
  const caps = await detect();
@@ -22685,25 +22875,78 @@ function createRequestBodyTrustController(options = {}) {
22685
22875
  caps.reasons
22686
22876
  );
22687
22877
  }
22688
- if (!await existsFile(caKeyFile) || !await existsFile(caCertFile)) {
22878
+ anchorRemovalWarning = void 0;
22879
+ rememberedFingerprint = void 0;
22880
+ replacingPrevious = false;
22881
+ reusedExisting = false;
22882
+ previousKeyPem = void 0;
22883
+ previousPubPem = void 0;
22884
+ const reusable = await reusableCaMaterial();
22885
+ reusedExisting = reusable;
22886
+ const liveAnchor = await readPreviousRogatioCa(caCertFile);
22887
+ const siblingAnchor = await readPreviousRogatioCa(caPreviousCertFile);
22888
+ let preserved;
22889
+ if (!reusable) {
22890
+ replacingPrevious = liveAnchor !== void 0 || siblingAnchor !== void 0;
22689
22891
  const { privateKey } = generateCaKeyPair(TRUST_LIMITS.caKeyBits);
22690
22892
  const certResult = await createCertificate(
22691
22893
  "CN=Rogatio Request-Body CA",
22692
22894
  privateKey,
22693
22895
  TRUST_LIMITS.caValidityDays
22694
22896
  );
22695
- const certPem = certResult.certPem;
22696
- const certKeyPem = certResult.keyPem;
22697
- await writeFileAtomic(caKeyFile, certKeyPem);
22698
- await writeFileAtomic(caPubFile, certPem);
22699
- await writeFileAtomic(caCertFile, certPem);
22700
- }
22897
+ const nextFingerprint = sha1Fingerprint(certResult.certPem);
22898
+ if (liveAnchor && siblingAnchor && siblingAnchor.fingerprint !== liveAnchor.fingerprint && siblingAnchor.fingerprint !== nextFingerprint) {
22899
+ await retireAnchor(siblingAnchor);
22900
+ }
22901
+ if (liveAnchor) {
22902
+ await writeFileAtomic(caPreviousCertFile, liveAnchor.pem, 384);
22903
+ const accepted = await readPreviousRogatioCa(caPreviousCertFile);
22904
+ if (!accepted || accepted.fingerprint !== liveAnchor.fingerprint) {
22905
+ throw new TrustError("trust.internal", "invalid-ca-certificate", [
22906
+ "invalid-ca-certificate"
22907
+ ]);
22908
+ }
22909
+ rememberedFingerprint = accepted.fingerprint;
22910
+ preserved = accepted;
22911
+ } else if (siblingAnchor && siblingAnchor.fingerprint !== nextFingerprint) {
22912
+ rememberedFingerprint = siblingAnchor.fingerprint;
22913
+ preserved = siblingAnchor;
22914
+ } else if (siblingAnchor) {
22915
+ await discardPreviousCa(caPreviousCertFile);
22916
+ }
22917
+ previousKeyPem = await readNoFollowRegularFile(caKeyFile);
22918
+ previousPubPem = await readNoFollowRegularFile(caPubFile);
22919
+ await writeFileAtomic(caKeyFile, certResult.keyPem, 384);
22920
+ await writeFileAtomic(caPubFile, certResult.certPem);
22921
+ await writeFileAtomic(caCertFile, certResult.certPem);
22922
+ }
22923
+ let installedNew = false;
22701
22924
  if (caTrustInstaller && !installerCalled) {
22702
22925
  await caTrustInstaller(await readFile2(caCertFile, "utf8"));
22703
22926
  installerCalled = true;
22927
+ installedNew = true;
22928
+ }
22929
+ if (reusable && siblingAnchor && (installedNew || !caTrustInstaller)) {
22930
+ if (liveAnchor && siblingAnchor.fingerprint === liveAnchor.fingerprint) {
22931
+ await discardPreviousCa(caPreviousCertFile);
22932
+ } else {
22933
+ await retireAnchor(siblingAnchor);
22934
+ await discardPreviousCa(caPreviousCertFile);
22935
+ }
22936
+ }
22937
+ if (!reusable && installedNew && preserved) {
22938
+ const current = await readPreviousRogatioCa(caCertFile);
22939
+ if (current && current.fingerprint === preserved.fingerprint) {
22940
+ await discardPreviousCa(caPreviousCertFile);
22941
+ } else if (current && current.fingerprint !== preserved.fingerprint) {
22942
+ await retireAnchor(preserved);
22943
+ await discardPreviousCa(caPreviousCertFile);
22944
+ }
22704
22945
  }
22705
22946
  }
22706
22947
  async function removeCa() {
22948
+ const saved = await readPreviousRogatioCa(caPreviousCertFile);
22949
+ const live = await readPreviousRogatioCa(caCertFile);
22707
22950
  const caWasPresent = await existsFile(caKeyFile);
22708
22951
  await rm(caKeyFile, { force: true });
22709
22952
  await rm(caPubFile, { force: true });
@@ -22711,6 +22954,16 @@ function createRequestBodyTrustController(options = {}) {
22711
22954
  if (caWasPresent && caTrustRemover) {
22712
22955
  await caTrustRemover();
22713
22956
  }
22957
+ if (!saved) return;
22958
+ if (!caWasPresent || saved.fingerprint !== live?.fingerprint) {
22959
+ if (!caTrustAnchorRemover) {
22960
+ throw new TrustError("trust.internal", "previous-anchor-not-removed", [
22961
+ "previous-anchor-not-removed"
22962
+ ]);
22963
+ }
22964
+ await caTrustAnchorRemover(saved.fingerprint, saved.pem);
22965
+ }
22966
+ await rm(caPreviousCertFile, { force: true });
22714
22967
  }
22715
22968
  async function uninstall() {
22716
22969
  try {
@@ -22736,7 +22989,7 @@ function createRequestBodyTrustController(options = {}) {
22736
22989
  reasons: [codeOf(error, "trust.internal")]
22737
22990
  };
22738
22991
  }
22739
- return { ok: true, state: "trusted" };
22992
+ return anchorRemovalWarning ? { ok: true, state: "trusted", reasons: [anchorRemovalWarning] } : { ok: true, state: "trusted" };
22740
22993
  }
22741
22994
  async function status() {
22742
22995
  let installed = false;
@@ -22755,7 +23008,7 @@ function createRequestBodyTrustController(options = {}) {
22755
23008
  capabilityReasons: caps.reasons
22756
23009
  };
22757
23010
  }
22758
- async function verify() {
23011
+ async function verify2() {
22759
23012
  const reasons = [];
22760
23013
  let manifestExists = false;
22761
23014
  let manifestValid = false;
@@ -22801,7 +23054,23 @@ function createRequestBodyTrustController(options = {}) {
22801
23054
  reasons
22802
23055
  };
22803
23056
  }
22804
- return { install, uninstall, status, verify };
23057
+ return { install, uninstall, status, verify: verify2 };
23058
+ }
23059
+ var PREVIOUS_CA_PEM_MAX_BYTES = 16 * 1024;
23060
+ function spawnSecurity(args) {
23061
+ return new Promise((resolve12) => {
23062
+ const child = spawn("security", [...args]);
23063
+ let stderr = "";
23064
+ child.stderr?.on("data", (data) => {
23065
+ stderr += data.toString();
23066
+ });
23067
+ child.on("close", (code) => resolve12({ code: code ?? 1, stderr }));
23068
+ child.on("error", (err) => resolve12({ code: 1, stderr: err.message }));
23069
+ });
23070
+ }
23071
+ function securityMiss(stderr) {
23072
+ const text = stderr.toLowerCase();
23073
+ return text.includes("not found") || text.includes("does not exist");
22805
23074
  }
22806
23075
  var darwinAdapter = {
22807
23076
  platform: "darwin",
@@ -22910,6 +23179,53 @@ var darwinAdapter = {
22910
23179
  ]);
22911
23180
  }
22912
23181
  }
23182
+ },
23183
+ async caTrustAnchorRemover(fingerprintSha1, certPem) {
23184
+ if (!/^[0-9a-f]{40}$/.test(fingerprintSha1)) {
23185
+ throw new TrustError("trust.internal", "invalid-ca-certificate", [
23186
+ "invalid-ca-certificate"
23187
+ ]);
23188
+ }
23189
+ if (typeof certPem !== "string" || certPem.length === 0 || certPem.length > PREVIOUS_CA_PEM_MAX_BYTES) {
23190
+ throw new TrustError("trust.internal", "invalid-ca-certificate", [
23191
+ "invalid-ca-certificate"
23192
+ ]);
23193
+ }
23194
+ const keychain = this.defaultCaInstallPath();
23195
+ const dir = await mkdtemp(join3(tmpdir(), "rogatio-ca-"));
23196
+ const tmpPath = join3(dir, "previous.crt");
23197
+ let failed = false;
23198
+ try {
23199
+ const handle = await open3(tmpPath, "wx", 384);
23200
+ try {
23201
+ await handle.writeFile(certPem, "utf8");
23202
+ await handle.chmod(384);
23203
+ } finally {
23204
+ await handle.close();
23205
+ }
23206
+ const trust = await spawnSecurity(["remove-trusted-cert", "-d", tmpPath]);
23207
+ if (trust.code !== 0 && !securityMiss(trust.stderr)) failed = true;
23208
+ const item = await spawnSecurity([
23209
+ "delete-certificate",
23210
+ "-Z",
23211
+ fingerprintSha1,
23212
+ keychain
23213
+ ]);
23214
+ if (item.code !== 0 && !securityMiss(item.stderr)) failed = true;
23215
+ } catch (error) {
23216
+ if (error instanceof TrustError) throw error;
23217
+ failed = true;
23218
+ } finally {
23219
+ try {
23220
+ await rm2(dir, { recursive: true, force: true });
23221
+ } catch {
23222
+ }
23223
+ }
23224
+ if (failed) {
23225
+ throw new TrustError("trust.internal", "keychain-unwritable", [
23226
+ "keychain-unwritable"
23227
+ ]);
23228
+ }
22913
23229
  }
22914
23230
  };
22915
23231
  var darwin_default = darwinAdapter;
@@ -22919,6 +23235,46 @@ function hasSudo() {
22919
23235
  const result = spawnSync2("which", ["sudo"], { timeout: 1e3 });
22920
23236
  return result.status === 0;
22921
23237
  }
23238
+ function installFailureReason(stderr) {
23239
+ const text = stderr.toLowerCase();
23240
+ if (text.includes("permission") || text.includes("not permitted") || text.includes("no tty") || text.includes("terminal")) {
23241
+ return "elevation-required";
23242
+ }
23243
+ return "ca-store-unwritable";
23244
+ }
23245
+ function runSudo(args, stdin) {
23246
+ return new Promise((resolve12) => {
23247
+ let stderr = "";
23248
+ let settled = false;
23249
+ const finish2 = (code) => {
23250
+ if (settled) return;
23251
+ settled = true;
23252
+ resolve12({ code, stderr });
23253
+ };
23254
+ const child = spawn2("sudo", [...args], {
23255
+ stdio: [stdin === void 0 ? "ignore" : "pipe", "ignore", "pipe"]
23256
+ });
23257
+ child.stderr?.on("data", (data) => {
23258
+ stderr += data.toString();
23259
+ });
23260
+ child.on("error", (err) => {
23261
+ stderr += err.message;
23262
+ finish2(1);
23263
+ });
23264
+ child.on("close", (code) => {
23265
+ finish2(code ?? 1);
23266
+ });
23267
+ if (stdin === void 0) return;
23268
+ const input = child.stdin;
23269
+ if (!input) {
23270
+ finish2(1);
23271
+ return;
23272
+ }
23273
+ input.on("error", () => {
23274
+ });
23275
+ input.end(stdin);
23276
+ });
23277
+ }
22922
23278
  var linuxAdapter = {
22923
23279
  platform: "linux",
22924
23280
  defaultManifestDir: () => {
@@ -22964,6 +23320,11 @@ var linuxAdapter = {
22964
23320
  };
22965
23321
  },
22966
23322
  async caTrustInstaller(certPem) {
23323
+ if (!isInstallableCaCertificate(certPem)) {
23324
+ throw new TrustError("trust.internal", "invalid-ca-certificate", [
23325
+ "invalid-ca-certificate"
23326
+ ]);
23327
+ }
22967
23328
  try {
22968
23329
  mkdirSync(CA_DIR, { recursive: true });
22969
23330
  } catch {
@@ -22971,48 +23332,14 @@ var linuxAdapter = {
22971
23332
  "ca-store-unwritable"
22972
23333
  ]);
22973
23334
  }
22974
- const certResult = await new Promise(
22975
- (resolve12) => {
22976
- const child = spawn2("sudo", [
22977
- "sh",
22978
- "-c",
22979
- `cat > "${CERT_PATH}" << 'EOF'
22980
- ${certPem}
22981
- EOF`
22982
- ]);
22983
- let stderr = "";
22984
- child.stderr?.on("data", (data) => {
22985
- stderr += data.toString();
22986
- });
22987
- child.on("close", (code) => resolve12({ code: code ?? 1, stderr }));
22988
- child.on("error", (err) => resolve12({ code: 1, stderr: err.message }));
22989
- }
22990
- );
23335
+ const certResult = await runSudo(["tee", CERT_PATH], certPem);
22991
23336
  if (certResult.code !== 0) {
22992
- const stderr = certResult.stderr.toLowerCase();
22993
- let reason = "ca-store-unwritable";
22994
- if (stderr.includes("permission") || stderr.includes("not permitted") || stderr.includes("no tty") || stderr.includes("terminal")) {
22995
- reason = "elevation-required";
22996
- }
23337
+ const reason = installFailureReason(certResult.stderr);
22997
23338
  throw new TrustError("trust.internal", reason, [reason]);
22998
23339
  }
22999
- const result = await new Promise(
23000
- (resolve12) => {
23001
- const child = spawn2("sudo", ["update-ca-certificates"]);
23002
- let stderr = "";
23003
- child.stderr?.on("data", (data) => {
23004
- stderr += data.toString();
23005
- });
23006
- child.on("close", (code) => resolve12({ code: code ?? 1, stderr }));
23007
- child.on("error", (err) => resolve12({ code: 1, stderr: err.message }));
23008
- }
23009
- );
23340
+ const result = await runSudo(["update-ca-certificates"]);
23010
23341
  if (result.code !== 0) {
23011
- const stderr = result.stderr.toLowerCase();
23012
- let reason = "ca-store-unwritable";
23013
- if (stderr.includes("permission") || stderr.includes("not permitted") || stderr.includes("no tty") || stderr.includes("terminal")) {
23014
- reason = "elevation-required";
23015
- }
23342
+ const reason = installFailureReason(result.stderr);
23016
23343
  throw new TrustError("trust.internal", reason, [reason]);
23017
23344
  }
23018
23345
  },
@@ -23021,6 +23348,10 @@ EOF`
23021
23348
  unlinkSync2(CERT_PATH);
23022
23349
  } catch {
23023
23350
  }
23351
+ },
23352
+ async caTrustAnchorRemover(fingerprintSha1, certPem) {
23353
+ void fingerprintSha1;
23354
+ void certPem;
23024
23355
  }
23025
23356
  };
23026
23357
  var linux_default = linuxAdapter;
@@ -23044,6 +23375,11 @@ var unsupportedAdapter = {
23044
23375
  throw new TrustError("trust.internal", "unsupported-platform", [
23045
23376
  "no-capability-provider"
23046
23377
  ]);
23378
+ },
23379
+ async caTrustAnchorRemover() {
23380
+ throw new TrustError("trust.internal", "unsupported-platform", [
23381
+ "no-capability-provider"
23382
+ ]);
23047
23383
  }
23048
23384
  };
23049
23385
  var win32Adapter = {
@@ -23132,6 +23468,33 @@ var win32Adapter = {
23132
23468
  ]);
23133
23469
  }
23134
23470
  }
23471
+ },
23472
+ async caTrustAnchorRemover(fingerprintSha1, certPem) {
23473
+ void certPem;
23474
+ if (!/^[0-9a-f]{40}$/.test(fingerprintSha1)) {
23475
+ throw new TrustError("trust.internal", "invalid-ca-certificate", [
23476
+ "invalid-ca-certificate"
23477
+ ]);
23478
+ }
23479
+ const result = await new Promise(
23480
+ (resolve12) => {
23481
+ const child = spawn3("certutil", ["-delstore", "Root", fingerprintSha1]);
23482
+ let stderr = "";
23483
+ child.stderr?.on("data", (data) => {
23484
+ stderr += data.toString();
23485
+ });
23486
+ child.on("close", (code) => resolve12({ code: code ?? 1, stderr }));
23487
+ child.on("error", (err) => resolve12({ code: 1, stderr: err.message }));
23488
+ }
23489
+ );
23490
+ if (result.code !== 0) {
23491
+ const stderr = result.stderr.toLowerCase();
23492
+ if (!stderr.includes("not found") && !stderr.includes("does not exist") && !stderr.includes("cannot find")) {
23493
+ throw new TrustError("trust.internal", "elevation-required", [
23494
+ "elevation-required"
23495
+ ]);
23496
+ }
23497
+ }
23135
23498
  }
23136
23499
  };
23137
23500
  var win32_default = win32Adapter;
@@ -23160,7 +23523,8 @@ function createInstalledTrustController() {
23160
23523
  manifestDir: adapter.defaultManifestDir(),
23161
23524
  detectCapabilities: () => adapter.detect(),
23162
23525
  caTrustInstaller: (cert) => adapter.caTrustInstaller(cert),
23163
- caTrustRemover: () => adapter.caTrustRemover()
23526
+ caTrustRemover: () => adapter.caTrustRemover(),
23527
+ caTrustAnchorRemover: (fingerprint, certPem) => adapter.caTrustAnchorRemover(fingerprint, certPem)
23164
23528
  });
23165
23529
  }
23166
23530
  var PROTOCOL = "v1";
@@ -24016,7 +24380,7 @@ async function resolveConfinedRoot(value) {
24016
24380
  }
24017
24381
  let directory;
24018
24382
  try {
24019
- directory = await open2(value, constants5.O_RDONLY | constants5.O_DIRECTORY);
24383
+ directory = await open4(value, constants5.O_RDONLY | constants5.O_DIRECTORY);
24020
24384
  const info = await directory.stat();
24021
24385
  if (!info.isDirectory()) return failure("runtime.root-invalid");
24022
24386
  return { ok: true, value: await realpath2(value) };
@@ -27843,7 +28207,7 @@ import {
27843
28207
  rename as rename2,
27844
28208
  stat as stat2,
27845
28209
  unlink as unlink2,
27846
- writeFile as writeFile3
28210
+ writeFile as writeFile2
27847
28211
  } from "node:fs/promises";
27848
28212
  import { basename as basename2, dirname as dirname5, join as join7 } from "node:path";
27849
28213
  var ProjectStorageError = class extends Error {
@@ -28060,7 +28424,7 @@ async function writeDocument(id2, data) {
28060
28424
  );
28061
28425
  }
28062
28426
  await mkdir3(dirname5(id2), { recursive: true });
28063
- await writeFile3(tempPath, JSON.stringify(data, null, 2), "utf-8");
28427
+ await writeFile2(tempPath, JSON.stringify(data, null, 2), "utf-8");
28064
28428
  await rename2(tempPath, id2);
28065
28429
  } catch (e) {
28066
28430
  if (e instanceof ProjectFileError) throw e;
@@ -29109,7 +29473,7 @@ function previewRuleAction(operation, url) {
29109
29473
  }
29110
29474
 
29111
29475
  // packages/cli/src/mock-root-config.ts
29112
- import { mkdir as mkdir4, readFile as readFile4, realpath as realpath3, rename as rename3, writeFile as writeFile4 } from "node:fs/promises";
29476
+ import { mkdir as mkdir4, readFile as readFile4, realpath as realpath3, rename as rename3, writeFile as writeFile3 } from "node:fs/promises";
29113
29477
  import { homedir } from "node:os";
29114
29478
  import { dirname as dirname8, isAbsolute as isAbsolute4, join as join8, resolve as resolve4 } from "node:path";
29115
29479
  function mockRootConfigPath(env = process.env) {
@@ -29155,7 +29519,7 @@ async function writeMap(map, env) {
29155
29519
  const path2 = mockRootConfigPath(env);
29156
29520
  await mkdir4(dirname8(path2), { recursive: true });
29157
29521
  const temporary = `${path2}.${process.pid}.tmp`;
29158
- await writeFile4(
29522
+ await writeFile3(
29159
29523
  temporary,
29160
29524
  `${JSON.stringify({ projects: map }, null, 2)}
29161
29525
  `,
@@ -32524,7 +32888,7 @@ function errorMessage(error) {
32524
32888
  }
32525
32889
 
32526
32890
  // packages/cli/src/commands/runtime.ts
32527
- import { chmod, mkdir as mkdir5, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
32891
+ import { chmod, mkdir as mkdir5, rm as rm3, writeFile as writeFile4 } from "node:fs/promises";
32528
32892
  import { dirname as dirname10, join as join9, resolve as resolve8 } from "node:path";
32529
32893
  function generateHostWrapper(rogatioCommand, logPath) {
32530
32894
  const parts = rogatioCommand.split(" ");
@@ -32545,7 +32909,7 @@ async function writeHostWrapper(hostPath, installRoot) {
32545
32909
  `${process.execPath} ${process.argv[1] ?? "rogatio"}`,
32546
32910
  logPath
32547
32911
  );
32548
- await writeFile5(hostPath, script, "utf8");
32912
+ await writeFile4(hostPath, script, "utf8");
32549
32913
  await chmod(hostPath, 493);
32550
32914
  }
32551
32915
  function makeTrustController() {
@@ -32635,11 +32999,11 @@ Note: reload the extension at chrome://extensions for the native host to be dete
32635
32999
  case "uninstall": {
32636
33000
  const uninstallResult = await controller.uninstall();
32637
33001
  try {
32638
- await rm2(hostPath, { force: true });
33002
+ await rm3(hostPath, { force: true });
32639
33003
  } catch {
32640
33004
  }
32641
33005
  try {
32642
- await rm2(join9(installRoot, "runtime-host.log"), { force: true });
33006
+ await rm3(join9(installRoot, "runtime-host.log"), { force: true });
32643
33007
  } catch {
32644
33008
  }
32645
33009
  return reportTrust(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogatio/cli",
3
- "version": "6.16.0",
3
+ "version": "6.16.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Local-first browser request and response rules — editor host, file verification, test runner, and runtime dispatch.",
@@ -48,8 +48,8 @@
48
48
  "@rogatio/requestly-import": "workspace:*",
49
49
  "@rogatio/runtime": "workspace:*",
50
50
  "@rogatio/schema": "workspace:*",
51
- "@types/node": "26.6.3",
51
+ "@types/node": "26.6.4",
52
52
  "typescript": "7.0.2",
53
- "vitest": "5.0.2"
53
+ "vitest": "5.0.3"
54
54
  }
55
55
  }