@rogatio/cli 6.7.0 → 6.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -41,7 +41,7 @@ rogatio <command> [options]
41
41
  | `rogatio import requestly <export.json>` | Migrate a Requestly export into `.rogatio.json`. `--out <path>` chooses the file (default `.rogatio.json`); `--merge` appends onto an existing project; `--json` prints the report. |
42
42
  | `rogatio test [path] [url...]` | Run offline dry-run tests against `.rogatio.json`. |
43
43
  | `rogatio ai <setup\|ls\|show\|delete\|test>` | AI provider configuration. `setup` interactive; `ls` list; `show` redacted; `delete` remove; `test` connection. |
44
- | `rogatio runtime <install\|uninstall\|verify>` | Register the native-messaging host with `install --extension-id <id>` and trust the device-local CA in one **transactional** call; it needs elevated privileges (Linux `sudo`, macOS keychain authorization, Windows Administrator) and rolls back with exit `1` and `trust unsupported: <reasons>` when a capability or elevation is missing. `uninstall` removes the host manifest, the device-local CA files, and the CA trust installation (idempotent). `verify` reports whether manifest, `runtime-host` wrapper, allowed origins, and CA trust are all valid. |
44
+ | `rogatio runtime <install\|uninstall\|verify>` | Register the native-messaging host with `install` and trust the device-local CA in one **transactional** call; it needs elevated privileges (Linux `sudo`, macOS keychain authorization, Windows Administrator) and rolls back with exit `1` and `trust unsupported: <reasons>` when a capability or elevation is missing. Release users never pass `--extension-id`. That flag is only for development (a local unpacked build without the release key) and for forks. `uninstall` removes the host manifest, the device-local CA files, and the CA trust installation (idempotent). `verify` reports whether manifest, `runtime-host` wrapper, allowed origins, and CA trust are all valid, and fails when `allowed_origins` omits the release ID. Release users never pass `--extension-id` on verify either. |
45
45
  | `rogatio runtime host [path]` | Run the consolidated native-messaging host for the project (pair/authorize/body transforms over stdio). Normally launched by the browser; run manually only for debugging. `--root <dir>` overrides the confined file root; `--mock-port <n>` binds the loopback mock-response faucet. |
46
46
 
47
47
  Global options: `--help, -h` and `--version, -v`. Run `rogatio <command> --help`
@@ -73,8 +73,10 @@ rogatio runtime host .rogatio.json
73
73
  ## Local runtime
74
74
 
75
75
  ```sh
76
- # Register the native-messaging host and trust the device-local CA (needs elevation)
77
- rogatio runtime install --extension-id <id>
76
+ # Register the native-messaging host and trust the device-local CA (needs elevation).
77
+ # Release users never pass --extension-id. It is only for development
78
+ # (a local unpacked build without the release key) and for forks.
79
+ rogatio runtime install
78
80
 
79
81
  # Check manifest, runtime-host wrapper, allowed origins, and CA trust
80
82
  rogatio runtime verify
@@ -2079,11 +2079,11 @@ import { Worker } from "node:worker_threads";
2079
2079
  import { lookup as dnsLookup } from "node:dns/promises";
2080
2080
  import { request as httpRequest } from "node:http";
2081
2081
  import { request as httpsRequest } from "node:https";
2082
- import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
2082
+ import { createHash as createHash2, randomBytes, timingSafeEqual } from "node:crypto";
2083
2083
  import { randomBytes as randomBytes2 } from "node:crypto";
2084
2084
  import { readFile as readFile2, realpath as realpath2, stat } from "node:fs/promises";
2085
2085
  import { isAbsolute as isAbsolute2, relative as relative2, resolve, sep } from "node:path";
2086
- import { createHash as createHash2 } from "node:crypto";
2086
+ import { createHash as createHash3 } from "node:crypto";
2087
2087
  import { mkdir as mkdir2, readFile as readFile3, rename, rm, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
2088
2088
  import { basename, dirname as dirname2, isAbsolute as isAbsolute3, join as join2, relative as relative3 } from "node:path";
2089
2089
  import { generateKeyPairSync, webcrypto } from "node:crypto";
@@ -22056,6 +22056,27 @@ function parseEnvelope(json) {
22056
22056
  };
22057
22057
  return envelope;
22058
22058
  }
22059
+ var RELEASE_EXTENSION_ID = "dkngkciiiabbdjcopbipkpndfmpbmjom";
22060
+ var EXTENSION_ID_RE = /^[a-p]{32}$/;
22061
+ function extensionOrigin(extensionId) {
22062
+ return `chrome-extension://${extensionId}/`;
22063
+ }
22064
+ function extensionOriginListed(extensionId, allowedOrigins) {
22065
+ if (!EXTENSION_ID_RE.test(extensionId)) return false;
22066
+ const origin = extensionOrigin(extensionId);
22067
+ const bare = origin.slice(0, -1);
22068
+ return allowedOrigins.some((value) => value === origin || value === bare);
22069
+ }
22070
+ function describeExtensionIdMismatch(expectedExtensionId, allowedOrigins) {
22071
+ const allowed = allowedOrigins.length > 0 ? allowedOrigins.join(", ") : "(none)";
22072
+ const command = expectedExtensionId === RELEASE_EXTENSION_ID ? "rogatio runtime install" : `rogatio runtime install --extension-id ${expectedExtensionId}`;
22073
+ return [
22074
+ `Extension ID ${expectedExtensionId} is not in the host manifest allowed_origins.`,
22075
+ `The host manifest allows: ${allowed}.`,
22076
+ "Chrome refuses the connection until allowed_origins includes this ID (Access to the specified native messaging host is forbidden).",
22077
+ `Run: ${command}`
22078
+ ].join("\n");
22079
+ }
22059
22080
  function supportedContentType(value) {
22060
22081
  if (value === void 0) return false;
22061
22082
  const mediaType = value.split(";", 1)[0]?.trim().toLowerCase() ?? "";
@@ -23439,7 +23460,7 @@ function createCapabilityState(preset, now) {
23439
23460
  };
23440
23461
  }
