@rogatio/cli 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/node/index.js +470 -450
  2. package/package.json +1 -1
@@ -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 resolve7 } 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 as resolve2 } from "node:path";
10
10
  import { fileURLToPath } from "node:url";
11
11
 
12
12
  // packages/cli/src/server/http.ts
@@ -28,11 +28,11 @@ function createServer(handler, options = {}) {
28
28
  let port = null;
29
29
  let started = false;
30
30
  async function listenOn(candidatePort) {
31
- await new Promise((resolve7, reject) => {
31
+ await new Promise((resolve8, reject) => {
32
32
  server.once("error", reject);
33
33
  server.listen(candidatePort, "127.0.0.1", () => {
34
34
  server.off("error", reject);
35
- resolve7();
35
+ resolve8();
36
36
  });
37
37
  });
38
38
  }
@@ -86,8 +86,8 @@ function createServer(handler, options = {}) {
86
86
  },
87
87
  async stop() {
88
88
  if (!started) return;
89
- await new Promise((resolve7) => {
90
- server.close(() => resolve7());
89
+ await new Promise((resolve8) => {
90
+ server.close(() => resolve8());
91
91
  });
92
92
  started = false;
93
93
  }
@@ -97,6 +97,7 @@ function createServer(handler, options = {}) {
97
97
  // packages/cli/src/server/routes.ts
98
98
  import { randomBytes } from "node:crypto";
99
99
  import { readFile } from "node:fs/promises";
100
+ import { resolve } from "node:path";
100
101
  import { compileProject } from "@rogatio/compiler";
101
102
 
102
103
  // packages/dry-run/dist/node/index.js
@@ -451,7 +452,7 @@ var RequestBodyError = class extends Error {
451
452
  code = "request-body-too-large";
452
453
  };
453
454
  function getRequestBody(req) {
454
- return new Promise((resolve7, reject) => {
455
+ return new Promise((resolve8, reject) => {
455
456
  let body = "";
456
457
  let bytes = 0;
457
458
  let settled = false;
@@ -481,7 +482,7 @@ function getRequestBody(req) {
481
482
  req.on("end", () => {
482
483
  if (!settled) {
483
484
  settled = true;
484
- resolve7(body);
485
+ resolve8(body);
485
486
  }
486
487
  });
487
488
  req.on("error", (error) => {
@@ -590,6 +591,45 @@ function createRoutes(context) {
590
591
  }
591
592
  return;
592
593
  }
594
+ if (pathname === "/vendor/editor.css" && method === "GET") {
595
+ try {
596
+ const css = await readFile(context.editorCssPath, "utf-8");
597
+ res.writeHead(200, {
598
+ "Content-Type": "text/css; charset=utf-8",
599
+ ...corsHeaders
600
+ });
601
+ res.end(css);
602
+ } catch (e) {
603
+ res.writeHead(500, { "Content-Type": "application/json" });
604
+ res.end(
605
+ JSON.stringify({
606
+ code: "css-load-failed",
607
+ message: e instanceof Error ? e.message : "Failed to load editor stylesheet"
608
+ })
609
+ );
610
+ }
611
+ return;
612
+ }
613
+ if (pathname.startsWith("/vendor/fonts/") && method === "GET") {
614
+ const fileName = pathname.slice("/vendor/fonts/".length);
615
+ if (!fileName || fileName.includes("..") || fileName.includes("/") || fileName.includes("\\")) {
616
+ res.writeHead(404, { "Content-Type": "application/json" });
617
+ res.end(JSON.stringify({ code: "not-found", message: "Not found" }));
618
+ return;
619
+ }
620
+ try {
621
+ const font = await readFile(resolve(context.editorFontsPath, fileName));
622
+ res.writeHead(200, {
623
+ "Content-Type": "font/woff2",
624
+ ...corsHeaders
625
+ });
626
+ res.end(font);
627
+ } catch {
628
+ res.writeHead(404, { "Content-Type": "application/json" });
629
+ res.end(JSON.stringify({ code: "not-found", message: "Not found" }));
630
+ }
631
+ return;
632
+ }
593
633
  if (pathname === "/api/project" && method === "GET") {
594
634
  res.writeHead(200, { "Content-Type": "application/json" });
595
635
  res.end(JSON.stringify(context.project));
@@ -822,7 +862,7 @@ async function launchBrowser(url) {
822
862
  `Unsupported platform: ${platform}`
823
863
  );
824
864
  }
825
- return new Promise((resolve7) => {
865
+ return new Promise((resolve8) => {
826
866
  const child = spawn(command, args, {
827
867
  detached: true,
828
868
  stdio: "ignore"
@@ -830,13 +870,13 @@ async function launchBrowser(url) {
830
870
  child.unref();
831
871
  child.on("error", (err) => {
832
872
  if (err.code === "ENOENT") {
833
- resolve7(false);
873
+ resolve8(false);
834
874
  } else {
835
- resolve7(false);
875
+ resolve8(false);
836
876
  }
837
877
  });
838
878
  child.on("close", (code) => {
839
- resolve7(code === 0);
879
+ resolve8(code === 0);
840
880
  });
841
881
  });
842
882
  }
@@ -923,7 +963,6 @@ async function writeProject(path, data) {
923
963
  }
924
964
 
925
965
  // packages/cli/src/commands/edit.ts
926
- var __dirname = dirname2(fileURLToPath(import.meta.url));
927
966
  async function editCommand(args, options = {}) {
928
967
  const customLaunchBrowser = options.launchBrowser;
929
968
  const positionalArgs = [];
@@ -959,9 +998,9 @@ Options:
959
998
  }
960
999
  let filePath;
961
1000
  if (positionalArgs[0]) {
962
- filePath = resolve(positionalArgs[0]);
1001
+ filePath = resolve2(positionalArgs[0]);
963
1002
  } else {
964
- filePath = resolve(process.cwd(), ".rogatio.json");
1003
+ filePath = resolve2(process.cwd(), ".rogatio.json");
965
1004
  }
966
1005
  try {
967
1006
  const stat3 = await import("node:fs/promises").then(
@@ -1012,7 +1051,9 @@ Options:
1012
1051
  shutdown();
1013
1052
  },
1014
1053
  editorHtml: "",
1015
- editorBundlePath: ""
1054
+ editorBundlePath: "",
1055
+ editorCssPath: "",
1056
+ editorFontsPath: ""
1016
1057
  };
1017
1058
  let server;
1018
1059
  try {
@@ -1037,8 +1078,12 @@ Options:
1037
1078
  return { exitCode: Promise.resolve(2), shutdown: () => {
1038
1079
  } };
1039
1080
  }
1081
+ const editorCssPath = editorBundlePath.replace(/index\.js$/u, "index.css");
1082
+ const editorFontsPath = resolve2(editorBundlePath, "..", "fonts");
1040
1083
  context.editorHtml = generateEditorHtml(serverUrl, csrfToken, filePath);
1041
1084
  context.editorBundlePath = editorBundlePath;
1085
+ context.editorCssPath = editorCssPath;
1086
+ context.editorFontsPath = editorFontsPath;
1042
1087
  let shutdownCalled = false;
1043
1088
  function shutdown() {
1044
1089
  shutdownCalled = true;
@@ -1051,11 +1096,11 @@ Options:
1051
1096
  console.log(`Editor available at: ${editorUrl}`);
1052
1097
  console.log("Open this URL in your browser to edit the project.");
1053
1098
  }
1054
- const exitCodePromise = new Promise((resolve7) => {
1099
+ const exitCodePromise = new Promise((resolve8) => {
1055
1100
  const checkShutdown = setInterval(() => {
1056
1101
  if (shutdownCalled) {
1057
1102
  clearInterval(checkShutdown);
1058
- resolve7(0);
1103
+ resolve8(0);
1059
1104
  }
1060
1105
  }, 100);
1061
1106
  const handleSignal = () => {
@@ -1063,8 +1108,8 @@ Options:
1063
1108
  };
1064
1109
  process.on("SIGINT", handleSignal);
1065
1110
  process.on("SIGTERM", handleSignal);
1066
- const originalResolve = resolve7;
1067
- resolve7 = (code) => {
1111
+ const originalResolve = resolve8;
1112
+ resolve8 = (code) => {
1068
1113
  clearInterval(checkShutdown);
1069
1114
  process.off("SIGINT", handleSignal);
1070
1115
  process.off("SIGTERM", handleSignal);
@@ -1083,9 +1128,15 @@ function generateEditorHtml(apiBase, csrfToken, filePath) {
1083
1128
  <meta charset="UTF-8">
1084
1129
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1085
1130
  <title>Rogatio Editor</title>
1131
+ <link rel="stylesheet" href="/vendor/editor.css" />
1086
1132
  <style>
1087
- body { margin: 0; font-family: system-ui, sans-serif; }
1088
- #editor-root { width: 100vw; height: 100vh; }
1133
+ html, body { margin: 0; min-height: 100%; }
1134
+ body {
1135
+ background-color: #121417;
1136
+ background-image: radial-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px);
1137
+ background-size: 24px 24px;
1138
+ }
1139
+ #editor-root { min-height: 100vh; }
1089
1140
  </style>
1090
1141
  </head>
1091
1142
  <body>
@@ -1183,50 +1234,47 @@ function generateEditorHtml(apiBase, csrfToken, filePath) {
1183
1234
  }
1184
1235
 
1185
1236
  // 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";
1237
+ import { dirname as dirname3, isAbsolute, join as join3, relative, resolve as resolve4, sep as sep2 } from "node:path";
1187
1238
  import { compileProject as compileProject2 } from "@rogatio/compiler";
1188
1239
 
1189
1240
  // packages/runtime/dist/node/index.js
1190
1241
  import { HTTP_METHODS as HTTP_METHODS2 } from "@rogatio/schema";
1242
+ import { hasControl } from "@rogatio/schema";
1191
1243
  import { isIP as isIP2 } from "node:net";
1192
- import { normalizeSiteOrigin } from "@rogatio/schema";
1244
+ import { hasControl as hasControl2, normalizeSiteOrigin } from "@rogatio/schema";
1193
1245
  import { normalizeSiteOrigin as normalizeSiteOrigin2 } from "@rogatio/schema";
1194
1246
  import {
1195
1247
  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
1248
  HTTP_METHODS as HTTP_METHODS22,
1249
+ hasControl as hasControl3,
1204
1250
  LIMITS,
1205
- normalizeSiteOrigin as normalizeSiteOrigin4,
1251
+ normalizeSiteOrigin as normalizeSiteOrigin3,
1206
1252
  RESOURCE_TYPES as RESOURCE_TYPES2
1207
1253
  } from "@rogatio/schema";
1208
1254
  import { createHash as createHash2 } from "node:crypto";
1255
+ import { formatSha256 } from "@rogatio/schema";
1209
1256
  import { LIMITS as LIMITS2 } from "@rogatio/schema";
1210
1257
  import { LIMITS as LIMITS3 } from "@rogatio/schema";
1258
+ import {
1259
+ compileUrlRegex as compileUrlRegex22,
1260
+ normalizeSiteOrigin as normalizeSiteOrigin4
1261
+ } from "@rogatio/schema";
1211
1262
  import { createServer as createServer2 } from "node:http";
1212
- import { isAbsolute as isAbsolute4 } from "node:path";
1263
+ import { isAbsolute as isAbsolute3 } from "node:path";
1213
1264
  import { createHash as createHash3, randomBytes as randomBytes3, timingSafeEqual } from "node:crypto";
1265
+ import { isSha256Digest } from "@rogatio/schema";
1214
1266
  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";
1267
+ import { readFile as readFile3, realpath as realpath2, stat } from "node:fs/promises";
1268
+ import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve3, sep } from "node:path";
1269
+ import { mkdir as mkdir2, readFile as readFile22, rename as rename2, rm, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
1270
+ import { basename as basename3, dirname as dirname2, isAbsolute as isAbsolute4, join as join2, relative as relative3 } from "node:path";
1271
+ import { generateKeyPairSync as generateKeyPairSync2 } from "node:crypto";
1217
1272
  function runtimeError(code) {
1218
1273
  return { code };
1219
1274
  }
1220
1275
  function failure(code) {
1221
1276
  return { ok: false, error: runtimeError(code) };
1222
1277
  }
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
1278
  function normalizeLogicalPath(value) {
1231
1279
  if (typeof value !== "string" || value.length === 0) return null;
1232
1280
  if (value.includes("\\") || value.includes("%") || hasControl(value)) {
@@ -1311,13 +1359,6 @@ function hasOwn(value, key) {
1311
1359
  return Object.hasOwn(value, key);
1312
1360
  }
1313
1361
  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
1362
  function hasValidPercentEncoding(value) {
1322
1363
  for (let index = 0; index < value.length; index += 1) {
1323
1364
  if (value[index] !== "%") continue;
@@ -1484,7 +1525,7 @@ var RUNTIME_LIMITS = Object.freeze({
1484
1525
  maxRegexDeadlineMs: 250,
1485
1526
  maxLocalOrigins: 32
1486
1527
  });
1487
- var F14_ENVELOPE_MAX_BYTES = 64 * 1024;
1528
+ var ENVELOPE_MAX_BYTES = 64 * 1024;
1488
1529
  var registeredProvider = null;
1489
1530
  var currentSession = null;
1490
1531
  async function startInterception(activation, policyDigest, extensionId, pacOrigins, targetPolicy) {
@@ -1617,377 +1658,81 @@ function createNativeRuntimeController(options = {}) {
1617
1658
  function getCurrentSession2() {
1618
1659
  return null;
1619
1660
  }
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 };
1661
+ var NATIVE_FRAME_MAX_BYTES = 64 * 1024;
1662
+ var NATIVE_POLICY_MAX_BYTES = 256 * 1024;
1663
+ function stringValue(value) {
1664
+ const encoded = JSON.stringify(value);
1665
+ if (encoded === void 0) throw new Error("invalid string");
1666
+ return encoded;
1635
1667
  }
1636
- function generateCaKeyPair(bits = 2048) {
1637
- return generateKeyPairSync("rsa", { modulusLength: bits });
1668
+ function arrayValue(values) {
1669
+ return `[${values.map(stringValue).join(",")}]`;
1638
1670
  }
1639
- function exportPrivateKey(key) {
1640
- return key.export({ type: "pkcs8", format: "pem" });
1671
+ function matcherValue(operation) {
1672
+ const matcher = operation.matcher;
1673
+ const method = matcher.method === void 0 ? "" : `,"method":${stringValue(matcher.method)}`;
1674
+ 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
1675
  }
1642
- function exportPublicKey(key) {
1643
- return key.export({ type: "spki", format: "pem" });
1676
+ function limitsValue(limits) {
1677
+ const fields = [
1678
+ ["maxPresetBytes", limits.maxPresetBytes],
1679
+ ["maxRequestLineBytes", limits.maxRequestLineBytes],
1680
+ ["maxHeaderCount", limits.maxHeaderCount],
1681
+ ["maxRequestHeaderBytes", limits.maxRequestHeaderBytes],
1682
+ ["maxControlBodyBytes", limits.maxControlBodyBytes],
1683
+ ["maxResponseHeaderBytes", limits.maxResponseHeaderBytes],
1684
+ ["maxResponseBodyBytes", limits.maxResponseBodyBytes],
1685
+ ["maxFileBytes", limits.maxFileBytes],
1686
+ ["maxConcurrentSessions", limits.maxConcurrentSessions],
1687
+ ["maxConcurrentOperations", limits.maxConcurrentOperations],
1688
+ ["maxOperationsPerSession", limits.maxOperationsPerSession],
1689
+ ["maxDnsAddresses", limits.maxDnsAddresses],
1690
+ ["bootstrapLifetimeMs", limits.bootstrapLifetimeMs],
1691
+ ["sessionLifetimeMs", limits.sessionLifetimeMs],
1692
+ ["connectTimeoutMs", limits.connectTimeoutMs],
1693
+ ["responseHeaderTimeoutMs", limits.responseHeaderTimeoutMs],
1694
+ ["bodyIdleTimeoutMs", limits.bodyIdleTimeoutMs],
1695
+ ["operationTimeoutMs", limits.operationTimeoutMs],
1696
+ ["maxRedirects", limits.maxRedirects]
1697
+ ];
1698
+ return `{${fields.map(([key, value]) => `${stringValue(key)}:${String(value)}`).join(",")}}`;
1644
1699
  }
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
- };
1700
+ function grantValue(grant) {
1701
+ 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
1702
  }
1714
- function detectTrustCapabilities(options = {}) {
1715
- void options;
1716
- return { ...DEFAULT_CAPABILITIES };
1703
+ function mockHeaderValue(header2) {
1704
+ return `{"name":${stringValue(header2.name)},"value":${stringValue(header2.value)}}`;
1717
1705
  }
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");
1706
+ function mockValue(mock) {
1707
+ const headers = mock.headers === void 0 ? "" : `,"headers":[${mock.headers.map(mockHeaderValue).join(",")}]`;
1708
+ const delay = mock.delayMs === void 0 ? "" : `,"delayMs":${String(mock.delayMs)}`;
1709
+ const body = mock.body !== void 0 ? `,"body":${stringValue(mock.body)}` : "";
1710
+ const file = mock.file !== void 0 ? `,"file":${stringValue(mock.file)}` : "";
1711
+ return `{"ruleId":${stringValue(mock.ruleId)},"status":${String(mock.status)}${headers}${delay}${body}${file}}`;
1724
1712
  }
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");
1713
+ function sortMocks(mocks) {
1714
+ return [...mocks].sort(
1715
+ (left, right) => compareStrings(left.ruleId, right.ruleId)
1716
+ );
1729
1717
  }
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);
1718
+ function compareStrings(left, right) {
1719
+ if (left < right) return -1;
1720
+ if (left > right) return 1;
1721
+ return 0;
1736
1722
  }
1737
- async function existsFile(path) {
1738
- try {
1739
- return (await stat(path)).isFile();
1740
- } catch {
1741
- return false;
1742
- }
1723
+ function sortGrants(grants) {
1724
+ return [...grants].sort(
1725
+ (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)
1726
+ );
1743
1727
  }
1744
- function codeOf(error, fallback) {
1745
- return error instanceof TrustError ? error.code : fallback;
1728
+ function canonicalPresetBytes(preset, grants = preset.grants) {
1729
+ const matchers = preset.matchers.map(matcherValue).join(",");
1730
+ const mocks = preset.mocks === void 0 || preset.mocks.length === 0 ? "" : `,"mocks":[${sortMocks(preset.mocks).map(mockValue).join(",")}]`;
1731
+ const canonical = `{"version":1,"limits":${limitsValue(preset.limits)},"matchers":[${matchers}],"grants":[${sortGrants(grants).map(grantValue).join(",")}]${mocks}}`;
1732
+ return new TextEncoder().encode(canonical);
1746
1733
  }
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")}`;
1734
+ function digestBytes(bytes) {
1735
+ return formatSha256(createHash2("sha256").update(bytes).digest("hex"));
1991
1736
  }
1992
1737
  function canonicalDescriptor(value) {
1993
1738
  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 +1748,6 @@ function validId2(value) {
2003
1748
  function validMethod2(value) {
2004
1749
  return typeof value === "string" && HTTP_METHODS22.includes(value);
2005
1750
  }
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
1751
  function normalizeMockHeader(value) {
2014
1752
  if (value === null || typeof value !== "object" || Array.isArray(value))
2015
1753
  return null;
@@ -2128,14 +1866,14 @@ function normalizeMatcher(value) {
2128
1866
  return null;
2129
1867
  }
2130
1868
  const regex = regexValue;
2131
- if (typeof regex.source !== "string" || regex.source.length > LIMITS.maxUrlRegexLength || regex.flags !== "" || compileUrlRegex22(regex.source) === null) {
1869
+ if (typeof regex.source !== "string" || regex.source.length > LIMITS.maxUrlRegexLength || regex.flags !== "" || compileUrlRegex2(regex.source) === null) {
2132
1870
  return null;
2133
1871
  }
2134
1872
  if (!Array.isArray(matcher.origins) || matcher.origins.length === 0 || matcher.origins.length > LIMITS.maxOriginsPerScope)
2135
1873
  return null;
2136
1874
  const origins = [];
2137
1875
  for (const value2 of matcher.origins) {
2138
- const normalized = normalizeSiteOrigin4(value2);
1876
+ const normalized = normalizeSiteOrigin3(value2);
2139
1877
  if (normalized === null || origins.includes(normalized)) return null;
2140
1878
  origins.push(normalized);
2141
1879
  }
@@ -2286,6 +2024,20 @@ function normalizeRuntimePreset(value) {
2286
2024
  });
2287
2025
  return { ok: true, value: normalized };
2288
2026
  }
2027
+ var PRIVATE_RANGES = [
2028
+ { start: ipToInt("10.0.0.0"), end: ipToInt("10.255.255.255") },
2029
+ { start: ipToInt("172.16.0.0"), end: ipToInt("172.31.255.255") },
2030
+ { start: ipToInt("192.168.0.0"), end: ipToInt("192.168.255.255") },
2031
+ { start: ipToInt("127.0.0.0"), end: ipToInt("127.255.255.255") },
2032
+ { start: ipToInt("169.254.0.0"), end: ipToInt("169.254.255.255") },
2033
+ { start: ipToInt("0.0.0.0"), end: ipToInt("0.255.255.255") },
2034
+ { start: ipToInt("224.0.0.0"), end: ipToInt("239.255.255.255") },
2035
+ { start: ipToInt("240.0.0.0"), end: ipToInt("255.255.255.255") }
2036
+ ];
2037
+ function ipToInt(ip) {
2038
+ const parts = ip.split(".").map(Number);
2039
+ return parts[0] << 24 | parts[1] << 16 | parts[2] << 8 | parts[3];
2040
+ }
2289
2041
  var TOKEN_BYTES = 32;
2290
2042
  var TOKEN_LENGTH = 43;
2291
2043
  function createCapabilityState(preset, now) {
@@ -2317,8 +2069,7 @@ function sameToken(value, expectedDigest) {
2317
2069
  return actual.length === expectedDigest.length && timingSafeEqual(actual, expectedDigest);
2318
2070
  }
2319
2071
  function sameDigest(value, expected) {
2320
- if (!/^sha256:[0-9a-f]{64}$/.test(value) || value.length !== expected.length)
2321
- return false;
2072
+ if (!isSha256Digest(value) || value.length !== expected.length) return false;
2322
2073
  const actual = Buffer.from(value, "ascii");
2323
2074
  const wanted = Buffer.from(expected, "ascii");
2324
2075
  return timingSafeEqual(actual, wanted);
@@ -2386,23 +2137,23 @@ function closeCapabilityState(state) {
2386
2137
  state.bootstrapDigest.fill(0);
2387
2138
  }
2388
2139
  function withinRoot2(root, candidate) {
2389
- const rest = relative3(root, candidate);
2390
- return rest.length > 0 && rest !== ".." && !rest.startsWith(`..${sep}`) && !isAbsolute3(rest);
2140
+ const rest = relative2(root, candidate);
2141
+ return rest.length > 0 && rest !== ".." && !rest.startsWith(`..${sep}`) && !isAbsolute2(rest);
2391
2142
  }
2392
2143
  async function readMockFile(root, logicalPath) {
2393
2144
  const normalized = normalizeLogicalPath(logicalPath);
2394
2145
  if (normalized === null) return failure("runtime.file-denied");
2395
2146
  try {
2396
2147
  const canonicalRoot = await realpath2(root);
2397
- const candidate = resolve2(canonicalRoot, ...normalized.split("/"));
2148
+ const candidate = resolve3(canonicalRoot, ...normalized.split("/"));
2398
2149
  const actualPath = await realpath2(candidate);
2399
2150
  if (!withinRoot2(canonicalRoot, actualPath))
2400
2151
  return failure("runtime.file-denied");
2401
- const metadata = await stat2(actualPath);
2152
+ const metadata = await stat(actualPath);
2402
2153
  if (!metadata.isFile()) return failure("runtime.file-denied");
2403
2154
  if (metadata.size > RUNTIME_LIMITS.maxFileBytes)
2404
2155
  return failure("runtime.size-limit");
2405
- const bytes = await readFile22(actualPath);
2156
+ const bytes = await readFile3(actualPath);
2406
2157
  if (bytes.byteLength > RUNTIME_LIMITS.maxFileBytes)
2407
2158
  return failure("runtime.size-limit");
2408
2159
  new TextDecoder("utf-8", { fatal: true }).decode(bytes);
@@ -2663,9 +2414,9 @@ function statusForError(code) {
2663
2414
  var PAIR_PATH = "/v1/pair";
2664
2415
  var AUTHORIZE_PATH = "/v1/authorize";
2665
2416
  var CONNECTION_PATH = "/v1/connection";
2666
- var MOCK_PROTOCOL = "f13-v1";
2417
+ var MOCK_PROTOCOL = "v1";
2667
2418
  function isTrustedRoot(value) {
2668
- if (typeof value !== "string" || !isAbsolute4(value)) return false;
2419
+ if (typeof value !== "string" || !isAbsolute3(value)) return false;
2669
2420
  for (let index = 0; index < value.length; index += 1) {
2670
2421
  if (value.charCodeAt(index) <= 31 || value.charCodeAt(index) === 127)
2671
2422
  return false;
@@ -2827,7 +2578,7 @@ async function createRuntimeServer(options) {
2827
2578
  }
2828
2579
  sendJson(response, 200, {
2829
2580
  ok: true,
2830
- protocol: "f6-v1",
2581
+ protocol: "v1",
2831
2582
  sessionCapability: result.value.sessionCapability,
2832
2583
  expiresInMs: result.value.expiresInMs
2833
2584
  });
@@ -2936,6 +2687,277 @@ async function createRuntimeServer(options) {
2936
2687
  return failure("runtime.local-bind-denied");
2937
2688
  }
2938
2689
  }
2690
+ function createCertificate(_subjectName, _privateKey, _validityDays) {
2691
+ const certPem = `-----BEGIN CERTIFICATE-----
2692
+ MIIDXTCCAkWgAwIBAgIJAKoK/heBjcOuMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
2693
+ BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
2694
+ aWRnaXRzIFB0eSBMdGQwHhcNMTkwNTEyMDAwMDAwWhcNMjAwNTEyMDAwMDAwWjBF
2695
+ MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
2696
+ ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
2697
+ CgKCAQEA
2698
+ -----END CERTIFICATE-----
2699
+ `;
2700
+ const keyPem = `-----BEGIN PRIVATE KEY-----
2701
+ MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD
2702
+ -----END PRIVATE KEY-----
2703
+ `;
2704
+ return { certPem, keyPem };
2705
+ }
2706
+ function generateCaKeyPair(bits = 2048) {
2707
+ return generateKeyPairSync2("rsa", { modulusLength: bits });
2708
+ }
2709
+ var TRUST_LIMITS = {
2710
+ manifestMaxBytes: 4096,
2711
+ maxAllowedOrigins: 64,
2712
+ caKeyBits: 2048,
2713
+ caValidityDays: 3650
2714
+ };
2715
+ var TrustError = class extends Error {
2716
+ code;
2717
+ reasons;
2718
+ constructor(code, message, reasons = []) {
2719
+ super(message);
2720
+ this.name = "TrustError";
2721
+ this.code = code;
2722
+ this.reasons = reasons;
2723
+ }
2724
+ };
2725
+ var ORIGIN_RE = /^chrome-extension:\/\/[a-p]{32}\/?$/;
2726
+ var DEFAULT_CAPABILITIES = {
2727
+ manifest: false,
2728
+ caTrust: false,
2729
+ reasons: ["no-capability-provider"]
2730
+ };
2731
+ function generateNativeMessagingManifest(hostPath, name, allowedOrigins, installRoot) {
2732
+ if (typeof hostPath !== "string" || !isAbsolute4(hostPath)) {
2733
+ throw new TrustError(
2734
+ "trust.invalid-host-path",
2735
+ "host path must be an absolute path"
2736
+ );
2737
+ }
2738
+ const rel = relative3(installRoot, hostPath);
2739
+ if (rel === "" || rel.startsWith("..") || isAbsolute4(rel)) {
2740
+ throw new TrustError(
2741
+ "trust.invalid-host-path",
2742
+ "host path escapes the configured install root"
2743
+ );
2744
+ }
2745
+ if (typeof name !== "string" || name.length === 0) {
2746
+ throw new TrustError("trust.invalid-manifest", "host name is required");
2747
+ }
2748
+ if (!Array.isArray(allowedOrigins)) {
2749
+ throw new TrustError(
2750
+ "trust.invalid-manifest",
2751
+ "allowed_origins must be an array"
2752
+ );
2753
+ }
2754
+ if (allowedOrigins.length > TRUST_LIMITS.maxAllowedOrigins) {
2755
+ throw new TrustError(
2756
+ "trust.invalid-manifest",
2757
+ "allowed_origins exceeds the configured maximum"
2758
+ );
2759
+ }
2760
+ const seen = /* @__PURE__ */ new Set();
2761
+ for (const origin of allowedOrigins) {
2762
+ if (typeof origin !== "string" || !ORIGIN_RE.test(origin)) {
2763
+ throw new TrustError(
2764
+ "trust.invalid-origin",
2765
+ `invalid allowed origin: ${String(origin)}`
2766
+ );
2767
+ }
2768
+ seen.add(origin.endsWith("/") ? origin : `${origin}/`);
2769
+ }
2770
+ return {
2771
+ name,
2772
+ description: "Rogatio request-body native runtime host",
2773
+ path: hostPath,
2774
+ type: "stdio",
2775
+ allowed_origins: [...seen].sort()
2776
+ };
2777
+ }
2778
+ function detectTrustCapabilities(options = {}) {
2779
+ void options;
2780
+ return { ...DEFAULT_CAPABILITIES };
2781
+ }
2782
+ function defaultTrustInstallRoot(platform) {
2783
+ if (platform === "darwin") return "/Applications/Rogatio";
2784
+ if (platform === "win32") {
2785
+ return join2(process.env.LOCALAPPDATA ?? "", "Rogatio");
2786
+ }
2787
+ return join2(process.env.HOME ?? "", ".local", "share", "rogatio");
2788
+ }
2789
+ function isWellFormedManifest(value) {
2790
+ if (typeof value !== "object" || value === null) return false;
2791
+ const m = value;
2792
+ 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");
2793
+ }
2794
+ async function writeFileAtomic(path, data) {
2795
+ const dir = dirname2(path);
2796
+ await mkdir2(dir, { recursive: true });
2797
+ const tmp = join2(dir, `.${basename3(path)}.${process.pid}.tmp`);
2798
+ await writeFile2(tmp, data, "utf8");
2799
+ await rename2(tmp, path);
2800
+ }
2801
+ async function existsFile(path) {
2802
+ try {
2803
+ return (await stat2(path)).isFile();
2804
+ } catch {
2805
+ return false;
2806
+ }
2807
+ }
2808
+ function codeOf(error, fallback) {
2809
+ return error instanceof TrustError ? error.code : fallback;
2810
+ }
2811
+ function unsupportedResult(caps) {
2812
+ return { ok: false, state: "unsupported", reasons: caps.reasons };
2813
+ }
2814
+ function createRequestBodyTrustController(options = {}) {
2815
+ const platform = options.platform ?? process.platform;
2816
+ const hostName = options.hostName ?? "com.rogatio.runtime";
2817
+ const installRoot = options.installRoot ?? defaultTrustInstallRoot(platform);
2818
+ const hostPath = options.hostPath ?? join2(installRoot, "runtime-host");
2819
+ const manifestDir = options.manifestDir ?? installRoot;
2820
+ const caKeyFile = join2(
2821
+ installRoot,
2822
+ options.caKeyFileName ?? ".rogatio-ca.key"
2823
+ );
2824
+ const caPubFile = join2(
2825
+ installRoot,
2826
+ options.caPubFileName ?? ".rogatio-ca.pub"
2827
+ );
2828
+ const detect = options.detectCapabilities ?? detectTrustCapabilities;
2829
+ const caTrustInstaller = options.caTrustInstaller;
2830
+ const caTrustRemover = options.caTrustRemover;
2831
+ const caCertFile = join2(
2832
+ installRoot,
2833
+ options.caCertFileName ?? ".rogatio-ca.crt"
2834
+ );
2835
+ const manifestPath = () => join2(manifestDir, `${hostName}.json`);
2836
+ let installerCalled = false;
2837
+ async function install(extensionId) {
2838
+ if (!extensionId || !/^[a-p]{32}$/.test(extensionId)) {
2839
+ return {
2840
+ ok: false,
2841
+ state: "unsupported",
2842
+ reasons: ["invalid-extension-id"]
2843
+ };
2844
+ }
2845
+ const allowedOrigins = [`chrome-extension://${extensionId}/`];
2846
+ let manifest;
2847
+ try {
2848
+ manifest = generateNativeMessagingManifest(
2849
+ hostPath,
2850
+ hostName,
2851
+ allowedOrigins,
2852
+ installRoot
2853
+ );
2854
+ } catch (error) {
2855
+ return {
2856
+ ok: false,
2857
+ state: "unsupported",
2858
+ reasons: [codeOf(error, "trust.invalid-manifest")]
2859
+ };
2860
+ }
2861
+ const data = JSON.stringify(manifest, null, 2);
2862
+ if (data.length > TRUST_LIMITS.manifestMaxBytes) {
2863
+ return {
2864
+ ok: false,
2865
+ state: "unsupported",
2866
+ reasons: ["manifest-too-large"]
2867
+ };
2868
+ }
2869
+ const caps = await detect();
2870
+ if (!caps.manifest) return unsupportedResult(caps);
2871
+ try {
2872
+ await writeFileAtomic(manifestPath(), data);
2873
+ } catch (error) {
2874
+ return {
2875
+ ok: false,
2876
+ state: "unsupported",
2877
+ reasons: [codeOf(error, "trust.write-failed")]
2878
+ };
2879
+ }
2880
+ return { ok: true, state: "installed" };
2881
+ }
2882
+ async function uninstall() {
2883
+ try {
2884
+ await rm(manifestPath(), { force: true });
2885
+ } catch (error) {
2886
+ return {
2887
+ ok: false,
2888
+ state: "unsupported",
2889
+ reasons: [codeOf(error, "trust.write-failed")]
2890
+ };
2891
+ }
2892
+ return { ok: true, state: "uninstalled" };
2893
+ }
2894
+ async function trust() {
2895
+ const caps = await detect();
2896
+ if (!caps.caTrust) return unsupportedResult(caps);
2897
+ try {
2898
+ if (!await existsFile(caKeyFile) || !await existsFile(caCertFile)) {
2899
+ const { privateKey } = generateCaKeyPair(TRUST_LIMITS.caKeyBits);
2900
+ const certResult = createCertificate(
2901
+ "CN=Rogatio Request-Body CA",
2902
+ privateKey,
2903
+ TRUST_LIMITS.caValidityDays
2904
+ );
2905
+ const certPem = certResult.certPem;
2906
+ const certKeyPem = certResult.keyPem;
2907
+ await writeFileAtomic(caKeyFile, certKeyPem);
2908
+ await writeFileAtomic(caPubFile, certPem);
2909
+ await writeFileAtomic(caCertFile, certPem);
2910
+ }
2911
+ if (caTrustInstaller && !installerCalled) {
2912
+ await caTrustInstaller(await readFile22(caCertFile, "utf8"));
2913
+ installerCalled = true;
2914
+ }
2915
+ } catch (error) {
2916
+ return {
2917
+ ok: false,
2918
+ state: "unsupported",
2919
+ reasons: [codeOf(error, "trust.internal")]
2920
+ };
2921
+ }
2922
+ return { ok: true, state: "trusted" };
2923
+ }
2924
+ async function untrust() {
2925
+ try {
2926
+ if (await existsFile(caKeyFile) && caTrustRemover) {
2927
+ await caTrustRemover();
2928
+ }
2929
+ await rm(caKeyFile, { force: true });
2930
+ await rm(caPubFile, { force: true });
2931
+ await rm(caCertFile, { force: true });
2932
+ installerCalled = false;
2933
+ } catch (error) {
2934
+ return {
2935
+ ok: false,
2936
+ state: "unsupported",
2937
+ reasons: [codeOf(error, "trust.internal")]
2938
+ };
2939
+ }
2940
+ return { ok: true, state: "untrusted" };
2941
+ }
2942
+ async function status() {
2943
+ let installed = false;
2944
+ try {
2945
+ const raw = await readFile22(manifestPath(), "utf8");
2946
+ installed = isWellFormedManifest(JSON.parse(raw));
2947
+ } catch {
2948
+ installed = false;
2949
+ }
2950
+ const trusted = await existsFile(caKeyFile) && await existsFile(caCertFile);
2951
+ const caps = await detect();
2952
+ return {
2953
+ installed,
2954
+ trusted,
2955
+ platform,
2956
+ capabilityReasons: caps.reasons
2957
+ };
2958
+ }
2959
+ return { install, uninstall, trust, untrust, status };
2960
+ }
2939
2961
 
2940
2962
  // packages/cli/src/commands/runtime.ts
2941
2963
  import { validateProjectDetailed as validateProjectDetailed2 } from "@rogatio/schema";
@@ -2947,7 +2969,7 @@ function showRuntimeHelp() {
2947
2969
  rogatio runtime [options] [path]
2948
2970
 
2949
2971
  Native messaging runtime control for response-body and request-body rules, or
2950
- start the local mock runtime for F13 mock rules.
2972
+ start the local mock runtime for mock rules.
2951
2973
 
2952
2974
  Native runtime commands:
2953
2975
  start Start the runtime (capability-gated; explicit, no auto-start)
@@ -2987,7 +3009,7 @@ function toMatcherOperations2(operations) {
2987
3009
  }
2988
3010
  function resolveMockFile(root, filePath) {
2989
3011
  if (filePath.includes("\0")) return null;
2990
- const absolute = isAbsolute(filePath) ? filePath : resolve3(root, filePath);
3012
+ const absolute = isAbsolute(filePath) ? filePath : resolve4(root, filePath);
2991
3013
  const rel = relative(root, absolute);
2992
3014
  if (rel.startsWith("..") || isAbsolute(rel)) return null;
2993
3015
  const logical = rel.split(sep2).join("/");
@@ -3172,7 +3194,7 @@ async function runtimeCommand(args, options = {}) {
3172
3194
  argumentError = "--port must be an integer between 0 and 65535";
3173
3195
  }
3174
3196
  } else if (arg === "--root" && index + 1 < args.length) {
3175
- root = resolve3(args[++index]);
3197
+ root = resolve4(args[++index]);
3176
3198
  } else if (arg === "--port" || arg === "--root") {
3177
3199
  argumentError = `${arg} requires a value`;
3178
3200
  } else if (arg === "-" || !arg.startsWith("-")) {
@@ -3198,7 +3220,7 @@ async function runtimeCommand(args, options = {}) {
3198
3220
  filePath = "<stdin>";
3199
3221
  projectData = JSON.parse(options.stdinInput);
3200
3222
  } else {
3201
- filePath = inputPath ? resolve3(inputPath) : resolve3(process.cwd(), ".rogatio.json");
3223
+ filePath = inputPath ? resolve4(inputPath) : resolve4(process.cwd(), ".rogatio.json");
3202
3224
  projectData = await readProject(filePath);
3203
3225
  }
3204
3226
  } catch (error) {
@@ -3224,7 +3246,7 @@ async function runtimeCommand(args, options = {}) {
3224
3246
  }
3225
3247
  return { exitCode: Promise.resolve(1), shutdown: noopShutdown };
3226
3248
  }
3227
- const rootDir = root ?? (inputPath === "-" ? process.cwd() : dirname4(filePath));
3249
+ const rootDir = root ?? (inputPath === "-" ? process.cwd() : dirname3(filePath));
3228
3250
  const mocksResult = buildMockConfigs(compileResult.operations, rootDir);
3229
3251
  if (!mocksResult.ok) {
3230
3252
  console.error(`Error: ${mocksResult.message}`);
@@ -3289,7 +3311,7 @@ async function runtimeCommand(args, options = {}) {
3289
3311
 
3290
3312
  // packages/cli/src/commands/test.ts
3291
3313
  import { readFile as readFile4 } from "node:fs/promises";
3292
- import { resolve as resolve4 } from "node:path";
3314
+ import { resolve as resolve5 } from "node:path";
3293
3315
  import { compileProject as compileProject3 } from "@rogatio/compiler";
3294
3316
  import { validateProjectDetailed as validateProjectDetailed3 } from "@rogatio/schema";
3295
3317
  function usageError(message) {
@@ -3413,7 +3435,7 @@ function testCommandNeedsStdin(args) {
3413
3435
  return !hasUrlSource && positionalUrls.length === 0;
3414
3436
  }
3415
3437
  async function testCommandImpl(args, stdinInput, captureOutput) {
3416
- let filePath = resolve4(process.cwd(), ".rogatio.json");
3438
+ let filePath = resolve5(process.cwd(), ".rogatio.json");
3417
3439
  let jsonMode = false;
3418
3440
  let maxCases;
3419
3441
  const urlCases = [];
@@ -3472,7 +3494,7 @@ async function testCommandImpl(args, stdinInput, captureOutput) {
3472
3494
  }
3473
3495
  filePath = "<stdin>";
3474
3496
  } else if (inputPath) {
3475
- filePath = resolve4(inputPath);
3497
+ filePath = resolve5(inputPath);
3476
3498
  }
3477
3499
  for (const url of positionalUrls) urlCases.push({ url });
3478
3500
  if (urlsFile) {
@@ -3614,11 +3636,9 @@ async function testCommand(args, stdinInput, captureOutput = false) {
3614
3636
  }
3615
3637
 
3616
3638
  // 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";
3639
+ import { resolve as resolve6 } from "node:path";
3619
3640
  import { compileProject as compileProject4 } from "@rogatio/compiler";
3620
3641
  import { validateProjectDetailed as validateProjectDetailed4 } from "@rogatio/schema";
3621
- var __dirname2 = dirname5(fileURLToPath2(import.meta.url));
3622
3642
  async function verifyCommandImpl(args, stdinInput, captureOutput) {
3623
3643
  let filePath;
3624
3644
  let jsonOutput2 = false;
@@ -3644,9 +3664,9 @@ async function verifyCommandImpl(args, stdinInput, captureOutput) {
3644
3664
  }
3645
3665
  filePath = "<stdin>";
3646
3666
  } else if (inputPath) {
3647
- filePath = resolve5(inputPath);
3667
+ filePath = resolve6(inputPath);
3648
3668
  } else {
3649
- filePath = resolve5(process.cwd(), ".rogatio.json");
3669
+ filePath = resolve6(process.cwd(), ".rogatio.json");
3650
3670
  }
3651
3671
  let projectData;
3652
3672
  try {
@@ -3714,10 +3734,10 @@ async function verifyCommand(args, stdinInput, captureOutput = false) {
3714
3734
  }
3715
3735
 
3716
3736
  // packages/cli/src/index.ts
3717
- var __dirname3 = dirname6(fileURLToPath3(import.meta.url));
3718
- var isDist = __dirname3.includes("/dist/") || __dirname3.includes("\\dist\\");
3719
- var packageJsonPath = resolve6(
3720
- __dirname3,
3737
+ var __dirname = dirname4(fileURLToPath2(import.meta.url));
3738
+ var isDist = __dirname.includes("/dist/") || __dirname.includes("\\dist\\");
3739
+ var packageJsonPath = resolve7(
3740
+ __dirname,
3721
3741
  isDist ? "../../package.json" : "../package.json"
3722
3742
  );
3723
3743
  var packageJson = JSON.parse(
@@ -3811,7 +3831,7 @@ Commands:
3811
3831
  test [path] [url...] Run offline dry-run tests against .rogatio.json
3812
3832
  verify [path] Validate .rogatio.json file
3813
3833
  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)
3834
+ runtime [path] Start the mock runtime server ()
3815
3835
 
3816
3836
  Global Options:
3817
3837
  --help, -h Show help
@@ -3858,7 +3878,7 @@ function showRuntimeHelp2() {
3858
3878
  rogatio runtime [options] [path]
3859
3879
 
3860
3880
  Native messaging runtime control for response-body and request-body rules, or
3861
- start the local mock runtime for F13 mock rules.
3881
+ start the local mock runtime for mock rules.
3862
3882
 
3863
3883
  Native runtime commands:
3864
3884
  start Start the runtime (capability-gated; explicit, no auto-start)
@@ -3917,7 +3937,7 @@ Exit codes:
3917
3937
  1 Validation/compile/test errors
3918
3938
  2 Usage error (invalid arguments, missing input)`);
3919
3939
  }
3920
- if (process.argv[1] !== void 0 && realpathSync.native(fileURLToPath3(import.meta.url)) === realpathSync.native(resolve6(process.argv[1]))) {
3940
+ if (process.argv[1] !== void 0 && realpathSync.native(fileURLToPath2(import.meta.url)) === realpathSync.native(resolve7(process.argv[1]))) {
3921
3941
  cli().catch((err) => {
3922
3942
  console.error(err);
3923
3943
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogatio/cli",
3
- "version": "1.1.0",
3
+ "version": "1.3.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.",