@rogatio/cli 1.0.0 → 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.
package/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # @rogatio/cli
2
+
3
+ Local-first browser request & response rules — editor host, file verification, test
4
+ runner, and runtime dispatch for [Rogatio](https://github.com/drmaas/rogatio).
5
+
6
+ Rogatio keeps all rules in a single version-controlled `.rogatio.json` file. The CLI
7
+ launches the visual editor, validates and dry-runs rules offline, and controls the
8
+ optional local runtime used for mocks and response/request body rewriting.
9
+
10
+ ## Install
11
+
12
+ ```sh
13
+ npm install -g @rogatio/cli
14
+ ```
15
+
16
+ Requires Node.js 24+.
17
+
18
+ ## Usage
19
+
20
+ ```sh
21
+ rogatio <command> [options]
22
+ ```
23
+
24
+ | Command | Description |
25
+ | --- | --- |
26
+ | `rogatio edit [path]` | Launch the browser editor for `.rogatio.json`. |
27
+ | `rogatio verify [path]` | Validate a `.rogatio.json` file (schema + compiler). |
28
+ | `rogatio test [path] [url...]` | Run offline dry-run tests against `.rogatio.json`. |
29
+ | `rogatio runtime <start\|stop\|status\|install\|trust\|untrust\|uninstall>` | Control the native messaging runtime and request-body trust. |
30
+ | `rogatio runtime [path]` | Start the local mock runtime server. |
31
+
32
+ Global options: `--help, -h` and `--version, -v`. Run `rogatio <command> --help`
33
+ for command-specific usage.
34
+
35
+ ### Examples
36
+
37
+ ```sh
38
+ # Open the editor for the project file in the current directory
39
+ rogatio edit
40
+
41
+ # Validate a project file (reads stdin with '-')
42
+ rogatio verify .rogatio.json
43
+
44
+ # Dry-run rules against a set of URLs
45
+ rogatio test .rogatio.json https://example.com/ https://example.com/app.js
46
+
47
+ # Start the local mock runtime on the default port
48
+ rogatio runtime
49
+ ```
50
+
51
+ ## Exit codes
52
+
53
+ - `0` — success
54
+ - `1` — invalid project (diagnostics present) or test/validation errors
55
+ - `2` — usage or IO error
56
+
57
+ ## Related
58
+
59
+ - [Rogatio repository](https://github.com/drmaas/rogatio)
60
+ - [Chrome extension](https://github.com/drmaas/rogatio/releases) (Manifest V3)
61
+
62
+ ## License
63
+
64
+ MIT
@@ -2,11 +2,11 @@
2
2
 
3
3
  // packages/cli/src/index.ts
4
4
  import { realpathSync } from "node:fs";
5
- import { dirname as dirname6, resolve as resolve6 } from "node:path";
6
- import { fileURLToPath as fileURLToPath3 } from "node:url";
5
+ import { dirname as dirname4, resolve as resolve6 } from "node:path";
6
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
7
7
 
8
8
  // packages/cli/src/commands/edit.ts
9
- import { dirname as dirname2, resolve } from "node:path";
9
+ import { resolve } from "node:path";
10
10
  import { fileURLToPath } from "node:url";
11
11
 
12
12
  // packages/cli/src/server/http.ts
@@ -923,7 +923,6 @@ async function writeProject(path, data) {
923
923
  }
924
924
 
925
925
  // packages/cli/src/commands/edit.ts
926
- var __dirname = dirname2(fileURLToPath(import.meta.url));
927
926
  async function editCommand(args, options = {}) {
928
927
  const customLaunchBrowser = options.launchBrowser;
929
928
  const positionalArgs = [];
@@ -1183,50 +1182,47 @@ function generateEditorHtml(apiBase, csrfToken, filePath) {
1183
1182
  }
1184
1183
 
1185
1184
  // packages/cli/src/commands/runtime.ts
1186
- import { dirname as dirname4, isAbsolute, join as join3, relative, resolve as resolve3, sep as sep2 } from "node:path";
1185
+ import { dirname as dirname3, isAbsolute, join as join3, relative, resolve as resolve3, sep as sep2 } from "node:path";
1187
1186
  import { compileProject as compileProject2 } from "@rogatio/compiler";
1188
1187
 
1189
1188
  // packages/runtime/dist/node/index.js
1190
1189
  import { HTTP_METHODS as HTTP_METHODS2 } from "@rogatio/schema";
1190
+ import { hasControl } from "@rogatio/schema";
1191
1191
  import { isIP as isIP2 } from "node:net";
1192
- import { normalizeSiteOrigin } from "@rogatio/schema";
1192
+ import { hasControl as hasControl2, normalizeSiteOrigin } from "@rogatio/schema";
1193
1193
  import { normalizeSiteOrigin as normalizeSiteOrigin2 } from "@rogatio/schema";
1194
1194
  import {
1195
1195
  compileUrlRegex as compileUrlRegex2,
1196
- normalizeSiteOrigin as normalizeSiteOrigin3
1197
- } from "@rogatio/schema";
1198
- import { mkdir as mkdir2, readFile as readFile3, rename as rename2, rm, stat, writeFile as writeFile2 } from "node:fs/promises";
1199
- import { basename as basename3, dirname as dirname3, isAbsolute as isAbsolute2, join as join2, relative as relative2 } from "node:path";
1200
- import { generateKeyPairSync } from "node:crypto";
1201
- import {
1202
- compileUrlRegex as compileUrlRegex22,
1203
1196
  HTTP_METHODS as HTTP_METHODS22,
1197
+ hasControl as hasControl3,
1204
1198
  LIMITS,
1205
- normalizeSiteOrigin as normalizeSiteOrigin4,
1199
+ normalizeSiteOrigin as normalizeSiteOrigin3,
1206
1200
  RESOURCE_TYPES as RESOURCE_TYPES2
1207
1201
  } from "@rogatio/schema";
1208
1202
  import { createHash as createHash2 } from "node:crypto";
1203
+ import { formatSha256 } from "@rogatio/schema";
1209
1204
  import { LIMITS as LIMITS2 } from "@rogatio/schema";
1210
1205
  import { LIMITS as LIMITS3 } from "@rogatio/schema";
1206
+ import {
1207
+ compileUrlRegex as compileUrlRegex22,
1208
+ normalizeSiteOrigin as normalizeSiteOrigin4
1209
+ } from "@rogatio/schema";
1211
1210
  import { createServer as createServer2 } from "node:http";
1212
- import { isAbsolute as isAbsolute4 } from "node:path";
1211
+ import { isAbsolute as isAbsolute3 } from "node:path";
1213
1212
  import { createHash as createHash3, randomBytes as randomBytes3, timingSafeEqual } from "node:crypto";
1213
+ import { isSha256Digest } from "@rogatio/schema";
1214
1214
  import { randomBytes as randomBytes22 } from "node:crypto";
1215
- import { readFile as readFile22, realpath as realpath2, stat as stat2 } from "node:fs/promises";
1216
- import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve2, sep } from "node:path";
1215
+ import { readFile as readFile3, realpath as realpath2, stat } from "node:fs/promises";
1216
+ import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2, sep } from "node:path";
1217
+ import { mkdir as mkdir2, readFile as readFile22, rename as rename2, rm, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
1218
+ import { basename as basename3, dirname as dirname2, isAbsolute as isAbsolute4, join as join2, relative as relative3 } from "node:path";
1219
+ import { generateKeyPairSync as generateKeyPairSync2 } from "node:crypto";
1217
1220
  function runtimeError(code) {
1218
1221
  return { code };
1219
1222
  }
1220
1223
  function failure(code) {
1221
1224
  return { ok: false, error: runtimeError(code) };
1222
1225
  }
1223
- function hasControl(value) {
1224
- for (let index = 0; index < value.length; index += 1) {
1225
- const code = value.charCodeAt(index);
1226
- if (code <= 31 || code === 127) return true;
1227
- }
1228
- return false;
1229
- }
1230
1226
  function normalizeLogicalPath(value) {
1231
1227
  if (typeof value !== "string" || value.length === 0) return null;
1232
1228
  if (value.includes("\\") || value.includes("%") || hasControl(value)) {
@@ -1311,13 +1307,6 @@ function hasOwn(value, key) {
1311
1307
  return Object.hasOwn(value, key);
1312
1308
  }
1313
1309
  var ID_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
1314
- function hasControl2(value) {
1315
- for (let index = 0; index < value.length; index += 1) {
1316
- const code = value.charCodeAt(index);
1317
- if (code <= 32 || code === 127) return true;
1318
- }
1319
- return false;
1320
- }
1321
1310
  function hasValidPercentEncoding(value) {
1322
1311
  for (let index = 0; index < value.length; index += 1) {
1323
1312
  if (value[index] !== "%") continue;
@@ -1484,7 +1473,7 @@ var RUNTIME_LIMITS = Object.freeze({
1484
1473
  maxRegexDeadlineMs: 250,
1485
1474
  maxLocalOrigins: 32
1486
1475
  });
1487
- var F14_ENVELOPE_MAX_BYTES = 64 * 1024;
1476
+ var ENVELOPE_MAX_BYTES = 64 * 1024;
1488
1477
  var registeredProvider = null;
1489
1478
  var currentSession = null;
1490
1479
  async function startInterception(activation, policyDigest, extensionId, pacOrigins, targetPolicy) {
@@ -1617,377 +1606,81 @@ function createNativeRuntimeController(options = {}) {
1617
1606
  function getCurrentSession2() {
1618
1607
  return null;
1619
1608
  }
1620
- function createCertificate(_subjectName, _privateKey, _validityDays) {
1621
- const certPem = `-----BEGIN CERTIFICATE-----
1622
- MIIDXTCCAkWgAwIBAgIJAKoK/heBjcOuMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
1623
- BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
1624
- aWRnaXRzIFB0eSBMdGQwHhcNMTkwNTEyMDAwMDAwWhcNMjAwNTEyMDAwMDAwWjBF
1625
- MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
1626
- ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
1627
- CgKCAQEA
1628
- -----END CERTIFICATE-----
1629
- `;
1630
- const keyPem = `-----BEGIN PRIVATE KEY-----
1631
- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD
1632
- -----END PRIVATE KEY-----
1633
- `;
1634
- return { certPem, keyPem };
1609
+ var NATIVE_FRAME_MAX_BYTES = 64 * 1024;
1610
+ var NATIVE_POLICY_MAX_BYTES = 256 * 1024;
1611
+ function stringValue(value) {
1612
+ const encoded = JSON.stringify(value);
1613
+ if (encoded === void 0) throw new Error("invalid string");
1614
+ return encoded;
1635
1615
  }
1636
- function generateCaKeyPair(bits = 2048) {
1637
- return generateKeyPairSync("rsa", { modulusLength: bits });
1616
+ function arrayValue(values) {
1617
+ return `[${values.map(stringValue).join(",")}]`;
1638
1618
  }
1639
- function exportPrivateKey(key) {
1640
- return key.export({ type: "pkcs8", format: "pem" });
1619
+ function matcherValue(operation) {
1620
+ const matcher = operation.matcher;
1621
+ const method = matcher.method === void 0 ? "" : `,"method":${stringValue(matcher.method)}`;
1622
+ return `{"kind":"matcher","groupId":${stringValue(operation.groupId)},"ruleId":${stringValue(operation.ruleId)},"matcher":{"urlRegex":{"source":${stringValue(matcher.urlRegex.source)},"flags":""},"origins":${arrayValue(matcher.origins)},"resourceTypes":${arrayValue(matcher.resourceTypes)},"priority":${String(matcher.priority)}${method}}}`;
1641
1623
  }
1642
- function exportPublicKey(key) {
1643
- return key.export({ type: "spki", format: "pem" });
1624
+ function limitsValue(limits) {
1625
+ const fields = [
1626
+ ["maxPresetBytes", limits.maxPresetBytes],
1627
+ ["maxRequestLineBytes", limits.maxRequestLineBytes],
1628
+ ["maxHeaderCount", limits.maxHeaderCount],
1629
+ ["maxRequestHeaderBytes", limits.maxRequestHeaderBytes],
1630
+ ["maxControlBodyBytes", limits.maxControlBodyBytes],
1631
+ ["maxResponseHeaderBytes", limits.maxResponseHeaderBytes],
1632
+ ["maxResponseBodyBytes", limits.maxResponseBodyBytes],
1633
+ ["maxFileBytes", limits.maxFileBytes],
1634
+ ["maxConcurrentSessions", limits.maxConcurrentSessions],
1635
+ ["maxConcurrentOperations", limits.maxConcurrentOperations],
1636
+ ["maxOperationsPerSession", limits.maxOperationsPerSession],
1637
+ ["maxDnsAddresses", limits.maxDnsAddresses],
1638
+ ["bootstrapLifetimeMs", limits.bootstrapLifetimeMs],
1639
+ ["sessionLifetimeMs", limits.sessionLifetimeMs],
1640
+ ["connectTimeoutMs", limits.connectTimeoutMs],
1641
+ ["responseHeaderTimeoutMs", limits.responseHeaderTimeoutMs],
1642
+ ["bodyIdleTimeoutMs", limits.bodyIdleTimeoutMs],
1643
+ ["operationTimeoutMs", limits.operationTimeoutMs],
1644
+ ["maxRedirects", limits.maxRedirects]
1645
+ ];
1646
+ return `{${fields.map(([key, value]) => `${stringValue(key)}:${String(value)}`).join(",")}}`;
1644
1647
  }
1645
- var F16_TRUST_LIMITS = {
1646
- manifestMaxBytes: 4096,
1647
- maxAllowedOrigins: 64,
1648
- caKeyBits: 2048,
1649
- caValidityDays: 3650
1650
- };
1651
- var TrustError = class extends Error {
1652
- code;
1653
- reasons;
1654
- constructor(code, message, reasons = []) {
1655
- super(message);
1656
- this.name = "TrustError";
1657
- this.code = code;
1658
- this.reasons = reasons;
1659
- }
1660
- };
1661
- var ORIGIN_RE = /^chrome-extension:\/\/[a-p]{32}\/?$/;
1662
- var DEFAULT_CAPABILITIES = {
1663
- manifest: false,
1664
- caTrust: false,
1665
- reasons: ["no-capability-provider"]
1666
- };
1667
- function generateNativeMessagingManifest(hostPath, name, allowedOrigins, installRoot) {
1668
- if (typeof hostPath !== "string" || !isAbsolute2(hostPath)) {
1669
- throw new TrustError(
1670
- "trust.invalid-host-path",
1671
- "host path must be an absolute path"
1672
- );
1673
- }
1674
- const rel = relative2(installRoot, hostPath);
1675
- if (rel === "" || rel.startsWith("..") || isAbsolute2(rel)) {
1676
- throw new TrustError(
1677
- "trust.invalid-host-path",
1678
- "host path escapes the configured install root"
1679
- );
1680
- }
1681
- if (typeof name !== "string" || name.length === 0) {
1682
- throw new TrustError("trust.invalid-manifest", "host name is required");
1683
- }
1684
- if (!Array.isArray(allowedOrigins)) {
1685
- throw new TrustError(
1686
- "trust.invalid-manifest",
1687
- "allowed_origins must be an array"
1688
- );
1689
- }
1690
- if (allowedOrigins.length > F16_TRUST_LIMITS.maxAllowedOrigins) {
1691
- throw new TrustError(
1692
- "trust.invalid-manifest",
1693
- "allowed_origins exceeds the configured maximum"
1694
- );
1695
- }
1696
- const seen = /* @__PURE__ */ new Set();
1697
- for (const origin of allowedOrigins) {
1698
- if (typeof origin !== "string" || !ORIGIN_RE.test(origin)) {
1699
- throw new TrustError(
1700
- "trust.invalid-origin",
1701
- `invalid allowed origin: ${String(origin)}`
1702
- );
1703
- }
1704
- seen.add(origin.endsWith("/") ? origin : `${origin}/`);
1705
- }
1706
- return {
1707
- name,
1708
- description: "Rogatio request-body native runtime host",
1709
- path: hostPath,
1710
- type: "stdio",
1711
- allowed_origins: [...seen].sort()
1712
- };
1648
+ function grantValue(grant) {
1649
+ return `{"groupId":${stringValue(grant.groupId)},"ruleId":${stringValue(grant.ruleId)},"operationId":${stringValue(grant.operationId)},"kind":${stringValue(grant.kind)},"target":${stringValue(grant.target)},"method":${stringValue(grant.method)}}`;
1713
1650
  }
1714
- function detectTrustCapabilities(options = {}) {
1715
- void options;
1716
- return { ...DEFAULT_CAPABILITIES };
1651
+ function mockHeaderValue(header2) {
1652
+ return `{"name":${stringValue(header2.name)},"value":${stringValue(header2.value)}}`;
1717
1653
  }
1718
- function defaultTrustInstallRoot(platform) {
1719
- if (platform === "darwin") return "/Applications/Rogatio";
1720
- if (platform === "win32") {
1721
- return join2(process.env.LOCALAPPDATA ?? "", "Rogatio");
1722
- }
1723
- return join2(process.env.HOME ?? "", ".local", "share", "rogatio");
1654
+ function mockValue(mock) {
1655
+ const headers = mock.headers === void 0 ? "" : `,"headers":[${mock.headers.map(mockHeaderValue).join(",")}]`;
1656
+ const delay = mock.delayMs === void 0 ? "" : `,"delayMs":${String(mock.delayMs)}`;
1657
+ const body = mock.body !== void 0 ? `,"body":${stringValue(mock.body)}` : "";
1658
+ const file = mock.file !== void 0 ? `,"file":${stringValue(mock.file)}` : "";
1659
+ return `{"ruleId":${stringValue(mock.ruleId)},"status":${String(mock.status)}${headers}${delay}${body}${file}}`;
1724
1660
  }
1725
- function isWellFormedManifest(value) {
1726
- if (typeof value !== "object" || value === null) return false;
1727
- const m = value;
1728
- 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");
1661
+ function sortMocks(mocks) {
1662
+ return [...mocks].sort(
1663
+ (left, right) => compareStrings(left.ruleId, right.ruleId)
1664
+ );
1729
1665
  }
1730
- async function writeFileAtomic(path, data) {
1731
- const dir = dirname3(path);
1732
- await mkdir2(dir, { recursive: true });
1733
- const tmp = join2(dir, `.${basename3(path)}.${process.pid}.tmp`);
1734
- await writeFile2(tmp, data, "utf8");
1735
- await rename2(tmp, path);
1666
+ function compareStrings(left, right) {
1667
+ if (left < right) return -1;
1668
+ if (left > right) return 1;
1669
+ return 0;
1736
1670
  }
1737
- async function existsFile(path) {
1738
- try {
1739
- return (await stat(path)).isFile();
1740
- } catch {
1741
- return false;
1742
- }
1671
+ function sortGrants(grants) {
1672
+ return [...grants].sort(
1673
+ (left, right) => compareStrings(left.groupId, right.groupId) || compareStrings(left.ruleId, right.ruleId) || compareStrings(left.operationId, right.operationId) || compareStrings(left.kind, right.kind) || compareStrings(left.target, right.target) || compareStrings(left.method, right.method)
1674
+ );
1743
1675
  }
1744
- function codeOf(error, fallback) {
1745
- return error instanceof TrustError ? error.code : fallback;
1676
+ function canonicalPresetBytes(preset, grants = preset.grants) {
1677
+ const matchers = preset.matchers.map(matcherValue).join(",");
1678
+ const mocks = preset.mocks === void 0 || preset.mocks.length === 0 ? "" : `,"mocks":[${sortMocks(preset.mocks).map(mockValue).join(",")}]`;
1679
+ const canonical = `{"version":1,"limits":${limitsValue(preset.limits)},"matchers":[${matchers}],"grants":[${sortGrants(grants).map(grantValue).join(",")}]${mocks}}`;
1680
+ return new TextEncoder().encode(canonical);
1746
1681
  }
1747
- function unsupportedResult(caps) {
1748
- return { ok: false, state: "unsupported", reasons: caps.reasons };
1749
- }
1750
- function createRequestBodyTrustController(options = {}) {
1751
- const platform = options.platform ?? process.platform;
1752
- const hostName = options.hostName ?? "com.rogatio.runtime";
1753
- const _allowedOrigins = options.allowedOrigins ?? [];
1754
- const installRoot = options.installRoot ?? defaultTrustInstallRoot(platform);
1755
- const hostPath = options.hostPath ?? join2(installRoot, "runtime-host");
1756
- const manifestDir = options.manifestDir ?? installRoot;
1757
- const caKeyFile = join2(
1758
- installRoot,
1759
- options.caKeyFileName ?? ".rogatio-ca.key"
1760
- );
1761
- const caPubFile = join2(
1762
- installRoot,
1763
- options.caPubFileName ?? ".rogatio-ca.pub"
1764
- );
1765
- const detect = options.detectCapabilities ?? detectTrustCapabilities;
1766
- const caTrustInstaller = options.caTrustInstaller;
1767
- const caTrustRemover = options.caTrustRemover;
1768
- const caCertFile = join2(
1769
- installRoot,
1770
- options.caCertFileName ?? ".rogatio-ca.crt"
1771
- );
1772
- const manifestPath = () => join2(manifestDir, `${hostName}.json`);
1773
- let installerCalled = false;
1774
- async function install(extensionId) {
1775
- if (!extensionId || !/^[a-p]{32}$/.test(extensionId)) {
1776
- return {
1777
- ok: false,
1778
- state: "unsupported",
1779
- reasons: ["invalid-extension-id"]
1780
- };
1781
- }
1782
- const allowedOrigins = [`chrome-extension://${extensionId}/`];
1783
- let manifest;
1784
- try {
1785
- manifest = generateNativeMessagingManifest(
1786
- hostPath,
1787
- hostName,
1788
- allowedOrigins,
1789
- installRoot
1790
- );
1791
- } catch (error) {
1792
- return {
1793
- ok: false,
1794
- state: "unsupported",
1795
- reasons: [codeOf(error, "trust.invalid-manifest")]
1796
- };
1797
- }
1798
- const data = JSON.stringify(manifest, null, 2);
1799
- if (data.length > F16_TRUST_LIMITS.manifestMaxBytes) {
1800
- return {
1801
- ok: false,
1802
- state: "unsupported",
1803
- reasons: ["manifest-too-large"]
1804
- };
1805
- }
1806
- const caps = await detect();
1807
- if (!caps.manifest) return unsupportedResult(caps);
1808
- try {
1809
- await writeFileAtomic(manifestPath(), data);
1810
- } catch (error) {
1811
- return {
1812
- ok: false,
1813
- state: "unsupported",
1814
- reasons: [codeOf(error, "trust.write-failed")]
1815
- };
1816
- }
1817
- return { ok: true, state: "installed" };
1818
- }
1819
- async function uninstall() {
1820
- try {
1821
- await rm(manifestPath(), { force: true });
1822
- } catch (error) {
1823
- return {
1824
- ok: false,
1825
- state: "unsupported",
1826
- reasons: [codeOf(error, "trust.write-failed")]
1827
- };
1828
- }
1829
- return { ok: true, state: "uninstalled" };
1830
- }
1831
- async function trust() {
1832
- const caps = await detect();
1833
- if (!caps.caTrust) return unsupportedResult(caps);
1834
- try {
1835
- if (!await existsFile(caKeyFile) || !await existsFile(caCertFile)) {
1836
- const { privateKey, publicKey } = generateCaKeyPair(
1837
- F16_TRUST_LIMITS.caKeyBits
1838
- );
1839
- const _privateKeyPem = exportPrivateKey(privateKey);
1840
- const _pubPem = exportPublicKey(publicKey);
1841
- const certResult = createCertificate(
1842
- "CN=Rogatio Request-Body CA",
1843
- privateKey,
1844
- F16_TRUST_LIMITS.caValidityDays
1845
- );
1846
- const certPem = certResult.certPem;
1847
- const certKeyPem = certResult.keyPem;
1848
- await writeFileAtomic(caKeyFile, certKeyPem);
1849
- await writeFileAtomic(caPubFile, certPem);
1850
- await writeFileAtomic(caCertFile, certPem);
1851
- }
1852
- if (caTrustInstaller && !installerCalled) {
1853
- await caTrustInstaller(await readFile3(caCertFile, "utf8"));
1854
- installerCalled = true;
1855
- }
1856
- } catch (error) {
1857
- return {
1858
- ok: false,
1859
- state: "unsupported",
1860
- reasons: [codeOf(error, "trust.internal")]
1861
- };
1862
- }
1863
- return { ok: true, state: "trusted" };
1864
- }
1865
- async function untrust() {
1866
- try {
1867
- if (await existsFile(caKeyFile) && caTrustRemover) {
1868
- await caTrustRemover();
1869
- }
1870
- await rm(caKeyFile, { force: true });
1871
- await rm(caPubFile, { force: true });
1872
- await rm(caCertFile, { force: true });
1873
- installerCalled = false;
1874
- } catch (error) {
1875
- return {
1876
- ok: false,
1877
- state: "unsupported",
1878
- reasons: [codeOf(error, "trust.internal")]
1879
- };
1880
- }
1881
- return { ok: true, state: "untrusted" };
1882
- }
1883
- async function status() {
1884
- let installed = false;
1885
- try {
1886
- const raw = await readFile3(manifestPath(), "utf8");
1887
- installed = isWellFormedManifest(JSON.parse(raw));
1888
- } catch {
1889
- installed = false;
1890
- }
1891
- const trusted = await existsFile(caKeyFile) && await existsFile(caCertFile);
1892
- const caps = await detect();
1893
- return {
1894
- installed,
1895
- trusted,
1896
- platform,
1897
- capabilityReasons: caps.reasons
1898
- };
1899
- }
1900
- return { install, uninstall, trust, untrust, status };
1901
- }
1902
- var NATIVE_FRAME_MAX_BYTES = 64 * 1024;
1903
- var NATIVE_POLICY_MAX_BYTES = 256 * 1024;
1904
- var PRIVATE_RANGES = [
1905
- { start: ipToInt("10.0.0.0"), end: ipToInt("10.255.255.255") },
1906
- { start: ipToInt("172.16.0.0"), end: ipToInt("172.31.255.255") },
1907
- { start: ipToInt("192.168.0.0"), end: ipToInt("192.168.255.255") },
1908
- { start: ipToInt("127.0.0.0"), end: ipToInt("127.255.255.255") },
1909
- { start: ipToInt("169.254.0.0"), end: ipToInt("169.254.255.255") },
1910
- { start: ipToInt("0.0.0.0"), end: ipToInt("0.255.255.255") },
1911
- { start: ipToInt("224.0.0.0"), end: ipToInt("239.255.255.255") },
1912
- { start: ipToInt("240.0.0.0"), end: ipToInt("255.255.255.255") }
1913
- ];
1914
- function ipToInt(ip) {
1915
- const parts = ip.split(".").map(Number);
1916
- return parts[0] << 24 | parts[1] << 16 | parts[2] << 8 | parts[3];
1917
- }
1918
- function stringValue(value) {
1919
- const encoded = JSON.stringify(value);
1920
- if (encoded === void 0) throw new Error("invalid string");
1921
- return encoded;
1922
- }
1923
- function arrayValue(values) {
1924
- return `[${values.map(stringValue).join(",")}]`;
1925
- }
1926
- function matcherValue(operation) {
1927
- const matcher = operation.matcher;
1928
- const method = matcher.method === void 0 ? "" : `,"method":${stringValue(matcher.method)}`;
1929
- return `{"kind":"matcher","groupId":${stringValue(operation.groupId)},"ruleId":${stringValue(operation.ruleId)},"matcher":{"urlRegex":{"source":${stringValue(matcher.urlRegex.source)},"flags":""},"origins":${arrayValue(matcher.origins)},"resourceTypes":${arrayValue(matcher.resourceTypes)},"priority":${String(matcher.priority)}${method}}}`;
1930
- }
1931
- function limitsValue(limits) {
1932
- const fields = [
1933
- ["maxPresetBytes", limits.maxPresetBytes],
1934
- ["maxRequestLineBytes", limits.maxRequestLineBytes],
1935
- ["maxHeaderCount", limits.maxHeaderCount],
1936
- ["maxRequestHeaderBytes", limits.maxRequestHeaderBytes],
1937
- ["maxControlBodyBytes", limits.maxControlBodyBytes],
1938
- ["maxResponseHeaderBytes", limits.maxResponseHeaderBytes],
1939
- ["maxResponseBodyBytes", limits.maxResponseBodyBytes],
1940
- ["maxFileBytes", limits.maxFileBytes],
1941
- ["maxConcurrentSessions", limits.maxConcurrentSessions],
1942
- ["maxConcurrentOperations", limits.maxConcurrentOperations],
1943
- ["maxOperationsPerSession", limits.maxOperationsPerSession],
1944
- ["maxDnsAddresses", limits.maxDnsAddresses],
1945
- ["bootstrapLifetimeMs", limits.bootstrapLifetimeMs],
1946
- ["sessionLifetimeMs", limits.sessionLifetimeMs],
1947
- ["connectTimeoutMs", limits.connectTimeoutMs],
1948
- ["responseHeaderTimeoutMs", limits.responseHeaderTimeoutMs],
1949
- ["bodyIdleTimeoutMs", limits.bodyIdleTimeoutMs],
1950
- ["operationTimeoutMs", limits.operationTimeoutMs],
1951
- ["maxRedirects", limits.maxRedirects]
1952
- ];
1953
- return `{${fields.map(([key, value]) => `${stringValue(key)}:${String(value)}`).join(",")}}`;
1954
- }
1955
- function grantValue(grant) {
1956
- return `{"groupId":${stringValue(grant.groupId)},"ruleId":${stringValue(grant.ruleId)},"operationId":${stringValue(grant.operationId)},"kind":${stringValue(grant.kind)},"target":${stringValue(grant.target)},"method":${stringValue(grant.method)}}`;
1957
- }
1958
- function mockHeaderValue(header2) {
1959
- return `{"name":${stringValue(header2.name)},"value":${stringValue(header2.value)}}`;
1960
- }
1961
- function mockValue(mock) {
1962
- const headers = mock.headers === void 0 ? "" : `,"headers":[${mock.headers.map(mockHeaderValue).join(",")}]`;
1963
- const delay = mock.delayMs === void 0 ? "" : `,"delayMs":${String(mock.delayMs)}`;
1964
- const body = mock.body !== void 0 ? `,"body":${stringValue(mock.body)}` : "";
1965
- const file = mock.file !== void 0 ? `,"file":${stringValue(mock.file)}` : "";
1966
- return `{"ruleId":${stringValue(mock.ruleId)},"status":${String(mock.status)}${headers}${delay}${body}${file}}`;
1967
- }
1968
- function sortMocks(mocks) {
1969
- return [...mocks].sort(
1970
- (left, right) => compareStrings(left.ruleId, right.ruleId)
1971
- );
1972
- }
1973
- function compareStrings(left, right) {
1974
- if (left < right) return -1;
1975
- if (left > right) return 1;
1976
- return 0;
1977
- }
1978
- function sortGrants(grants) {
1979
- return [...grants].sort(
1980
- (left, right) => compareStrings(left.groupId, right.groupId) || compareStrings(left.ruleId, right.ruleId) || compareStrings(left.operationId, right.operationId) || compareStrings(left.kind, right.kind) || compareStrings(left.target, right.target) || compareStrings(left.method, right.method)
1981
- );
1982
- }
1983
- function canonicalPresetBytes(preset, grants = preset.grants) {
1984
- const matchers = preset.matchers.map(matcherValue).join(",");
1985
- const mocks = preset.mocks === void 0 || preset.mocks.length === 0 ? "" : `,"mocks":[${sortMocks(preset.mocks).map(mockValue).join(",")}]`;
1986
- const canonical = `{"version":1,"limits":${limitsValue(preset.limits)},"matchers":[${matchers}],"grants":[${sortGrants(grants).map(grantValue).join(",")}]${mocks}}`;
1987
- return new TextEncoder().encode(canonical);
1988
- }
1989
- function digestBytes(bytes) {
1990
- return `sha256:${createHash2("sha256").update(bytes).digest("hex")}`;
1682
+ function digestBytes(bytes) {
1683
+ return formatSha256(createHash2("sha256").update(bytes).digest("hex"));
1991
1684
  }
1992
1685
  function canonicalDescriptor(value) {
1993
1686
  return `{"groupId":${stringValue(value.groupId)},"ruleId":${stringValue(value.ruleId)},"operationId":${stringValue(value.operationId)},"kind":${stringValue(value.kind)},"target":${stringValue(value.target)},"method":${stringValue(value.method)}}`;
@@ -2003,13 +1696,6 @@ function validId2(value) {
2003
1696
  function validMethod2(value) {
2004
1697
  return typeof value === "string" && HTTP_METHODS22.includes(value);
2005
1698
  }
2006
- function hasControl3(value) {
2007
- for (let index = 0; index < value.length; index += 1) {
2008
- const code = value.charCodeAt(index);
2009
- if (code <= 31 || code === 127) return true;
2010
- }
2011
- return false;
2012
- }
2013
1699
  function normalizeMockHeader(value) {
2014
1700
  if (value === null || typeof value !== "object" || Array.isArray(value))
2015
1701
  return null;
@@ -2128,14 +1814,14 @@ function normalizeMatcher(value) {
2128
1814
  return null;
2129
1815
  }
2130
1816
  const regex = regexValue;
2131
- if (typeof regex.source !== "string" || regex.source.length > LIMITS.maxUrlRegexLength || regex.flags !== "" || compileUrlRegex22(regex.source) === null) {
1817
+ if (typeof regex.source !== "string" || regex.source.length > LIMITS.maxUrlRegexLength || regex.flags !== "" || compileUrlRegex2(regex.source) === null) {
2132
1818
  return null;
2133
1819
  }
2134
1820
  if (!Array.isArray(matcher.origins) || matcher.origins.length === 0 || matcher.origins.length > LIMITS.maxOriginsPerScope)
2135
1821
  return null;
2136
1822
  const origins = [];
2137
1823
  for (const value2 of matcher.origins) {
2138
- const normalized = normalizeSiteOrigin4(value2);
1824
+ const normalized = normalizeSiteOrigin3(value2);
2139
1825
  if (normalized === null || origins.includes(normalized)) return null;
2140
1826
  origins.push(normalized);
2141
1827
  }
@@ -2286,6 +1972,20 @@ function normalizeRuntimePreset(value) {
2286
1972
  });
2287
1973
  return { ok: true, value: normalized };
2288
1974
  }
1975
+ var PRIVATE_RANGES = [
1976
+ { start: ipToInt("10.0.0.0"), end: ipToInt("10.255.255.255") },
1977
+ { start: ipToInt("172.16.0.0"), end: ipToInt("172.31.255.255") },
1978
+ { start: ipToInt("192.168.0.0"), end: ipToInt("192.168.255.255") },
1979
+ { start: ipToInt("127.0.0.0"), end: ipToInt("127.255.255.255") },
1980
+ { start: ipToInt("169.254.0.0"), end: ipToInt("169.254.255.255") },
1981
+ { start: ipToInt("0.0.0.0"), end: ipToInt("0.255.255.255") },
1982
+ { start: ipToInt("224.0.0.0"), end: ipToInt("239.255.255.255") },
1983
+ { start: ipToInt("240.0.0.0"), end: ipToInt("255.255.255.255") }
1984
+ ];
1985
+ function ipToInt(ip) {
1986
+ const parts = ip.split(".").map(Number);
1987
+ return parts[0] << 24 | parts[1] << 16 | parts[2] << 8 | parts[3];
1988
+ }
2289
1989
  var TOKEN_BYTES = 32;
2290
1990
  var TOKEN_LENGTH = 43;
2291
1991
  function createCapabilityState(preset, now) {
@@ -2317,8 +2017,7 @@ function sameToken(value, expectedDigest) {
2317
2017
  return actual.length === expectedDigest.length && timingSafeEqual(actual, expectedDigest);
2318
2018
  }
2319
2019
  function sameDigest(value, expected) {
2320
- if (!/^sha256:[0-9a-f]{64}$/.test(value) || value.length !== expected.length)
2321
- return false;
2020
+ if (!isSha256Digest(value) || value.length !== expected.length) return false;
2322
2021
  const actual = Buffer.from(value, "ascii");
2323
2022
  const wanted = Buffer.from(expected, "ascii");
2324
2023
  return timingSafeEqual(actual, wanted);
@@ -2386,8 +2085,8 @@ function closeCapabilityState(state) {
2386
2085
  state.bootstrapDigest.fill(0);
2387
2086
  }
2388
2087
  function withinRoot2(root, candidate) {
2389
- const rest = relative3(root, candidate);
2390
- return rest.length > 0 && rest !== ".." && !rest.startsWith(`..${sep}`) && !isAbsolute3(rest);
2088
+ const rest = relative2(root, candidate);
2089
+ return rest.length > 0 && rest !== ".." && !rest.startsWith(`..${sep}`) && !isAbsolute2(rest);
2391
2090
  }
2392
2091
  async function readMockFile(root, logicalPath) {
2393
2092
  const normalized = normalizeLogicalPath(logicalPath);
@@ -2398,11 +2097,11 @@ async function readMockFile(root, logicalPath) {
2398
2097
  const actualPath = await realpath2(candidate);
2399
2098
  if (!withinRoot2(canonicalRoot, actualPath))
2400
2099
  return failure("runtime.file-denied");
2401
- const metadata = await stat2(actualPath);
2100
+ const metadata = await stat(actualPath);
2402
2101
  if (!metadata.isFile()) return failure("runtime.file-denied");
2403
2102
  if (metadata.size > RUNTIME_LIMITS.maxFileBytes)
2404
2103
  return failure("runtime.size-limit");
2405
- const bytes = await readFile22(actualPath);
2104
+ const bytes = await readFile3(actualPath);
2406
2105
  if (bytes.byteLength > RUNTIME_LIMITS.maxFileBytes)
2407
2106
  return failure("runtime.size-limit");
2408
2107
  new TextDecoder("utf-8", { fatal: true }).decode(bytes);
@@ -2663,9 +2362,9 @@ function statusForError(code) {
2663
2362
  var PAIR_PATH = "/v1/pair";
2664
2363
  var AUTHORIZE_PATH = "/v1/authorize";
2665
2364
  var CONNECTION_PATH = "/v1/connection";
2666
- var MOCK_PROTOCOL = "f13-v1";
2365
+ var MOCK_PROTOCOL = "v1";
2667
2366
  function isTrustedRoot(value) {
2668
- if (typeof value !== "string" || !isAbsolute4(value)) return false;
2367
+ if (typeof value !== "string" || !isAbsolute3(value)) return false;
2669
2368
  for (let index = 0; index < value.length; index += 1) {
2670
2369
  if (value.charCodeAt(index) <= 31 || value.charCodeAt(index) === 127)
2671
2370
  return false;
@@ -2827,7 +2526,7 @@ async function createRuntimeServer(options) {
2827
2526
  }
2828
2527
  sendJson(response, 200, {
2829
2528
  ok: true,
2830
- protocol: "f6-v1",
2529
+ protocol: "v1",
2831
2530
  sessionCapability: result.value.sessionCapability,
2832
2531
  expiresInMs: result.value.expiresInMs
2833
2532
  });
@@ -2936,6 +2635,277 @@ async function createRuntimeServer(options) {
2936
2635
  return failure("runtime.local-bind-denied");
2937
2636
  }
2938
2637
  }
2638
+ function createCertificate(_subjectName, _privateKey, _validityDays) {
2639
+ const certPem = `-----BEGIN CERTIFICATE-----
2640
+ MIIDXTCCAkWgAwIBAgIJAKoK/heBjcOuMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
2641
+ BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
2642
+ aWRnaXRzIFB0eSBMdGQwHhcNMTkwNTEyMDAwMDAwWhcNMjAwNTEyMDAwMDAwWjBF
2643
+ MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
2644
+ ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
2645
+ CgKCAQEA
2646
+ -----END CERTIFICATE-----
2647
+ `;
2648
+ const keyPem = `-----BEGIN PRIVATE KEY-----
2649
+ MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD
2650
+ -----END PRIVATE KEY-----
2651
+ `;
2652
+ return { certPem, keyPem };
2653
+ }
2654
+ function generateCaKeyPair(bits = 2048) {
2655
+ return generateKeyPairSync2("rsa", { modulusLength: bits });
2656
+ }
2657
+ var TRUST_LIMITS = {
2658
+ manifestMaxBytes: 4096,
2659
+ maxAllowedOrigins: 64,
2660
+ caKeyBits: 2048,
2661
+ caValidityDays: 3650
2662
+ };
2663
+ var TrustError = class extends Error {
2664
+ code;
2665
+ reasons;
2666
+ constructor(code, message, reasons = []) {
2667
+ super(message);
2668
+ this.name = "TrustError";
2669
+ this.code = code;
2670
+ this.reasons = reasons;
2671
+ }
2672
+ };
2673
+ var ORIGIN_RE = /^chrome-extension:\/\/[a-p]{32}\/?$/;
2674
+ var DEFAULT_CAPABILITIES = {
2675
+ manifest: false,
2676
+ caTrust: false,
2677
+ reasons: ["no-capability-provider"]
2678
+ };
2679
+ function generateNativeMessagingManifest(hostPath, name, allowedOrigins, installRoot) {
2680
+ if (typeof hostPath !== "string" || !isAbsolute4(hostPath)) {
2681
+ throw new TrustError(
2682
+ "trust.invalid-host-path",
2683
+ "host path must be an absolute path"
2684
+ );
2685
+ }
2686
+ const rel = relative3(installRoot, hostPath);
2687
+ if (rel === "" || rel.startsWith("..") || isAbsolute4(rel)) {
2688
+ throw new TrustError(
2689
+ "trust.invalid-host-path",
2690
+ "host path escapes the configured install root"
2691
+ );
2692
+ }
2693
+ if (typeof name !== "string" || name.length === 0) {
2694
+ throw new TrustError("trust.invalid-manifest", "host name is required");
2695
+ }
2696
+ if (!Array.isArray(allowedOrigins)) {
2697
+ throw new TrustError(
2698
+ "trust.invalid-manifest",
2699
+ "allowed_origins must be an array"
2700
+ );
2701
+ }
2702
+ if (allowedOrigins.length > TRUST_LIMITS.maxAllowedOrigins) {
2703
+ throw new TrustError(
2704
+ "trust.invalid-manifest",
2705
+ "allowed_origins exceeds the configured maximum"
2706
+ );
2707
+ }
2708
+ const seen = /* @__PURE__ */ new Set();
2709
+ for (const origin of allowedOrigins) {
2710
+ if (typeof origin !== "string" || !ORIGIN_RE.test(origin)) {
2711
+ throw new TrustError(
2712
+ "trust.invalid-origin",
2713
+ `invalid allowed origin: ${String(origin)}`
2714
+ );
2715
+ }
2716
+ seen.add(origin.endsWith("/") ? origin : `${origin}/`);
2717
+ }
2718
+ return {
2719
+ name,
2720
+ description: "Rogatio request-body native runtime host",
2721
+ path: hostPath,
2722
+ type: "stdio",
2723
+ allowed_origins: [...seen].sort()
2724
+ };
2725
+ }
2726
+ function detectTrustCapabilities(options = {}) {
2727
+ void options;
2728
+ return { ...DEFAULT_CAPABILITIES };
2729
+ }
2730
+ function defaultTrustInstallRoot(platform) {
2731
+ if (platform === "darwin") return "/Applications/Rogatio";
2732
+ if (platform === "win32") {
2733
+ return join2(process.env.LOCALAPPDATA ?? "", "Rogatio");
2734
+ }
2735
+ return join2(process.env.HOME ?? "", ".local", "share", "rogatio");
2736
+ }
2737
+ function isWellFormedManifest(value) {
2738
+ if (typeof value !== "object" || value === null) return false;
2739
+ const m = value;
2740
+ 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");
2741
+ }
2742
+ async function writeFileAtomic(path, data) {
2743
+ const dir = dirname2(path);
2744
+ await mkdir2(dir, { recursive: true });
2745
+ const tmp = join2(dir, `.${basename3(path)}.${process.pid}.tmp`);
2746
+ await writeFile2(tmp, data, "utf8");
2747
+ await rename2(tmp, path);
2748
+ }
2749
+ async function existsFile(path) {
2750
+ try {
2751
+ return (await stat2(path)).isFile();
2752
+ } catch {
2753
+ return false;
2754
+ }
2755
+ }
2756
+ function codeOf(error, fallback) {
2757
+ return error instanceof TrustError ? error.code : fallback;
2758
+ }
2759
+ function unsupportedResult(caps) {
2760
+ return { ok: false, state: "unsupported", reasons: caps.reasons };
2761
+ }
2762
+ function createRequestBodyTrustController(options = {}) {
2763
+ const platform = options.platform ?? process.platform;
2764
+ const hostName = options.hostName ?? "com.rogatio.runtime";
2765
+ const installRoot = options.installRoot ?? defaultTrustInstallRoot(platform);
2766
+ const hostPath = options.hostPath ?? join2(installRoot, "runtime-host");
2767
+ const manifestDir = options.manifestDir ?? installRoot;
2768
+ const caKeyFile = join2(
2769
+ installRoot,
2770
+ options.caKeyFileName ?? ".rogatio-ca.key"
2771
+ );
2772
+ const caPubFile = join2(
2773
+ installRoot,
2774
+ options.caPubFileName ?? ".rogatio-ca.pub"
2775
+ );
2776
+ const detect = options.detectCapabilities ?? detectTrustCapabilities;
2777
+ const caTrustInstaller = options.caTrustInstaller;
2778
+ const caTrustRemover = options.caTrustRemover;
2779
+ const caCertFile = join2(
2780
+ installRoot,
2781
+ options.caCertFileName ?? ".rogatio-ca.crt"
2782
+ );
2783
+ const manifestPath = () => join2(manifestDir, `${hostName}.json`);
2784
+ let installerCalled = false;
2785
+ async function install(extensionId) {
2786
+ if (!extensionId || !/^[a-p]{32}$/.test(extensionId)) {
2787
+ return {
2788
+ ok: false,
2789
+ state: "unsupported",
2790
+ reasons: ["invalid-extension-id"]
2791
+ };
2792
+ }
2793
+ const allowedOrigins = [`chrome-extension://${extensionId}/`];
2794
+ let manifest;
2795
+ try {
2796
+ manifest = generateNativeMessagingManifest(
2797
+ hostPath,
2798
+ hostName,
2799
+ allowedOrigins,
2800
+ installRoot
2801
+ );
2802
+ } catch (error) {
2803
+ return {
2804
+ ok: false,
2805
+ state: "unsupported",
2806
+ reasons: [codeOf(error, "trust.invalid-manifest")]
2807
+ };
2808
+ }
2809
+ const data = JSON.stringify(manifest, null, 2);
2810
+ if (data.length > TRUST_LIMITS.manifestMaxBytes) {
2811
+ return {
2812
+ ok: false,
2813
+ state: "unsupported",
2814
+ reasons: ["manifest-too-large"]
2815
+ };
2816
+ }
2817
+ const caps = await detect();
2818
+ if (!caps.manifest) return unsupportedResult(caps);
2819
+ try {
2820
+ await writeFileAtomic(manifestPath(), data);
2821
+ } catch (error) {
2822
+ return {
2823
+ ok: false,
2824
+ state: "unsupported",
2825
+ reasons: [codeOf(error, "trust.write-failed")]
2826
+ };
2827
+ }
2828
+ return { ok: true, state: "installed" };
2829
+ }
2830
+ async function uninstall() {
2831
+ try {
2832
+ await rm(manifestPath(), { force: true });
2833
+ } catch (error) {
2834
+ return {
2835
+ ok: false,
2836
+ state: "unsupported",
2837
+ reasons: [codeOf(error, "trust.write-failed")]
2838
+ };
2839
+ }
2840
+ return { ok: true, state: "uninstalled" };
2841
+ }
2842
+ async function trust() {
2843
+ const caps = await detect();
2844
+ if (!caps.caTrust) return unsupportedResult(caps);
2845
+ try {
2846
+ if (!await existsFile(caKeyFile) || !await existsFile(caCertFile)) {
2847
+ const { privateKey } = generateCaKeyPair(TRUST_LIMITS.caKeyBits);
2848
+ const certResult = createCertificate(
2849
+ "CN=Rogatio Request-Body CA",
2850
+ privateKey,
2851
+ TRUST_LIMITS.caValidityDays
2852
+ );
2853
+ const certPem = certResult.certPem;
2854
+ const certKeyPem = certResult.keyPem;
2855
+ await writeFileAtomic(caKeyFile, certKeyPem);
2856
+ await writeFileAtomic(caPubFile, certPem);
2857
+ await writeFileAtomic(caCertFile, certPem);
2858
+ }
2859
+ if (caTrustInstaller && !installerCalled) {
2860
+ await caTrustInstaller(await readFile22(caCertFile, "utf8"));
2861
+ installerCalled = true;
2862
+ }
2863
+ } catch (error) {
2864
+ return {
2865
+ ok: false,
2866
+ state: "unsupported",
2867
+ reasons: [codeOf(error, "trust.internal")]
2868
+ };
2869
+ }
2870
+ return { ok: true, state: "trusted" };
2871
+ }
2872
+ async function untrust() {
2873
+ try {
2874
+ if (await existsFile(caKeyFile) && caTrustRemover) {
2875
+ await caTrustRemover();
2876
+ }
2877
+ await rm(caKeyFile, { force: true });
2878
+ await rm(caPubFile, { force: true });
2879
+ await rm(caCertFile, { force: true });
2880
+ installerCalled = false;
2881
+ } catch (error) {
2882
+ return {
2883
+ ok: false,
2884
+ state: "unsupported",
2885
+ reasons: [codeOf(error, "trust.internal")]
2886
+ };
2887
+ }
2888
+ return { ok: true, state: "untrusted" };
2889
+ }
2890
+ async function status() {
2891
+ let installed = false;
2892
+ try {
2893
+ const raw = await readFile22(manifestPath(), "utf8");
2894
+ installed = isWellFormedManifest(JSON.parse(raw));
2895
+ } catch {
2896
+ installed = false;
2897
+ }
2898
+ const trusted = await existsFile(caKeyFile) && await existsFile(caCertFile);
2899
+ const caps = await detect();
2900
+ return {
2901
+ installed,
2902
+ trusted,
2903
+ platform,
2904
+ capabilityReasons: caps.reasons
2905
+ };
2906
+ }
2907
+ return { install, uninstall, trust, untrust, status };
2908
+ }
2939
2909
 
2940
2910
  // packages/cli/src/commands/runtime.ts
2941
2911
  import { validateProjectDetailed as validateProjectDetailed2 } from "@rogatio/schema";
@@ -2947,7 +2917,7 @@ function showRuntimeHelp() {
2947
2917
  rogatio runtime [options] [path]
2948
2918
 
2949
2919
  Native messaging runtime control for response-body and request-body rules, or
2950
- start the local mock runtime for F13 mock rules.
2920
+ start the local mock runtime for mock rules.
2951
2921
 
2952
2922
  Native runtime commands:
2953
2923
  start Start the runtime (capability-gated; explicit, no auto-start)
@@ -3224,7 +3194,7 @@ async function runtimeCommand(args, options = {}) {
3224
3194
  }
3225
3195
  return { exitCode: Promise.resolve(1), shutdown: noopShutdown };
3226
3196
  }
3227
- const rootDir = root ?? (inputPath === "-" ? process.cwd() : dirname4(filePath));
3197
+ const rootDir = root ?? (inputPath === "-" ? process.cwd() : dirname3(filePath));
3228
3198
  const mocksResult = buildMockConfigs(compileResult.operations, rootDir);
3229
3199
  if (!mocksResult.ok) {
3230
3200
  console.error(`Error: ${mocksResult.message}`);
@@ -3614,11 +3584,9 @@ async function testCommand(args, stdinInput, captureOutput = false) {
3614
3584
  }
3615
3585
 
3616
3586
  // packages/cli/src/commands/verify.ts
3617
- import { dirname as dirname5, resolve as resolve5 } from "node:path";
3618
- import { fileURLToPath as fileURLToPath2 } from "node:url";
3587
+ import { resolve as resolve5 } from "node:path";
3619
3588
  import { compileProject as compileProject4 } from "@rogatio/compiler";
3620
3589
  import { validateProjectDetailed as validateProjectDetailed4 } from "@rogatio/schema";
3621
- var __dirname2 = dirname5(fileURLToPath2(import.meta.url));
3622
3590
  async function verifyCommandImpl(args, stdinInput, captureOutput) {
3623
3591
  let filePath;
3624
3592
  let jsonOutput2 = false;
@@ -3714,10 +3682,10 @@ async function verifyCommand(args, stdinInput, captureOutput = false) {
3714
3682
  }
3715
3683
 
3716
3684
  // packages/cli/src/index.ts
3717
- var __dirname3 = dirname6(fileURLToPath3(import.meta.url));
3718
- var isDist = __dirname3.includes("/dist/") || __dirname3.includes("\\dist\\");
3685
+ var __dirname = dirname4(fileURLToPath2(import.meta.url));
3686
+ var isDist = __dirname.includes("/dist/") || __dirname.includes("\\dist\\");
3719
3687
  var packageJsonPath = resolve6(
3720
- __dirname3,
3688
+ __dirname,
3721
3689
  isDist ? "../../package.json" : "../package.json"
3722
3690
  );
3723
3691
  var packageJson = JSON.parse(
@@ -3811,7 +3779,7 @@ Commands:
3811
3779
  test [path] [url...] Run offline dry-run tests against .rogatio.json
3812
3780
  verify [path] Validate .rogatio.json file
3813
3781
  runtime <start|stop|status|install|trust|untrust|uninstall> Native messaging runtime and request-body trust control
3814
- runtime [path] Start the mock runtime server (F13)
3782
+ runtime [path] Start the mock runtime server ()
3815
3783
 
3816
3784
  Global Options:
3817
3785
  --help, -h Show help
@@ -3858,7 +3826,7 @@ function showRuntimeHelp2() {
3858
3826
  rogatio runtime [options] [path]
3859
3827
 
3860
3828
  Native messaging runtime control for response-body and request-body rules, or
3861
- start the local mock runtime for F13 mock rules.
3829
+ start the local mock runtime for mock rules.
3862
3830
 
3863
3831
  Native runtime commands:
3864
3832
  start Start the runtime (capability-gated; explicit, no auto-start)
@@ -3917,7 +3885,7 @@ Exit codes:
3917
3885
  1 Validation/compile/test errors
3918
3886
  2 Usage error (invalid arguments, missing input)`);
3919
3887
  }
3920
- if (process.argv[1] !== void 0 && realpathSync.native(fileURLToPath3(import.meta.url)) === realpathSync.native(resolve6(process.argv[1]))) {
3888
+ if (process.argv[1] !== void 0 && realpathSync.native(fileURLToPath2(import.meta.url)) === realpathSync.native(resolve6(process.argv[1]))) {
3921
3889
  cli().catch((err) => {
3922
3890
  console.error(err);
3923
3891
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogatio/cli",
3
- "version": "1.0.0",
3
+ "version": "1.2.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.",