23441
23462
  function digestToken(value) {
23442
- return createHash("sha256").update(value, "utf8").digest();
23463
+ return createHash2("sha256").update(value, "utf8").digest();
23443
23464
  }
23444
23465
  function isToken(value) {
23445
23466
  if (typeof value !== "string" || value.length !== TOKEN_LENGTH) return false;
@@ -23657,7 +23678,7 @@ function canonicalPresetBytes(preset, grants = preset.grants) {
23657
23678
  return new TextEncoder().encode(canonical);
23658
23679
  }
23659
23680
  function digestBytes(bytes) {
23660
- return formatSha256(createHash2("sha256").update(bytes).digest("hex"));
23681
+ return formatSha256(createHash3("sha256").update(bytes).digest("hex"));
23661
23682
  }
23662
23683
  var ID_PATTERN2 = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
23663
23684
  function exactKeys(value, required) {
@@ -24942,17 +24963,16 @@ function createRequestBodyTrustController(options = {}) {
24942
24963
  let manifestValid = false;
24943
24964
  let binaryExists = false;
24944
24965
  let binaryExecutable = false;
24945
- let allowedOriginsCount = 0;
24966
+ let allowedOrigins = [];
24946
24967
  try {
24947
24968
  const raw = await readFile3(manifestPath(), "utf8");
24948
24969
  manifestExists = true;
24949
24970
  const parsed = JSON.parse(raw);
24950
24971
  if (isWellFormedManifest(parsed)) {
24951
24972
  manifestValid = true;
24952
- const manifest = parsed;
24953
- allowedOriginsCount = manifest.allowed_origins.length;
24973
+ allowedOrigins = [...parsed.allowed_origins];
24954
24974
  try {
24955
- const fileStat = await stat2(manifest.path);
24975
+ const fileStat = await stat2(parsed.path);
24956
24976
  binaryExists = fileStat.isFile();
24957
24977
  binaryExecutable = (fileStat.mode & 73) !== 0;
24958
24978
  if (!binaryExists) reasons.push("binary-not-found");
@@ -24961,7 +24981,7 @@ function createRequestBodyTrustController(options = {}) {
24961
24981
  binaryExists = false;
24962
24982
  reasons.push("binary-not-found");
24963
24983
  }
24964
- if (allowedOriginsCount === 0) reasons.push("no-allowed-origins");
24984
+ if (allowedOrigins.length === 0) reasons.push("no-allowed-origins");
24965
24985
  } else {
24966
24986
  reasons.push("manifest-invalid");
24967
24987
  }
@@ -24970,7 +24990,7 @@ function createRequestBodyTrustController(options = {}) {
24970
24990
  }
24971
24991
  const caTrusted = await existsFile(caKeyFile) && await existsFile(caCertFile);
24972
24992
  if (!caTrusted) reasons.push("ca-not-trusted");
24973
- const ok = manifestExists && manifestValid && binaryExists && binaryExecutable && caTrusted && allowedOriginsCount > 0;
24993
+ const ok = manifestExists && manifestValid && binaryExists && binaryExecutable && caTrusted && allowedOrigins.length > 0;
24974
24994
  return {
24975
24995
  ok,
24976
24996
  manifestExists,
@@ -24978,7 +24998,8 @@ function createRequestBodyTrustController(options = {}) {
24978
24998
  binaryExists,
24979
24999
  binaryExecutable,
24980
25000
  caTrusted,
24981
- allowedOriginsCount,
25001
+ allowedOriginsCount: allowedOrigins.length,
25002
+ allowedOrigins,
24982
25003
  reasons
24983
25004
  };
24984
25005
  }
@@ -25736,23 +25757,31 @@ Native messaging runtime control for response-body and request-body rules.
25736
25757
 
25737
25758
  Commands:
25738
25759
  install Install the native-messaging host manifest and (on capable
25739
- platforms) provision the device-local CA (requires --extension-id).
25760
+ platforms) provision the device-local CA. Uses the pinned release
25761
+ extension ID. Release users never pass --extension-id. That flag is
25762
+ only for development (a local unpacked build without the release
25763
+ key) and for forks.
25740
25764
  CA trust requires root/admin privileges (Linux: sudo, macOS: keychain
25741
25765
  password, Windows: Administrator). Transactional: when CA trust is
25742
25766
  unavailable the manifest is rolled back and the command exits 1.
25743
25767
  uninstall Remove the native-messaging host manifest and device-local CA trust
25744
25768
  (idempotent).
25745
25769
  verify Check that the manifest, runtime-host wrapper, allowed origins, and
25746
- device-local CA trust are all present and valid.
25770
+ device-local CA trust are all present and valid. Fails when
25771
+ allowed_origins does not include the release ID. Release users
25772
+ never pass --extension-id. That flag is only for development (a
25773
+ local unpacked build without the release key) and for forks.
25747
25774
  host [path] Run the native-messaging runtime host. The browser launches this
25748
25775
  process via the native-messaging manifest to handle rule matching.
25749
25776
 
25750
25777
  The lifecycle of the runtime (start/stop) is driven from the extension's
25751
- Start/Stop controls, not the CLI. Run 'rogatio runtime install --extension-id
25752
- <id>' once to register the host, then use the extension.
25778
+ Start/Stop controls, not the CLI. Run 'rogatio runtime install' once to
25779
+ register the host for a release build, then use the extension.
25753
25780
 
25754
25781
  Options:
25755
- --extension-id Extension ID for native messaging manifest (required for install)
25782
+ --extension-id Extension ID for the native messaging manifest. Release users
25783
+ never need this. It is only for development (a local unpacked
25784
+ build without the release key) and for forks.
25756
25785
  --root <dir> Root for confined runtime file access (host only; default: project directory)
25757
25786
  --mock-port <n> Bind the loopback mock-response faucet to this port (host only, 1-65535)
25758
25787
  --help, -h Show this help
@@ -29961,30 +29990,42 @@ function reportTrust(subcommand, result, okMessage) {
29961
29990
  );
29962
29991
  return 1;
29963
29992
  }
29993
+ function parseExtensionIdFlag(args) {
29994
+ const extIdIndex = args.indexOf("--extension-id");
29995
+ if (extIdIndex === -1) return { extensionId: RELEASE_EXTENSION_ID };
29996
+ const extensionId = args[extIdIndex + 1];
29997
+ if (extensionId === void 0 || extensionId.startsWith("-")) {
29998
+ return {
29999
+ error: "Error: --extension-id requires a value",
30000
+ showHelp: true
30001
+ };
30002
+ }
30003
+ if (!/^[a-p]{32}$/.test(extensionId)) {
30004
+ return {
30005
+ error: "Error: --extension-id must be exactly 32 lowercase characters from a through p",
30006
+ showHelp: false
30007
+ };
30008
+ }
30009
+ return { extensionId };
30010
+ }
29964
30011
  async function trustRuntimeCommand(args) {
29965
30012
  const subcommand = args[0];
29966
- let extensionId;
29967
- if (subcommand === "install") {
29968
- const extIdIndex = args.indexOf("--extension-id");
29969
- if (extIdIndex === -1 || extIdIndex + 1 >= args.length) {
29970
- console.error("Error: --extension-id is required for install command");
29971
- showRuntimeHelp();
29972
- return 2;
29973
- }
29974
- extensionId = args[extIdIndex + 1];
29975
- if (!/^[a-p]{32}$/.test(extensionId)) {
29976
- console.error(
29977
- "Error: --extension-id must be exactly 32 lowercase characters from a through p"
29978
- );
30013
+ let extensionId = RELEASE_EXTENSION_ID;
30014
+ if (subcommand === "install" || subcommand === "verify") {
30015
+ const parsed = parseExtensionIdFlag(args);
30016
+ if ("error" in parsed) {
30017
+ console.error(parsed.error);
30018
+ if (parsed.showHelp) showRuntimeHelp();
29979
30019
  return 2;
29980
30020
  }
30021
+ extensionId = parsed.extensionId;
29981
30022
  }
29982
30023
  const controller = makeTrustController();
29983
30024
  const installRoot = defaultTrustInstallRoot(process.platform);
29984
30025
  const hostPath = join7(installRoot, "runtime-host");
29985
30026
  switch (subcommand) {
29986
30027
  case "install": {
29987
- const installResult = await controller.install(extensionId ?? "");
30028
+ const installResult = await controller.install(extensionId);
29988
30029
  if (installResult.ok) {
29989
30030
  try {
29990
30031
  await writeHostWrapper(hostPath, installRoot);
@@ -29997,7 +30038,9 @@ async function trustRuntimeCommand(args) {
29997
30038
  return reportTrust(
29998
30039
  "install",
29999
30040
  installResult,
30000
- "runtime install complete: manifest + device-local CA trusted + runtime-host wrapper created\nNote: reload the extension at chrome://extensions for the native host to be detected."
30041
+ `runtime install complete: manifest + device-local CA trusted + runtime-host wrapper created
30042
+ Pinned extension ID: ${extensionId}
30043
+ Note: reload the extension at chrome://extensions for the native host to be detected.`
30001
30044
  );
30002
30045
  }
30003
30046
  case "uninstall": {
@@ -30018,9 +30061,11 @@ async function trustRuntimeCommand(args) {
30018
30061
  }
30019
30062
  case "verify": {
30020
30063
  const result = await controller.verify();
30021
- if (result.ok) {
30064
+ const originMismatch = result.manifestValid && result.allowedOrigins.length > 0 && !extensionOriginListed(extensionId, result.allowedOrigins);
30065
+ if (result.ok && !originMismatch) {
30022
30066
  console.log(
30023
- "runtime verification passed: manifest, binary, and CA trust all OK"
30067
+ `runtime verification passed: manifest, binary, and CA trust all OK
30068
+ allowed_origins includes chrome-extension://${extensionId}/`
30024
30069
  );
30025
30070
  return 0;
30026
30071
  }
@@ -30035,10 +30080,18 @@ async function trustRuntimeCommand(args) {
30035
30080
  if (result.allowedOriginsCount === 0)
30036
30081
  console.error(" no allowed_origins in manifest");
30037
30082
  if (!result.caTrusted) console.error(" device-local CA not trusted");
30083
+ if (originMismatch) {
30084
+ for (const line of describeExtensionIdMismatch(
30085
+ extensionId,
30086
+ result.allowedOrigins
30087
+ ).split("\n")) {
30088
+ console.error(` ${line}`);
30089
+ }
30090
+ }
30038
30091
  for (const reason of result.reasons) {
30039
30092
  if (reason === "manifest-not-found")
30040
30093
  console.error(
30041
- " Hint: run 'rogatio runtime install --extension-id <id>' to register the host manifest"
30094
+ " Hint: run 'rogatio runtime install' to register the host manifest (release users never pass --extension-id; it is only for development without the release key, and for forks)"
30042
30095
  );
30043
30096
  if (reason === "binary-not-found")
30044
30097
  console.error(
@@ -30050,11 +30103,11 @@ async function trustRuntimeCommand(args) {
30050
30103
  );
30051
30104
  if (reason === "no-allowed-origins")
30052
30105
  console.error(
30053
- " Hint: re-run 'rogatio runtime install --extension-id <id>' to pin your extension ID"
30106
+ " Hint: re-run 'rogatio runtime install' to pin your extension ID (release users never pass --extension-id; it is only for development without the release key, and for forks)"
30054
30107
  );
30055
30108
  if (reason === "ca-not-trusted")
30056
30109
  console.error(
30057
- " Hint: re-run 'rogatio runtime install --extension-id <id>' with elevated privileges to trust the CA"
30110
+ " Hint: re-run 'rogatio runtime install' with elevated privileges to trust the CA"
30058
30111
  );
30059
30112
  }
30060
30113
  return 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogatio/cli",
3
- "version": "6.7.0",
3
+ "version": "6.7.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Local-first browser request and response rules — editor host, file verification, test runner, and runtime dispatch.",