jorgex-pi 0.8.30 → 0.8.32

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
@@ -132,7 +132,10 @@ Context7 uses the canonical endpoint `https://mcp.context7.com/mcp`. `CONTEXT7_A
132
132
 
133
133
  The runner's `status` and `doctor` commands report whether Context7 configuration permits registration; `available` does not mean that an HTTP handshake has occurred. `sync` fails closed on a Context7 conflict and does not modify MCP configuration. At `session_start`, JorgeX registers Context7 and strict DevTools through the external adapter's runtime event bus; it does not create a bundled adapter or fallback factory. Missing, duplicate, malformed, unreadable, or conflicting official setup fails closed with a remediation to run `engram setup pi` and correct the external configuration.
134
134
 
135
- When a compatible Stack projection provides `PI_CODING_AGENT_DIR/jorgex-pi/devtools.v1.json`, the bridge validates that optional handoff and registers strict DevTools through the external adapter. The handoff must contain only `schemaVersion: 1`, `enabled: true`, an absolute executable command and the canonical `dlx chrome-devtools-mcp@1.6.0 --isolated --redact-network-headers --no-performance-crux --no-usage-statistics` arguments. Pi reads it at bootstrap and never edits it; Stack owns its lifecycle. DevTools is advertised only when that managed handoff is valid and the external adapter accepts the registration. The registration is lazy/proxy (`directTools: false`); `MCP_DIRECT_TOOLS` is not used. Changing or removing the handoff requires reloading Pi because registration is created once per session.
135
+ When a compatible Stack projection provides `PI_CODING_AGENT_DIR/jorgex-pi/devtools.v1.json`, the bridge validates the optional handoff and registers strict DevTools through the external adapter. The historical `schemaVersion: 1` form remains accepted for existing receipts: it requires an absolute executable command and `dlx chrome-devtools-mcp@<exact-stable-version> --isolated --redact-network-headers --no-performance-crux --no-usage-statistics`. The new `schemaVersion: 2` form uses an absolute executable Node command and `args: ["/absolute/path/to/managed-devtools.mjs", "--isolated", "--redact-network-headers", "--no-performance-crux", "--no-usage-statistics"]`; the entry must exist as a regular local JS file, and `dlx` is rejected. Both forms have exactly the four fields `schemaVersion`, `enabled: true`, `command`, and `args`; extra fields and changed flags fail closed. Pi reads but never edits the handoff or fetches browser packages. Stack owns its lifecycle, verifies the installed tree and launcher integrity before execution, and must publish the local form only after verifying the package; **Pi validates shape and executability, not the checksum or ownership of the launcher**. DevTools is advertised only after the handoff and external adapter registration succeed. The registration stays lazy/proxy (`directTools: false`), and a change to the handoff requires reloading Pi.
136
+
137
+ This capability is consumer-only: the currently published Stack release does not produce the v2 handoff. The separate Stack browser PR must implement and verify the byte-bound launcher before enabling new local DevTools installations; publishing Pi alone does not change personal configurations or make v1 `dlx` byte-bound.
138
+ For v2, the executable must resolve to the same Node binary running Pi. If Pi later uses a different Node installation, Stack must re-verify and re-project the local handoff; Pi rejects the old command rather than launching it with an unknown interpreter.
136
139
 
137
140
  The official setup owns the Engram binary, database, and memories. Pi only validates the external configuration needed for the session and never installs, updates, removes, or takes ownership of that state. Child environment handling is provider/runtime-owned; JorgeX defines no environment allowlist. Provider-managed `gentle-engram` hooks may automatically persist session, prompt, and compaction context; JorgeX does not own or configure those hooks.
138
141
 
@@ -176,12 +179,12 @@ The optional Stack handoff is `PI_CODING_AGENT_DIR/jorgex-pi/playwright.v1.json`
176
179
  {
177
180
  "schemaVersion": 1,
178
181
  "enabled": true,
179
- "version": "0.1.18",
182
+ "version": "<exact-stable-version>",
180
183
  "command": "/absolute/path/to/playwright-cli"
181
184
  }
182
185
  ```
183
186
 
184
- Pi accepts the handoff only when `schemaVersion` is `1`, `enabled` is `true`, `version` is exactly `0.1.18`, `command` is absolute and executable, and invoking that path with `--version` returns the pinned version. The resolver does not search `PATH`, inspect browser profiles, open a user session, or test whether a live browser can launch. Stack owns installation of the global CLI and Chromium, including browser readiness checks; the Pi capability check validates only the executable handoff.
187
+ Pi accepts the handoff only when `schemaVersion` is `1`, `enabled` is `true`, `version` is an exact stable semver, `command` is absolute and executable, and invoking that path with `--version` returns exactly the handoff version. The resolver does not search `PATH`, inspect browser profiles, open a user session, or test whether a live browser can launch. Stack owns acquisition of the shared CLI and Chromium, including browser readiness checks; the Pi capability check validates only the executable handoff and does not download, checksum, or authenticate the CLI binary.
185
188
 
