@rogatio/cli 6.16.4 → 6.17.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.
@@ -0,0 +1 @@
1
+ export declare function cli(args?: string[]): Promise<number>;
@@ -2200,19 +2200,21 @@ function compileProject(value) {
2200
2200
  };
2201
2201
  }
2202
2202
  }
2203
- function sourceMatches(source, url) {
2204
- const regex = compileUrlRegex(source.value);
2205
- if (regex === null) return false;
2206
- if (source.key === "url") {
2207
- return regex.test(url);
2208
- }
2203
+ function sourceMatchText(source, url) {
2204
+ if (source.key === "url") return url;
2209
2205
  try {
2210
- const hostname = new URL(url).hostname;
2211
- return regex.test(hostname);
2206
+ return new URL(url).hostname;
2212
2207
  } catch {
2213
- return false;
2208
+ return null;
2214
2209
  }
2215
2210
  }
2211
+ function sourceMatches(source, url) {
2212
+ const regex = compileUrlRegex(source.value);
2213
+ if (regex === null) return false;
2214
+ const text = sourceMatchText(source, url);
2215
+ if (text === null) return false;
2216
+ return regex.test(text);
2217
+ }
2216
2218
  var PAC_STEER_MARKER = "steer:";
2217
2219
  function decodePacSteer(entry) {
2218
2220
  if (!entry.startsWith(PAC_STEER_MARKER)) return null;
@@ -22596,6 +22598,15 @@ function defaultTrustInstallRoot(platform) {
22596
22598
  }
22597
22599
  return join2(process.env.HOME ?? "", ".local", "share", "rogatio");
22598
22600
  }
22601
+ var MANIFEST_DIR_RECORD = "native-messaging-dirs.json";
22602
+ function isSafeManifestDir(dir) {
22603
+ if (!isAbsolute2(dir) || dir.includes("\0")) return false;
22604
+ const parts = dir.split(/[\\/]/).filter((part) => part.length > 0);
22605
+ if (parts.includes("..") || parts.length < 2) return false;
22606
+ const browser = parts[parts.length - 2];
22607
+ const leaf = parts[parts.length - 1];
22608
+ return leaf === "NativeMessagingHosts" && (browser === "google-chrome" || browser === "chromium");
22609
+ }
22599
22610
  function isWellFormedManifest(value) {
22600
22611
  if (typeof value !== "object" || value === null) return false;
22601
22612
  const m = value;
@@ -22752,12 +22763,110 @@ function createRequestBodyTrustController(options = {}) {
22752
22763
  installRoot,
22753
22764
  options.caCertFileName ?? ".rogatio-ca.crt"
22754
22765
  );
22766
+ const storePendingPath = join2(
22767
+ dirname2(caKeyFile),
22768
+ ".rogatio-ca-store-pending"
22769
+ );
22755
22770
  const caPreviousCertFile = join2(
22756
22771
  dirname2(caCertFile),
22757
22772
  ".rogatio-ca.previous.crt"
22758
22773
  );
22759
- const manifestPath = () => join2(manifestDir, `${hostName}.json`);
22774
+ const configuredManifestDirs = options.manifestDirs && options.manifestDirs.length > 0 ? [...options.manifestDirs] : [manifestDir];
22775
+ const recordsManifestDirs = options.manifestDirs !== void 0 && options.manifestDirs.length > 0;
22776
+ const manifestRecordPath = join2(installRoot, MANIFEST_DIR_RECORD);
22760
22777
  let installerCalled = false;
22778
+ function parseRecordedDirs(raw) {
22779
+ let parsed;
22780
+ try {
22781
+ parsed = JSON.parse(raw);
22782
+ } catch {
22783
+ return void 0;
22784
+ }
22785
+ if (!Array.isArray(parsed) || parsed.length === 0 || parsed.length > 8) {
22786
+ return void 0;
22787
+ }
22788
+ const dirs = [];
22789
+ const seen = /* @__PURE__ */ new Set();
22790
+ for (const entry of parsed) {
22791
+ if (typeof entry !== "string" || seen.has(entry) || !isSafeManifestDir(entry)) {
22792
+ return void 0;
22793
+ }
22794
+ seen.add(entry);
22795
+ dirs.push(entry);
22796
+ }
22797
+ return dirs;
22798
+ }
22799
+ async function readRecordedDirs() {
22800
+ if (!recordsManifestDirs) return void 0;
22801
+ try {
22802
+ return parseRecordedDirs(await readFile2(manifestRecordPath, "utf8"));
22803
+ } catch {
22804
+ return void 0;
22805
+ }
22806
+ }
22807
+ async function activeManifestDirs() {
22808
+ const recorded = await readRecordedDirs();
22809
+ if (recorded) return recorded;
22810
+ if (!recordsManifestDirs) return configuredManifestDirs;
22811
+ const present = [];
22812
+ for (const dir of configuredManifestDirs) {
22813
+ if (await existsFile(join2(dir, `${hostName}.json`))) present.push(dir);
22814
+ }
22815
+ return present.length > 0 ? present : configuredManifestDirs;
22816
+ }
22817
+ async function snapshotFile(path2) {
22818
+ let previous;
22819
+ try {
22820
+ previous = await readFile2(path2, "utf8");
22821
+ } catch {
22822
+ previous = void 0;
22823
+ }
22824
+ return { path: path2, previous, written: false };
22825
+ }
22826
+ async function restoreSnapshot(snap) {
22827
+ if (!snap.written) return;
22828
+ if (snap.previous !== void 0) {
22829
+ await writeFileAtomic(snap.path, snap.previous);
22830
+ return;
22831
+ }
22832
+ await rm(snap.path, { force: true });
22833
+ }
22834
+ async function rollbackWritten(snaps) {
22835
+ for (const snap of snaps) {
22836
+ try {
22837
+ await restoreSnapshot(snap);
22838
+ } catch {
22839
+ }
22840
+ }
22841
+ }
22842
+ async function writeManifestFiles(data) {
22843
+ const snaps = [];
22844
+ try {
22845
+ for (const dir of configuredManifestDirs) {
22846
+ if (recordsManifestDirs && !isSafeManifestDir(dir)) {
22847
+ throw new Error("trust.write-failed");
22848
+ }
22849
+ const snap = await snapshotFile(join2(dir, `${hostName}.json`));
22850
+ snaps.push(snap);
22851
+ await writeFileAtomic(snap.path, data);
22852
+ snap.written = true;
22853
+ }
22854
+ if (recordsManifestDirs) {
22855
+ const recordSnap = await snapshotFile(manifestRecordPath);
22856
+ snaps.push(recordSnap);
22857
+ await writeFileAtomic(
22858
+ manifestRecordPath,
22859
+ `${JSON.stringify(configuredManifestDirs)}
22860
+ `
22861
+ );
22862
+ recordSnap.written = true;
22863
+ }
22864
+ return { ok: true, snaps };
22865
+ } catch (error) {
22866
+ await rollbackWritten(snaps);
22867
+ return { ok: false, reason: codeOf(error, "trust.write-failed") };
22868
+ }
22869
+ }
22761
22870
  async function install(extensionId) {
22762
22871
  if (!extensionId || !/^[a-p]{32}$/.test(extensionId)) {
22763
22872
  return {
@@ -22792,21 +22901,17 @@ function createRequestBodyTrustController(options = {}) {
22792
22901
  }
22793
22902
  const caps = await detect();
22794
22903
  if (!caps.manifest) return unsupportedResult(caps);
22795
- try {
22796
- await writeFileAtomic(manifestPath(), data);
22797
- } catch (error) {
22904
+ const written = await writeManifestFiles(data);
22905
+ if (!written.ok) {
22798
22906
  return {
22799
22907
  ok: false,
22800
22908
  state: "unsupported",
22801
- reasons: [codeOf(error, "trust.write-failed")]
22909
+ reasons: [written.reason]
22802
22910
  };
22803
22911
  }
22804
22912
  const caTrustCaps = await detect();
22805
22913
  if (!caTrustCaps.caTrust) {
22806
- try {
22807
- await rm(manifestPath(), { force: true });
22808
- } catch {
22809
- }
22914
+ await rollbackWritten(written.snaps);
22810
22915
  return unsupportedResult(caTrustCaps);
22811
22916
  }
22812
22917
  const trustResult = await trust();
@@ -22815,13 +22920,11 @@ function createRequestBodyTrustController(options = {}) {
22815
22920
  await rollbackReplacedCa();
22816
22921
  } catch {
22817
22922
  }
22818
- try {
22819
- await rm(manifestPath(), { force: true });
22820
- } catch {
22821
- }
22923
+ await rollbackWritten(written.snaps);
22822
22924
  installerCalled = false;
22823
22925
  return trustResult;
22824
22926
  }
22927
+ await rm(storePendingPath, { force: true });
22825
22928
  return trustResult.reasons ? { ok: true, state: "installed", reasons: trustResult.reasons } : { ok: true, state: "installed" };
22826
22929
  }
22827
22930
  async function reusableCaMaterial() {
@@ -22951,8 +23054,8 @@ function createRequestBodyTrustController(options = {}) {
22951
23054
  }
22952
23055
  let installedNew = false;
22953
23056
  if (caTrustInstaller && !installerCalled) {
22954
- await caTrustInstaller(await readFile2(caCertFile, "utf8"));
22955
23057
  installerCalled = true;
23058
+ await caTrustInstaller(await readFile2(caCertFile, "utf8"));
22956
23059
  installedNew = true;
22957
23060
  }
22958
23061
  if (reusable && siblingAnchor && (installedNew || !caTrustInstaller)) {
@@ -22977,11 +23080,15 @@ function createRequestBodyTrustController(options = {}) {
22977
23080
  const saved = await readPreviousRogatioCa(caPreviousCertFile);
22978
23081
  const live = await readPreviousRogatioCa(caCertFile);
22979
23082
  const caWasPresent = await existsFile(caKeyFile);
23083
+ if (caWasPresent && caTrustRemover) {
23084
+ await writeFileAtomic(storePendingPath, "1\n", 384);
23085
+ }
22980
23086
  await rm(caKeyFile, { force: true });
22981
23087
  await rm(caPubFile, { force: true });
22982
23088
  await rm(caCertFile, { force: true });
22983
- if (caWasPresent && caTrustRemover) {
23089
+ if (caTrustRemover && await existsFile(storePendingPath)) {
22984
23090
  await caTrustRemover();
23091
+ await rm(storePendingPath, { force: true });
22985
23092
  }
22986
23093
  if (!saved) return;
22987
23094
  if (!caWasPresent || saved.fingerprint !== live?.fingerprint) {
@@ -22996,14 +23103,19 @@ function createRequestBodyTrustController(options = {}) {
22996
23103
  }
22997
23104
  async function uninstall() {
22998
23105
  try {
22999
- await rm(manifestPath(), { force: true });
23106
+ const dirs = await activeManifestDirs();
23107
+ for (const dir of dirs) {
23108
+ await rm(join2(dir, `${hostName}.json`), { force: true });
23109
+ }
23110
+ if (recordsManifestDirs) await rm(manifestRecordPath, { force: true });
23000
23111
  await removeCa();
23001
23112
  installerCalled = false;
23002
23113
  } catch (error) {
23114
+ const reason = error instanceof TrustError ? error.message : error instanceof Error ? error.message : String(error);
23003
23115
  return {
23004
23116
  ok: false,
23005
23117
  state: "unsupported",
23006
- reasons: [error instanceof Error ? error.message : String(error)]
23118
+ reasons: [reason]
23007
23119
  };
23008
23120
  }
23009
23121
  return { ok: true, state: "uninstalled" };
@@ -23020,13 +23132,30 @@ function createRequestBodyTrustController(options = {}) {
23020
23132
  }
23021
23133
  return anchorRemovalWarning ? { ok: true, state: "trusted", reasons: [anchorRemovalWarning] } : { ok: true, state: "trusted" };
23022
23134
  }
23135
+ function manifestAcceptable(parsed) {
23136
+ if (!recordsManifestDirs) return true;
23137
+ return parsed.name === hostName && parsed.path === hostPath && parsed.type === "stdio";
23138
+ }
23139
+ function sameOrigins(left, right) {
23140
+ if (left.length !== right.length) return false;
23141
+ for (let index = 0; index < left.length; index += 1) {
23142
+ if (left[index] !== right[index]) return false;
23143
+ }
23144
+ return true;
23145
+ }
23023
23146
  async function status() {
23024
- let installed = false;
23025
- try {
23026
- const raw = await readFile2(manifestPath(), "utf8");
23027
- installed = isWellFormedManifest(JSON.parse(raw));
23028
- } catch {
23029
- installed = false;
23147
+ const dirs = await activeManifestDirs();
23148
+ let installed = dirs.length > 0;
23149
+ for (const dir of dirs) {
23150
+ try {
23151
+ const raw = await readFile2(join2(dir, `${hostName}.json`), "utf8");
23152
+ const parsed = JSON.parse(raw);
23153
+ if (!isWellFormedManifest(parsed) || !manifestAcceptable(parsed)) {
23154
+ installed = false;
23155
+ }
23156
+ } catch {
23157
+ installed = false;
23158
+ }
23030
23159
  }
23031
23160
  const trusted = await existsFile(caKeyFile) && await existsFile(caCertFile);
23032
23161
  const caps = await detect();
@@ -23039,34 +23168,50 @@ function createRequestBodyTrustController(options = {}) {
23039
23168
  }
23040
23169
  async function verify2() {
23041
23170
  const reasons = [];
23042
- let manifestExists = false;
23043
- let manifestValid = false;
23171
+ const pushReason = (reason) => {
23172
+ if (!reasons.includes(reason)) reasons.push(reason);
23173
+ };
23174
+ const dirs = await activeManifestDirs();
23175
+ let manifestExists = dirs.length > 0;
23176
+ let manifestValid = dirs.length > 0;
23044
23177
  let binaryExists = false;
23045
23178
  let binaryExecutable = false;
23046
23179
  let allowedOrigins = [];
23047
- try {
23048
- const raw = await readFile2(manifestPath(), "utf8");
23049
- manifestExists = true;
23050
- const parsed = JSON.parse(raw);
23051
- if (isWellFormedManifest(parsed)) {
23052
- manifestValid = true;
23053
- allowedOrigins = [...parsed.allowed_origins];
23054
- try {
23055
- const fileStat = await stat(parsed.path);
23056
- binaryExists = fileStat.isFile();
23057
- binaryExecutable = platform === "win32" ? binaryExists : (fileStat.mode & 73) !== 0;
23058
- if (!binaryExists) reasons.push("binary-not-found");
23059
- if (!binaryExecutable) reasons.push("binary-not-executable");
23060
- } catch {
23061
- binaryExists = false;
23062
- reasons.push("binary-not-found");
23180
+ let binaryPath;
23181
+ for (const dir of dirs) {
23182
+ try {
23183
+ const raw = await readFile2(join2(dir, `${hostName}.json`), "utf8");
23184
+ const parsed = JSON.parse(raw);
23185
+ if (!isWellFormedManifest(parsed) || !manifestAcceptable(parsed)) {
23186
+ manifestValid = false;
23187
+ pushReason("manifest-invalid");
23188
+ continue;
23063
23189
  }
23064
- if (allowedOrigins.length === 0) reasons.push("no-allowed-origins");
23065
- } else {
23066
- reasons.push("manifest-invalid");
23190
+ if (binaryPath === void 0) {
23191
+ binaryPath = parsed.path;
23192
+ allowedOrigins = [...parsed.allowed_origins];
23193
+ } else if (parsed.path !== binaryPath || !sameOrigins(parsed.allowed_origins, allowedOrigins)) {
23194
+ manifestValid = false;
23195
+ pushReason("manifest-invalid");
23196
+ }
23197
+ } catch {
23198
+ manifestExists = false;
23199
+ manifestValid = false;
23200
+ pushReason("manifest-not-found");
23067
23201
  }
23068
- } catch {
23069
- reasons.push("manifest-not-found");
23202
+ }
23203
+ if (binaryPath !== void 0) {
23204
+ try {
23205
+ const fileStat = await stat(binaryPath);
23206
+ binaryExists = fileStat.isFile();
23207
+ binaryExecutable = platform === "win32" ? binaryExists : (fileStat.mode & 73) !== 0;
23208
+ if (!binaryExists) pushReason("binary-not-found");
23209
+ if (!binaryExecutable) pushReason("binary-not-executable");
23210
+ } catch {
23211
+ binaryExists = false;
23212
+ pushReason("binary-not-found");
23213
+ }
23214
+ if (allowedOrigins.length === 0) pushReason("no-allowed-origins");
23070
23215
  }
23071
23216
  const caTrusted = await existsFile(caKeyFile) && await existsFile(caCertFile);
23072
23217
  if (!caTrusted) reasons.push("ca-not-trusted");
@@ -23259,10 +23404,99 @@ var darwinAdapter = {
23259
23404
  };
23260
23405
  var darwin_default = darwinAdapter;
23261
23406
  var CA_DIR = "/usr/local/share/ca-certificates";
23262
- var CERT_PATH = posixJoin2(CA_DIR, "rogatio-ca.crt");
23263
- function hasSudo() {
23264
- const result = spawnSync2("which", ["sudo"], { timeout: 1e3 });
23265
- return result.status === 0;
23407
+ var CERT_NAME = "rogatio-ca.crt";
23408
+ var DEBIAN_CERT = posixJoin2(CA_DIR, CERT_NAME);
23409
+ var ARCH_TRUST_SOURCE = "/etc/ca-certificates/trust-source";
23410
+ var FEDORA_TRUST_SOURCE = "/etc/pki/ca-trust/source";
23411
+ var ARCH_ANCHORS = "/etc/ca-certificates/trust-source/anchors";
23412
+ var FEDORA_ANCHORS = "/etc/pki/ca-trust/source/anchors";
23413
+ var P11_CERTS = [
23414
+ posixJoin2(ARCH_ANCHORS, CERT_NAME),
23415
+ posixJoin2(FEDORA_ANCHORS, CERT_NAME)
23416
+ ];
23417
+ var BROWSERS = ["google-chrome", "chromium"];
23418
+ function nodeCode(err) {
23419
+ const code = err?.code;
23420
+ return typeof code === "string" && code.length > 0 ? code : "EACCES";
23421
+ }
23422
+ function exists(path2) {
23423
+ try {
23424
+ accessSync2(path2);
23425
+ return true;
23426
+ } catch (err) {
23427
+ return nodeCode(err) !== "ENOENT";
23428
+ }
23429
+ }
23430
+ function writable(path2) {
23431
+ try {
23432
+ accessSync2(path2, constants3.W_OK);
23433
+ return true;
23434
+ } catch {
23435
+ return false;
23436
+ }
23437
+ }
23438
+ function toolOnPath(name) {
23439
+ const result = spawnSync2("which", [name], { timeout: 1e3 });
23440
+ if (result.status === 0) return true;
23441
+ const code = result.error?.code;
23442
+ if (code !== "ENOENT") return false;
23443
+ const pathEnv = process.env.PATH ?? "";
23444
+ for (const dir of pathEnv.split(":")) {
23445
+ if (dir.length === 0) continue;
23446
+ try {
23447
+ accessSync2(posixJoin2(dir, name));
23448
+ return true;
23449
+ } catch {
23450
+ }
23451
+ }
23452
+ return false;
23453
+ }
23454
+ function caBackend() {
23455
+ if (toolOnPath("update-ca-certificates")) return "debian";
23456
+ if (toolOnPath("update-ca-trust")) return "p11kit";
23457
+ return void 0;
23458
+ }
23459
+ function p11kitAnchorsDir() {
23460
+ const arch = exists(ARCH_TRUST_SOURCE);
23461
+ const fedora = exists(FEDORA_TRUST_SOURCE);
23462
+ if (arch && !fedora) return ARCH_ANCHORS;
23463
+ if (fedora && !arch) return FEDORA_ANCHORS;
23464
+ return void 0;
23465
+ }
23466
+ function needsElevation(dir) {
23467
+ if (writable(dir)) return false;
23468
+ try {
23469
+ accessSync2(dir, constants3.W_OK);
23470
+ } catch (err) {
23471
+ if (nodeCode(err) === "ENOENT" && writable(dirname4(dir))) return false;
23472
+ }
23473
+ return !toolOnPath("sudo");
23474
+ }
23475
+ function configHome() {
23476
+ const xdg = process.env.XDG_CONFIG_HOME;
23477
+ if (typeof xdg === "string" && xdg.startsWith("/")) return xdg;
23478
+ const home = process.env.HOME ?? "";
23479
+ return posixJoin2(home, ".config");
23480
+ }
23481
+ function manifestDirFor(browser) {
23482
+ return posixJoin2(configHome(), browser, "NativeMessagingHosts");
23483
+ }
23484
+ function canHostManifest(dir) {
23485
+ try {
23486
+ accessSync2(dir, constants3.W_OK);
23487
+ return true;
23488
+ } catch (err) {
23489
+ if (nodeCode(err) !== "ENOENT") return false;
23490
+ }
23491
+ try {
23492
+ accessSync2(dirname4(dir), constants3.W_OK);
23493
+ return true;
23494
+ } catch {
23495
+ return false;
23496
+ }
23497
+ }
23498
+ function installableManifestDirs() {
23499
+ return BROWSERS.map(manifestDirFor).filter(canHostManifest);
23266
23500
  }
23267
23501
  function installFailureReason(stderr) {
23268
23502
  const text = stderr.toLowerCase();
@@ -23304,44 +23538,36 @@ function runSudo(args, stdin) {
23304
23538
  input.end(stdin);
23305
23539
  });
23306
23540
  }
23541
+ async function sudoOrThrow(args, stdin) {
23542
+ const result = await runSudo(args, stdin);
23543
+ if (result.code === 0) return;
23544
+ const reason = installFailureReason(result.stderr);
23545
+ throw new TrustError("trust.internal", reason, [reason]);
23546
+ }
23307
23547
  var linuxAdapter = {
23308
23548
  platform: "linux",
23309
23549
  defaultManifestDir: () => {
23310
23550
  const home = process.env.HOME ?? "";
23311
23551
  return posixJoin2(home, ".config/google-chrome/NativeMessagingHosts");
23312
23552
  },
23553
+ manifestDirs: () => installableManifestDirs(),
23313
23554
  defaultCaInstallPath: () => CA_DIR,
23314
23555
  detect() {
23315
23556
  const reasons = [];
23316
- const manifestDir = this.defaultManifestDir();
23317
- const updateCa = spawnSync2("which", ["update-ca-certificates"], {
23318
- timeout: 1e3
23319
- });
23320
- if (updateCa.status !== 0) {
23321
- reasons.push("tooling-missing");
23322
- }
23323
- try {
23324
- accessSync2(manifestDir, constants3.W_OK);
23325
- } catch {
23557
+ const backend = caBackend();
23558
+ if (!backend) reasons.push("tooling-missing");
23559
+ if (installableManifestDirs().length === 0) {
23326
23560
  reasons.push("manifest-dir-unwritable");
23327
23561
  }
23328
- try {
23329
- accessSync2(CA_DIR, constants3.W_OK);
23330
- } catch (err) {
23331
- if (err?.code === "ENOENT") {
23332
- try {
23333
- accessSync2(dirname4(CA_DIR), constants3.W_OK);
23334
- } catch {
23335
- if (!hasSudo()) {
23336
- reasons.push("elevation-required");
23337
- }
23338
- }
23339
- } else if (!hasSudo()) {
23340
- reasons.push("elevation-required");
23341
- }
23562
+ if (backend === "debian") {
23563
+ if (needsElevation(CA_DIR)) reasons.push("elevation-required");
23564
+ } else if (backend === "p11kit") {
23565
+ const anchors = p11kitAnchorsDir();
23566
+ if (!anchors) reasons.push("ca-store-unwritable");
23567
+ else if (needsElevation(anchors)) reasons.push("elevation-required");
23342
23568
  }
23343
23569
  const manifest = !reasons.includes("tooling-missing") && !reasons.includes("manifest-dir-unwritable");
23344
- const caTrust = !reasons.includes("tooling-missing") && !reasons.includes("elevation-required");
23570
+ const caTrust = !reasons.includes("tooling-missing") && !reasons.includes("elevation-required") && !reasons.includes("ca-store-unwritable");
23345
23571
  return {
23346
23572
  manifest,
23347
23573
  caTrust,
@@ -23354,29 +23580,55 @@ var linuxAdapter = {
23354
23580
  "invalid-ca-certificate"
23355
23581
  ]);
23356
23582
  }
23357
- try {
23358
- mkdirSync(CA_DIR, { recursive: true });
23359
- } catch {
23583
+ if (caBackend() === "debian") {
23584
+ try {
23585
+ mkdirSync(CA_DIR, { recursive: true });
23586
+ } catch {
23587
+ throw new TrustError("trust.internal", "ca-store-unwritable", [
23588
+ "ca-store-unwritable"
23589
+ ]);
23590
+ }
23591
+ await sudoOrThrow(["tee", DEBIAN_CERT], certPem);
23592
+ await sudoOrThrow(["update-ca-certificates"]);
23593
+ return;
23594
+ }
23595
+ if (caBackend() !== "p11kit") {
23596
+ throw new TrustError("trust.internal", "tooling-missing", [
23597
+ "tooling-missing"
23598
+ ]);
23599
+ }
23600
+ const anchors = p11kitAnchorsDir();
23601
+ if (!anchors) {
23360
23602
  throw new TrustError("trust.internal", "ca-store-unwritable", [
23361
23603
  "ca-store-unwritable"
23362
23604
  ]);
23363
23605
  }
23364
- const certResult = await runSudo(["tee", CERT_PATH], certPem);
23365
- if (certResult.code !== 0) {
23366
- const reason = installFailureReason(certResult.stderr);
23367
- throw new TrustError("trust.internal", reason, [reason]);
23368
- }
23369
- const result = await runSudo(["update-ca-certificates"]);
23370
- if (result.code !== 0) {
23371
- const reason = installFailureReason(result.stderr);
23372
- throw new TrustError("trust.internal", reason, [reason]);
23373
- }
23606
+ const certPath = posixJoin2(anchors, CERT_NAME);
23607
+ await sudoOrThrow(["mkdir", "-p", anchors]);
23608
+ await sudoOrThrow(["tee", certPath], certPem);
23609
+ const refresh = await runSudo(["update-ca-trust"]);
23610
+ if (refresh.code === 0) return;
23611
+ await sudoOrThrow(["rm", "-f", certPath]);
23612
+ const rebuilt = await runSudo(["update-ca-trust"]);
23613
+ if (rebuilt.code !== 0) {
23614
+ const reason2 = installFailureReason(rebuilt.stderr);
23615
+ throw new TrustError("trust.internal", reason2, [reason2]);
23616
+ }
23617
+ const reason = installFailureReason(refresh.stderr);
23618
+ throw new TrustError("trust.internal", reason, [reason]);
23374
23619
  },
23375
23620
  async caTrustRemover() {
23376
23621
  try {
23377
- unlinkSync2(CERT_PATH);
23622
+ unlinkSync2(DEBIAN_CERT);
23378
23623
  } catch {
23379
23624
  }
23625
+ if (!toolOnPath("update-ca-trust")) return;
23626
+ const present = P11_CERTS.filter((path2) => exists(path2));
23627
+ if (caBackend() !== "p11kit" && present.length === 0) return;
23628
+ for (const certPath of present) {
23629
+ await sudoOrThrow(["rm", "-f", certPath]);
23630
+ }
23631
+ await sudoOrThrow(["update-ca-trust"]);
23380
23632
  },
23381
23633
  async caTrustAnchorRemover(fingerprintSha1, certPem) {
23382
23634
  void fingerprintSha1;
@@ -23543,13 +23795,15 @@ function createInstalledTrustController() {
23543
23795
  const platform = process.platform;
23544
23796
  const adapter = selectTrustPlatformAdapter(platform);
23545
23797
  const installRoot = defaultTrustInstallRoot(platform);
23798
+ const manifestDirs = adapter.manifestDirs?.() ?? [];
23546
23799
  return createRequestBodyTrustController({
23547
23800
  platform,
23548
23801
  installRoot,
23549
23802
  hostPath: join5(installRoot, "runtime-host"),
23550
23803
  hostName: "com.rogatio.runtime",
23551
23804
  allowedOrigins: [],
23552
- manifestDir: adapter.defaultManifestDir(),
23805
+ manifestDir: manifestDirs[0] ?? adapter.defaultManifestDir(),
23806
+ ...manifestDirs.length > 0 ? { manifestDirs } : {},
23553
23807
  detectCapabilities: () => adapter.detect(),
23554
23808
  caTrustInstaller: (cert) => adapter.caTrustInstaller(cert),
23555
23809
  caTrustRemover: () => adapter.caTrustRemover(),
@@ -24419,29 +24673,6 @@ async function resolveConfinedRoot(value) {
24419
24673
  await directory?.close().catch(() => void 0);
24420
24674
  }
24421
24675
  }
24422
- var MOCK_LISTENER_PREFIX = "/.rogatio/mock/";
24423
- function isSingleSegment(value) {
24424
- return value.length > 0 && value.length <= 2048 && value !== "." && value !== ".." && !value.includes("/") && !value.includes("\\") && !value.includes("?") && !hasControl(value);
24425
- }
24426
- function parseMockRedirect(rawUrl) {
24427
- const queryAt = rawUrl.indexOf("?");
24428
- const path2 = queryAt === -1 ? rawUrl : rawUrl.slice(0, queryAt);
24429
- if (!path2.startsWith(MOCK_LISTENER_PREFIX)) return null;
24430
- const rest = path2.slice(MOCK_LISTENER_PREFIX.length);
24431
- const slash = rest.indexOf("/");
24432
- if (slash <= 0) return null;
24433
- if (rest.indexOf("/", slash + 1) !== -1) return null;
24434
- let token;
24435
- let digest;
24436
- try {
24437
- token = decodeURIComponent(rest.slice(0, slash));
24438
- digest = decodeURIComponent(rest.slice(slash + 1));
24439
- } catch {
24440
- return null;
24441
- }
24442
- if (!isSingleSegment(token) || !isSingleSegment(digest)) return null;
24443
- return { token, digest };
24444
- }
24445
24676
  var REGEX_WORKER_SOURCE = `
24446
24677
  const workerThreads =
24447
24678
  typeof process.getBuiltinModule === "function"
@@ -24450,11 +24681,35 @@ const workerThreads =
24450
24681
  const { parentPort } = workerThreads;
24451
24682
  parentPort.postMessage({ type: "ready" });
24452
24683
  parentPort.on("message", (msg) => {
24453
- if (msg.type !== "replace") return;
24684
+ if (msg.type !== "replace" && msg.type !== "test" && msg.type !== "exec") return;
24454
24685
  parentPort.postMessage({ id: msg.id, type: "ack" });
24455
24686
  try {
24456
- const out = msg.text.replace(new RegExp(msg.pattern, "gu"), msg.replacement);
24457
- parentPort.postMessage({ id: msg.id, type: "result", ok: true, out });
24687
+ if (msg.type === "replace") {
24688
+ const out = msg.text.replace(new RegExp(msg.pattern, "gu"), msg.replacement);
24689
+ parentPort.postMessage({ id: msg.id, type: "result", ok: true, out });
24690
+ return;
24691
+ }
24692
+ const regex = new RegExp(msg.pattern);
24693
+ if (msg.type === "test") {
24694
+ parentPort.postMessage({
24695
+ id: msg.id,
24696
+ type: "result",
24697
+ ok: true,
24698
+ matched: regex.test(msg.text),
24699
+ });
24700
+ return;
24701
+ }
24702
+ const match = regex.exec(msg.text);
24703
+ const captures =
24704
+ match === null
24705
+ ? null
24706
+ : match.slice(1).map((part) => (part === undefined ? null : part));
24707
+ parentPort.postMessage({
24708
+ id: msg.id,
24709
+ type: "result",
24710
+ ok: true,
24711
+ captures,
24712
+ });
24458
24713
  } catch (err) {
24459
24714
  parentPort.postMessage({
24460
24715
  id: msg.id,
@@ -24470,6 +24725,7 @@ var regexWorker = null;
24470
24725
  var regexWorkerBoot = null;
24471
24726
  var nextRegexJobId = 1;
24472
24727
  var regexWorkerBootDelayForTestsMs = 0;
24728
+ var failNextRegexWorkerBootForTests = false;
24473
24729
  function discardRegexWorker(worker) {
24474
24730
  if (regexWorker === worker) {
24475
24731
  regexWorker = null;
@@ -24479,6 +24735,10 @@ function discardRegexWorker(worker) {
24479
24735
  });
24480
24736
  }
24481
24737
  function bootRegexWorker() {
24738
+ if (failNextRegexWorkerBootForTests) {
24739
+ failNextRegexWorkerBootForTests = false;
24740
+ return Promise.reject(new Error("regex-worker-boot-failed"));
24741
+ }
24482
24742
  if (regexWorker !== null) return Promise.resolve(regexWorker);
24483
24743
  if (regexWorkerBoot !== null) return regexWorkerBoot;
24484
24744
  const boot = new Promise((resolve12, reject) => {
@@ -24546,7 +24806,7 @@ function bootRegexWorker() {
24546
24806
  });
24547
24807
  return boot;
24548
24808
  }
24549
- function runOnRegexWorker(worker, id2, text, pattern, replacement, deadlineMs) {
24809
+ function runOnRegexWorker(worker, id2, type, text, pattern, replacement, deadlineMs) {
24550
24810
  return new Promise((resolve12, reject) => {
24551
24811
  let timer = null;
24552
24812
  const cleanup = () => {
@@ -24567,8 +24827,15 @@ function runOnRegexWorker(worker, id2, text, pattern, replacement, deadlineMs) {
24567
24827
  }
24568
24828
  if (msg.type === "result") {
24569
24829
  cleanup();
24570
- if (msg.ok && typeof msg.out === "string") resolve12(msg.out);
24571
- else reject(new Error(msg.error || "regex-invalid"));
24830
+ if (!msg.ok) {
24831
+ reject(new Error(msg.error || "regex-invalid"));
24832
+ return;
24833
+ }
24834
+ resolve12({
24835
+ ...typeof msg.out === "string" ? { out: msg.out } : {},
24836
+ ...typeof msg.matched === "boolean" ? { matched: msg.matched } : {},
24837
+ ...msg.captures !== void 0 ? { captures: msg.captures } : {}
24838
+ });
24572
24839
  }
24573
24840
  };
24574
24841
  const onError = (error) => {
@@ -24582,23 +24849,109 @@ function runOnRegexWorker(worker, id2, text, pattern, replacement, deadlineMs) {
24582
24849
  worker.on("message", onMessage);
24583
24850
  worker.once("error", onError);
24584
24851
  worker.once("exit", onExit);
24585
- worker.postMessage({ id: id2, type: "replace", text, pattern, replacement });
24852
+ worker.postMessage({
24853
+ id: id2,
24854
+ type,
24855
+ text,
24856
+ pattern,
24857
+ ...replacement !== void 0 ? { replacement } : {}
24858
+ });
24586
24859
  });
24587
24860
  }
24588
- function runRegexReplace(text, pattern, replacement, deadlineMs) {
24861
+ function nextJobId() {
24589
24862
  const id2 = nextRegexJobId;
24590
24863
  nextRegexJobId += 1;
24864
+ return id2;
24865
+ }
24866
+ function runRegexReplace(text, pattern, replacement, deadlineMs) {
24867
+ const id2 = nextJobId();
24591
24868
  return bootRegexWorker().then(
24592
- (worker) => runOnRegexWorker(worker, id2, text, pattern, replacement, deadlineMs),
24593
- () => {
24594
- try {
24595
- return text.replace(new RegExp(pattern, "gu"), replacement);
24596
- } catch {
24597
- throw new Error("regex-invalid");
24598
- }
24599
- }
24869
+ (worker) => runOnRegexWorker(
24870
+ worker,
24871
+ id2,
24872
+ "replace",
24873
+ text,
24874
+ pattern,
24875
+ replacement,
24876
+ deadlineMs
24877
+ ).then((result) => {
24878
+ if (typeof result.out !== "string") throw new Error("regex-invalid");
24879
+ return result.out;
24880
+ })
24881
+ );
24882
+ }
24883
+ function capturesFromWorker(captures) {
24884
+ if (captures === null || captures === void 0) return null;
24885
+ return captures.map((part) => part === null ? void 0 : part);
24886
+ }
24887
+ function runRegexTest(text, pattern, deadlineMs) {
24888
+ const id2 = nextJobId();
24889
+ return bootRegexWorker().then(
24890
+ (worker) => runOnRegexWorker(
24891
+ worker,
24892
+ id2,
24893
+ "test",
24894
+ text,
24895
+ pattern,
24896
+ void 0,
24897
+ deadlineMs
24898
+ ).then(
24899
+ (result) => result.matched === true,
24900
+ () => false
24901
+ ),
24902
+ () => false
24600
24903
  );
24601
24904
  }
24905
+ function runRegexExec(text, pattern, deadlineMs) {
24906
+ const id2 = nextJobId();
24907
+ return bootRegexWorker().then(
24908
+ (worker) => runOnRegexWorker(
24909
+ worker,
24910
+ id2,
24911
+ "exec",
24912
+ text,
24913
+ pattern,
24914
+ void 0,
24915
+ deadlineMs
24916
+ ).then(
24917
+ (result) => capturesFromWorker(result.captures),
24918
+ () => null
24919
+ ),
24920
+ () => null
24921
+ );
24922
+ }
24923
+ async function sourceMatchesWithinDeadline(source, url) {
24924
+ if (compileUrlRegex(source.value) === null) return false;
24925
+ const text = sourceMatchText(source, url);
24926
+ if (text === null) return false;
24927
+ return runRegexTest(text, source.value, RUNTIME_LIMITS.maxRegexDeadlineMs);
24928
+ }
24929
+ function matchUrlCapturesWithinDeadline(urlRegex, url) {
24930
+ return runRegexExec(url, urlRegex, RUNTIME_LIMITS.maxRegexDeadlineMs);
24931
+ }
24932
+ var MOCK_LISTENER_PREFIX = "/.rogatio/mock/";
24933
+ function isSingleSegment(value) {
24934
+ return value.length > 0 && value.length <= 2048 && value !== "." && value !== ".." && !value.includes("/") && !value.includes("\\") && !value.includes("?") && !hasControl(value);
24935
+ }
24936
+ function parseMockRedirect(rawUrl) {
24937
+ const queryAt = rawUrl.indexOf("?");
24938
+ const path2 = queryAt === -1 ? rawUrl : rawUrl.slice(0, queryAt);
24939
+ if (!path2.startsWith(MOCK_LISTENER_PREFIX)) return null;
24940
+ const rest = path2.slice(MOCK_LISTENER_PREFIX.length);
24941
+ const slash = rest.indexOf("/");
24942
+ if (slash <= 0) return null;
24943
+ if (rest.indexOf("/", slash + 1) !== -1) return null;
24944
+ let token;
24945
+ let digest;
24946
+ try {
24947
+ token = decodeURIComponent(rest.slice(0, slash));
24948
+ digest = decodeURIComponent(rest.slice(slash + 1));
24949
+ } catch {
24950
+ return null;
24951
+ }
24952
+ if (!isSingleSegment(token) || !isSingleSegment(digest)) return null;
24953
+ return { token, digest };
24954
+ }
24602
24955
  function supportedContentType(value) {
24603
24956
  if (value === void 0) return false;
24604
24957
  const mediaType = value.split(";", 1)[0]?.trim().toLowerCase() ?? "";
@@ -24630,7 +24983,7 @@ async function rewriteRequestBody(input, action, captureContext) {
24630
24983
  }
24631
24984
  outputText = action.body;
24632
24985
  if (captureContext !== void 0) {
24633
- const captures = matchUrlCaptures(
24986
+ const captures = await matchUrlCapturesWithinDeadline(
24634
24987
  captureContext.urlRegex,
24635
24988
  captureContext.url
24636
24989
  );
@@ -24870,7 +25223,7 @@ async function rewriteResponseBody(input, replacements, replaceAction, captureCo
24870
25223
  if (captureContext === void 0) {
24871
25224
  text = replaceAction.body;
24872
25225
  } else {
24873
- const captures = matchUrlCaptures(
25226
+ const captures = await matchUrlCapturesWithinDeadline(
24874
25227
  captureContext.urlRegex,
24875
25228
  captureContext.url
24876
25229
  );
@@ -24902,8 +25255,11 @@ async function rewriteResponseBody(input, replacements, replaceAction, captureCo
24902
25255
  }
24903
25256
  };
24904
25257
  }
24905
- function replaceResponseBody(body, contentType, captureContext) {
24906
- const captures = captureContext === void 0 ? [] : matchUrlCaptures(captureContext.urlRegex, captureContext.url);
25258
+ async function replaceResponseBody(body, contentType, captureContext) {
25259
+ const captures = captureContext === void 0 ? [] : await matchUrlCapturesWithinDeadline(
25260
+ captureContext.urlRegex,
25261
+ captureContext.url
25262
+ );
24907
25263
  if (captureContext !== void 0 && captures === null) {
24908
25264
  return failure("runtime.url-capture-mismatch");
24909
25265
  }
@@ -24925,7 +25281,7 @@ async function fetchAndRewriteAuthorizedResponse(operation, action, options = {}
24925
25281
  const response = await fetchAuthorized(operation, options);
24926
25282
  if (!response.ok) return response;
24927
25283
  const contentType = responseHeader(response.value.headers, "content-type");
24928
- const transformed = isReplaceAction(action) ? replaceResponseBody(
25284
+ const transformed = isReplaceAction(action) ? await replaceResponseBody(
24929
25285
  action.body,
24930
25286
  contentType,
24931
25287
  operation.sourceValue === void 0 ? void 0 : { url: operation.target, urlRegex: operation.sourceValue }
@@ -24995,7 +25351,7 @@ function projectHasRule(project, groupId, ruleId) {
24995
25351
  function isBodyOperation(operation) {
24996
25352
  return operation.kind === "request-body" || operation.kind === "response-body";
24997
25353
  }
24998
- function revalidateAuthority(project, operations, request) {
25354
+ async function revalidateAuthority(project, operations, request) {
24999
25355
  const projectSnap = snapshotOwnData3(project);
25000
25356
  if (!projectSnap.valid) return { allowed: false, reason: "project-invalid" };
25001
25357
  const opsSnap = snapshotOwnData3(operations);
@@ -25016,7 +25372,7 @@ function revalidateAuthority(project, operations, request) {
25016
25372
  return { allowed: false, reason: "project-inconsistent" };
25017
25373
  }
25018
25374
  const matcher = operation.matcher;
25019
- if (!sourceMatches(matcher.source, request.url)) {
25375
+ if (!await sourceMatchesWithinDeadline(matcher.source, request.url)) {
25020
25376
  return { allowed: false, reason: "url-mismatch" };
25021
25377
  }
25022
25378
  const requestOrigin = originOf(request.url);
@@ -25056,18 +25412,19 @@ function revalidateAuthority(project, operations, request) {
25056
25412
  function isBodyOp(op) {
25057
25413
  return op.kind === "request-body" || op.kind === "response-body";
25058
25414
  }
25059
- function matcherMatches(op, url, method) {
25060
- if (!sourceMatches(op.matcher.source, url)) return false;
25415
+ async function matcherMatches(op, url, method) {
25416
+ if (!await sourceMatchesWithinDeadline(op.matcher.source, url))
25417
+ return false;
25061
25418
  if (op.matcher.method !== void 0 && op.matcher.method !== method.toUpperCase()) {
25062
25419
  return false;
25063
25420
  }
25064
25421
  return true;
25065
25422
  }
25066
- function pickHighest(operations, kind, url, method) {
25423
+ async function pickHighest(operations, kind, url, method) {
25067
25424
  let best = null;
25068
25425
  for (const op of operations) {
25069
25426
  if (!isBodyOp(op) || op.kind !== kind) continue;
25070
- if (!matcherMatches(op, url, method)) continue;
25427
+ if (!await matcherMatches(op, url, method)) continue;
25071
25428
  if (best === null || op.matcher.priority > best.matcher.priority || op.matcher.priority === best.matcher.priority && op.ruleId.localeCompare(best.ruleId) < 0) {
25072
25429
  best = op;
25073
25430
  }
@@ -25436,17 +25793,21 @@ function initiatorFromHeaders(headers, targetUrl) {
25436
25793
  return void 0;
25437
25794
  }
25438
25795
  }
25439
- function authorizeOp(policy, op, url, method, headers) {
25796
+ async function authorizeOp(policy, op, url, method, headers) {
25440
25797
  const resourceType = op.matcher.resourceTypes[0];
25441
25798
  const initiator = initiatorFromHeaders(headers, url);
25442
- const decision = revalidateAuthority(policy.project, policy.operations, {
25443
- groupId: op.groupId,
25444
- ruleId: op.ruleId,
25445
- url,
25446
- method,
25447
- ...initiator !== void 0 ? { initiator } : {},
25448
- ...resourceType !== void 0 ? { resourceType } : {}
25449
- });
25799
+ const decision = await revalidateAuthority(
25800
+ policy.project,
25801
+ policy.operations,
25802
+ {
25803
+ groupId: op.groupId,
25804
+ ruleId: op.ruleId,
25805
+ url,
25806
+ method,
25807
+ ...initiator !== void 0 ? { initiator } : {},
25808
+ ...resourceType !== void 0 ? { resourceType } : {}
25809
+ }
25810
+ );
25450
25811
  return decision.allowed;
25451
25812
  }
25452
25813
  async function startInterceptProxy(options = {}) {
@@ -25494,13 +25855,13 @@ async function startInterceptProxy(options = {}) {
25494
25855
  }
25495
25856
  const method = (req.method ?? "GET").toUpperCase();
25496
25857
  const active = policy;
25497
- const requestOp = active === null ? null : pickHighest(
25858
+ const requestOp = active === null ? null : await pickHighest(
25498
25859
  active.operations,
25499
25860
  "request-body",
25500
25861
  targetUrl,
25501
25862
  method
25502
25863
  );
25503
- const responseOp = active === null ? null : pickHighest(
25864
+ const responseOp = active === null ? null : await pickHighest(
25504
25865
  active.operations,
25505
25866
  "response-body",
25506
25867
  targetUrl,
@@ -25520,7 +25881,13 @@ async function startInterceptProxy(options = {}) {
25520
25881
  }
25521
25882
  let requestBody = bodyResult.body;
25522
25883
  if (needsRequestBody && requestOp !== null && active !== null) {
25523
- if (!authorizeOp(active, requestOp, targetUrl, method, req.headers)) {
25884
+ if (!await authorizeOp(
25885
+ active,
25886
+ requestOp,
25887
+ targetUrl,
25888
+ method,
25889
+ req.headers
25890
+ )) {
25524
25891
  } else {
25525
25892
  const rewritten = await rewriteRequestBody(
25526
25893
  {
@@ -25569,7 +25936,13 @@ async function startInterceptProxy(options = {}) {
25569
25936
  let responseBody = upstream.body;
25570
25937
  if (responseOp !== null && active !== null) {
25571
25938
  if (responseBody.byteLength > RUNTIME_LIMITS.maxResponseBodyBytes) {
25572
- } else if (!authorizeOp(active, responseOp, targetUrl, method, req.headers)) {
25939
+ } else if (!await authorizeOp(
25940
+ active,
25941
+ responseOp,
25942
+ targetUrl,
25943
+ method,
25944
+ req.headers
25945
+ )) {
25573
25946
  } else {
25574
25947
  const action = responseOp.responseBody;
25575
25948
  const isReplace = "mode" in action && action.mode === "replace";
@@ -25693,7 +26066,10 @@ async function startInterceptProxy(options = {}) {
25693
26066
  const operation = active.operations.find(
25694
26067
  (op) => op.kind === "response-body" && op.ruleId === parsed.ruleId
25695
26068
  );
25696
- if (operation === void 0 || !sourceMatches(operation.matcher.source, parsed.originalUrl)) {
26069
+ if (operation === void 0 || !await sourceMatchesWithinDeadline(
26070
+ operation.matcher.source,
26071
+ parsed.originalUrl
26072
+ )) {
25697
26073
  res.writeHead(404);
25698
26074
  res.end();
25699
26075
  return;
@@ -25709,15 +26085,19 @@ async function startInterceptProxy(options = {}) {
25709
26085
  res.end();
25710
26086
  return;
25711
26087
  }
25712
- const allowed = revalidateAuthority(active.project, active.operations, {
25713
- groupId: operation.groupId,
25714
- ruleId: operation.ruleId,
25715
- url: parsed.originalUrl,
25716
- target,
25717
- method: "GET",
25718
- initiator: initiatorFromHeaders(req.headers, parsed.originalUrl) ?? new URL(target).origin,
25719
- resourceType: operation.matcher.resourceTypes[0]
25720
- });
26088
+ const allowed = await revalidateAuthority(
26089
+ active.project,
26090
+ active.operations,
26091
+ {
26092
+ groupId: operation.groupId,
26093
+ ruleId: operation.ruleId,
26094
+ url: parsed.originalUrl,
26095
+ target,
26096
+ method: "GET",
26097
+ initiator: initiatorFromHeaders(req.headers, parsed.originalUrl) ?? new URL(target).origin,
26098
+ resourceType: operation.matcher.resourceTypes[0]
26099
+ }
26100
+ );
25721
26101
  if (!allowed.allowed) {
25722
26102
  res.writeHead(403);
25723
26103
  res.end();
@@ -33142,10 +33522,10 @@ function reportTrust(subcommand, result, okMessage) {
33142
33522
  const reasons = result.reasons ?? ["unknown"];
33143
33523
  console.error(`trust unsupported: ${reasons.join(", ")}`);
33144
33524
  const hints = {
33145
- "tooling-missing": "Hint: install required tooling (macOS: security; Linux: update-ca-certificates; Windows: certutil is built-in)",
33146
- "manifest-dir-unwritable": "Hint: check permissions on Chrome NativeMessagingHosts directory or run with appropriate access",
33525
+ "tooling-missing": "Hint: install required tooling (macOS: security; Linux: update-ca-certificates or update-ca-trust; Windows: certutil is built-in)",
33526
+ "manifest-dir-unwritable": "Hint: check permissions on the browser NativeMessagingHosts directory (Google Chrome or Chromium) or run with appropriate access",
33147
33527
  "keychain-unwritable": "Hint: macOS login keychain not writable; try running with appropriate keychain access or use sudo for system keychain",
33148
- "ca-store-unwritable": "Hint: /usr/local/share/ca-certificates not writable; check directory permissions",
33528
+ "ca-store-unwritable": "Hint: the CA trust store is not writable, or this system has no supported anchors directory. Debian uses /usr/local/share/ca-certificates. Arch uses /etc/ca-certificates/trust-source/anchors. Fedora and RHEL use /etc/pki/ca-trust/source/anchors",
33149
33529
  "elevation-required": "Hint: device-local CA trust requires root (Linux: sudo, macOS: keychain password, Windows: Administrator). Re-run with elevated privileges"
33150
33530
  };
33151
33531
  for (const reason of reasons) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogatio/cli",
3
- "version": "6.16.4",
3
+ "version": "6.17.0",
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.",
@@ -20,9 +20,11 @@
20
20
  "files": [
21
21
  "dist"
22
22
  ],
23
+ "main": "./dist/node/index.js",
24
+ "types": "./dist/node/index.d.ts",
23
25
  "exports": {
24
26
  ".": {
25
- "types": "./src/index.ts",
27
+ "types": "./dist/node/index.d.ts",
26
28
  "import": "./dist/node/index.js"
27
29
  }
28
30
  },