186
189
  When the handoff is absent, malformed, or fails the version check, the capability stays hidden and the browser guidance is omitted. A manually written file that satisfies the same schema and executable/version checks is accepted; Pi does not authenticate its provenance. Once a valid handoff is present, Pi exposes the routing with the managed command path. The bootstrap resolves it in `before_agent_start` for each agent turn and does not rewrite it; changing or removing the file is re-evaluated on the next turn. The capability is advertised as `playwright-handoff-v1`; this resolver state does not certify live browser readiness. Its adoption is a separate Stack change and does not alter Pi's canonical snapshot or shared source commit.
187
190
 
@@ -2,8 +2,8 @@
2
2
  "schemaVersion": 1,
3
3
  "package": {
4
4
  "name": "jorgex-pi",
5
- "version": "0.8.30",
6
- "source": "npm:jorgex-pi@0.8.30"
5
+ "version": "0.8.32",
6
+ "source": "npm:jorgex-pi@0.8.32"
7
7
  },
8
8
  "pi": {
9
9
  "minimumVersion": "0.84.2",
@@ -1,4 +1,4 @@
1
- import { accessSync, constants, readFileSync, statSync } from "node:fs";
1
+ import { accessSync, constants, lstatSync, readFileSync, realpathSync, statSync } from "node:fs";
2
2
  import { posix, win32 } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { inspectContext7Config, inspectOfficialPackages, resolvePiAgentDir } from "./context7-config.mjs";
@@ -17,14 +17,15 @@ export const RUNTIME_SNAPSHOT_VERSION = 1;
17
17
  export const CONTEXT7_URL = "https://mcp.context7.com/mcp";
18
18
  const OFFICIAL_ENGRAM_ARGS = ["mcp", "--tools=agent"];
19
19
  const DEVTOOLS_HANDOFF_RELATIVE_PATH = ["jorgex-pi", "devtools.v1.json"];
20
- const DEVTOOLS_HANDOFF_ARGS = [
21
- "dlx",
22
- "chrome-devtools-mcp@1.6.0",
20
+ const DEVTOOLS_PACKAGE_PREFIX = "chrome-devtools-mcp@";
21
+ const STABLE_EXACT_SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
22
+ const DEVTOOLS_FIXED_SUFFIX_ARGS = [
23
23
  "--isolated",
24
24
  "--redact-network-headers",
25
25
  "--no-performance-crux",
26
26
  "--no-usage-statistics",
27
27
  ];
28
+ const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/;
28
29
 
29
30
  // The bridge never invokes setup or writes settings/MCP state. It inspects the
30
31
  // official external setup and reports the state; the bootstrap registers only
@@ -198,23 +199,60 @@ function readChromeDevToolsHandoff({ env, platform }) {
198
199
  if (keys.join("\0") !== ["args", "command", "enabled", "schemaVersion"].join("\0")) {
199
200
  throw new Error(`Chrome DevTools handoff has an invalid schema at ${handoffPath}`);
200
201
  }
201
- if (handoff.schemaVersion !== 1 || handoff.enabled !== true) {
202
+ if ((handoff.schemaVersion !== 1 && handoff.schemaVersion !== 2) || handoff.enabled !== true) {
202
203
  throw new Error(`Chrome DevTools handoff has an unsupported schema at ${handoffPath}`);
203
204
  }
204
- if (typeof handoff.command !== "string" || !paths.isAbsolute(handoff.command)) {
205
+ if (typeof handoff.command !== "string" || CONTROL_CHARACTERS.test(handoff.command) || !paths.isAbsolute(handoff.command)) {
205
206
  throw new Error(`Chrome DevTools handoff command must be an absolute path at ${handoffPath}`);
206
207
  }
207
208
  if (!isExecutable(handoff.command, platform)) {
208
209
  throw new Error(`Chrome DevTools handoff command is not executable at ${handoffPath}`);
209
210
  }
210
- if (!Array.isArray(handoff.args)
211
- || handoff.args.length !== DEVTOOLS_HANDOFF_ARGS.length
212
- || handoff.args.some((arg, index) => arg !== DEVTOOLS_HANDOFF_ARGS[index])) {
211
+ if (handoff.schemaVersion === 2 && !isCurrentNode(handoff.command, paths, platform)) {
212
+ throw new Error(`Chrome DevTools handoff command must be this Pi runtime's Node executable at ${handoffPath}`);
213
+ }
214
+ if (handoff.schemaVersion === 1 ? !isDevToolsArgs(handoff.args) : !isLocalDevToolsArgs(handoff.args, paths)) {
213
215
  throw new Error(`Chrome DevTools handoff has invalid arguments at ${handoffPath}`);
214
216
  }
215
217
  return { command: handoff.command, args: handoff.args };
216
218
  }
217
219
 
220
+ function isDevToolsArgs(args) {
221
+ if (!Array.isArray(args) || args.length !== 6) return false;
222
+ if (args[0] !== "dlx" || !isDevToolsPackageArg(args[1])) return false;
223
+ return args.slice(2).every((arg, index) => arg === DEVTOOLS_FIXED_SUFFIX_ARGS[index]);
224
+ }
225
+
226
+ function isLocalDevToolsArgs(args, paths) {
227
+ if (!Array.isArray(args) || args.length !== DEVTOOLS_FIXED_SUFFIX_ARGS.length + 1) return false;
228
+ const entry = args[0];
229
+ if (typeof entry !== "string" || CONTROL_CHARACTERS.test(entry) || !paths.isAbsolute(entry)
230
+ || ![".js", ".mjs"].includes(paths.extname(entry).toLowerCase())) return false;
231
+ try {
232
+ if (!lstatSync(entry).isFile()) return false;
233
+ } catch {
234
+ return false;
235
+ }
236
+ return args.slice(1).every((arg, index) => arg === DEVTOOLS_FIXED_SUFFIX_ARGS[index]);
237
+ }
238
+
239
+ function isCurrentNode(command, paths, platform) {
240
+ try {
241
+ return samePath(realpathSync(command), realpathSync(process.execPath), paths, platform);
242
+ } catch {
243
+ return false;
244
+ }
245
+ }
246
+
247
+ function isDevToolsPackageArg(arg) {
248
+ if (typeof arg !== "string" || !arg.startsWith(DEVTOOLS_PACKAGE_PREFIX)) return false;
249
+ return isStableExactVersion(arg.slice(DEVTOOLS_PACKAGE_PREFIX.length));
250
+ }
251
+
252
+ function isStableExactVersion(version) {
253
+ return typeof version === "string" && STABLE_EXACT_SEMVER.test(version);
254
+ }
255
+
218
256
  export function resolveConfiguredEngramBinary({
219
257
  env = process.env,
220
258
  platform = process.platform,
@@ -4,7 +4,7 @@ import { homedir } from "node:os";
4
4
  import { posix, win32 } from "node:path";
5
5
 
6
6
  const HANDOFF_RELATIVE_PATH = ["jorgex-pi", "playwright.v1.json"];
7
- const PLAYWRIGHT_VERSION = "0.1.18";
7
+ const STABLE_EXACT_SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
8
8
  const VERSION_TIMEOUT_MS = 5_000;
9
9
  const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/;
10
10
  const WINDOWS_CMD_METACHARACTERS = /[&|<>()^%!"\r\n]/;
@@ -35,7 +35,7 @@ export function resolvePlaywrightCapability({
35
35
  ...(invocation.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}),
36
36
  env: { ...env, NO_UPDATE_NOTIFIER: "1" },
37
37
  });
38
- if (!isExpectedVersion(output)) return hiddenCapability();
38
+ if (!isExpectedVersion(output, handoff.version)) return hiddenCapability();
39
39
 
40
40
  return { status: "ready", commandPath: handoff.command };
41
41
  } catch {
@@ -60,10 +60,10 @@ function readHandoff(handoffPath, paths) {
60
60
  if (!isRecord(parsed)) return undefined;
61
61
  const keys = Object.keys(parsed).sort();
62
62
  if (keys.join("\0") !== ["command", "enabled", "schemaVersion", "version"].join("\0")) return undefined;
63
- if (parsed.schemaVersion !== 1 || parsed.enabled !== true || parsed.version !== PLAYWRIGHT_VERSION) return undefined;
63
+ if (parsed.schemaVersion !== 1 || parsed.enabled !== true || !isStableExactVersion(parsed.version)) return undefined;
64
64
  if (typeof parsed.command !== "string" || CONTROL_CHARACTERS.test(parsed.command)) return undefined;
65
65
  if (!paths.isAbsolute(parsed.command)) return undefined;
66
- return { command: parsed.command };
66
+ return { command: parsed.command, version: parsed.version };
67
67
  }
68
68
 
69
69
  function resolveDefaultAgentDir({ env, platform, paths }) {
@@ -97,8 +97,14 @@ function planExecutable(command, platform, env) {
97
97
  };
98
98
  }
99
99
 
100
- function isExpectedVersion(output) {
101
- return /^(?:playwright-cli\s+)?0\.1\.18$/i.test(String(output).trim());
100
+ function isExpectedVersion(output, version) {
101
+ const trimmed = String(output).trim();
102
+ const reported = /^playwright-cli\s+/i.test(trimmed) ? trimmed.replace(/^playwright-cli\s+/i, "") : trimmed;
103
+ return reported === version;
104
+ }
105
+
106
+ function isStableExactVersion(version) {
107
+ return typeof version === "string" && STABLE_EXACT_SEMVER.test(version);
102
108
  }
103
109
 
104
110
  function isExecutable(path, platform) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jorgex-pi",
3
- "version": "0.8.30",
3
+ "version": "0.8.32",
4
4
  "description": "Pi-native runtime package for the JorgeX harness.",
5
5
  "license": "MIT",
6
6
  "repository": {