jorgex-stack 1.9.66 → 1.9.67

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/dist/cli.js CHANGED
@@ -500,9 +500,9 @@ function readSystemPromptFile(target, rootDir) {
500
500
  if (rootDir !== void 0) {
501
501
  const probe = fs4.existsSync(target) ? target : path5.dirname(target);
502
502
  if (fs4.existsSync(probe) && fs4.existsSync(rootDir)) {
503
- const resolved2 = fs4.realpathSync(probe);
503
+ const resolved3 = fs4.realpathSync(probe);
504
504
  const root = fs4.realpathSync(rootDir);
505
- if (resolved2 !== root && !isContainedIn(resolved2, root)) {
505
+ if (resolved3 !== root && !isContainedIn(resolved3, root)) {
506
506
  throw new Error(`El prompt ${target} sale del destino aislado.`);
507
507
  }
508
508
  }
@@ -1276,12 +1276,12 @@ function sameIdentity(a, b) {
1276
1276
  return a.dev === b.dev && a.ino === b.ino && a.size === b.size && a.mtimeMs === b.mtimeMs && a.ctimeMs === b.ctimeMs;
1277
1277
  }
1278
1278
  function assertDirectory(configDir) {
1279
- const resolved2 = path11.resolve(configDir);
1280
- const stat = fs7.lstatSync(resolved2);
1279
+ const resolved3 = path11.resolve(configDir);
1280
+ const stat = fs7.lstatSync(resolved3);
1281
1281
  if (!stat.isDirectory() || stat.isSymbolicLink()) {
1282
- throw new Error(`Pi MCP config directory is not a real directory: ${resolved2}`);
1282
+ throw new Error(`Pi MCP config directory is not a real directory: ${resolved3}`);
1283
1283
  }
1284
- return resolved2;
1284
+ return resolved3;
1285
1285
  }
1286
1286
  function adapterPackagePath(configDir) {
1287
1287
  return path11.join(configDir, "npm", "node_modules", ADAPTER_PACKAGE, "package.json");
@@ -1558,6 +1558,16 @@ function migrateOfficialPiMcpConfig(input) {
1558
1558
  if (!removeExactSource(legacyPath, source)) return { migrated: false, backupPath };
1559
1559
  return { migrated: destination === null, backupPath };
1560
1560
  }
1561
+ function declaredPiMcpConfigFiles(stageDir) {
1562
+ const file = path11.join(stageDir, "npm", "node_modules", "jorgex-pi", "contract", "jorgex-pi.v1.json");
1563
+ const contract = JSON.parse(fs7.readFileSync(file, "utf8"));
1564
+ const declaration = contract.mcpAdapterConfig;
1565
+ if (declaration === void 0) return [];
1566
+ if (declaration.schemaVersion !== 1 || !Array.isArray(declaration.files) || !declaration.files.every((file2) => typeof file2 === "string")) {
1567
+ throw new Error("El candidato Pi declara un contrato MCP inv\xE1lido.");
1568
+ }
1569
+ return declaration.files;
1570
+ }
1561
1571
  var ADAPTER_PACKAGE, MAX_CONFIG_BYTES, SEMVER, OFFICIAL_ARGS;
1562
1572
  var init_pi_mcp_config = __esm({
1563
1573
  "src/lib/pi-mcp-config.ts"() {
@@ -1633,15 +1643,15 @@ function resolveTrustedNpmRoots(runtime, preciseTargets) {
1633
1643
  if (runtime !== "pi") return [];
1634
1644
  const roots = /* @__PURE__ */ new Set();
1635
1645
  for (const t of preciseTargets) {
1636
- const resolved2 = path12.resolve(t);
1637
- if (path12.basename(resolved2) === "npm") roots.add(resolved2);
1646
+ const resolved3 = path12.resolve(t);
1647
+ if (path12.basename(resolved3) === "npm") roots.add(resolved3);
1638
1648
  }
1639
1649
  return [...roots];
1640
1650
  }
1641
1651
  function findTrustedRootFor(linkPath, trustedRoots) {
1642
- const resolved2 = path12.resolve(linkPath);
1652
+ const resolved3 = path12.resolve(linkPath);
1643
1653
  for (const root of trustedRoots) {
1644
- if (isContainedIn(resolved2, root)) return root;
1654
+ if (isContainedIn(resolved3, root)) return root;
1645
1655
  }
1646
1656
  return null;
1647
1657
  }
@@ -1865,17 +1875,17 @@ function restoreTrustedSymlinks(snapshot, trustedRoots) {
1865
1875
  function findSetupSymlinkViolation(targets, homeDir, trustedRoots = []) {
1866
1876
  const homeResolved = path12.resolve(homeDir);
1867
1877
  for (const target of targets) {
1868
- const resolved2 = path12.resolve(target);
1878
+ const resolved3 = path12.resolve(target);
1869
1879
  try {
1870
- if (fs8.lstatSync(resolved2).isSymbolicLink()) {
1871
- return `${resolved2}: el target es un symlink (no se respalda ni se ejecuta setup)`;
1880
+ if (fs8.lstatSync(resolved3).isSymbolicLink()) {
1881
+ return `${resolved3}: el target es un symlink (no se respalda ni se ejecuta setup)`;
1872
1882
  }
1873
1883
  } catch (error) {
1874
1884
  if (errnoCode2(error) !== "ENOENT") {
1875
- return `${resolved2}: ilegible (${errnoCode2(error)}, no se puede descartar alias)`;
1885
+ return `${resolved3}: ilegible (${errnoCode2(error)}, no se puede descartar alias)`;
1876
1886
  }
1877
1887
  }
1878
- let dir = path12.dirname(resolved2);
1888
+ let dir = path12.dirname(resolved3);
1879
1889
  while (dir !== homeResolved && isContainedIn(dir, homeResolved)) {
1880
1890
  try {
1881
1891
  if (fs8.lstatSync(dir).isSymbolicLink()) {
@@ -1891,8 +1901,8 @@ function findSetupSymlinkViolation(targets, homeDir, trustedRoots = []) {
1891
1901
  dir = parent;
1892
1902
  }
1893
1903
  try {
1894
- if (fs8.statSync(resolved2).isDirectory()) {
1895
- const bad = findSymlinkInTree(resolved2, trustedRoots);
1904
+ if (fs8.statSync(resolved3).isDirectory()) {
1905
+ const bad = findSymlinkInTree(resolved3, trustedRoots);
1896
1906
  if (bad !== null) return bad;
1897
1907
  }
1898
1908
  } catch {
@@ -2077,11 +2087,11 @@ async function runOfficialSetup(runtime, deps) {
2077
2087
  cleanupFailed = true;
2078
2088
  }
2079
2089
  }
2080
- const incomplete = restoreFailed || symlinkRestoreFailed || cleanupFailed;
2090
+ const incomplete2 = restoreFailed || symlinkRestoreFailed || cleanupFailed;
2081
2091
  const combinedRestoreError = [restoreError, symlinkRestoreError].filter((p7) => p7 !== void 0).join("; ") || void 0;
2082
2092
  restoreError = combinedRestoreError;
2083
2093
  const detail = [spawnResult.stderr, verifyResult.stderr, verifyResult.reason, (verifyResult.layers ?? []).join(", ")].map((part) => (part ?? "").trim()).filter((part) => part !== "").join("; ") || "setup oficial fall\xF3";
2084
- const recoveryDetail = incomplete ? `${detail}; recuperaci\xF3n incompleta${restoreError !== void 0 ? `: ${restoreError}` : ""}` : detail;
2094
+ const recoveryDetail = incomplete2 ? `${detail}; recuperaci\xF3n incompleta${restoreError !== void 0 ? `: ${restoreError}` : ""}` : detail;
2085
2095
  return {
2086
2096
  ok: false,
2087
2097
  ownershipTransferred: false,
@@ -2089,8 +2099,8 @@ async function runOfficialSetup(runtime, deps) {
2089
2099
  stderr: recoveryDetail,
2090
2100
  reason: recoveryDetail,
2091
2101
  ...verifyResult.layers === void 0 ? {} : { layers: verifyResult.layers },
2092
- ...incomplete ? { incompleteRecovery: true } : {},
2093
- recovery: incomplete ? "incomplete" : "complete",
2102
+ ...incomplete2 ? { incompleteRecovery: true } : {},
2103
+ recovery: incomplete2 ? "incomplete" : "complete",
2094
2104
  backupId,
2095
2105
  ...restoreError === void 0 ? {} : { restoreError }
2096
2106
  };
@@ -2169,9 +2179,9 @@ function validateOfficialSetupDestination(runtime, configDir, homeDir) {
2169
2179
  return `runOfficialSetupIfNeeded: configDir OpenCode incompatible (${configDir}); el provider solo alinea <parent>/opencode v\xEDa XDG_CONFIG_HOME.`;
2170
2180
  }
2171
2181
  if (runtime === "pi") {
2172
- const resolved2 = path12.resolve(configDir);
2182
+ const resolved3 = path12.resolve(configDir);
2173
2183
  const homeResolved = path12.resolve(homeDir);
2174
- if (resolved2 !== homeResolved && !isContainedIn(resolved2, homeResolved)) {
2184
+ if (resolved3 !== homeResolved && !isContainedIn(resolved3, homeResolved)) {
2175
2185
  return `runOfficialSetupIfNeeded: Pi PI_CODING_AGENT_DIR (${configDir}) fuera de HOME (${homeDir}): queda fuera de la frontera de restore acotada a HOME; usa un PI_CODING_AGENT_DIR dentro de HOME o ajusta HOME antes de reintentar.`;
2176
2186
  }
2177
2187
  return null;
@@ -3303,12 +3313,12 @@ function resolveStrictInstallPath(configDir) {
3303
3313
  return { ok: false, detail: `installPath:missing (absoluto requerido en ${registryFile})` };
3304
3314
  }
3305
3315
  const expectedRoot = path14.resolve(path14.join(configDir, "plugins", "cache", "engram"));
3306
- const resolved2 = path14.resolve(installPath);
3307
- if (resolved2 !== expectedRoot && !resolved2.startsWith(expectedRoot + path14.sep)) {
3308
- return { ok: false, detail: `installPath:foreign (${resolved2} fuera de ${expectedRoot})` };
3316
+ const resolved3 = path14.resolve(installPath);
3317
+ if (resolved3 !== expectedRoot && !resolved3.startsWith(expectedRoot + path14.sep)) {
3318
+ return { ok: false, detail: `installPath:foreign (${resolved3} fuera de ${expectedRoot})` };
3309
3319
  }
3310
3320
  const configResolved = path14.resolve(configDir);
3311
- let cursor = resolved2;
3321
+ let cursor = resolved3;
3312
3322
  while (cursor !== null && cursor !== configResolved) {
3313
3323
  try {
3314
3324
  if (fs10.lstatSync(cursor).isSymbolicLink()) {
@@ -3325,13 +3335,13 @@ function resolveStrictInstallPath(configDir) {
3325
3335
  cursor = parent === configResolved || parent.startsWith(configResolved + path14.sep) ? parent : null;
3326
3336
  }
3327
3337
  try {
3328
- if (!fs10.statSync(resolved2).isDirectory()) {
3338
+ if (!fs10.statSync(resolved3).isDirectory()) {
3329
3339
  return { ok: false, detail: "installPath: no es un directorio" };
3330
3340
  }
3331
3341
  } catch {
3332
3342
  return { ok: false, detail: "installPath:missing (no existe)" };
3333
3343
  }
3334
- return { ok: true, installPath: resolved2 };
3344
+ return { ok: true, installPath: resolved3 };
3335
3345
  }
3336
3346
  function checkStrictEnabled(configDir) {
3337
3347
  const settingsFile = path14.join(configDir, "settings.json");
@@ -3434,12 +3444,12 @@ function checkClaudeOfficialHooksAt(installPath) {
3434
3444
  }
3435
3445
  async function verifyOfficialSetup2(args) {
3436
3446
  const explicit = isExplicitClaudeMode(args.isExplicitClaudeConfigDir);
3437
- const resolved2 = resolveStrictInstallPath(args.configDir);
3447
+ const resolved3 = resolveStrictInstallPath(args.configDir);
3438
3448
  const enabled = checkStrictEnabled(args.configDir);
3439
- const hasPlugin = resolved2.ok && enabled.ok;
3440
- const pluginDetail = !resolved2.ok ? resolved2.detail ?? "plugin:missing" : !enabled.ok ? enabled.detail ?? "plugin:disabled (enabledPlugins)" : null;
3449
+ const hasPlugin = resolved3.ok && enabled.ok;
3450
+ const pluginDetail = !resolved3.ok ? resolved3.detail ?? "plugin:missing" : !enabled.ok ? enabled.detail ?? "plugin:disabled (enabledPlugins)" : null;
3441
3451
  const hasMcp = typeof args.engramBin === "string" && args.engramBin !== "" ? checkClaudeOfficialMcp(args.configDir, args.engramBin, args.homeDir, explicit) : false;
3442
- const hasHooks = resolved2.ok && resolved2.installPath !== void 0 ? checkClaudeOfficialHooksAt(resolved2.installPath) : false;
3452
+ const hasHooks = resolved3.ok && resolved3.installPath !== void 0 ? checkClaudeOfficialHooksAt(resolved3.installPath) : false;
3443
3453
  const passed = [];
3444
3454
  const missing = [];
3445
3455
  if (hasPlugin) passed.push("plugin");
@@ -4416,6 +4426,7 @@ args = [${(server.args ?? []).map(tomlString).join(", ")}]`);
4416
4426
  // src/adapters/pi.ts
4417
4427
  var pi_exports = {};
4418
4428
  __export(pi_exports, {
4429
+ isNamedPiSource: () => isNamedPiSource,
4419
4430
  piAdapter: () => piAdapter,
4420
4431
  piSystemPromptFile: () => piSystemPromptFile,
4421
4432
  verifyOfficialSetup: () => verifyOfficialSetup4
@@ -4676,7 +4687,7 @@ var init_pi = __esm({
4676
4687
 
4677
4688
  // src/cli.ts
4678
4689
  import * as p6 from "@clack/prompts";
4679
- import fs45 from "fs";
4690
+ import fs49 from "fs";
4680
4691
  import { pathToFileURL as pathToFileURL2 } from "url";
4681
4692
 
4682
4693
  // src/install.ts
@@ -5356,16 +5367,16 @@ function assertRegularFile(file, label) {
5356
5367
  return stat;
5357
5368
  }
5358
5369
  function assertContained2(realRoot, candidate, label) {
5359
- let resolved2;
5370
+ let resolved3;
5360
5371
  try {
5361
- resolved2 = fs19.realpathSync(candidate);
5372
+ resolved3 = fs19.realpathSync(candidate);
5362
5373
  } catch {
5363
5374
  fail2(`cannot resolve ${label}: ${candidate}`);
5364
5375
  }
5365
- if (resolved2 !== realRoot && !resolved2.startsWith(realRoot + path25.sep)) {
5376
+ if (resolved3 !== realRoot && !resolved3.startsWith(realRoot + path25.sep)) {
5366
5377
  fail2(`${label} escapes the stage`);
5367
5378
  }
5368
- return resolved2;
5379
+ return resolved3;
5369
5380
  }
5370
5381
  function canonicalTarballUrl2(packageName, version) {
5371
5382
  const shortName = packageName.slice(packageName.lastIndexOf("/") + 1);
@@ -5531,8 +5542,8 @@ function localTarballPath(spec, stageDir) {
5531
5542
  if (!spec.startsWith("file:") || spec.startsWith("file://")) fail2("root lock source is not a local file");
5532
5543
  const relative = spec.slice("file:".length);
5533
5544
  if (relative === "" || relative.includes("\0")) fail2("root lock source is invalid");
5534
- const resolved2 = path25.resolve(stageDir, relative);
5535
- return resolved2;
5545
+ const resolved3 = path25.resolve(stageDir, relative);
5546
+ return resolved3;
5536
5547
  }
5537
5548
  function parseLockPackages(lock, packageName, release, stageDir, artifactPath) {
5538
5549
  if (!isRecord4(lock) || lock.lockfileVersion !== "9.0" || !isRecord4(lock.packages)) {
@@ -6231,6 +6242,29 @@ function realpathOrFail(file, label) {
6231
6242
  fail3(`cannot resolve ${label}: ${file}`);
6232
6243
  }
6233
6244
  }
6245
+ function assertBrowserExecutable(value) {
6246
+ const executable = absolutePath(value, "browser executable");
6247
+ if (executable !== value || CONTROL_CHARACTERS2.test(executable)) fail3("browser executable path must be canonical and contain no control characters");
6248
+ const stat = lstatOrFail2(executable, "browser executable");
6249
+ if (!stat.isFile() || stat.isSymbolicLink()) fail3("browser executable must be a regular non-symlink file");
6250
+ if (realpathOrFail(executable, "browser executable") !== executable) fail3("browser executable path contains a symlink");
6251
+ try {
6252
+ fs20.accessSync(executable, process.platform === "win32" ? fs20.constants.F_OK : fs20.constants.X_OK);
6253
+ } catch {
6254
+ fail3("browser executable is not executable");
6255
+ }
6256
+ return executable;
6257
+ }
6258
+ function detectChromiumExecutable() {
6259
+ const candidates = process.platform === "linux" ? ["/usr/bin/chromium-browser", "/usr/bin/chromium"] : process.platform === "darwin" ? ["/Applications/Chromium.app/Contents/MacOS/Chromium"] : process.platform === "win32" ? ["C:\\Program Files\\Chromium\\Application\\chrome.exe", "C:\\Program Files (x86)\\Chromium\\Application\\chrome.exe"] : [];
6260
+ for (const candidate of candidates) {
6261
+ try {
6262
+ return assertBrowserExecutable(fs20.realpathSync(candidate));
6263
+ } catch {
6264
+ }
6265
+ }
6266
+ return void 0;
6267
+ }
6234
6268
  function assertRealDirectory2(directory, label) {
6235
6269
  const stat = lstatOrFail2(directory, label);
6236
6270
  if (!stat.isDirectory() || stat.isSymbolicLink()) fail3(`${label} must be a real directory: ${directory}`);
@@ -6253,9 +6287,9 @@ function isContained(root, candidate, allowEqual) {
6253
6287
  return relative !== ".." && !relative.startsWith(`..${path26.sep}`) && !path26.isAbsolute(relative);
6254
6288
  }
6255
6289
  function assertContained3(root, candidate, label) {
6256
- const resolved2 = realpathOrFail(candidate, label);
6257
- if (!isContained(root, resolved2, false)) fail3(`${label} escapes its root`);
6258
- return resolved2;
6290
+ const resolved3 = realpathOrFail(candidate, label);
6291
+ if (!isContained(root, resolved3, false)) fail3(`${label} escapes its root`);
6292
+ return resolved3;
6259
6293
  }
6260
6294
  function assertStateDirectory(input) {
6261
6295
  const directory = absolutePath(input, "stateDir");
@@ -6324,6 +6358,8 @@ function validateStagedInput(input) {
6324
6358
  fail3("unsupported managed browser package");
6325
6359
  }
6326
6360
  const packageName = input.packageName;
6361
+ if (input.browserExecutablePath !== void 0 && packageName !== "chrome-devtools-mcp") fail3("browser executable selector is supported only for DevTools");
6362
+ const browserExecutablePath = input.browserExecutablePath === void 0 ? void 0 : assertBrowserExecutable(input.browserExecutablePath);
6327
6363
  const stateDir = assertStateDirectory(input.stateDir);
6328
6364
  const release = validateRelease(input.release);
6329
6365
  if (!isRecord5(input.staged)) fail3("staged tree evidence must be an object");
@@ -6348,7 +6384,7 @@ function validateStagedInput(input) {
6348
6384
  if (relativeEntry === "" || relativeEntry === ".." || relativeEntry.startsWith(`..${path26.sep}`) || path26.isAbsolute(relativeEntry)) {
6349
6385
  fail3("entryPath must be contained by staged node_modules");
6350
6386
  }
6351
- return { stateDir, packageName, release, nodeModulesPath, treePath: realTreePath, entryPath, treeSha256, closure };
6387
+ return { stateDir, packageName, release, nodeModulesPath, treePath: realTreePath, entryPath, treeSha256, closure, browserExecutablePath };
6352
6388
  }
6353
6389
  async function acquireLock(lockPath) {
6354
6390
  for (let attempt = 0; attempt < LOCK_ATTEMPTS; attempt += 1) {
@@ -6429,7 +6465,7 @@ function resolveStagedBrowserEntry(staged, packageName) {
6429
6465
  if (!stat.isFile() || stat.isSymbolicLink() || !isContained(realRoot, realEntry, false)) {
6430
6466
  fail3("staged package bin must be a regular file in its root");
6431
6467
  }
6432
- return entry;
6468
+ return realEntry;
6433
6469
  }
6434
6470
  function readOptionalRegularFile(file, label, maxBytes) {
6435
6471
  let stat;
@@ -6611,7 +6647,7 @@ function hashRegularFile(file) {
6611
6647
  }
6612
6648
  }
6613
6649
  }
6614
- function launcherSource(nodeModulesPath, entryPath, expectedTreeSha256) {
6650
+ function launcherSource(nodeModulesPath, entryPath, expectedTreeSha256, browserExecutablePath) {
6615
6651
  return `const fs = (await import("node:fs")).default;
6616
6652
  const path = (await import("node:path")).default;
6617
6653
  const { createHash } = await import("node:crypto");
@@ -6754,7 +6790,8 @@ function treeHash(tree) {
6754
6790
 
6755
6791
  if (treeHash(root) !== expected) throw new Error("managed browser tree digest drifted before launch");
6756
6792
  process.argv[1] = entry;
6757
- await import(pathToFileURL(entry).href);
6793
+ ${browserExecutablePath === void 0 ? "" : `process.argv.push(${JSON.stringify(`--executablePath=${browserExecutablePath}`)});
6794
+ `}await import(pathToFileURL(entry).href);
6758
6795
  `;
6759
6796
  }
6760
6797
  function managedTreeVerifierSource() {
@@ -7015,7 +7052,13 @@ function writeActivePointerAtomic(packageDir, pointer, previous) {
7015
7052
  }
7016
7053
  }
7017
7054
  function sameVerifiedCandidate(receipt, prepared) {
7018
- return receipt.packageName === prepared.packageName && receipt.version === prepared.release.version && receipt.integrity === prepared.release.integrity && receipt.treeSha256 === prepared.treeSha256 && JSON.stringify(receipt.closure) === JSON.stringify(prepared.closure);
7055
+ const expectedEntry = path26.join(receipt.treePath, path26.relative(prepared.nodeModulesPath, prepared.entryPath));
7056
+ return receipt.packageName === prepared.packageName && receipt.version === prepared.release.version && receipt.integrity === prepared.release.integrity && receipt.treeSha256 === prepared.treeSha256 && JSON.stringify(receipt.closure) === JSON.stringify(prepared.closure) && receipt.entryPath === expectedEntry && receipt.launcherSha256 === createHash3("sha256").update(launcherSource(
7057
+ receipt.treePath,
7058
+ expectedEntry,
7059
+ prepared.treeSha256,
7060
+ prepared.browserExecutablePath
7061
+ )).digest("hex");
7019
7062
  }
7020
7063
  async function activateManagedBrowserTree(input) {
7021
7064
  const prepared = validateStagedInput(input);
@@ -7071,7 +7114,7 @@ async function activateManagedBrowserTree(input) {
7071
7114
  const finalNodeModules = path26.join(finalRoot, "node_modules");
7072
7115
  const finalEntry = path26.join(finalNodeModules, relativeEntry);
7073
7116
  const launcherPath = path26.join(finalRoot, "launcher.mjs");
7074
- writeNewFile(launcherPath, launcherSource(finalNodeModules, finalEntry, prepared.treeSha256), 448);
7117
+ writeNewFile(launcherPath, launcherSource(finalNodeModules, finalEntry, prepared.treeSha256, prepared.browserExecutablePath), 448);
7075
7118
  const launcherSha256 = hashRegularFile(launcherPath);
7076
7119
  const receipt = {
7077
7120
  schemaVersion: 1,
@@ -7208,14 +7251,15 @@ async function activateVerifiedBrowserArtifact(context, options) {
7208
7251
  fetchImpl: options.fetchImpl
7209
7252
  });
7210
7253
  if (context.packageName === "chrome-devtools-mcp") {
7211
- await verifyStagedDevtoolsCliArtifact({ stageDir, staged, release: context.release });
7254
+ await verifyStagedDevtoolsCliArtifact({ stageDir, staged, release: context.release, browserExecutablePath: options.browserExecutablePath });
7212
7255
  }
7213
7256
  return activateManagedBrowserTree({
7214
7257
  stateDir: options.stateDir,
7215
7258
  packageName: context.packageName,
7216
7259
  release: context.release,
7217
7260
  staged,
7218
- entryPath: resolveStagedBrowserEntry(staged, context.packageName)
7261
+ entryPath: resolveStagedBrowserEntry(staged, context.packageName),
7262
+ browserExecutablePath: options.browserExecutablePath
7219
7263
  });
7220
7264
  }
7221
7265
  var PLAYWRIGHT_PKG = "@playwright/cli";
@@ -7332,10 +7376,12 @@ const { parseArguments } = await import(pathToFileURL(process.argv[1]).href);
7332
7376
  if (typeof parseArguments !== "function") process.exit(1);
7333
7377
  const parsed = parseArguments("probe", [
7334
7378
  process.execPath, process.argv[1],
7335
- "--isolated", "--redact-network-headers", "--no-performance-crux", "--no-usage-statistics"
7379
+ "--isolated", "--redact-network-headers", "--no-performance-crux", "--no-usage-statistics",
7380
+ ...(process.argv[2] === undefined ? [] : ["--executablePath=" + process.argv[2]])
7336
7381
  ], process.env);
7337
7382
  process.stdout.write(JSON.stringify([
7338
- parsed.isolated, parsed.redactNetworkHeaders, parsed.performanceCrux, parsed.usageStatistics
7383
+ parsed.isolated, parsed.redactNetworkHeaders, parsed.performanceCrux, parsed.usageStatistics,
7384
+ ...(process.argv[2] === undefined ? [] : [parsed.executablePath])
7339
7385
  ]));
7340
7386
  `;
7341
7387
  async function verifyStagedDevtoolsCliArtifact(input, deps) {
@@ -7351,18 +7397,18 @@ async function verifyStagedDevtoolsCliArtifact(input, deps) {
7351
7397
  const run = deps?.run ?? defaultDevtoolsCliRun;
7352
7398
  if (typeof run !== "function") fail4("run must be a function");
7353
7399
  const env = restrictedStageEnv(stageDir);
7354
- await assertDevtoolsPrivacyProof(run, realBinPath, packageDir, stageDir, env);
7400
+ await assertDevtoolsPrivacyProof(run, realBinPath, packageDir, stageDir, env, input.browserExecutablePath);
7355
7401
  return { binPath, version: release.version };
7356
7402
  }
7357
7403
  function isRecord6(value) {
7358
7404
  return value !== null && typeof value === "object" && !Array.isArray(value);
7359
7405
  }
7360
7406
  function assertInsideStage(realStage, candidate, label) {
7361
- const resolved2 = path27.resolve(candidate);
7362
- if (resolved2 !== realStage && !resolved2.startsWith(realStage + path27.sep)) {
7407
+ const resolved3 = path27.resolve(candidate);
7408
+ if (resolved3 !== realStage && !resolved3.startsWith(realStage + path27.sep)) {
7363
7409
  fail4(`${label} escapes the stage`);
7364
7410
  }
7365
- return resolved2;
7411
+ return resolved3;
7366
7412
  }
7367
7413
  async function defaultDevtoolsCliRun(command, args, options) {
7368
7414
  const planned = planDetectedBinCommand(command, args);
@@ -7423,11 +7469,12 @@ async function runDevtoolsCli(run, command, args, cwd, env, label) {
7423
7469
  throw asBrowserError(`${label} failed: ${error instanceof Error ? error.message : String(error)}`);
7424
7470
  }
7425
7471
  }
7426
- async function assertDevtoolsPrivacyProof(run, scriptPath, realPackageDir, stageDir, env) {
7472
+ async function assertDevtoolsPrivacyProof(run, scriptPath, realPackageDir, stageDir, env, browserExecutablePath) {
7473
+ const selectorArgs = browserExecutablePath === void 0 ? [] : [`--executablePath=${browserExecutablePath}`];
7427
7474
  const help = await runDevtoolsCli(
7428
7475
  run,
7429
7476
  process.execPath,
7430
- [scriptPath, ...DEVTOOLS_PRIVACY_FLAGS2, "--help"],
7477
+ [scriptPath, ...DEVTOOLS_PRIVACY_FLAGS2, ...selectorArgs, "--help"],
7431
7478
  stageDir,
7432
7479
  env,
7433
7480
  "devtools help probe"
@@ -7441,6 +7488,9 @@ async function assertDevtoolsPrivacyProof(run, scriptPath, realPackageDir, stage
7441
7488
  fail4(`devtools help is missing ${flag}`);
7442
7489
  }
7443
7490
  }
7491
+ if (browserExecutablePath !== void 0 && !help.stdout.includes("--executablePath")) {
7492
+ fail4("devtools help is missing --executablePath");
7493
+ }
7444
7494
  const parserModule = path27.join(realPackageDir, "build", "src", "config", "mcp-options.js");
7445
7495
  let realParserModule;
7446
7496
  try {
@@ -7455,13 +7505,14 @@ async function assertDevtoolsPrivacyProof(run, scriptPath, realPackageDir, stage
7455
7505
  const parserProbe = await runDevtoolsCli(
7456
7506
  run,
7457
7507
  process.execPath,
7458
- ["--input-type=module", "-e", DEVTOOLS_PARSER_PROBE, realParserModule],
7508
+ ["--input-type=module", "-e", DEVTOOLS_PARSER_PROBE, realParserModule, ...browserExecutablePath === void 0 ? [] : [browserExecutablePath]],
7459
7509
  stageDir,
7460
7510
  probeEnv,
7461
7511
  "devtools parser probe"
7462
7512
  );
7463
- if (parserProbe.status !== 0 || parserProbe.stdout?.trim() !== "[true,true,false,false]") {
7464
- fail4("devtools parser does not honor the mandatory privacy flags");
7513
+ const expectedParsed = [true, true, false, false, ...browserExecutablePath === void 0 ? [] : [browserExecutablePath]];
7514
+ if (parserProbe.status !== 0 || parserProbe.stdout?.trim() !== JSON.stringify(expectedParsed)) {
7515
+ fail4("devtools parser does not honor the mandatory privacy flags or browser executable selector");
7465
7516
  }
7466
7517
  }
7467
7518
 
@@ -7692,8 +7743,8 @@ function primaryModelOwnershipError(file = primaryModelOwnershipFile()) {
7692
7743
  return `Primary model: ownership inv\xE1lido en ${file}. Corrige o borra ese archivo antes de reintentar.`;
7693
7744
  }
7694
7745
  function primaryModelConfigKey(configDir) {
7695
- const resolved2 = path28.resolve(configDir);
7696
- return process.platform === "win32" ? resolved2.toLowerCase() : resolved2;
7746
+ const resolved3 = path28.resolve(configDir);
7747
+ return process.platform === "win32" ? resolved3.toLowerCase() : resolved3;
7697
7748
  }
7698
7749
  function loadPrimaryModelOwnership(file, runtime, configDir) {
7699
7750
  const key = primaryModelConfigKey(configDir);
@@ -8682,7 +8733,7 @@ async function runInstall(opts) {
8682
8733
  p.log.error("Playwright CLI y navegador se han instalado y la preferencia qued\xF3 activa, pero la gu\xEDa de navegador qued\xF3 en estado parcial. Ejecuta 'jorgex-stack sync' para repararla.");
8683
8734
  } else {
8684
8735
  const verified = verifiedCapability ?? (preparedEnv === void 0 ? inspectPlaywrightCapability({ browserVerified: true, expectedVersion: candidate.version }) : inspectPlaywrightCapability({ browserVerified: true, env: preparedEnv, expectedVersion: candidate.version }));
8685
- if (hasInjectedActivation && verified.effective && verified.cli.status === "current" && verified.cli.binPath !== null && verified.cli.detectedVersion !== null && verified.browserCache.status === "ready") {
8736
+ if (verified.effective && verified.cli.status === "current" && verified.cli.binPath !== null && verified.cli.detectedVersion !== null && verified.browserCache.status === "ready") {
8686
8737
  opts.onPlaywrightCapability?.(verified);
8687
8738
  }
8688
8739
  p.log.success("Playwright CLI instalado y arranque de Chromium verificado.");
@@ -8818,12 +8869,12 @@ function validateRelease2(release) {
8818
8869
  return { expectedSha512: assertCanonicalSha512(integrity, "release integrity"), version };
8819
8870
  }
8820
8871
  function assertRealDir(p7, label) {
8821
- const resolved2 = path32.resolve(p7);
8822
- const st = lstatOrNull(resolved2);
8872
+ const resolved3 = path32.resolve(p7);
8873
+ const st = lstatOrNull(resolved3);
8823
8874
  if (st === null || !st.isDirectory() || st.isSymbolicLink()) {
8824
8875
  fail5(`${label} must be a real directory: ${p7}`);
8825
8876
  }
8826
- return resolved2;
8877
+ return resolved3;
8827
8878
  }
8828
8879
  function readBoundedBytes(file, maxBytes, label) {
8829
8880
  const st = lstatOrNull(file);
@@ -8941,7 +8992,7 @@ function readContainedLinkTarget(linkPath, allowedRoot) {
8941
8992
  if (target === "" || path32.isAbsolute(target)) {
8942
8993
  fail5(`staged symlink must be relative: ${linkPath}`);
8943
8994
  }
8944
- const isStrictChild6 = (child) => {
8995
+ const isStrictChild8 = (child) => {
8945
8996
  const rel = path32.relative(allowedRoot, child);
8946
8997
  return rel !== "" && rel !== ".." && !rel.startsWith(`..${path32.sep}`) && !path32.isAbsolute(rel);
8947
8998
  };
@@ -8953,7 +9004,7 @@ function readContainedLinkTarget(linkPath, allowedRoot) {
8953
9004
  const isLast = i === ops.length - 1;
8954
9005
  if (part === "..") {
8955
9006
  cur = path32.dirname(cur);
8956
- if (cur !== allowedRoot && !isStrictChild6(cur)) {
9007
+ if (cur !== allowedRoot && !isStrictChild8(cur)) {
8957
9008
  fail5(`staged symlink escapes staged tree: ${linkPath} -> ${target}`);
8958
9009
  }
8959
9010
  const st2 = lstatOrNull(cur);
@@ -8967,7 +9018,7 @@ function readContainedLinkTarget(linkPath, allowedRoot) {
8967
9018
  continue;
8968
9019
  }
8969
9020
  const next = path32.join(cur, part);
8970
- if (next !== allowedRoot && !isStrictChild6(next)) {
9021
+ if (next !== allowedRoot && !isStrictChild8(next)) {
8971
9022
  fail5(`staged symlink escapes staged tree: ${linkPath} -> ${target}`);
8972
9023
  }
8973
9024
  const st = lstatOrNull(next);
@@ -11838,7 +11889,7 @@ async function latestGithubCommit(repo) {
11838
11889
  }
11839
11890
  }
11840
11891
  function validateExtractedTree(destDir) {
11841
- const resolved2 = path37.resolve(destDir);
11892
+ const resolved3 = path37.resolve(destDir);
11842
11893
  const walk = (dir) => {
11843
11894
  let entries;
11844
11895
  try {
@@ -11855,14 +11906,14 @@ function validateExtractedTree(destDir) {
11855
11906
  return false;
11856
11907
  }
11857
11908
  if (stat.isSymbolicLink()) return false;
11858
- if (!isContainedIn(full, resolved2)) return false;
11909
+ if (!isContainedIn(full, resolved3)) return false;
11859
11910
  if (entry.isDirectory()) {
11860
11911
  if (!walk(full)) return false;
11861
11912
  }
11862
11913
  }
11863
11914
  return true;
11864
11915
  };
11865
- return walk(resolved2);
11916
+ return walk(resolved3);
11866
11917
  }
11867
11918
  function resolveTarBin() {
11868
11919
  if (process.platform !== "win32") return "tar";
@@ -11872,7 +11923,7 @@ function resolveTarBin() {
11872
11923
  async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
11873
11924
  const url = `https://codeload.github.com/${repo}/tar.gz/${sha}`;
11874
11925
  const tmp = path37.join(os4.tmpdir(), `jorgex-tarball-${Date.now()}.tar.gz`);
11875
- const fail12 = (reason) => {
11926
+ const fail14 = (reason) => {
11876
11927
  try {
11877
11928
  fs30.rmSync(destDir, { recursive: true, force: true });
11878
11929
  } catch {
@@ -11886,11 +11937,11 @@ async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
11886
11937
  });
11887
11938
  if (!res.ok) {
11888
11939
  if (res.status === 403 || res.status === 429) rateLimitHit = true;
11889
- return fail12(
11940
+ return fail14(
11890
11941
  `HTTP ${res.status}${res.status === 403 || res.status === 429 ? " \u2014 rate limit; define GH_TOKEN o inicia sesi\xF3n en gh" : ""}`
11891
11942
  );
11892
11943
  }
11893
- if (!res.body) return fail12("respuesta HTTP sin cuerpo");
11944
+ if (!res.body) return fail14("respuesta HTTP sin cuerpo");
11894
11945
  await pipeline(
11895
11946
  Readable.fromWeb(res.body),
11896
11947
  fs30.createWriteStream(tmp)
@@ -11902,21 +11953,21 @@ async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
11902
11953
  } catch (err) {
11903
11954
  const e = err;
11904
11955
  const detail = (e.stderr?.toString().trim() || e.message || "").split("\n")[0];
11905
- return fail12(detail ? `tar fall\xF3: ${detail}` : "tar no disponible o fall\xF3 la extracci\xF3n");
11956
+ return fail14(detail ? `tar fall\xF3: ${detail}` : "tar no disponible o fall\xF3 la extracci\xF3n");
11906
11957
  }
11907
11958
  const resolvedDest = path37.resolve(destDir);
11908
11959
  const validateRoot = validateSubdir ? path37.resolve(resolvedDest, validateSubdir) : resolvedDest;
11909
11960
  if (validateRoot !== resolvedDest && !isContainedIn(validateRoot, resolvedDest)) {
11910
- return fail12(`la ruta de validaci\xF3n "${validateSubdir}" escapa del destino`);
11961
+ return fail14(`la ruta de validaci\xF3n "${validateSubdir}" escapa del destino`);
11911
11962
  }
11912
11963
  if (fs30.existsSync(validateRoot) && !validateExtractedTree(validateRoot)) {
11913
- return fail12("el \xE1rbol extra\xEDdo contiene symlinks o rutas fuera del destino");
11964
+ return fail14("el \xE1rbol extra\xEDdo contiene symlinks o rutas fuera del destino");
11914
11965
  }
11915
11966
  const validated = fs30.existsSync(validateRoot);
11916
11967
  return { ok: true, validated };
11917
11968
  } catch (err) {
11918
11969
  const timedOut = err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError");
11919
- return fail12(timedOut ? "timeout de descarga (120s)" : err instanceof Error ? `fallo de red: ${err.message}` : "error desconocido");
11970
+ return fail14(timedOut ? "timeout de descarga (120s)" : err instanceof Error ? `fallo de red: ${err.message}` : "error desconocido");
11920
11971
  } finally {
11921
11972
  try {
11922
11973
  fs30.rmSync(tmp, { force: true });
@@ -13149,8 +13200,8 @@ init_model_map();
13149
13200
 
13150
13201
  // src/lib/pi-runtime.ts
13151
13202
  import os6 from "os";
13152
- import path49 from "path";
13153
- import fs41 from "fs";
13203
+ import path53 from "path";
13204
+ import fs45 from "fs";
13154
13205
  import { spawnSync as spawnSync5 } from "child_process";
13155
13206
 
13156
13207
  // src/lib/pi-runtime-pin.json
@@ -13627,18 +13678,15 @@ function assertPiExecutable(piExecutable) {
13627
13678
  return absolute;
13628
13679
  }
13629
13680
  function assertRealDir2(p7, label) {
13630
- const resolved2 = path43.resolve(p7);
13631
- const st = lstatOrNull3(resolved2);
13681
+ const resolved3 = path43.resolve(p7);
13682
+ const st = lstatOrNull3(resolved3);
13632
13683
  if (st === null || !st.isDirectory() || st.isSymbolicLink()) {
13633
13684
  fail8(`${label} must be a real directory: ${p7}`);
13634
13685
  }
13635
- return resolved2;
13686
+ return resolved3;
13636
13687
  }
13637
13688
  function sleep(ms) {
13638
- return new Promise((resolve) => {
13639
- const timer = setTimeout(resolve, ms);
13640
- if (typeof timer.unref === "function") timer.unref();
13641
- });
13689
+ return new Promise((resolve) => setTimeout(resolve, ms));
13642
13690
  }
13643
13691
  function runtimePath(piExecutable) {
13644
13692
  const entries = process.platform === "win32" ? [
@@ -13780,7 +13828,7 @@ async function stopOwnedPiProcess(child) {
13780
13828
  }
13781
13829
  destroyChild(child);
13782
13830
  }
13783
- function validateCommandsData(value) {
13831
+ function validateCommandsData(value, requireProviders) {
13784
13832
  if (!Array.isArray(value)) return { ok: false, message: "get_commands data.commands must be an array" };
13785
13833
  const names = [];
13786
13834
  for (const entry of value) {
@@ -13794,11 +13842,11 @@ function validateCommandsData(value) {
13794
13842
  if (seen.has(name)) return { ok: false, message: `duplicate command name: ${name}` };
13795
13843
  seen.add(name);
13796
13844
  }
13797
- const missing = [...REQUIRED_COMMANDS].filter((required) => !seen.has(required));
13845
+ const missing = [...REQUIRED_COMMANDS, ...requireProviders ? ["mcp-adapter", "mcp"] : []].filter((required) => !seen.has(required));
13798
13846
  if (missing.length > 0) return { ok: false, message: `missing required commands: ${missing.join(", ")}` };
13799
13847
  return { ok: true, names };
13800
13848
  }
13801
- function evaluateLines(lines, stateId, commandsId) {
13849
+ function evaluateLines(lines, stateId, commandsId, requireProviders) {
13802
13850
  let stateSeen = false;
13803
13851
  let commandsNames = null;
13804
13852
  for (const entry of lines) {
@@ -13806,7 +13854,7 @@ function evaluateLines(lines, stateId, commandsId) {
13806
13854
  const type = entry["type"];
13807
13855
  if (type === "extension_ui_request" && entry["method"] === "notify" && entry["notifyType"] === "error") {
13808
13856
  const message = typeof entry["message"] === "string" ? entry["message"] : "extension notification error";
13809
- if (message !== EXPECTED_MISSING_PROVIDER) {
13857
+ if (requireProviders || message !== EXPECTED_MISSING_PROVIDER) {
13810
13858
  return { kind: "failed", message: `extension notification: ${message.slice(0, 300)}` };
13811
13859
  }
13812
13860
  }
@@ -13836,7 +13884,7 @@ function evaluateLines(lines, stateId, commandsId) {
13836
13884
  if (!isRecord12(entry["data"])) {
13837
13885
  return { kind: "failed", message: "get_commands response misses data" };
13838
13886
  }
13839
- const checked = validateCommandsData(entry["data"]["commands"]);
13887
+ const checked = validateCommandsData(entry["data"]["commands"], requireProviders);
13840
13888
  if (!checked.ok) return { kind: "failed", message: checked.message };
13841
13889
  commandsNames = checked.names;
13842
13890
  }
@@ -13941,6 +13989,14 @@ async function runSmoke(input) {
13941
13989
  }
13942
13990
  if (timeout > 2147483647) fail8("timeoutMs exceeds platform bound");
13943
13991
  const env = buildSandboxEnv(stageRoot, stageResolved, piResolved);
13992
+ if (input.providerSetup !== void 0) {
13993
+ env.ENGRAM_BIN = assertPiExecutable(input.providerSetup.engramBin);
13994
+ const endpoint = new URL(input.providerSetup.engramUrl);
13995
+ if (endpoint.protocol !== "http:" || endpoint.hostname !== "127.0.0.1" || !endpoint.port || endpoint.username || endpoint.password) {
13996
+ fail8("provider smoke requires its isolated loopback endpoint");
13997
+ }
13998
+ env.ENGRAM_URL = endpoint.href;
13999
+ }
13944
14000
  const planned = planDetectedBinCommand(piResolved, [...EXPECTED_ARGV]);
13945
14001
  if (planned === null) fail8("unsafe executable for Windows launch");
13946
14002
  const stateId = 1;
@@ -13989,7 +14045,7 @@ ${JSON.stringify({ id: commandsId, type: "get_commands" })}
13989
14045
  if (out.malformed !== null) return { kind: "malformed", message: `malformed line-delimited JSON: ${out.malformed}` };
13990
14046
  const err = parseStderrComplete(stderrParts);
13991
14047
  if (err.rejected !== null) return { kind: "malformed", message: `unexpected stderr text: ${err.rejected}` };
13992
- return evaluateLines([...out.lines, ...err.lines], stateId, commandsId);
14048
+ return evaluateLines([...out.lines, ...err.lines], stateId, commandsId, input.providerSetup !== void 0);
13993
14049
  };
13994
14050
  const scheduleReady = () => {
13995
14051
  if (settled) return;
@@ -14099,7 +14155,6 @@ ${JSON.stringify({ id: commandsId, type: "get_commands" })}
14099
14155
  });
14100
14156
  try {
14101
14157
  spawned.stdin?.write(requests);
14102
- spawned.stdin?.end();
14103
14158
  } catch (error) {
14104
14159
  void settleFailure(`stdin failed: ${error instanceof Error ? error.message : String(error)}`);
14105
14160
  }
@@ -14551,9 +14606,9 @@ function piPackageVersion(file) {
14551
14606
  return null;
14552
14607
  }
14553
14608
  }
14554
- function managedPiVersion(executable, resolved2) {
14555
- const binDir = path45.dirname(resolved2);
14556
- if (path45.basename(resolved2) !== "pi" || path45.basename(binDir) !== "bin") {
14609
+ function managedPiVersion(executable, resolved3) {
14610
+ const binDir = path45.dirname(resolved3);
14611
+ if (path45.basename(resolved3) !== "pi" || path45.basename(binDir) !== "bin") {
14557
14612
  return invalidManagedPi("launcher gestionado de Pi no reconocido");
14558
14613
  }
14559
14614
  const agentDir = path45.dirname(binDir);
@@ -14593,9 +14648,9 @@ function managedPiVersion(executable, resolved2) {
14593
14648
  return invalidManagedPi("no se pudo leer la metadata del release Pi gestionado");
14594
14649
  }
14595
14650
  }
14596
- function hasManagedPiMarker(resolved2) {
14597
- const binDir = path45.dirname(resolved2);
14598
- if (path45.basename(resolved2) !== "pi" || path45.basename(binDir) !== "bin") return false;
14651
+ function hasManagedPiMarker(resolved3) {
14652
+ const binDir = path45.dirname(resolved3);
14653
+ if (path45.basename(resolved3) !== "pi" || path45.basename(binDir) !== "bin") return false;
14599
14654
  const installDir = path45.join(path45.dirname(binDir), "install");
14600
14655
  try {
14601
14656
  return fs37.lstatSync(path45.join(installDir, "managed-install.json"), { throwIfNoEntry: false }) !== void 0 || fs37.lstatSync(path45.join(installDir, "current-version"), { throwIfNoEntry: false }) !== void 0;
@@ -14605,14 +14660,14 @@ function hasManagedPiMarker(resolved2) {
14605
14660
  }
14606
14661
  function inspectPiHostVersion(executable) {
14607
14662
  const absoluteExecutable = path45.resolve(executable);
14608
- let resolved2;
14663
+ let resolved3;
14609
14664
  try {
14610
- resolved2 = fs37.realpathSync(absoluteExecutable);
14665
+ resolved3 = fs37.realpathSync(absoluteExecutable);
14611
14666
  } catch {
14612
14667
  return { version: null, diagnostic: "no se pudo resolver el ejecutable Pi" };
14613
14668
  }
14614
- if (hasManagedPiMarker(resolved2)) return managedPiVersion(absoluteExecutable, resolved2);
14615
- let current = path45.dirname(resolved2);
14669
+ if (hasManagedPiMarker(resolved3)) return managedPiVersion(absoluteExecutable, resolved3);
14670
+ let current = path45.dirname(resolved3);
14616
14671
  for (let depth = 0; depth < 8; depth += 1) {
14617
14672
  for (const manifest of [
14618
14673
  path45.join(current, "package.json"),
@@ -14643,10 +14698,10 @@ function lstatOrNull5(p7) {
14643
14698
  return null;
14644
14699
  }
14645
14700
  }
14646
- function assertRealDir3(resolved2, label) {
14647
- const st = lstatOrNull5(resolved2);
14701
+ function assertRealDir3(resolved3, label) {
14702
+ const st = lstatOrNull5(resolved3);
14648
14703
  if (st === null || !st.isDirectory() || st.isSymbolicLink()) {
14649
- fail10(`${label} must be a real directory: ${resolved2}`);
14704
+ fail10(`${label} must be a real directory: ${resolved3}`);
14650
14705
  }
14651
14706
  }
14652
14707
  function validatePaths(paths) {
@@ -16478,29 +16533,1089 @@ async function activatePreparedPiInstall(input, deps) {
16478
16533
  return { kind: "installed", receipt };
16479
16534
  }
16480
16535
 
16481
- // src/lib/pi-cached-artifact.ts
16536
+ // src/lib/pi-provider-update.ts
16537
+ import fs43 from "fs";
16538
+ import path51 from "path";
16539
+ init_pi_mcp_config();
16482
16540
  init_npm_provider();
16483
- import { createHash as createHash9, timingSafeEqual as timingSafeEqual6 } from "crypto";
16541
+
16542
+ // src/lib/pi-provider-stage.ts
16543
+ init_npm_provider();
16544
+ import { randomBytes as randomBytes4 } from "crypto";
16484
16545
  import fs40 from "fs";
16485
16546
  import path48 from "path";
16547
+ var PROVIDER_NAMES = ["gentle-engram", "pi-mcp-adapter"];
16548
+ var MAX_JSON_BYTES3 = 1 * 1024 * 1024;
16549
+ var MAX_LOCK_BYTES2 = 4 * 1024 * 1024;
16550
+ var PROVIDER_PACKAGE_ROOT = "pi-extensions";
16551
+ function fail11(message) {
16552
+ throw new Error(`pi-provider-stage: ${message}`);
16553
+ }
16554
+ function isRecord14(value) {
16555
+ return value !== null && typeof value === "object" && !Array.isArray(value);
16556
+ }
16557
+ function lstatOrNull7(target) {
16558
+ try {
16559
+ return fs40.lstatSync(target);
16560
+ } catch {
16561
+ return null;
16562
+ }
16563
+ }
16564
+ function isStrictChild5(child, root) {
16565
+ const relative = path48.relative(path48.resolve(root), path48.resolve(child));
16566
+ return relative !== "" && relative !== ".." && !relative.startsWith(`..${path48.sep}`) && !path48.isAbsolute(relative);
16567
+ }
16568
+ function assertRealDirectory3(target, label) {
16569
+ const resolved3 = path48.resolve(target);
16570
+ const stat = lstatOrNull7(resolved3);
16571
+ if (stat === null || !stat.isDirectory() || stat.isSymbolicLink()) {
16572
+ fail11(`${label} must be a real directory: ${target}`);
16573
+ }
16574
+ return resolved3;
16575
+ }
16576
+ function assertRegularFile2(target, label, maxBytes = MAX_JSON_BYTES3) {
16577
+ const stat = lstatOrNull7(target);
16578
+ if (stat === null || !stat.isFile() || stat.isSymbolicLink()) {
16579
+ fail11(`${label} must be a regular file: ${target}`);
16580
+ }
16581
+ if (stat.size > maxBytes) fail11(`${label} exceeds size limit: ${target}`);
16582
+ }
16583
+ function readJson(target, label, maxBytes = MAX_JSON_BYTES3) {
16584
+ assertRegularFile2(target, label, maxBytes);
16585
+ let parsed;
16586
+ try {
16587
+ parsed = JSON.parse(fs40.readFileSync(target, "utf8"));
16588
+ } catch {
16589
+ fail11(`${label} is malformed: ${target}`);
16590
+ }
16591
+ if (!isRecord14(parsed)) fail11(`${label} must be an object: ${target}`);
16592
+ return parsed;
16593
+ }
16594
+ function assertStageBoundary(homeDir, agentDir) {
16595
+ const home = assertRealDirectory3(homeDir, "homeDir");
16596
+ const agent = assertRealDirectory3(agentDir, "agentDir");
16597
+ if (!isStrictChild5(agent, home)) fail11("agentDir must be inside homeDir");
16598
+ return { home, agent };
16599
+ }
16600
+ function runtimePath3(piExecutable) {
16601
+ const entries = process.platform === "win32" ? [
16602
+ path48.dirname(piExecutable),
16603
+ path48.dirname(process.execPath),
16604
+ process.env.SystemRoot ? path48.join(process.env.SystemRoot, "System32") : null
16605
+ ] : [path48.dirname(piExecutable), path48.dirname(process.execPath), "/usr/local/bin", "/usr/bin", "/bin"];
16606
+ return [...new Set(entries.filter((entry) => entry !== null && entry !== ""))].join(path48.delimiter);
16607
+ }
16608
+ function validatePiExecutable(value) {
16609
+ if (typeof value !== "string" || value === "" || !path48.isAbsolute(value)) {
16610
+ fail11("piExecutable must be a non-empty absolute path");
16611
+ }
16612
+ if (path48.basename(value).toLowerCase() === "npm" || path48.basename(value).toLowerCase().startsWith("npm.")) {
16613
+ fail11("piExecutable must be the detected Pi CLI, never npm");
16614
+ }
16615
+ return path48.resolve(value);
16616
+ }
16617
+ function assertRelease2(name, release) {
16618
+ if (!isRecord14(release)) fail11(`${name} release must be an object`);
16619
+ if (!isStableSemverVersion(release.version)) fail11(`${name} release has an invalid version`);
16620
+ if (typeof release.tarballUrl !== "string" || !isCanonicalSha512Integrity(release.integrity)) {
16621
+ fail11(`${name} release has invalid registry metadata`);
16622
+ }
16623
+ return {
16624
+ version: release.version,
16625
+ tarballUrl: release.tarballUrl,
16626
+ integrity: release.integrity
16627
+ };
16628
+ }
16629
+ function createDirectory(target) {
16630
+ try {
16631
+ fs40.mkdirSync(target, { recursive: true, mode: 448 });
16632
+ fs40.chmodSync(target, 448);
16633
+ } catch {
16634
+ fail11(`cannot create private stage directory: ${target}`);
16635
+ }
16636
+ }
16637
+ function createStageRoot(homeDir) {
16638
+ for (let attempt = 0; attempt < 5; attempt += 1) {
16639
+ const candidate = path48.join(homeDir, `provider-stage-${randomBytes4(16).toString("hex")}`);
16640
+ try {
16641
+ fs40.mkdirSync(candidate, { mode: 448 });
16642
+ fs40.chmodSync(candidate, 448);
16643
+ return candidate;
16644
+ } catch (error) {
16645
+ if (error.code === "EEXIST") continue;
16646
+ fail11(`cannot create private stage root: ${error.message}`);
16647
+ }
16648
+ }
16649
+ fail11("cannot create private stage root after retries");
16650
+ }
16651
+ function stageEnvironment(stageRoot, stageAgentDir, piExecutable) {
16652
+ const home = path48.join(stageRoot, "home");
16653
+ const appdata = path48.join(stageRoot, "appdata");
16654
+ const localappdata = path48.join(stageRoot, "localappdata");
16655
+ const xdgConfig = path48.join(stageRoot, "xdg-config");
16656
+ const xdgData = path48.join(stageRoot, "xdg-data");
16657
+ const xdgCache = path48.join(stageRoot, "xdg-cache");
16658
+ const temporary = path48.join(stageRoot, "tmp");
16659
+ const npmCache = path48.join(stageRoot, "npm-cache");
16660
+ const npmConfig = path48.join(stageRoot, "npmrc");
16661
+ for (const directory of [
16662
+ home,
16663
+ appdata,
16664
+ localappdata,
16665
+ xdgConfig,
16666
+ xdgData,
16667
+ xdgCache,
16668
+ temporary,
16669
+ npmCache,
16670
+ path48.dirname(npmConfig),
16671
+ stageAgentDir
16672
+ ]) createDirectory(directory);
16673
+ const env = {
16674
+ HOME: home,
16675
+ USERPROFILE: home,
16676
+ APPDATA: appdata,
16677
+ LOCALAPPDATA: localappdata,
16678
+ XDG_CONFIG_HOME: xdgConfig,
16679
+ XDG_DATA_HOME: xdgData,
16680
+ XDG_CACHE_HOME: xdgCache,
16681
+ TEMP: temporary,
16682
+ TMP: temporary,
16683
+ TMPDIR: temporary,
16684
+ npm_config_cache: npmCache,
16685
+ npm_config_userconfig: npmConfig,
16686
+ npm_config_install_strategy: "nested",
16687
+ npm_config_ignore_scripts: "true",
16688
+ npm_config_audit: "false",
16689
+ npm_config_fund: "false",
16690
+ npm_config_progress: "false",
16691
+ NPM_CONFIG_IGNORE_SCRIPTS: "true",
16692
+ NPM_CONFIG_UPDATE_NOTIFIER: "false",
16693
+ PI_CODING_AGENT_DIR: stageAgentDir,
16694
+ PATH: runtimePath3(piExecutable)
16695
+ };
16696
+ if (process.platform === "win32") {
16697
+ for (const key of ["SystemRoot", "SystemDrive", "WINDIR", "COMSPEC", "PATHEXT", "OS"]) {
16698
+ const value = process.env[key];
16699
+ if (value) env[key] = value;
16700
+ }
16701
+ }
16702
+ return env;
16703
+ }
16704
+ function assertFileAlias(spec, npmDir, artifactPath, label) {
16705
+ if (typeof spec !== "string" || !spec.startsWith("file:")) fail11(`${label} must be a file: alias`);
16706
+ const rest = spec.slice("file:".length);
16707
+ if (rest === "") fail11(`${label} has an empty file: alias`);
16708
+ if (path48.resolve(npmDir, rest) !== path48.resolve(artifactPath)) {
16709
+ fail11(`${label} does not resolve to the verified artifact`);
16710
+ }
16711
+ return spec;
16712
+ }
16713
+ function normalizeBins(raw, packageName) {
16714
+ if (typeof raw === "string") {
16715
+ if (raw === "") fail11(`${packageName} declares an empty bin target`);
16716
+ return { [packageName]: raw };
16717
+ }
16718
+ if (!isRecord14(raw)) fail11(`${packageName} must declare a bin map`);
16719
+ const result = {};
16720
+ for (const [name, target] of Object.entries(raw)) {
16721
+ if (name === "" || name.includes("/") || name.includes("\\") || typeof target !== "string" || target === "") {
16722
+ fail11(`${packageName} declares an invalid bin`);
16723
+ }
16724
+ result[name] = target;
16725
+ }
16726
+ if (Object.keys(result).length === 0) fail11(`${packageName} must declare at least one bin`);
16727
+ return result;
16728
+ }
16729
+ function assertBinTargets(packageRoot, bins) {
16730
+ const result = {};
16731
+ for (const [name, target] of Object.entries(bins)) {
16732
+ if (path48.isAbsolute(target)) fail11(`${name} bin must be relative`);
16733
+ const candidate = path48.resolve(packageRoot, target);
16734
+ if (!isStrictChild5(candidate, packageRoot)) fail11(`${name} bin escapes its package root`);
16735
+ const stat = lstatOrNull7(candidate);
16736
+ if (stat === null) fail11(`${name} bin target is missing`);
16737
+ let real;
16738
+ try {
16739
+ real = fs40.realpathSync(candidate);
16740
+ } catch {
16741
+ fail11(`${name} bin target cannot be resolved`);
16742
+ }
16743
+ if (!isStrictChild5(real, packageRoot)) fail11(`${name} bin escapes its package root`);
16744
+ const finalStat = fs40.statSync(candidate);
16745
+ if (!finalStat.isFile()) fail11(`${name} bin target is not a file`);
16746
+ result[name] = target;
16747
+ }
16748
+ return result;
16749
+ }
16750
+ function assertDirectRoots(nodeModules, provider) {
16751
+ const entries = fs40.readdirSync(nodeModules);
16752
+ const allowed = /* @__PURE__ */ new Set([provider, ".bin", ".package-lock.json"]);
16753
+ for (const entry of entries) {
16754
+ if (!allowed.has(entry)) fail11(`foreign direct provider root: ${entry}`);
16755
+ }
16756
+ const hiddenLock = path48.join(nodeModules, ".package-lock.json");
16757
+ if (lstatOrNull7(hiddenLock) !== null) assertRegularFile2(hiddenLock, "staged provider hidden lock", MAX_LOCK_BYTES2);
16758
+ const binDir = path48.join(nodeModules, ".bin");
16759
+ const binStat = lstatOrNull7(binDir);
16760
+ if (binStat !== null && (!binStat.isDirectory() || binStat.isSymbolicLink())) {
16761
+ fail11("provider .bin must be a real directory");
16762
+ }
16763
+ if (binStat !== null) {
16764
+ for (const entry of fs40.readdirSync(binDir, { withFileTypes: true })) {
16765
+ const target = path48.join(binDir, entry.name);
16766
+ if (!entry.isSymbolicLink()) continue;
16767
+ let raw;
16768
+ try {
16769
+ raw = fs40.readlinkSync(target);
16770
+ } catch {
16771
+ fail11(`provider .bin entry is unreadable: ${entry.name}`);
16772
+ }
16773
+ if (raw === "" || path48.isAbsolute(raw)) fail11(`provider .bin entry escapes stage: ${entry.name}`);
16774
+ const resolved3 = path48.resolve(binDir, raw);
16775
+ if (!isStrictChild5(resolved3, nodeModules)) fail11(`provider .bin entry escapes stage: ${entry.name}`);
16776
+ const stat = lstatOrNull7(resolved3);
16777
+ if (stat === null) fail11(`provider .bin entry is broken: ${entry.name}`);
16778
+ }
16779
+ }
16780
+ }
16781
+ function assertLockEntries(packages, provider) {
16782
+ for (const key of Object.keys(packages)) {
16783
+ if (key === "" || key === `node_modules/${provider}`) continue;
16784
+ if (!key.startsWith(`node_modules/${provider}/node_modules/`)) {
16785
+ fail11(`foreign lock root: ${key}`);
16786
+ }
16787
+ }
16788
+ }
16789
+ function inspectProviderTree(stageAgentDir, artifactPath, release, provider) {
16790
+ const npmDir = path48.join(stageAgentDir, "npm");
16791
+ const modules = path48.join(npmDir, "node_modules");
16792
+ assertRealDirectory3(npmDir, "staged provider npm root");
16793
+ assertRealDirectory3(modules, "staged provider node_modules");
16794
+ assertDirectRoots(modules, provider);
16795
+ const lockPath = path48.join(npmDir, "package-lock.json");
16796
+ const lock = readJson(lockPath, "staged provider lock", MAX_LOCK_BYTES2);
16797
+ if (lock.name !== PROVIDER_PACKAGE_ROOT || lock.lockfileVersion !== 3) {
16798
+ fail11("staged provider lock must be npm v3 pi-extensions");
16799
+ }
16800
+ if (!isRecord14(lock.packages)) fail11("staged provider lock misses packages");
16801
+ const packages = lock.packages;
16802
+ assertLockEntries(packages, provider);
16803
+ const root = packages[""];
16804
+ const parent = packages[`node_modules/${provider}`];
16805
+ if (!isRecord14(root) || !isRecord14(parent)) fail11(`staged provider lock misses ${provider}`);
16806
+ if (!isRecord14(root.dependencies)) fail11("staged provider lock misses root dependencies");
16807
+ const rootSpec = assertFileAlias(root.dependencies[provider], npmDir, artifactPath, "provider root alias");
16808
+ const parentSpec = assertFileAlias(parent.resolved, npmDir, artifactPath, "provider resolved alias");
16809
+ if (rootSpec !== parentSpec) fail11("provider root/resolved aliases differ");
16810
+ if (parent.version !== release.version || parent.integrity !== release.integrity) {
16811
+ fail11(`staged provider lock metadata mismatch: ${provider}`);
16812
+ }
16813
+ const rootManifest = readJson(path48.join(npmDir, "package.json"), "staged provider root manifest");
16814
+ if (rootManifest.name !== PROVIDER_PACKAGE_ROOT || !isRecord14(rootManifest.dependencies)) {
16815
+ fail11("staged provider root manifest is invalid");
16816
+ }
16817
+ if (rootManifest.dependencies[provider] !== rootSpec) {
16818
+ fail11("staged provider manifest alias differs from lock");
16819
+ }
16820
+ const packageRoot = path48.join(modules, provider);
16821
+ assertRealDirectory3(packageRoot, `staged ${provider} root`);
16822
+ const manifest = readJson(path48.join(packageRoot, "package.json"), `${provider} manifest`);
16823
+ if (manifest.name !== provider || manifest.version !== release.version) {
16824
+ fail11(`staged ${provider} manifest identity/version mismatch`);
16825
+ }
16826
+ const bins = assertBinTargets(packageRoot, normalizeBins(manifest.bin, provider));
16827
+ let treeSha256;
16828
+ try {
16829
+ treeSha256 = inventoryTreeSha256(packageRoot);
16830
+ } catch (error) {
16831
+ fail11(`staged ${provider} tree is unsafe: ${error instanceof Error ? error.message : String(error)}`);
16832
+ }
16833
+ return {
16834
+ name: provider,
16835
+ version: release.version,
16836
+ integrity: release.integrity,
16837
+ packageRoot,
16838
+ treeSha256,
16839
+ bins
16840
+ };
16841
+ }
16842
+ function failureDetail(result) {
16843
+ const stderr = typeof result.stderr === "string" ? result.stderr : "";
16844
+ const stdout = typeof result.stdout === "string" ? result.stdout : "";
16845
+ const detail = stderr !== "" ? stderr : stdout !== "" ? stdout : `exit ${result.exitCode}`;
16846
+ return detail.length > 500 ? `${detail.slice(0, 500)}\u2026` : detail;
16847
+ }
16848
+ function defaultStageRun(executable, args, options) {
16849
+ return runPiStageProcess(executable, args, options);
16850
+ }
16851
+ async function stagePiProviderPackages(input, deps = {}) {
16852
+ if (!isRecord14(input)) fail11("input must be an object");
16853
+ const { home, agent } = assertStageBoundary(input.homeDir, input.agentDir);
16854
+ const piExecutable = validatePiExecutable(input.piExecutable);
16855
+ if (!isRecord14(input.releases)) fail11("releases must be an object");
16856
+ const releases = {};
16857
+ for (const name of PROVIDER_NAMES) releases[name] = assertRelease2(name, input.releases[name]);
16858
+ const fetchImpl = deps.fetchImpl ?? fetch;
16859
+ if (typeof fetchImpl !== "function") fail11("fetchImpl must be a function");
16860
+ const run = deps.run ?? defaultStageRun;
16861
+ if (typeof run !== "function") fail11("run must be a function");
16862
+ const stageDir = createStageRoot(home);
16863
+ const packages = [];
16864
+ const stagedProviders = [];
16865
+ for (const provider of PROVIDER_NAMES) {
16866
+ const providerStage = path48.join(stageDir, provider);
16867
+ const stageAgentDir = path48.join(providerStage, "pi-agent");
16868
+ const downloads = path48.join(providerStage, "downloads");
16869
+ const workspace = path48.join(providerStage, "workspace");
16870
+ createDirectory(stageAgentDir);
16871
+ createDirectory(downloads);
16872
+ createDirectory(workspace);
16873
+ const artifactPath = path48.join(downloads, `${provider}-${releases[provider].version}.tgz`);
16874
+ const artifact = await downloadVerifiedNpmPackageTarball(provider, releases[provider], artifactPath, fetchImpl);
16875
+ const env = stageEnvironment(providerStage, stageAgentDir, piExecutable);
16876
+ stagedProviders.push({ provider, release: releases[provider], artifactPath: artifact.path, stageAgentDir, workspace, env });
16877
+ }
16878
+ for (const staged of stagedProviders) {
16879
+ const provider = staged.provider;
16880
+ const artifact = { path: staged.artifactPath };
16881
+ const source = `npm:${provider}@file:${artifact.path}`;
16882
+ let result;
16883
+ try {
16884
+ result = await run(piExecutable, ["install", source, "--no-approve"], { env: staged.env, cwd: staged.workspace });
16885
+ } catch (error) {
16886
+ fail11(`${provider} native stage failed: ${error instanceof Error ? error.message : String(error)}`);
16887
+ }
16888
+ if (!isRecord14(result) || typeof result.exitCode !== "number") fail11(`${provider} native stage returned invalid output`);
16889
+ if (result.exitCode !== 0) fail11(`${provider} native stage failed: ${failureDetail(result)}`);
16890
+ packages.push(inspectProviderTree(staged.stageAgentDir, artifact.path, staged.release, provider));
16891
+ }
16892
+ return { stageDir, packages };
16893
+ }
16894
+
16895
+ // src/lib/pi-provider-activation.ts
16896
+ init_pi();
16897
+ init_npm_provider();
16898
+ import crypto3 from "crypto";
16899
+ import fs41 from "fs";
16900
+ import path49 from "path";
16901
+ var PROVIDER_NAMES2 = ["gentle-engram", "pi-mcp-adapter"];
16902
+ var MAX_MANIFEST_BYTES2 = 1 * 1024 * 1024;
16903
+ var SHA256 = /^[a-f0-9]{64}$/;
16904
+ function resolved2(value) {
16905
+ return path49.resolve(value);
16906
+ }
16907
+ function isStrictChild6(child, root) {
16908
+ const rel = path49.relative(resolved2(root), resolved2(child));
16909
+ return rel !== "" && rel !== ".." && !rel.startsWith(`..${path49.sep}`) && !path49.isAbsolute(rel);
16910
+ }
16911
+ function isContainedOrEqual(child, root) {
16912
+ return resolved2(child) === resolved2(root) || isStrictChild6(child, root);
16913
+ }
16914
+ function lstatOrNull8(target) {
16915
+ try {
16916
+ return fs41.lstatSync(target);
16917
+ } catch {
16918
+ return null;
16919
+ }
16920
+ }
16921
+ function incomplete(message) {
16922
+ const error = new Error(`pi-provider-activation: ${message}`);
16923
+ error.recovery = "incomplete";
16924
+ throw error;
16925
+ }
16926
+ function fail12(message) {
16927
+ throw new Error(`pi-provider-activation: ${message}`);
16928
+ }
16929
+ function assertAncestorsClean2(target, boundary, label) {
16930
+ const boundaryResolved = resolved2(boundary);
16931
+ let current = resolved2(target);
16932
+ if (!isContainedOrEqual(current, boundaryResolved)) {
16933
+ fail12(`${label} escapes its boundary: ${target}`);
16934
+ }
16935
+ for (; ; ) {
16936
+ const stat = lstatOrNull8(current);
16937
+ if (stat !== null && stat.isSymbolicLink()) fail12(`${label} ancestor is a symlink: ${current}`);
16938
+ if (current === boundaryResolved) return;
16939
+ const parent = path49.dirname(current);
16940
+ if (parent === current || !isContainedOrEqual(parent, boundaryResolved)) {
16941
+ fail12(`${label} has an unsafe ancestor: ${current}`);
16942
+ }
16943
+ current = parent;
16944
+ }
16945
+ }
16946
+ function assertRealDirectory4(target, boundary, label) {
16947
+ const value = resolved2(target);
16948
+ if (!isStrictChild6(value, boundary) && value !== resolved2(boundary)) {
16949
+ fail12(`${label} escapes its boundary: ${target}`);
16950
+ }
16951
+ assertAncestorsClean2(value, boundary, label);
16952
+ const stat = lstatOrNull8(value);
16953
+ if (stat === null || !stat.isDirectory() || stat.isSymbolicLink()) {
16954
+ fail12(`${label} must be a real directory: ${target}`);
16955
+ }
16956
+ return value;
16957
+ }
16958
+ function assertOptionalRealDirectory(target, boundary, label) {
16959
+ const value = resolved2(target);
16960
+ if (!isStrictChild6(value, boundary)) fail12(`${label} escapes its boundary: ${target}`);
16961
+ assertAncestorsClean2(path49.dirname(value), boundary, label);
16962
+ const stat = lstatOrNull8(value);
16963
+ if (stat === null) return false;
16964
+ if (!stat.isDirectory() || stat.isSymbolicLink()) fail12(`${label} must be a real directory: ${target}`);
16965
+ return true;
16966
+ }
16967
+ function assertRegularFile3(target, boundary, label) {
16968
+ const value = resolved2(target);
16969
+ if (!isStrictChild6(value, boundary)) fail12(`${label} escapes its boundary: ${target}`);
16970
+ assertAncestorsClean2(path49.dirname(value), boundary, label);
16971
+ const stat = lstatOrNull8(value);
16972
+ if (stat === null || !stat.isFile() || stat.isSymbolicLink()) fail12(`${label} must be a regular file: ${target}`);
16973
+ }
16974
+ function readBoundedFile(target, boundary, label) {
16975
+ assertRegularFile3(target, boundary, label);
16976
+ const stat = lstatOrNull8(target);
16977
+ if (stat === null || stat.size > MAX_MANIFEST_BYTES2) fail12(`${label} exceeds size bound: ${target}`);
16978
+ try {
16979
+ return fs41.readFileSync(target);
16980
+ } catch {
16981
+ fail12(`cannot read ${label}: ${target}`);
16982
+ }
16983
+ }
16984
+ function readJsonObject(target, boundary, label) {
16985
+ let parsed;
16986
+ try {
16987
+ parsed = JSON.parse(readBoundedFile(target, boundary, label).toString("utf8"));
16988
+ } catch {
16989
+ fail12(`malformed ${label}: ${target}`);
16990
+ }
16991
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
16992
+ fail12(`${label} must be an object: ${target}`);
16993
+ }
16994
+ return parsed;
16995
+ }
16996
+ function realPathInside(target, root, label) {
16997
+ let actual;
16998
+ let rootActual;
16999
+ try {
17000
+ actual = fs41.realpathSync(target);
17001
+ rootActual = fs41.realpathSync(root);
17002
+ } catch {
17003
+ fail12(`${label} cannot be resolved safely: ${target}`);
17004
+ }
17005
+ if (!isStrictChild6(actual, rootActual)) fail12(`${label} escapes its stage root: ${target}`);
17006
+ }
17007
+ function normalizeBins2(raw, packageName, label) {
17008
+ if (typeof raw === "string") {
17009
+ if (raw === "") fail12(`${label} has an empty bin target`);
17010
+ return { [packageName]: raw };
17011
+ }
17012
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
17013
+ fail12(`${label} must declare a bin map`);
17014
+ }
17015
+ const out = {};
17016
+ for (const [name, target] of Object.entries(raw)) {
17017
+ if (name === "" || name.includes("/") || name.includes("\\") || typeof target !== "string" || target === "") {
17018
+ fail12(`${label} has an invalid bin declaration`);
17019
+ }
17020
+ out[name] = target;
17021
+ }
17022
+ return out;
17023
+ }
17024
+ function sortedMap(map) {
17025
+ return JSON.stringify(Object.fromEntries(Object.entries(map).sort(([a], [b]) => a.localeCompare(b))));
17026
+ }
17027
+ function validateBinTarget(packageRoot, target, label) {
17028
+ if (path49.isAbsolute(target)) fail12(`${label} must be relative: ${target}`);
17029
+ const lexical = resolved2(path49.join(packageRoot, target));
17030
+ if (!isStrictChild6(lexical, packageRoot)) fail12(`${label} escapes its package root: ${target}`);
17031
+ assertAncestorsClean2(path49.dirname(lexical), packageRoot, label);
17032
+ const stat = lstatOrNull8(lexical);
17033
+ if (stat === null) fail12(`${label} does not exist: ${target}`);
17034
+ let actual;
17035
+ try {
17036
+ actual = fs41.realpathSync(lexical);
17037
+ } catch {
17038
+ fail12(`${label} cannot be resolved: ${target}`);
17039
+ }
17040
+ if (!isStrictChild6(actual, resolved2(packageRoot))) fail12(`${label} escapes its package root: ${target}`);
17041
+ let finalStat;
17042
+ try {
17043
+ finalStat = fs41.statSync(lexical);
17044
+ } catch {
17045
+ fail12(`${label} cannot be stat'ed: ${target}`);
17046
+ }
17047
+ if (!finalStat.isFile()) {
17048
+ fail12(`${label} must point to a file: ${target}`);
17049
+ }
17050
+ return lexical;
17051
+ }
17052
+ function packageSource3(entry) {
17053
+ if (typeof entry === "string") return entry;
17054
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry)) return null;
17055
+ const source = entry.source;
17056
+ return typeof source === "string" ? source : null;
17057
+ }
17058
+ function canonicalProviderSource(source, packageName) {
17059
+ return isNamedPiSource(source, packageName);
17060
+ }
17061
+ function parseAndPlanSettings(settingsJson, packages) {
17062
+ let parsed;
17063
+ try {
17064
+ parsed = JSON.parse(settingsJson);
17065
+ } catch {
17066
+ fail12("settings.json is malformed");
17067
+ }
17068
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
17069
+ fail12("settings.json root must be an object");
17070
+ }
17071
+ const root = parsed;
17072
+ if (!Array.isArray(root.packages)) fail12("settings.json packages must be an array");
17073
+ const entries = root.packages;
17074
+ const planned = entries.map((entry) => {
17075
+ if (typeof entry === "string") return entry;
17076
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry)) {
17077
+ fail12("settings.json contains an entry without a canonical source");
17078
+ }
17079
+ const source = packageSource3(entry);
17080
+ if (source === null) fail12("settings.json contains an entry without a canonical source");
17081
+ return entry;
17082
+ });
17083
+ for (const provider of packages) {
17084
+ const matches = planned.map((entry, index) => ({ entry, index, source: packageSource3(entry) })).filter((value) => value.source !== null && canonicalProviderSource(value.source, provider.name));
17085
+ if (matches.length !== 1) {
17086
+ fail12(`settings.json must contain exactly one canonical source for ${provider.name}`);
17087
+ }
17088
+ const match = matches[0];
17089
+ if (match === void 0) fail12(`settings.json source missing for ${provider.name}`);
17090
+ const nextSource = `npm:${provider.name}@${provider.version}`;
17091
+ if (typeof match.entry === "string") {
17092
+ planned[match.index] = nextSource;
17093
+ } else {
17094
+ match.entry.source = nextSource;
17095
+ }
17096
+ }
17097
+ root.packages = planned;
17098
+ return JSON.stringify(root);
17099
+ }
17100
+ function validateSettingsFile(settingsPath, agentDir, expected) {
17101
+ assertRegularFile3(settingsPath, agentDir, "settings.json");
17102
+ let current;
17103
+ try {
17104
+ current = fs41.readFileSync(settingsPath, "utf8");
17105
+ } catch {
17106
+ fail12(`cannot read settings.json: ${settingsPath}`);
17107
+ }
17108
+ if (current !== expected) fail12("settings.json changed since staging; refusing activation");
17109
+ }
17110
+ function validatePackageInput(provider, expectedName) {
17111
+ if (provider.name !== expectedName) fail12(`unexpected provider package: ${provider.name}`);
17112
+ if (!isStableSemverVersion(provider.version)) fail12(`invalid provider version: ${provider.version}`);
17113
+ if (!isCanonicalSha512Integrity(provider.integrity)) fail12(`invalid provider integrity: ${provider.name}`);
17114
+ if (!SHA256.test(provider.treeSha256)) fail12(`invalid provider tree hash: ${provider.name}`);
17115
+ if (provider.bins === null || typeof provider.bins !== "object" || Array.isArray(provider.bins)) {
17116
+ fail12(`provider bins must be a map: ${provider.name}`);
17117
+ }
17118
+ }
17119
+ function validateCandidate(provider, stageDir) {
17120
+ const candidateRoot = resolved2(provider.packageRoot);
17121
+ const expectedRoot = path49.join(stageDir, provider.name, "pi-agent", "npm", "node_modules", provider.name);
17122
+ if (candidateRoot !== resolved2(expectedRoot)) fail12(`candidate root is not the direct staged provider root: ${provider.name}`);
17123
+ assertRealDirectory4(candidateRoot, stageDir, `candidate ${provider.name}`);
17124
+ realPathInside(candidateRoot, stageDir, `candidate ${provider.name}`);
17125
+ const manifest = readJsonObject(path49.join(candidateRoot, "package.json"), candidateRoot, `${provider.name} package.json`);
17126
+ if (manifest.name !== provider.name || manifest.version !== provider.version) {
17127
+ fail12(`candidate metadata does not match ${provider.name}`);
17128
+ }
17129
+ const manifestBins = normalizeBins2(manifest.bin, provider.name, `${provider.name} candidate bins`);
17130
+ if (sortedMap(manifestBins) !== sortedMap(provider.bins)) {
17131
+ fail12(`candidate bin map does not match metadata: ${provider.name}`);
17132
+ }
17133
+ for (const target of Object.values(manifestBins)) validateBinTarget(candidateRoot, target, `${provider.name} candidate bin`);
17134
+ let actualTree;
17135
+ try {
17136
+ actualTree = inventoryTreeSha256(candidateRoot);
17137
+ } catch (error) {
17138
+ fail12(`candidate tree is unsafe: ${provider.name}: ${error instanceof Error ? error.message : String(error)}`);
17139
+ }
17140
+ if (actualTree !== provider.treeSha256) fail12(`candidate tree hash changed: ${provider.name}`);
17141
+ return manifestBins;
17142
+ }
17143
+ function validateExistingPackage(activeRoot, agentDir, provider, candidateBins, modules) {
17144
+ const stat = lstatOrNull8(activeRoot);
17145
+ if (stat === null) return null;
17146
+ if (!stat.isDirectory() || stat.isSymbolicLink()) fail12(`existing provider root is not a real directory: ${provider.name}`);
17147
+ const manifest = readJsonObject(path49.join(activeRoot, "package.json"), activeRoot, `${provider.name} existing package.json`);
17148
+ if (manifest.name !== provider.name || typeof manifest.version !== "string" || manifest.version === "") {
17149
+ fail12(`existing provider metadata is not owned: ${provider.name}`);
17150
+ }
17151
+ const existingBins = normalizeBins2(manifest.bin, provider.name, `${provider.name} existing bins`);
17152
+ if (sortedMap(existingBins) !== sortedMap(candidateBins)) {
17153
+ fail12(`provider bin layout changed for ${provider.name}; manual migration required`);
17154
+ }
17155
+ for (const target of Object.values(existingBins)) validateBinTarget(activeRoot, target, `${provider.name} existing bin`);
17156
+ const existingTree = inventoryTreeSha256(activeRoot);
17157
+ const binDir = path49.join(modules, ".bin");
17158
+ if (lstatOrNull8(binDir) !== null) assertRealDirectory4(binDir, modules, "npm .bin");
17159
+ for (const [binName, target] of Object.entries(existingBins)) {
17160
+ const wrapper = path49.join(binDir, binName);
17161
+ const wrapperStat = lstatOrNull8(wrapper);
17162
+ if (wrapperStat === null || !wrapperStat.isSymbolicLink()) continue;
17163
+ let raw;
17164
+ try {
17165
+ raw = fs41.readlinkSync(wrapper);
17166
+ } catch {
17167
+ fail12(`provider bin wrapper is unreadable: ${wrapper}`);
17168
+ }
17169
+ const expected = resolved2(path49.join(activeRoot, target));
17170
+ const actual = resolved2(path49.join(path49.dirname(wrapper), raw));
17171
+ if (actual !== expected) fail12(`provider bin wrapper points elsewhere: ${wrapper}`);
17172
+ }
17173
+ return existingTree;
17174
+ }
17175
+ function atomicWrite(target, content, boundary) {
17176
+ assertAncestorsClean2(path49.dirname(target), boundary, "atomic target");
17177
+ const dir = path49.dirname(target);
17178
+ const temporary = path49.join(dir, `.jorgex-provider-${process.pid}-${crypto3.randomBytes(12).toString("hex")}.tmp`);
17179
+ let fd = null;
17180
+ try {
17181
+ fd = fs41.openSync(temporary, "wx", 384);
17182
+ fs41.writeFileSync(fd, content, "utf8");
17183
+ fs41.fsyncSync(fd);
17184
+ fs41.closeSync(fd);
17185
+ fd = null;
17186
+ fs41.renameSync(temporary, target);
17187
+ } finally {
17188
+ if (fd !== null) {
17189
+ try {
17190
+ fs41.closeSync(fd);
17191
+ } catch {
17192
+ }
17193
+ }
17194
+ if (lstatOrNull8(temporary) !== null) fs41.rmSync(temporary, { force: true });
17195
+ }
17196
+ }
17197
+ function sameRegularFile(target, expected, boundary) {
17198
+ const stat = lstatOrNull8(target);
17199
+ if (stat === null || !stat.isFile() || stat.isSymbolicLink()) return false;
17200
+ try {
17201
+ return fs41.readFileSync(target, "utf8") === expected;
17202
+ } catch {
17203
+ return false;
17204
+ }
17205
+ }
17206
+ function markRecovery(error, state) {
17207
+ const result = error instanceof Error ? error : new Error(String(error));
17208
+ result.recovery = state;
17209
+ return result;
17210
+ }
17211
+ function ensureTransactionFile(pathname, expected) {
17212
+ const stat = lstatOrNull8(pathname);
17213
+ if (stat === null || !stat.isFile() || stat.isSymbolicLink()) incomplete(`transaction state drifted: ${pathname}`);
17214
+ let current;
17215
+ try {
17216
+ current = fs41.readFileSync(pathname, "utf8");
17217
+ } catch {
17218
+ incomplete(`transaction state unreadable: ${pathname}`);
17219
+ }
17220
+ if (current !== expected) incomplete(`transaction state drifted: ${pathname}`);
17221
+ }
17222
+ function removeTransactionState(lockPath, lockContent, markerPath, markerContent) {
17223
+ assertAncestorsClean2(path49.dirname(lockPath), path49.dirname(path49.dirname(lockPath)), "transaction state");
17224
+ ensureTransactionFile(lockPath, lockContent);
17225
+ ensureTransactionFile(markerPath, markerContent);
17226
+ fs41.unlinkSync(lockPath);
17227
+ fs41.unlinkSync(markerPath);
17228
+ }
17229
+ function verifyPromotedPackage(state) {
17230
+ const stat = lstatOrNull8(state.activeRoot);
17231
+ if (stat === null || !stat.isDirectory() || stat.isSymbolicLink()) fail12(`promoted provider root missing: ${state.input.name}`);
17232
+ const manifest = readJsonObject(path49.join(state.activeRoot, "package.json"), state.activeRoot, `${state.input.name} active package.json`);
17233
+ if (manifest.name !== state.input.name || manifest.version !== state.input.version) {
17234
+ fail12(`promoted provider metadata mismatch: ${state.input.name}`);
17235
+ }
17236
+ let tree;
17237
+ try {
17238
+ tree = inventoryTreeSha256(state.activeRoot);
17239
+ } catch {
17240
+ fail12(`promoted provider tree unreadable: ${state.input.name}`);
17241
+ }
17242
+ if (tree !== state.input.treeSha256) fail12(`promoted provider tree drifted: ${state.input.name}`);
17243
+ }
17244
+ function verifyPreviousPackageBeforeRename(state, agentDir) {
17245
+ const stat = lstatOrNull8(state.activeRoot);
17246
+ if (!state.existed) {
17247
+ if (stat !== null) fail12(`unowned provider root appeared before promotion: ${state.input.name}`);
17248
+ return;
17249
+ }
17250
+ if (stat === null || !stat.isDirectory() || stat.isSymbolicLink()) {
17251
+ fail12(`existing provider root drifted before promotion: ${state.input.name}`);
17252
+ }
17253
+ assertAncestorsClean2(state.activeRoot, agentDir, `existing ${state.input.name}`);
17254
+ const manifest = readJsonObject(path49.join(state.activeRoot, "package.json"), state.activeRoot, `${state.input.name} existing package.json`);
17255
+ if (manifest.name !== state.input.name || typeof manifest.version !== "string" || manifest.version === "") {
17256
+ fail12(`existing provider metadata drifted before promotion: ${state.input.name}`);
17257
+ }
17258
+ let currentTree;
17259
+ try {
17260
+ currentTree = inventoryTreeSha256(state.activeRoot);
17261
+ } catch {
17262
+ fail12(`existing provider tree became unreadable before promotion: ${state.input.name}`);
17263
+ }
17264
+ if (currentTree !== state.previousTreeSha256) {
17265
+ fail12(`existing provider changed before promotion: ${state.input.name}`);
17266
+ }
17267
+ }
17268
+ function verifyBackup(state) {
17269
+ if (!state.existed) return;
17270
+ const stat = lstatOrNull8(state.backupRoot);
17271
+ if (stat === null || !stat.isDirectory() || stat.isSymbolicLink()) incomplete(`provider backup drifted: ${state.input.name}`);
17272
+ if (state.previousTreeSha256 === null) incomplete(`provider backup hash missing: ${state.input.name}`);
17273
+ let tree;
17274
+ try {
17275
+ tree = inventoryTreeSha256(state.backupRoot);
17276
+ } catch {
17277
+ incomplete(`provider backup unreadable: ${state.input.name}`);
17278
+ }
17279
+ if (tree !== state.previousTreeSha256) incomplete(`provider backup bytes drifted: ${state.input.name}`);
17280
+ }
17281
+ function restoreState(states, settingsPath, oldSettings, nextSettings, agentDir, lockPath, lockContent, markerPath, markerContent, backupDir, stageDir) {
17282
+ ensureTransactionFile(lockPath, lockContent);
17283
+ ensureTransactionFile(markerPath, markerContent);
17284
+ if (!sameRegularFile(settingsPath, nextSettings, agentDir) && !sameRegularFile(settingsPath, oldSettings, agentDir)) incomplete("settings drifted during provider rollback");
17285
+ const backupSettings = path49.join(backupDir, "settings.json");
17286
+ assertRegularFile3(backupSettings, backupDir, "provider settings backup");
17287
+ if (fs41.readFileSync(backupSettings, "utf8") !== oldSettings) incomplete("provider settings backup drifted");
17288
+ for (const state of states) {
17289
+ assertAncestorsClean2(path49.dirname(state.activeRoot), agentDir, `active ${state.input.name}`);
17290
+ if (state.phase === "pending") {
17291
+ verifyPreviousPackageBeforeRename(state, agentDir);
17292
+ continue;
17293
+ }
17294
+ assertAncestorsClean2(path49.dirname(state.backupRoot), backupDir, `provider backup ${state.input.name}`);
17295
+ verifyBackup(state);
17296
+ if (state.phase === "promoted") {
17297
+ verifyPromotedPackage(state);
17298
+ assertAncestorsClean2(path49.dirname(state.candidateRoot), stageDir, `candidate ${state.input.name}`);
17299
+ if (lstatOrNull8(state.candidateRoot) !== null) incomplete(`candidate stage root was replaced: ${state.input.name}`);
17300
+ } else if (lstatOrNull8(state.activeRoot) !== null) {
17301
+ incomplete(`provider root appeared after backup: ${state.input.name}`);
17302
+ }
17303
+ }
17304
+ for (const state of [...states].reverse()) {
17305
+ if (state.phase === "pending") continue;
17306
+ if (state.phase === "promoted") fs41.renameSync(state.activeRoot, state.candidateRoot);
17307
+ if (state.existed) fs41.renameSync(state.backupRoot, state.activeRoot);
17308
+ }
17309
+ atomicWrite(settingsPath, oldSettings, agentDir);
17310
+ for (const state of states) verifyPreviousPackageBeforeRename(state, agentDir);
17311
+ removeTransactionState(lockPath, lockContent, markerPath, markerContent);
17312
+ }
17313
+ async function activatePiProviderPackages(input) {
17314
+ const homeDir = assertRealDirectory4(input.homeDir, resolved2(input.homeDir), "homeDir");
17315
+ const agentDir = assertRealDirectory4(input.agentDir, homeDir, "agentDir");
17316
+ const stageDir = assertRealDirectory4(input.stageDir, homeDir, "stageDir");
17317
+ if (!isStrictChild6(stageDir, homeDir)) fail12("stageDir must be a strict child of homeDir");
17318
+ if (!Array.isArray(input.packages) || input.packages.length !== PROVIDER_NAMES2.length) {
17319
+ fail12("exactly the two owned provider packages are required");
17320
+ }
17321
+ const byName = new Map(input.packages.map((provider) => [provider.name, provider]));
17322
+ if (byName.size !== PROVIDER_NAMES2.length) fail12("provider packages must not be duplicated");
17323
+ const settingsPath = path49.join(agentDir, "settings.json");
17324
+ validateSettingsFile(settingsPath, agentDir, input.settingsJson);
17325
+ const nextSettings = parseAndPlanSettings(input.settingsJson, PROVIDER_NAMES2.map((name) => {
17326
+ const provider = byName.get(name);
17327
+ if (provider === void 0) fail12(`missing provider package: ${name}`);
17328
+ return provider;
17329
+ }));
17330
+ const npmDir = path49.join(agentDir, "npm");
17331
+ const modules = path49.join(npmDir, "node_modules");
17332
+ assertRealDirectory4(npmDir, agentDir, "npm root");
17333
+ assertRealDirectory4(modules, npmDir, "node_modules");
17334
+ const managedRoot = path49.join(npmDir, "jorgex-pi-managed");
17335
+ assertOptionalRealDirectory(managedRoot, npmDir, "managed root");
17336
+ if (lstatOrNull8(managedRoot) === null) {
17337
+ assertAncestorsClean2(path49.dirname(managedRoot), agentDir, "managed root");
17338
+ fs41.mkdirSync(managedRoot, { recursive: true, mode: 448 });
17339
+ }
17340
+ const lockPath = path49.join(managedRoot, "transaction.lock");
17341
+ const markerPath = path49.join(managedRoot, "active-transaction.json");
17342
+ if (lstatOrNull8(lockPath) !== null) fail12(`transaction lock busy: ${lockPath}`);
17343
+ if (lstatOrNull8(markerPath) !== null) fail12(`active transaction pending: ${markerPath}`);
17344
+ const states = [];
17345
+ for (const name of PROVIDER_NAMES2) {
17346
+ const provider = byName.get(name);
17347
+ if (provider === void 0) fail12(`missing provider package: ${name}`);
17348
+ validatePackageInput(provider, name);
17349
+ const candidateBins = validateCandidate(provider, stageDir);
17350
+ const activeRoot = path49.join(modules, name);
17351
+ assertAncestorsClean2(activeRoot, agentDir, `active ${name}`);
17352
+ const previousTreeSha256 = validateExistingPackage(activeRoot, agentDir, provider, candidateBins, modules);
17353
+ states.push({
17354
+ input: provider,
17355
+ candidateRoot: resolved2(provider.packageRoot),
17356
+ activeRoot,
17357
+ backupRoot: "",
17358
+ existed: previousTreeSha256 !== null,
17359
+ previousTreeSha256,
17360
+ phase: "pending"
17361
+ });
17362
+ }
17363
+ const backupDir = path49.join(stageDir, `.provider-activation-${crypto3.randomBytes(16).toString("hex")}`);
17364
+ const backupProviders = path49.join(backupDir, "providers");
17365
+ if (!isStrictChild6(backupDir, stageDir)) fail12("provider backup escaped stage root");
17366
+ const lockContent = `${JSON.stringify({ pid: process.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString(), backupDir })}
17367
+ `;
17368
+ const markerContent = `${JSON.stringify({ phase: "provider-activation", backupDir, packages: PROVIDER_NAMES2, pid: process.pid }, null, 2)}
17369
+ `;
17370
+ let lockAcquired = false;
17371
+ let markerCreated = false;
17372
+ let backupCreated = false;
17373
+ try {
17374
+ fs41.writeFileSync(lockPath, lockContent, { encoding: "utf8", flag: "wx", mode: 384 });
17375
+ lockAcquired = true;
17376
+ validateSettingsFile(settingsPath, agentDir, input.settingsJson);
17377
+ const unchanged = JSON.stringify(JSON.parse(input.settingsJson)) === nextSettings && states.every((state) => state.previousTreeSha256 === state.input.treeSha256);
17378
+ if (unchanged) {
17379
+ await input.verify();
17380
+ validateSettingsFile(settingsPath, agentDir, input.settingsJson);
17381
+ for (const state of states) verifyPreviousPackageBeforeRename(state, agentDir);
17382
+ ensureTransactionFile(lockPath, lockContent);
17383
+ fs41.unlinkSync(lockPath);
17384
+ lockAcquired = false;
17385
+ return { ok: true, changed: false, backupDir: null };
17386
+ }
17387
+ fs41.writeFileSync(markerPath, markerContent, { encoding: "utf8", flag: "wx", mode: 384 });
17388
+ markerCreated = true;
17389
+ fs41.mkdirSync(backupDir, { recursive: false, mode: 448 });
17390
+ backupCreated = true;
17391
+ fs41.mkdirSync(backupProviders, { recursive: false, mode: 448 });
17392
+ for (const state of states) {
17393
+ state.backupRoot = path49.join(backupProviders, state.input.name);
17394
+ }
17395
+ fs41.writeFileSync(path49.join(backupDir, "settings.json"), input.settingsJson, { encoding: "utf8", flag: "wx", mode: 384 });
17396
+ fs41.writeFileSync(path49.join(backupDir, "manifest.json"), `${JSON.stringify(states.map((state) => ({ name: state.input.name, existed: state.existed, treeSha256: state.previousTreeSha256 })), null, 2)}
17397
+ `, { encoding: "utf8", flag: "wx", mode: 384 });
17398
+ for (const state of states) {
17399
+ const currentCandidateTree = inventoryTreeSha256(state.candidateRoot);
17400
+ if (currentCandidateTree !== state.input.treeSha256) fail12(`candidate tree changed before promotion: ${state.input.name}`);
17401
+ assertAncestorsClean2(path49.dirname(state.candidateRoot), stageDir, `candidate ${state.input.name}`);
17402
+ assertAncestorsClean2(path49.dirname(state.activeRoot), agentDir, `active ${state.input.name}`);
17403
+ verifyPreviousPackageBeforeRename(state, agentDir);
17404
+ if (state.existed) {
17405
+ fs41.renameSync(state.activeRoot, state.backupRoot);
17406
+ state.phase = "backed-up";
17407
+ }
17408
+ fs41.renameSync(state.candidateRoot, state.activeRoot);
17409
+ state.phase = "promoted";
17410
+ }
17411
+ for (const state of states) verifyPromotedPackage(state);
17412
+ atomicWrite(settingsPath, nextSettings, agentDir);
17413
+ if (!sameRegularFile(settingsPath, nextSettings, agentDir)) fail12("settings readback failed after provider promotion");
17414
+ await input.verify();
17415
+ for (const state of states) verifyPromotedPackage(state);
17416
+ if (!sameRegularFile(settingsPath, nextSettings, agentDir)) fail12("settings drifted after provider verification");
17417
+ removeTransactionState(lockPath, lockContent, markerPath, markerContent);
17418
+ return { ok: true, changed: true, backupDir };
17419
+ } catch (error) {
17420
+ const original = error instanceof Error ? error : new Error(String(error));
17421
+ if (!lockAcquired || !markerCreated || !backupCreated) {
17422
+ try {
17423
+ if (markerCreated) ensureTransactionFile(markerPath, markerContent);
17424
+ if (markerCreated) fs41.unlinkSync(markerPath);
17425
+ if (lockAcquired) ensureTransactionFile(lockPath, lockContent);
17426
+ if (lockAcquired) fs41.unlinkSync(lockPath);
17427
+ } catch {
17428
+ incomplete(`activation failed before promotion and transaction cleanup was unsafe: ${original.message}`);
17429
+ }
17430
+ throw original;
17431
+ }
17432
+ try {
17433
+ await Promise.resolve();
17434
+ restoreState(states, settingsPath, input.settingsJson, nextSettings, agentDir, lockPath, lockContent, markerPath, markerContent, backupDir, stageDir);
17435
+ } catch (rollbackError) {
17436
+ const reason = rollbackError instanceof Error ? rollbackError.message : String(rollbackError);
17437
+ incomplete(`rollback incomplete; retaining provider backup and transaction marker: ${reason}; original: ${original.message}`);
17438
+ }
17439
+ throw markRecovery(original, "complete");
17440
+ }
17441
+ }
17442
+
17443
+ // src/lib/pi-provider-smoke.ts
17444
+ init_pi_mcp_config();
17445
+ import fs42 from "fs";
17446
+ import path50 from "path";
17447
+ import { createServer } from "http";
17448
+ async function smokePiProviderRuntime(input) {
17449
+ const root = fs42.mkdtempSync(path50.join(input.scratchRoot, "stage-providers-smoke-"));
17450
+ const endpoint = createServer((_request, response) => {
17451
+ response.writeHead(503, { "Content-Type": "application/json", Connection: "close" });
17452
+ response.end('{"error":"isolated package smoke"}');
17453
+ });
17454
+ let failed = false;
17455
+ let smokeError;
17456
+ try {
17457
+ const stageDir = path50.join(root, "pi-agent");
17458
+ const modules = path50.join(stageDir, "npm", "node_modules");
17459
+ fs42.mkdirSync(modules, { recursive: true });
17460
+ fs42.mkdirSync(path50.join(root, "workspace"));
17461
+ const packages = [];
17462
+ for (const [name, source] of Object.entries({ "jorgex-pi": input.jorgexPackageRoot, ...input.providerRoots })) {
17463
+ const packageRoot = fs42.realpathSync(source);
17464
+ const metadata = JSON.parse(fs42.readFileSync(path50.join(packageRoot, "package.json"), "utf8"));
17465
+ if (metadata.name !== name || typeof metadata.version !== "string" || !/^\d+\.\d+\.\d+$/.test(metadata.version)) {
17466
+ throw new Error(`Pi provider smoke: invalid ${name} identity`);
17467
+ }
17468
+ fs42.symlinkSync(path50.relative(modules, packageRoot), path50.join(modules, name), "dir");
17469
+ packages.push(`npm:${name}@${metadata.version}`);
17470
+ }
17471
+ fs42.writeFileSync(path50.join(stageDir, "settings.json"), JSON.stringify({ packages }));
17472
+ fs42.writeFileSync(resolvePiAdapterConfigPath(stageDir), JSON.stringify({ mcpServers: {
17473
+ engram: { command: input.engramBin, args: ["mcp", "--tools=agent"], lifecycle: "lazy", directTools: false }
17474
+ } }));
17475
+ await new Promise((resolve, reject) => {
17476
+ endpoint.once("error", reject);
17477
+ endpoint.listen(0, "127.0.0.1", () => {
17478
+ endpoint.off("error", reject);
17479
+ resolve();
17480
+ });
17481
+ });
17482
+ const address = endpoint.address();
17483
+ if (address === null || typeof address === "string") throw new Error("Pi provider smoke: no loopback endpoint");
17484
+ return await smokeStagedPiRuntime({
17485
+ piExecutable: input.piExecutable,
17486
+ stageDir,
17487
+ providerSetup: { engramBin: input.engramBin, engramUrl: `http://127.0.0.1:${address.port}` }
17488
+ });
17489
+ } catch (error) {
17490
+ failed = true;
17491
+ smokeError = error;
17492
+ throw error;
17493
+ } finally {
17494
+ endpoint.closeAllConnections();
17495
+ if (endpoint.listening) await new Promise((resolve) => endpoint.close(() => resolve()));
17496
+ try {
17497
+ fs42.rmSync(root, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
17498
+ } catch (cleanupError) {
17499
+ if (!failed) throw cleanupError;
17500
+ const detail = smokeError instanceof Error ? smokeError.message : String(smokeError);
17501
+ throw new AggregateError(
17502
+ [smokeError, cleanupError],
17503
+ `${detail}; Pi provider smoke cleanup failed at ${root}`,
17504
+ { cause: smokeError }
17505
+ );
17506
+ }
17507
+ }
17508
+ }
17509
+
17510
+ // src/lib/pi-provider-update.ts
17511
+ function isStrictChildPath(child, root) {
17512
+ const rel = path51.relative(path51.resolve(root), path51.resolve(child));
17513
+ return rel !== "" && rel !== ".." && !rel.startsWith(`..${path51.sep}`) && !path51.isAbsolute(rel);
17514
+ }
17515
+ async function completeUpdatedPiMcp(configDir, engramBin) {
17516
+ const { verifyOfficialSetup: verifyOfficialSetup5 } = await Promise.resolve().then(() => (init_pi(), pi_exports));
17517
+ const before = await verifyOfficialSetup5({ configDir, engramBin });
17518
+ if (!before.layers.includes("packages")) throw new Error(before.reason ?? "Setup de paquetes Pi incompleto.");
17519
+ migrateOfficialPiMcpConfig({ configDir, engramBin });
17520
+ const after = await verifyOfficialSetup5({ configDir, engramBin });
17521
+ if (!after.ok) throw new Error(after.reason ?? "Configuraci\xF3n MCP Pi no verificada.");
17522
+ }
17523
+ async function updatePiProviderPackages(input) {
17524
+ const { homeDir, agentDir } = input;
17525
+ if (!isStrictChildPath(agentDir, homeDir)) throw new Error("Pi provider agent directory is outside HOME");
17526
+ const receiptPath = path51.join(homeDir, ".jorgex-stack", "pi-receipt.json");
17527
+ const settingsPath = path51.join(agentDir, "settings.json");
17528
+ for (const file of [receiptPath, settingsPath]) {
17529
+ if (!fs43.lstatSync(file).isFile()) throw new Error("Pi provider update requires regular managed state files");
17530
+ }
17531
+ const receiptBytes = fs43.readFileSync(receiptPath, "utf8");
17532
+ const settingsJson = fs43.readFileSync(settingsPath, "utf8");
17533
+ const modules = path51.join(agentDir, "npm", "node_modules");
17534
+ const packageLink = path51.join(modules, "jorgex-pi");
17535
+ if (!fs43.lstatSync(packageLink).isSymbolicLink()) throw new Error("Pi provider update requires the managed private link");
17536
+ const linkTarget = fs43.readlinkSync(packageLink);
17537
+ const packageRoot = fs43.realpathSync(packageLink);
17538
+ const releaseDir = path51.dirname(path51.dirname(packageRoot));
17539
+ if (!isStrictChildPath(releaseDir, path51.join(agentDir, "npm", "jorgex-pi-managed", "releases")) || packageRoot !== path51.join(releaseDir, "node_modules", "jorgex-pi")) {
17540
+ throw new Error("Pi provider update found an untrusted private release path");
17541
+ }
17542
+ const privateTree = inventoryTreeSha256(releaseDir);
17543
+ const assertPrivateUnchanged = () => {
17544
+ if (!fs43.lstatSync(receiptPath).isFile() || fs43.readFileSync(receiptPath, "utf8") !== receiptBytes || !fs43.lstatSync(packageLink).isSymbolicLink() || fs43.readlinkSync(packageLink) !== linkTarget || fs43.realpathSync(packageLink) !== packageRoot || inventoryTreeSha256(releaseDir) !== privateTree) {
17545
+ throw new Error("Pi private receipt/link/tree drift during provider update");
17546
+ }
17547
+ };
17548
+ const releases = {
17549
+ "gentle-engram": await resolveLatestNpmPackageRelease("gentle-engram", fetch),
17550
+ "pi-mcp-adapter": await resolveLatestNpmPackageRelease("pi-mcp-adapter", fetch)
17551
+ };
17552
+ const staged = await stagePiProviderPackages({ ...input, releases });
17553
+ assertPrivateUnchanged();
17554
+ const providerRoots = Object.fromEntries(staged.packages.map((provider) => [provider.name, provider.packageRoot]));
17555
+ const activeRoots = { "gentle-engram": path51.join(modules, "gentle-engram"), "pi-mcp-adapter": path51.join(modules, "pi-mcp-adapter") };
17556
+ const adapterAgentDir = path51.dirname(path51.dirname(path51.dirname(providerRoots["pi-mcp-adapter"])));
17557
+ const selectedConfig = path51.basename(resolvePiAdapterConfigPath(adapterAgentDir));
17558
+ if (!declaredPiMcpConfigFiles(agentDir).includes(selectedConfig)) {
17559
+ throw new Error("The verified Pi package does not declare the candidate provider MCP reader");
17560
+ }
17561
+ for (const provider of staged.packages) {
17562
+ if (inventoryTreeSha256(provider.packageRoot) !== provider.treeSha256) throw new Error("Pi provider stage drift before smoke");
17563
+ }
17564
+ const smoke = { piExecutable: input.piExecutable, engramBin: input.engramBin, scratchRoot: staged.stageDir, jorgexPackageRoot: packageLink };
17565
+ await smokePiProviderRuntime({ ...smoke, providerRoots });
17566
+ assertPrivateUnchanged();
17567
+ const activation = await activatePiProviderPackages({
17568
+ homeDir,
17569
+ agentDir,
17570
+ stageDir: staged.stageDir,
17571
+ packages: staged.packages,
17572
+ settingsJson,
17573
+ verify: async () => {
17574
+ assertPrivateUnchanged();
17575
+ await smokePiProviderRuntime({ ...smoke, providerRoots: activeRoots });
17576
+ assertPrivateUnchanged();
17577
+ }
17578
+ });
17579
+ assertPrivateUnchanged();
17580
+ try {
17581
+ await completeUpdatedPiMcp(agentDir, input.engramBin);
17582
+ } catch (error) {
17583
+ throw new Error(`Pi providers activated and verified; MCP configuration pending with backup preserved: ${error instanceof Error ? error.message : String(error)}`);
17584
+ }
17585
+ try {
17586
+ fs43.rmSync(staged.stageDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
17587
+ } catch (error) {
17588
+ throw new Error(`Pi providers and MCP verified; stage cleanup pending at ${staged.stageDir}: ${error instanceof Error ? error.message : String(error)}`);
17589
+ }
17590
+ return { kind: activation.changed ? "updated" : "healthy", versions: {
17591
+ "gentle-engram": releases["gentle-engram"].version,
17592
+ "pi-mcp-adapter": releases["pi-mcp-adapter"].version
17593
+ } };
17594
+ }
17595
+
17596
+ // src/lib/pi-cached-artifact.ts
17597
+ init_npm_provider();
17598
+ import { createHash as createHash9, timingSafeEqual as timingSafeEqual6 } from "crypto";
17599
+ import fs44 from "fs";
17600
+ import path52 from "path";
16486
17601
  var PACKAGE_NAME5 = "jorgex-pi";
16487
17602
  var HEX643 = /^[0-9a-f]{64}$/;
16488
17603
  var HEX1283 = /^[0-9a-f]{128}$/;
16489
17604
  var MAX_TARBALL_BYTES4 = 128 * 1024 * 1024;
16490
17605
  var CHUNK_BYTES4 = 1024 * 1024;
16491
- function isRecord14(value) {
17606
+ function isRecord15(value) {
16492
17607
  return value !== null && typeof value === "object" && !Array.isArray(value);
16493
17608
  }
16494
- function lstatOrNull7(p7) {
17609
+ function lstatOrNull9(p7) {
16495
17610
  try {
16496
- return fs40.lstatSync(p7);
17611
+ return fs44.lstatSync(p7);
16497
17612
  } catch {
16498
17613
  return null;
16499
17614
  }
16500
17615
  }
16501
- function isStrictChild5(child, root) {
16502
- const rel = path48.relative(root, child);
16503
- return rel !== "" && rel !== ".." && !rel.startsWith(`..${path48.sep}`) && !path48.isAbsolute(rel);
17616
+ function isStrictChild7(child, root) {
17617
+ const rel = path52.relative(root, child);
17618
+ return rel !== "" && rel !== ".." && !rel.startsWith(`..${path52.sep}`) && !path52.isAbsolute(rel);
16504
17619
  }
16505
17620
  function equalHex2(aHex, bHex) {
16506
17621
  let a;
@@ -16515,43 +17630,43 @@ function equalHex2(aHex, bHex) {
16515
17630
  }
16516
17631
  function verifyCachedPiArtifact(input) {
16517
17632
  try {
16518
- if (!isRecord14(input)) return false;
17633
+ if (!isRecord15(input)) return false;
16519
17634
  const { receipt, homeDir, downloadsDir } = input;
16520
- if (typeof homeDir !== "string" || homeDir === "" || !path48.isAbsolute(homeDir)) return false;
16521
- if (typeof downloadsDir !== "string" || downloadsDir === "" || !path48.isAbsolute(downloadsDir)) {
17635
+ if (typeof homeDir !== "string" || homeDir === "" || !path52.isAbsolute(homeDir)) return false;
17636
+ if (typeof downloadsDir !== "string" || downloadsDir === "" || !path52.isAbsolute(downloadsDir)) {
16522
17637
  return false;
16523
17638
  }
16524
- const homeResolved = path48.resolve(homeDir);
16525
- const downloadsResolved = path48.resolve(downloadsDir);
16526
- const homeStat = lstatOrNull7(homeResolved);
17639
+ const homeResolved = path52.resolve(homeDir);
17640
+ const downloadsResolved = path52.resolve(downloadsDir);
17641
+ const homeStat = lstatOrNull9(homeResolved);
16527
17642
  if (homeStat === null || !homeStat.isDirectory() || homeStat.isSymbolicLink()) return false;
16528
- const downloadsStat = lstatOrNull7(downloadsResolved);
17643
+ const downloadsStat = lstatOrNull9(downloadsResolved);
16529
17644
  if (downloadsStat === null || !downloadsStat.isDirectory() || downloadsStat.isSymbolicLink()) {
16530
17645
  return false;
16531
17646
  }
16532
- if (!isStrictChild5(downloadsResolved, homeResolved)) return false;
16533
- const rel = path48.relative(homeResolved, downloadsResolved);
16534
- const parts = rel.split(path48.sep);
17647
+ if (!isStrictChild7(downloadsResolved, homeResolved)) return false;
17648
+ const rel = path52.relative(homeResolved, downloadsResolved);
17649
+ const parts = rel.split(path52.sep);
16535
17650
  let cursor = homeResolved;
16536
17651
  for (const part of parts) {
16537
17652
  if (part === "" || part === "." || part === "..") return false;
16538
- cursor = path48.join(cursor, part);
16539
- const st2 = lstatOrNull7(cursor);
17653
+ cursor = path52.join(cursor, part);
17654
+ const st2 = lstatOrNull9(cursor);
16540
17655
  if (st2 === null || !st2.isDirectory() || st2.isSymbolicLink()) return false;
16541
17656
  }
16542
- if (!isRecord14(receipt)) return false;
17657
+ if (!isRecord15(receipt)) return false;
16543
17658
  if (receipt["schemaVersion"] !== 1) return false;
16544
- if (!isRecord14(receipt["managedPackage"])) return false;
17659
+ if (!isRecord15(receipt["managedPackage"])) return false;
16545
17660
  const candidate = receipt["candidate"];
16546
- if (!isRecord14(candidate)) return false;
17661
+ if (!isRecord15(candidate)) return false;
16547
17662
  const pkg = candidate["package"];
16548
- if (!isRecord14(pkg)) return false;
17663
+ if (!isRecord15(pkg)) return false;
16549
17664
  if (pkg["name"] !== PACKAGE_NAME5) return false;
16550
17665
  const version = pkg["version"];
16551
17666
  if (!isStableSemverVersion(version)) return false;
16552
17667
  if (pkg["source"] !== `npm:${PACKAGE_NAME5}@${version}`) return false;
16553
17668
  const tarball = candidate["tarball"];
16554
- if (!isRecord14(tarball)) return false;
17669
+ if (!isRecord15(tarball)) return false;
16555
17670
  const bytes = tarball["bytes"];
16556
17671
  if (typeof bytes !== "number" || !Number.isInteger(bytes) || bytes < 0 || bytes > MAX_TARBALL_BYTES4) {
16557
17672
  return false;
@@ -16561,9 +17676,9 @@ function verifyCachedPiArtifact(input) {
16561
17676
  if (typeof sha2562 !== "string" || !HEX643.test(sha2562)) return false;
16562
17677
  if (typeof sha512 !== "string" || !HEX1283.test(sha512)) return false;
16563
17678
  const fileName = `${PACKAGE_NAME5}-${version}.tgz`;
16564
- const target = path48.join(downloadsResolved, fileName);
16565
- if (path48.dirname(target) !== downloadsResolved) return false;
16566
- const st = lstatOrNull7(target);
17679
+ const target = path52.join(downloadsResolved, fileName);
17680
+ if (path52.dirname(target) !== downloadsResolved) return false;
17681
+ const st = lstatOrNull9(target);
16567
17682
  if (st === null) return false;
16568
17683
  if (st.isSymbolicLink()) return false;
16569
17684
  if (!st.isFile()) return false;
@@ -16571,7 +17686,7 @@ function verifyCachedPiArtifact(input) {
16571
17686
  if (st.size > MAX_TARBALL_BYTES4) return false;
16572
17687
  let fd;
16573
17688
  try {
16574
- fd = fs40.openSync(target, "r");
17689
+ fd = fs44.openSync(target, "r");
16575
17690
  } catch {
16576
17691
  return false;
16577
17692
  }
@@ -16583,7 +17698,7 @@ function verifyCachedPiArtifact(input) {
16583
17698
  for (; ; ) {
16584
17699
  let read;
16585
17700
  try {
16586
- read = fs40.readSync(fd, buffer, 0, buffer.length, null);
17701
+ read = fs44.readSync(fd, buffer, 0, buffer.length, null);
16587
17702
  } catch {
16588
17703
  return false;
16589
17704
  }
@@ -16595,7 +17710,7 @@ function verifyCachedPiArtifact(input) {
16595
17710
  }
16596
17711
  } finally {
16597
17712
  try {
16598
- fs40.closeSync(fd);
17713
+ fs44.closeSync(fd);
16599
17714
  } catch {
16600
17715
  }
16601
17716
  }
@@ -16757,56 +17872,56 @@ async function resolvePiEngramRequirement(input, deps) {
16757
17872
  remedy: "La instalaci\xF3n termin\xF3, pero Engram no qued\xF3 detectable; configura ENGRAM_BIN."
16758
17873
  } : { kind: "existing", bin: detected, scope: "host" };
16759
17874
  }
16760
- function runtimePath3(piExecutable) {
16761
- const entries = process.platform === "win32" ? [piExecutable === void 0 ? null : path49.dirname(piExecutable), path49.dirname(process.execPath), process.env.SystemRoot ? path49.join(process.env.SystemRoot, "System32") : null] : [piExecutable === void 0 ? null : path49.dirname(piExecutable), path49.dirname(process.execPath), "/usr/local/bin", "/usr/bin", "/bin"];
16762
- return [...new Set(entries.filter((entry) => entry !== null))].join(path49.delimiter);
17875
+ function runtimePath4(piExecutable) {
17876
+ const entries = process.platform === "win32" ? [piExecutable === void 0 ? null : path53.dirname(piExecutable), path53.dirname(process.execPath), process.env.SystemRoot ? path53.join(process.env.SystemRoot, "System32") : null] : [piExecutable === void 0 ? null : path53.dirname(piExecutable), path53.dirname(process.execPath), "/usr/local/bin", "/usr/bin", "/bin"];
17877
+ return [...new Set(entries.filter((entry) => entry !== null))].join(path53.delimiter);
16763
17878
  }
16764
17879
  function targetPaths(targetDir, engramBin, piExecutable) {
16765
- const root = path49.resolve(targetDir);
16766
- const codingAgentDir = path49.join(root, "pi-agent");
16767
- const home = path49.join(root, "home");
16768
- const temporary = path49.join(root, "tmp");
17880
+ const root = path53.resolve(targetDir);
17881
+ const codingAgentDir = path53.join(root, "pi-agent");
17882
+ const home = path53.join(root, "home");
17883
+ const temporary = path53.join(root, "tmp");
16769
17884
  return {
16770
17885
  codingAgentDir,
16771
- receiptPath: path49.join(root, "state", "pi-receipt.json"),
16772
- packageRunner: path49.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
17886
+ receiptPath: path53.join(root, "state", "pi-receipt.json"),
17887
+ packageRunner: path53.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
16773
17888
  environment: {
16774
17889
  HOME: home,
16775
17890
  USERPROFILE: home,
16776
- APPDATA: path49.join(root, "appdata"),
16777
- LOCALAPPDATA: path49.join(root, "localappdata"),
16778
- XDG_CONFIG_HOME: path49.join(root, "xdg-config"),
16779
- XDG_DATA_HOME: path49.join(root, "xdg-data"),
16780
- XDG_CACHE_HOME: path49.join(root, "xdg-cache"),
17891
+ APPDATA: path53.join(root, "appdata"),
17892
+ LOCALAPPDATA: path53.join(root, "localappdata"),
17893
+ XDG_CONFIG_HOME: path53.join(root, "xdg-config"),
17894
+ XDG_DATA_HOME: path53.join(root, "xdg-data"),
17895
+ XDG_CACHE_HOME: path53.join(root, "xdg-cache"),
16781
17896
  TEMP: temporary,
16782
17897
  TMP: temporary,
16783
17898
  TMPDIR: temporary,
16784
- npm_config_cache: path49.join(root, "npm-cache"),
17899
+ npm_config_cache: path53.join(root, "npm-cache"),
16785
17900
  NPM_CONFIG_IGNORE_SCRIPTS: "true",
16786
17901
  NPM_CONFIG_UPDATE_NOTIFIER: "false",
16787
17902
  PI_CODING_AGENT_DIR: codingAgentDir,
16788
17903
  ...engramBin === null ? {} : { ENGRAM_BIN: engramBin },
16789
- PATH: runtimePath3(piExecutable)
17904
+ PATH: runtimePath4(piExecutable)
16790
17905
  }
16791
17906
  };
16792
17907
  }
16793
17908
  function userPaths(engramBin, piExecutable) {
16794
17909
  const home = os6.homedir();
16795
- const codingAgentDir = process.env.PI_CODING_AGENT_DIR ?? path49.join(home, ".pi", "agent");
17910
+ const codingAgentDir = process.env.PI_CODING_AGENT_DIR ?? path53.join(home, ".pi", "agent");
16796
17911
  return {
16797
17912
  codingAgentDir,
16798
- receiptPath: path49.join(dataDir(), "pi-receipt.json"),
16799
- packageRunner: path49.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
17913
+ receiptPath: path53.join(dataDir(), "pi-receipt.json"),
17914
+ packageRunner: path53.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
16800
17915
  environment: {
16801
17916
  HOME: home,
16802
- XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME ?? path49.join(home, ".config"),
16803
- XDG_CACHE_HOME: process.env.XDG_CACHE_HOME ?? path49.join(home, ".cache"),
17917
+ XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME ?? path53.join(home, ".config"),
17918
+ XDG_CACHE_HOME: process.env.XDG_CACHE_HOME ?? path53.join(home, ".cache"),
16804
17919
  TMPDIR: os6.tmpdir(),
16805
17920
  NPM_CONFIG_IGNORE_SCRIPTS: "true",
16806
17921
  NPM_CONFIG_UPDATE_NOTIFIER: "false",
16807
17922
  PI_CODING_AGENT_DIR: codingAgentDir,
16808
17923
  ...engramBin === null ? {} : { ENGRAM_BIN: engramBin },
16809
- PATH: runtimePath3(piExecutable)
17924
+ PATH: runtimePath4(piExecutable)
16810
17925
  }
16811
17926
  };
16812
17927
  }
@@ -16836,7 +17951,7 @@ function runPiRuntime(input, deps) {
16836
17951
  }
16837
17952
  const candidate = input.candidate ?? PI_RUNTIME_CANDIDATE;
16838
17953
  const paths = input.targetDir === void 0 ? userPaths(input.engramBin, input.detected.executable) : targetPaths(input.targetDir, input.engramBin, input.detected.executable);
16839
- const settingsJson = deps.readSettings(path49.join(paths.codingAgentDir, "settings.json"));
17954
+ const settingsJson = deps.readSettings(path53.join(paths.codingAgentDir, "settings.json"));
16840
17955
  const receiptJson = deps.readReceipt(paths.receiptPath);
16841
17956
  const lifecycleInput = {
16842
17957
  candidate,
@@ -16912,21 +18027,21 @@ function detectPiRuntime() {
16912
18027
  executable,
16913
18028
  version: inspection?.version ?? null,
16914
18029
  versionDiagnostic: inspection?.diagnostic ?? null,
16915
- codingAgentDir: process.env.PI_CODING_AGENT_DIR ?? path49.join(home, ".pi", "agent")
18030
+ codingAgentDir: process.env.PI_CODING_AGENT_DIR ?? path53.join(home, ".pi", "agent")
16916
18031
  };
16917
18032
  }
16918
18033
  function hasManagedPiRuntime(targetDir) {
16919
- const receipt = targetDir === void 0 ? path49.join(dataDir(), "pi-receipt.json") : path49.join(path49.resolve(targetDir), "state", "pi-receipt.json");
16920
- return fs41.statSync(receipt, { throwIfNoEntry: false })?.isFile() === true;
18034
+ const receipt = targetDir === void 0 ? path53.join(dataDir(), "pi-receipt.json") : path53.join(path53.resolve(targetDir), "state", "pi-receipt.json");
18035
+ return fs45.statSync(receipt, { throwIfNoEntry: false })?.isFile() === true;
16921
18036
  }
16922
18037
  function resolvePiEngramBin(targetDir) {
16923
18038
  if (targetDir === void 0) return detectEngram();
16924
- const candidate = path49.join(path49.resolve(targetDir), "bin", process.platform === "win32" ? "engram.exe" : "engram");
16925
- return fs41.statSync(candidate, { throwIfNoEntry: false })?.isFile() ? candidate : null;
18039
+ const candidate = path53.join(path53.resolve(targetDir), "bin", process.platform === "win32" ? "engram.exe" : "engram");
18040
+ return fs45.statSync(candidate, { throwIfNoEntry: false })?.isFile() ? candidate : null;
16926
18041
  }
16927
18042
  function readOptional(file, fallback) {
16928
18043
  try {
16929
- return fs41.readFileSync(file, "utf8");
18044
+ return fs45.readFileSync(file, "utf8");
16930
18045
  } catch (error) {
16931
18046
  if (error.code === "ENOENT") return fallback;
16932
18047
  throw error;
@@ -16950,24 +18065,6 @@ function runProcess(invocation) {
16950
18065
  stderr: result.stderr ?? (result.error?.message ?? "")
16951
18066
  };
16952
18067
  }
16953
- function declaredPiMcpConfigFiles(stageDir) {
16954
- const file = path49.join(stageDir, "npm", "node_modules", "jorgex-pi", "contract", "jorgex-pi.v1.json");
16955
- const contract = JSON.parse(fs41.readFileSync(file, "utf8"));
16956
- const declaration = contract.mcpAdapterConfig;
16957
- if (declaration === void 0) return [];
16958
- if (declaration.schemaVersion !== 1 || !Array.isArray(declaration.files) || !declaration.files.every((file2) => typeof file2 === "string")) {
16959
- throw new Error("El candidato Pi declara un contrato MCP inv\xE1lido.");
16960
- }
16961
- return declaration.files;
16962
- }
16963
- async function completeUpdatedPiMcp(configDir, engramBin) {
16964
- const { verifyOfficialSetup: verifyOfficialSetup5 } = await Promise.resolve().then(() => (init_pi(), pi_exports));
16965
- const before = await verifyOfficialSetup5({ configDir, engramBin });
16966
- if (!before.layers.includes("packages")) throw new Error(before.reason ?? "Setup de paquetes Pi incompleto.");
16967
- migrateOfficialPiMcpConfig({ configDir, engramBin });
16968
- const after = await verifyOfficialSetup5({ configDir, engramBin });
16969
- if (!after.ok) throw new Error(after.reason ?? "Configuraci\xF3n MCP Pi no verificada.");
16970
- }
16971
18068
  function setupPiFailedRemedy(setup) {
16972
18069
  const detail = setup.reason ?? setup.stderr ?? "setup oficial Pi fall\xF3";
16973
18070
  const recovery = setup.recovery ?? "none";
@@ -17010,7 +18107,7 @@ function preparedInstallMismatch(candidate, prepared) {
17010
18107
  return "la release preparada no coincide con la versi\xF3n/SRI del candidato";
17011
18108
  }
17012
18109
  const stageDir = prepared?.stageDir;
17013
- if (typeof stageDir !== "string" || stageDir === "" || !/(^|[\\/])stage-[0-9a-f]{32}[\\/]pi-agent$/.test(path49.resolve(stageDir))) {
18110
+ if (typeof stageDir !== "string" || stageDir === "" || !/(^|[\\/])stage-[0-9a-f]{32}[\\/]pi-agent$/.test(path53.resolve(stageDir))) {
17014
18111
  return "el stage preparado no tiene el layout aislado esperado (stage-<hex>/pi-agent)";
17015
18112
  }
17016
18113
  const evidence = prepared?.evidence;
@@ -17085,12 +18182,12 @@ function backupLegacyMigrationState(settingsPath, receiptPath, backupRoot) {
17085
18182
  }
17086
18183
  }
17087
18184
  function verifyActivePiRelease(input) {
17088
- const agentDir = path49.resolve(input.agentDir);
17089
- const npmDir = path49.join(agentDir, "npm");
17090
- const managedRoot = path49.join(npmDir, "jorgex-pi-managed");
18185
+ const agentDir = path53.resolve(input.agentDir);
18186
+ const npmDir = path53.join(agentDir, "npm");
18187
+ const managedRoot = path53.join(npmDir, "jorgex-pi-managed");
17091
18188
  let receiptRaw;
17092
18189
  try {
17093
- receiptRaw = fs41.readFileSync(input.receiptPath, "utf8");
18190
+ receiptRaw = fs45.readFileSync(input.receiptPath, "utf8");
17094
18191
  } catch {
17095
18192
  throw new Error(`pi-verify-active: no se pudo leer el receipt promovido: ${input.receiptPath}`);
17096
18193
  }
@@ -17119,7 +18216,7 @@ function verifyActivePiRelease(input) {
17119
18216
  throw new Error("pi-verify-active: el scope del receipt promovido no coincide con el esperado");
17120
18217
  }
17121
18218
  const scopeDir = scopeRecord["codingAgentDir"];
17122
- if (typeof scopeDir !== "string" || !path49.isAbsolute(scopeDir) || path49.resolve(scopeDir) !== agentDir) {
18219
+ if (typeof scopeDir !== "string" || !path53.isAbsolute(scopeDir) || path53.resolve(scopeDir) !== agentDir) {
17123
18220
  throw new Error("pi-verify-active: el codingAgentDir del receipt promovido no coincide con el agentDir");
17124
18221
  }
17125
18222
  const receiptEngram = record["engram"];
@@ -17150,40 +18247,40 @@ function verifyActivePiRelease(input) {
17150
18247
  throw new Error("pi-verify-active: el receipt promovido no conserva las seis dependencias de la evidencia");
17151
18248
  }
17152
18249
  const linkPath = managedRecord["linkPath"];
17153
- if (typeof linkPath !== "string" || !path49.isAbsolute(linkPath)) {
18250
+ if (typeof linkPath !== "string" || !path53.isAbsolute(linkPath)) {
17154
18251
  throw new Error("pi-verify-active: el receipt promovido no trae un linkPath absoluto");
17155
18252
  }
17156
- const linkResolved = path49.resolve(linkPath);
17157
- if (linkResolved !== path49.join(npmDir, "node_modules", "jorgex-pi")) {
18253
+ const linkResolved = path53.resolve(linkPath);
18254
+ if (linkResolved !== path53.join(npmDir, "node_modules", "jorgex-pi")) {
17158
18255
  throw new Error("pi-verify-active: el enlace publicado sale del entry propio");
17159
18256
  }
17160
18257
  const releaseDir = managedRecord["releaseDir"];
17161
- if (typeof releaseDir !== "string" || !path49.isAbsolute(releaseDir)) {
18258
+ if (typeof releaseDir !== "string" || !path53.isAbsolute(releaseDir)) {
17162
18259
  throw new Error("pi-verify-active: el receipt promovido no trae un releaseDir absoluto");
17163
18260
  }
17164
- const releaseResolved = path49.resolve(releaseDir);
17165
- const expectedRelease = path49.join(managedRoot, "releases", derivePiReleaseId(input.candidate.tarball.sha256, input.evidence));
18261
+ const releaseResolved = path53.resolve(releaseDir);
18262
+ const expectedRelease = path53.join(managedRoot, "releases", derivePiReleaseId(input.candidate.tarball.sha256, input.evidence));
17166
18263
  if (releaseResolved !== expectedRelease) {
17167
18264
  throw new Error("pi-verify-active: el release promovido no coincide con el esperado");
17168
18265
  }
17169
18266
  let target;
17170
18267
  try {
17171
- const linkStat = fs41.lstatSync(linkResolved);
18268
+ const linkStat = fs45.lstatSync(linkResolved);
17172
18269
  if (!linkStat.isSymbolicLink()) {
17173
18270
  throw new Error("pi-verify-active: el entry publicado no es un enlace");
17174
18271
  }
17175
- target = fs41.readlinkSync(linkResolved);
18272
+ target = fs45.readlinkSync(linkResolved);
17176
18273
  } catch (error) {
17177
18274
  if (error instanceof Error && error.message.startsWith("pi-verify-active: ")) throw error;
17178
18275
  throw new Error("pi-verify-active: no se pudo leer el enlace publicado");
17179
18276
  }
17180
- const packageRoot = path49.join(releaseResolved, "node_modules", "jorgex-pi");
17181
- if (target === "" || path49.isAbsolute(target) || path49.resolve(path49.dirname(linkResolved), target) !== packageRoot) {
18277
+ const packageRoot = path53.join(releaseResolved, "node_modules", "jorgex-pi");
18278
+ if (target === "" || path53.isAbsolute(target) || path53.resolve(path53.dirname(linkResolved), target) !== packageRoot) {
17182
18279
  throw new Error("pi-verify-active: el enlace publicado no apunta al release promovido");
17183
18280
  }
17184
18281
  let realRoot;
17185
18282
  try {
17186
- realRoot = fs41.realpathSync(linkResolved);
18283
+ realRoot = fs45.realpathSync(linkResolved);
17187
18284
  } catch {
17188
18285
  throw new Error("pi-verify-active: no se pudo resolver el realpath del enlace publicado");
17189
18286
  }
@@ -17200,11 +18297,18 @@ function verifyActivePiRelease(input) {
17200
18297
  throw new Error("pi-verify-active: el inventario del release promovido difiere de la evidencia");
17201
18298
  }
17202
18299
  }
17203
- function isStrictChildPath(child, root) {
17204
- const rel = path49.relative(path49.resolve(root), path49.resolve(child));
17205
- return rel !== "" && rel !== ".." && !rel.startsWith(`..${path49.sep}`) && !path49.isAbsolute(rel);
18300
+ function isStrictChildPath2(child, root) {
18301
+ const rel = path53.relative(path53.resolve(root), path53.resolve(child));
18302
+ return rel !== "" && rel !== ".." && !rel.startsWith(`..${path53.sep}`) && !path53.isAbsolute(rel);
18303
+ }
18304
+ function routesInstalledPiToUpdate(input) {
18305
+ if (input.operation !== "install") return false;
18306
+ const paths = input.targetDir === void 0 ? userPaths(input.engramBin, input.detected.executable) : targetPaths(input.targetDir, input.engramBin, input.detected.executable);
18307
+ const receipt = readOptional(paths.receiptPath, null);
18308
+ return receipt !== null && !isLegacyUnmanagedReceipt(receipt);
17206
18309
  }
17207
18310
  async function preparePiRuntimeSystem(input) {
18311
+ if (routesInstalledPiToUpdate(input)) return preparePiRuntimeSystem({ ...input, operation: "update" });
17208
18312
  if (input.operation !== "install" && input.operation !== "update") {
17209
18313
  return {
17210
18314
  kind: "blocked",
@@ -17220,7 +18324,7 @@ async function preparePiRuntimeSystem(input) {
17220
18324
  remedy: "El update con --target-dir nunca ejecuta preflight de red; aporta un stage preverificado o usa update real."
17221
18325
  };
17222
18326
  }
17223
- if (typeof input.engramBin !== "string" || input.engramBin === "" || !path49.isAbsolute(input.engramBin)) {
18327
+ if (typeof input.engramBin !== "string" || input.engramBin === "" || !path53.isAbsolute(input.engramBin)) {
17224
18328
  return {
17225
18329
  kind: "blocked",
17226
18330
  reason: "engram-required",
@@ -17228,7 +18332,7 @@ async function preparePiRuntimeSystem(input) {
17228
18332
  };
17229
18333
  }
17230
18334
  const updateExecutable = input.detected.executable;
17231
- if (typeof updateExecutable !== "string" || updateExecutable === "" || !path49.isAbsolute(updateExecutable)) {
18335
+ if (typeof updateExecutable !== "string" || updateExecutable === "" || !path53.isAbsolute(updateExecutable)) {
17232
18336
  return {
17233
18337
  kind: "blocked",
17234
18338
  reason: "pi-executable-missing",
@@ -17243,24 +18347,24 @@ async function preparePiRuntimeSystem(input) {
17243
18347
  };
17244
18348
  }
17245
18349
  const updateHomeDir = os6.homedir();
17246
- if (typeof updateHomeDir !== "string" || updateHomeDir === "" || !path49.isAbsolute(updateHomeDir)) {
18350
+ if (typeof updateHomeDir !== "string" || updateHomeDir === "" || !path53.isAbsolute(updateHomeDir)) {
17247
18351
  return {
17248
18352
  kind: "blocked",
17249
18353
  reason: "preflight-failed",
17250
18354
  remedy: "HOME ilegible para el preflight Pi; Pi no qued\xF3 activado."
17251
18355
  };
17252
18356
  }
17253
- const updateAgentDir = process.env.PI_CODING_AGENT_DIR ?? path49.join(updateHomeDir, ".pi", "agent");
17254
- const updateReceiptPath = path49.join(updateHomeDir, ".jorgex-stack", "pi-receipt.json");
17255
- const updateDownloadsDir = path49.join(updateHomeDir, ".jorgex-stack", "packages");
17256
- if (!isStrictChildPath(updateAgentDir, updateHomeDir)) {
18357
+ const updateAgentDir = process.env.PI_CODING_AGENT_DIR ?? path53.join(updateHomeDir, ".pi", "agent");
18358
+ const updateReceiptPath = path53.join(updateHomeDir, ".jorgex-stack", "pi-receipt.json");
18359
+ const updateDownloadsDir = path53.join(updateHomeDir, ".jorgex-stack", "packages");
18360
+ if (!isStrictChildPath2(updateAgentDir, updateHomeDir)) {
17257
18361
  return {
17258
18362
  kind: "blocked",
17259
18363
  reason: "agent-dir-outside-home",
17260
18364
  remedy: `PI_CODING_AGENT_DIR (${updateAgentDir}) est\xE1 fuera de la frontera de restore HOME (${updateHomeDir}); corrige el destino antes de reintentar; Pi no qued\xF3 activado.`
17261
18365
  };
17262
18366
  }
17263
- const updateSettingsPath = path49.join(updateAgentDir, "settings.json");
18367
+ const updateSettingsPath = path53.join(updateAgentDir, "settings.json");
17264
18368
  let updateReceiptRaw;
17265
18369
  let updateSettingsRaw;
17266
18370
  try {
@@ -17276,7 +18380,7 @@ async function preparePiRuntimeSystem(input) {
17276
18380
  const stripUpdateGateNewline = (value) => value.endsWith("\n") ? value.slice(0, -1) : value;
17277
18381
  const updateReceiptJson = updateReceiptRaw === null ? null : stripUpdateGateNewline(updateReceiptRaw);
17278
18382
  const updateSettingsJson = stripUpdateGateNewline(updateSettingsRaw);
17279
- const updatePackageRunner = path49.join(updateAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs");
18383
+ const updatePackageRunner = path53.join(updateAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs");
17280
18384
  const updateGate = verifyOfflineManagedPiRelease(
17281
18385
  {
17282
18386
  detected: {
@@ -17304,7 +18408,7 @@ async function preparePiRuntimeSystem(input) {
17304
18408
  return "remedy" in updateGate && typeof updateGate.remedy === "string" ? updateGate : { kind: "blocked", reason: updateGate.reason, remedy: `Verificaci\xF3n offline del receipt Pi fall\xF3 (${updateGate.reason}); sin preflight ni cambios. Pi no qued\xF3 activado.` };
17305
18409
  }
17306
18410
  try {
17307
- fs41.mkdirSync(updateDownloadsDir, { recursive: true });
18411
+ fs45.mkdirSync(updateDownloadsDir, { recursive: true });
17308
18412
  } catch (error) {
17309
18413
  return {
17310
18414
  kind: "blocked",
@@ -17355,7 +18459,7 @@ async function preparePiRuntimeSystem(input) {
17355
18459
  };
17356
18460
  }
17357
18461
  const piExecutable = input.detected.executable;
17358
- if (typeof piExecutable !== "string" || piExecutable === "" || !path49.isAbsolute(piExecutable)) {
18462
+ if (typeof piExecutable !== "string" || piExecutable === "" || !path53.isAbsolute(piExecutable)) {
17359
18463
  return {
17360
18464
  kind: "blocked",
17361
18465
  reason: "pi-executable-missing",
@@ -17371,8 +18475,8 @@ async function preparePiRuntimeSystem(input) {
17371
18475
  }
17372
18476
  const homeDir = os6.homedir();
17373
18477
  const agentDir = userPaths(input.engramBin, piExecutable).codingAgentDir;
17374
- const downloadsDir = path49.join(dataDir(), "packages");
17375
- if (!isStrictChildPath(agentDir, homeDir)) {
18478
+ const downloadsDir = path53.join(dataDir(), "packages");
18479
+ if (!isStrictChildPath2(agentDir, homeDir)) {
17376
18480
  return {
17377
18481
  kind: "blocked",
17378
18482
  reason: "agent-dir-outside-home",
@@ -17381,7 +18485,7 @@ async function preparePiRuntimeSystem(input) {
17381
18485
  }
17382
18486
  let preflightReceipt;
17383
18487
  try {
17384
- preflightReceipt = fs41.lstatSync(path49.join(dataDir(), "pi-receipt.json"), { throwIfNoEntry: false });
18488
+ preflightReceipt = fs45.lstatSync(path53.join(dataDir(), "pi-receipt.json"), { throwIfNoEntry: false });
17385
18489
  } catch (error) {
17386
18490
  if (error.code === "ENOENT") {
17387
18491
  preflightReceipt = void 0;
@@ -17396,7 +18500,7 @@ async function preparePiRuntimeSystem(input) {
17396
18500
  if (preflightReceipt === void 0) {
17397
18501
  let preflightEntry;
17398
18502
  try {
17399
- preflightEntry = fs41.lstatSync(path49.join(agentDir, "npm", "node_modules", "jorgex-pi"), {
18503
+ preflightEntry = fs45.lstatSync(path53.join(agentDir, "npm", "node_modules", "jorgex-pi"), {
17400
18504
  throwIfNoEntry: false
17401
18505
  });
17402
18506
  } catch (error) {
@@ -17419,7 +18523,7 @@ async function preparePiRuntimeSystem(input) {
17419
18523
  }
17420
18524
  }
17421
18525
  try {
17422
- fs41.mkdirSync(downloadsDir, { recursive: true });
18526
+ fs45.mkdirSync(downloadsDir, { recursive: true });
17423
18527
  } catch (error) {
17424
18528
  return {
17425
18529
  kind: "blocked",
@@ -17456,6 +18560,7 @@ async function preparePiRuntimeSystem(input) {
17456
18560
  }
17457
18561
  }
17458
18562
  async function runPiRuntimeSystem(input) {
18563
+ if (routesInstalledPiToUpdate(input)) return runPiRuntimeSystem({ ...input, operation: "update" });
17459
18564
  if (input.engramBin === null && input.operation !== "uninstall") return runPiRuntime(input, {
17460
18565
  readSettings: () => {
17461
18566
  throw new Error("unreachable");
@@ -17510,7 +18615,7 @@ async function runPiRuntimeSystem(input) {
17510
18615
  remedy: `${mismatch}; Pi no qued\xF3 activado y no se descarg\xF3 ni modific\xF3 nada.`
17511
18616
  };
17512
18617
  }
17513
- const settingsPath = path49.join(paths.codingAgentDir, "settings.json");
18618
+ const settingsPath = path53.join(paths.codingAgentDir, "settings.json");
17514
18619
  let settingsJson = readOptional(settingsPath, '{"packages":[]}');
17515
18620
  const receiptJson = readOptional(paths.receiptPath, null);
17516
18621
  const scopeKind = input.targetDir === void 0 ? "real" : "target-dir";
@@ -17553,8 +18658,8 @@ async function runPiRuntimeSystem(input) {
17553
18658
  };
17554
18659
  }
17555
18660
  try {
17556
- const freshEntry = path49.join(paths.codingAgentDir, "npm", "node_modules", "jorgex-pi");
17557
- if (fs41.lstatSync(freshEntry, { throwIfNoEntry: false }) !== void 0) {
18661
+ const freshEntry = path53.join(paths.codingAgentDir, "npm", "node_modules", "jorgex-pi");
18662
+ if (fs45.lstatSync(freshEntry, { throwIfNoEntry: false }) !== void 0) {
17558
18663
  return {
17559
18664
  kind: "blocked",
17560
18665
  reason: "unowned-entry",
@@ -17673,7 +18778,7 @@ async function runPiRuntimeSystem(input) {
17673
18778
  backupLegacyMigrationState(
17674
18779
  settingsPath,
17675
18780
  paths.receiptPath,
17676
- input.targetDir === void 0 ? void 0 : path49.join(path49.resolve(input.targetDir), "backups")
18781
+ input.targetDir === void 0 ? void 0 : path53.join(path53.resolve(input.targetDir), "backups")
17677
18782
  );
17678
18783
  } catch (error) {
17679
18784
  return {
@@ -17683,7 +18788,7 @@ async function runPiRuntimeSystem(input) {
17683
18788
  };
17684
18789
  }
17685
18790
  }
17686
- const homeDir = input.targetDir === void 0 ? os6.homedir() : path49.resolve(input.targetDir);
18791
+ const homeDir = input.targetDir === void 0 ? os6.homedir() : path53.resolve(input.targetDir);
17687
18792
  const agentDir = paths.codingAgentDir;
17688
18793
  const receiptPath = paths.receiptPath;
17689
18794
  const stageDir = prepared.stageDir;
@@ -17708,11 +18813,11 @@ async function runPiRuntimeSystem(input) {
17708
18813
  };
17709
18814
  const smokeStage = async (dir) => {
17710
18815
  await smokeStagedPiRuntime({ piExecutable: input.detected.executable, stageDir: dir });
17711
- await smokeLinkedPiRuntime({ piExecutable: input.detected.executable, packageRoot: path49.join(dir, "npm", "node_modules", "jorgex-pi"), scratchRoot: path49.dirname(dir) });
18816
+ await smokeLinkedPiRuntime({ piExecutable: input.detected.executable, packageRoot: path53.join(dir, "npm", "node_modules", "jorgex-pi"), scratchRoot: path53.dirname(dir) });
17712
18817
  };
17713
18818
  const verifyActive = async () => {
17714
18819
  verifyActivePiRelease({ agentDir, receiptPath, candidate: activationCandidate, evidence, scopeKind, engramBin });
17715
- await smokeLinkedPiRuntime({ piExecutable: input.detected.executable, packageRoot: path49.join(agentDir, "npm", "node_modules", "jorgex-pi"), scratchRoot: path49.dirname(stageDir) });
18820
+ await smokeLinkedPiRuntime({ piExecutable: input.detected.executable, packageRoot: path53.join(agentDir, "npm", "node_modules", "jorgex-pi"), scratchRoot: path53.dirname(stageDir) });
17716
18821
  verifyActivePiRelease({ agentDir, receiptPath, candidate: activationCandidate, evidence, scopeKind, engramBin });
17717
18822
  };
17718
18823
  try {
@@ -17739,7 +18844,7 @@ async function runPiRuntimeSystem(input) {
17739
18844
  }
17740
18845
  if (input.operation === "update") {
17741
18846
  const engramBinForUpdate = input.engramBin;
17742
- if (engramBinForUpdate === null || !path49.isAbsolute(engramBinForUpdate)) {
18847
+ if (engramBinForUpdate === null || !path53.isAbsolute(engramBinForUpdate)) {
17743
18848
  return {
17744
18849
  kind: "blocked",
17745
18850
  reason: "engram-required",
@@ -17770,10 +18875,10 @@ async function runPiRuntimeSystem(input) {
17770
18875
  remedy: `${updateMismatch}; Pi no qued\xF3 activado y no se descarg\xF3 ni modific\xF3 nada.`
17771
18876
  };
17772
18877
  }
17773
- const updateSettingsPath = path49.join(paths.codingAgentDir, "settings.json");
18878
+ const updateSettingsPath = path53.join(paths.codingAgentDir, "settings.json");
17774
18879
  const updateScopeKind = input.targetDir === void 0 ? "real" : "target-dir";
17775
- const updateHomeDir = input.targetDir === void 0 ? os6.homedir() : path49.resolve(input.targetDir);
17776
- const updateDownloadsDir = input.targetDir === void 0 ? path49.join(dataDir(), "packages") : path49.join(path49.resolve(input.targetDir), "downloads");
18880
+ const updateHomeDir = input.targetDir === void 0 ? os6.homedir() : path53.resolve(input.targetDir);
18881
+ const updateDownloadsDir = input.targetDir === void 0 ? path53.join(dataDir(), "packages") : path53.join(path53.resolve(input.targetDir), "downloads");
17777
18882
  const stripUpdateNewline = (value) => value.endsWith("\n") ? value.slice(0, -1) : value;
17778
18883
  let updateSettingsRaw;
17779
18884
  let updateReceiptRaw;
@@ -17851,7 +18956,7 @@ async function runPiRuntimeSystem(input) {
17851
18956
  }
17852
18957
  if (input.targetDir === void 0) {
17853
18958
  try {
17854
- const selectedFile = path49.basename(resolvePiAdapterConfigPath(paths.codingAgentDir));
18959
+ const selectedFile = path53.basename(resolvePiAdapterConfigPath(paths.codingAgentDir));
17855
18960
  if (selectedFile === "mcp-adapter.json" && !declaredPiMcpConfigFiles(updatePrepared.stageDir).includes(selectedFile)) {
17856
18961
  throw new Error("El candidato Pi verificado no declara lector mcp-adapter.json; no se modifica la instalaci\xF3n.");
17857
18962
  }
@@ -17935,11 +19040,11 @@ async function runPiRuntimeSystem(input) {
17935
19040
  };
17936
19041
  const updateSmokeStage = async (dir) => {
17937
19042
  await smokeStagedPiRuntime({ piExecutable: input.detected.executable, stageDir: dir });
17938
- await smokeLinkedPiRuntime({ piExecutable: input.detected.executable, packageRoot: path49.join(dir, "npm", "node_modules", "jorgex-pi"), scratchRoot: path49.dirname(dir) });
19043
+ await smokeLinkedPiRuntime({ piExecutable: input.detected.executable, packageRoot: path53.join(dir, "npm", "node_modules", "jorgex-pi"), scratchRoot: path53.dirname(dir) });
17939
19044
  };
17940
19045
  const updateVerifyActive = async () => {
17941
19046
  verifyActivePiRelease({ agentDir: updateAgentDir, receiptPath: updateReceiptPath, candidate: updateActivationCandidate, evidence: updateEvidence, scopeKind: updateScopeKind, engramBin: engramBinForUpdate });
17942
- await smokeLinkedPiRuntime({ piExecutable: input.detected.executable, packageRoot: path49.join(updateAgentDir, "npm", "node_modules", "jorgex-pi"), scratchRoot: path49.dirname(updatePrepared.stageDir) });
19047
+ await smokeLinkedPiRuntime({ piExecutable: input.detected.executable, packageRoot: path53.join(updateAgentDir, "npm", "node_modules", "jorgex-pi"), scratchRoot: path53.dirname(updatePrepared.stageDir) });
17943
19048
  verifyActivePiRelease({ agentDir: updateAgentDir, receiptPath: updateReceiptPath, candidate: updateActivationCandidate, evidence: updateEvidence, scopeKind: updateScopeKind, engramBin: engramBinForUpdate });
17944
19049
  };
17945
19050
  try {
@@ -17979,13 +19084,13 @@ async function runPiRuntimeSystem(input) {
17979
19084
  };
17980
19085
  }
17981
19086
  }
17982
- const packageRoot = path49.dirname(path49.dirname(paths.packageRunner));
19087
+ const packageRoot = path53.dirname(path53.dirname(paths.packageRunner));
17983
19088
  const writeReceipt = (receipt) => {
17984
19089
  writeText(paths.receiptPath, `${JSON.stringify(receipt, null, 2)}
17985
19090
  `);
17986
19091
  };
17987
19092
  if (input.operation === "sync") {
17988
- const settingsJson = readOptional(path49.join(paths.codingAgentDir, "settings.json"), '{"packages":[]}');
19093
+ const settingsJson = readOptional(path53.join(paths.codingAgentDir, "settings.json"), '{"packages":[]}');
17989
19094
  const receiptJson = readOptional(paths.receiptPath, null);
17990
19095
  if (receiptJson === null) {
17991
19096
  return {
@@ -18036,11 +19141,11 @@ async function runPiRuntimeSystem(input) {
18036
19141
  run: runProcess,
18037
19142
  verifyManagedArtifact: (receipt) => verifyCachedPiArtifact({
18038
19143
  receipt,
18039
- homeDir: input.targetDir === void 0 ? os6.homedir() : path49.resolve(input.targetDir),
18040
- downloadsDir: input.targetDir === void 0 ? path49.join(dataDir(), "packages") : path49.join(path49.resolve(input.targetDir), "downloads")
19144
+ homeDir: input.targetDir === void 0 ? os6.homedir() : path53.resolve(input.targetDir),
19145
+ downloadsDir: input.targetDir === void 0 ? path53.join(dataDir(), "packages") : path53.join(path53.resolve(input.targetDir), "downloads")
18041
19146
  }),
18042
- isPackageAbsent: () => !fs41.existsSync(packageRoot),
18043
- deleteReceipt: () => fs41.rmSync(paths.receiptPath, { force: true })
19147
+ isPackageAbsent: () => !fs45.existsSync(packageRoot),
19148
+ deleteReceipt: () => fs45.rmSync(paths.receiptPath, { force: true })
18044
19149
  }
18045
19150
  );
18046
19151
  }
@@ -18052,7 +19157,7 @@ async function runPiRuntimeSystem(input) {
18052
19157
  }
18053
19158
  }
18054
19159
  if (input.operation === "models") {
18055
- const settingsJson = readOptional(path49.join(paths.codingAgentDir, "settings.json"), '{"packages":[]}');
19160
+ const settingsJson = readOptional(path53.join(paths.codingAgentDir, "settings.json"), '{"packages":[]}');
18056
19161
  const receiptJson = readOptional(paths.receiptPath, null);
18057
19162
  if (receiptJson === null) {
18058
19163
  return {
@@ -18103,11 +19208,11 @@ async function runPiRuntimeSystem(input) {
18103
19208
  run: runProcess,
18104
19209
  verifyManagedArtifact: (receipt) => verifyCachedPiArtifact({
18105
19210
  receipt,
18106
- homeDir: input.targetDir === void 0 ? os6.homedir() : path49.resolve(input.targetDir),
18107
- downloadsDir: input.targetDir === void 0 ? path49.join(dataDir(), "packages") : path49.join(path49.resolve(input.targetDir), "downloads")
19211
+ homeDir: input.targetDir === void 0 ? os6.homedir() : path53.resolve(input.targetDir),
19212
+ downloadsDir: input.targetDir === void 0 ? path53.join(dataDir(), "packages") : path53.join(path53.resolve(input.targetDir), "downloads")
18108
19213
  }),
18109
- isPackageAbsent: () => !fs41.existsSync(packageRoot),
18110
- deleteReceipt: () => fs41.rmSync(paths.receiptPath, { force: true })
19214
+ isPackageAbsent: () => !fs45.existsSync(packageRoot),
19215
+ deleteReceipt: () => fs45.rmSync(paths.receiptPath, { force: true })
18111
19216
  }
18112
19217
  );
18113
19218
  }
@@ -18124,17 +19229,17 @@ async function runPiRuntimeSystem(input) {
18124
19229
  value,
18125
19230
  {
18126
19231
  backupSettings: () => createBackup(
18127
- [path49.join(paths.codingAgentDir, "settings.json")],
19232
+ [path53.join(paths.codingAgentDir, "settings.json")],
18128
19233
  "pi-package-uninstall",
18129
- input.targetDir === void 0 ? void 0 : path49.join(path49.resolve(input.targetDir), "backups")
19234
+ input.targetDir === void 0 ? void 0 : path53.join(path53.resolve(input.targetDir), "backups")
18130
19235
  ),
18131
19236
  run: runProcess,
18132
19237
  verifyManagedArtifact: (receipt) => verifyCachedPiArtifact({
18133
19238
  receipt,
18134
- homeDir: input.targetDir === void 0 ? os6.homedir() : path49.resolve(input.targetDir),
18135
- downloadsDir: input.targetDir === void 0 ? path49.join(dataDir(), "packages") : path49.join(path49.resolve(input.targetDir), "downloads")
19239
+ homeDir: input.targetDir === void 0 ? os6.homedir() : path53.resolve(input.targetDir),
19240
+ downloadsDir: input.targetDir === void 0 ? path53.join(dataDir(), "packages") : path53.join(path53.resolve(input.targetDir), "downloads")
18136
19241
  }),
18137
- readSettings: () => fs41.readFileSync(path49.join(paths.codingAgentDir, "settings.json"), "utf8"),
19242
+ readSettings: () => fs45.readFileSync(path53.join(paths.codingAgentDir, "settings.json"), "utf8"),
18138
19243
  verifyLegacyPackage: (receipt) => {
18139
19244
  const op = value;
18140
19245
  let effectiveEngram = op.engramBin;
@@ -18143,7 +19248,7 @@ async function runPiRuntimeSystem(input) {
18143
19248
  const engram = Reflect.get(receipt, "engram");
18144
19249
  if (engram === null || typeof engram !== "object" || Array.isArray(engram)) return false;
18145
19250
  const binary = Reflect.get(engram, "binary");
18146
- if (typeof binary !== "string" || binary === "" || !path49.isAbsolute(binary)) return false;
19251
+ if (typeof binary !== "string" || binary === "" || !path53.isAbsolute(binary)) return false;
18147
19252
  effectiveEngram = binary;
18148
19253
  }
18149
19254
  if (typeof effectiveEngram !== "string" || effectiveEngram === "") return false;
@@ -18167,17 +19272,17 @@ async function runPiRuntimeSystem(input) {
18167
19272
  }
18168
19273
  let currentReceiptRaw = null;
18169
19274
  try {
18170
- currentReceiptRaw = fs41.readFileSync(paths.receiptPath, "utf8");
19275
+ currentReceiptRaw = fs45.readFileSync(paths.receiptPath, "utf8");
18171
19276
  } catch {
18172
19277
  return { kind: "blocked", reason: "remove-failed" };
18173
19278
  }
18174
19279
  if (typeof op.receiptJson === "string" && currentReceiptRaw !== op.receiptJson) {
18175
19280
  return { kind: "blocked", reason: "receipt-untrusted" };
18176
19281
  }
18177
- const settingsPath = path49.join(paths.codingAgentDir, "settings.json");
18178
- const legacyEntry = path49.join(paths.codingAgentDir, "npm", "node_modules", "jorgex-pi");
19282
+ const settingsPath = path53.join(paths.codingAgentDir, "settings.json");
19283
+ const legacyEntry = path53.join(paths.codingAgentDir, "npm", "node_modules", "jorgex-pi");
18179
19284
  return deactivateVerifiedLegacyPiEntry({
18180
- homeDir: input.targetDir === void 0 ? os6.homedir() : path49.resolve(input.targetDir),
19285
+ homeDir: input.targetDir === void 0 ? os6.homedir() : path53.resolve(input.targetDir),
18181
19286
  agentDir: paths.codingAgentDir,
18182
19287
  receiptPath: paths.receiptPath,
18183
19288
  source,
@@ -18185,19 +19290,19 @@ async function runPiRuntimeSystem(input) {
18185
19290
  verify: () => {
18186
19291
  let entryStat = null;
18187
19292
  try {
18188
- entryStat = fs41.lstatSync(legacyEntry);
19293
+ entryStat = fs45.lstatSync(legacyEntry);
18189
19294
  } catch (error) {
18190
19295
  if (error.code !== "ENOENT") throw error;
18191
19296
  }
18192
19297
  if (entryStat !== null) throw new Error("managed entry still present after uninstall");
18193
19298
  let receiptStat = null;
18194
19299
  try {
18195
- receiptStat = fs41.lstatSync(paths.receiptPath);
19300
+ receiptStat = fs45.lstatSync(paths.receiptPath);
18196
19301
  } catch (error) {
18197
19302
  if (error.code !== "ENOENT") throw error;
18198
19303
  }
18199
19304
  if (receiptStat !== null) throw new Error("receipt still present after uninstall");
18200
- if (fs41.readFileSync(settingsPath, "utf8") !== nextSettings) {
19305
+ if (fs45.readFileSync(settingsPath, "utf8") !== nextSettings) {
18201
19306
  throw new Error("settings mismatch after uninstall");
18202
19307
  }
18203
19308
  }
@@ -18212,9 +19317,9 @@ async function runPiRuntimeSystem(input) {
18212
19317
  if (typeof mp.releaseDir !== "string" || typeof mp.linkPath !== "string" || typeof mp.backupDir !== "string") {
18213
19318
  return { kind: "blocked", reason: "remove-failed" };
18214
19319
  }
18215
- const settingsPath = path49.join(paths.codingAgentDir, "settings.json");
19320
+ const settingsPath = path53.join(paths.codingAgentDir, "settings.json");
18216
19321
  return deactivateVerifiedPiRelease({
18217
- homeDir: input.targetDir === void 0 ? os6.homedir() : path49.resolve(input.targetDir),
19322
+ homeDir: input.targetDir === void 0 ? os6.homedir() : path53.resolve(input.targetDir),
18218
19323
  agentDir: paths.codingAgentDir,
18219
19324
  receiptPath: paths.receiptPath,
18220
19325
  managedPackage: mp,
@@ -18222,33 +19327,33 @@ async function runPiRuntimeSystem(input) {
18222
19327
  verify: () => {
18223
19328
  let entryStat = null;
18224
19329
  try {
18225
- entryStat = fs41.lstatSync(mp.linkPath);
19330
+ entryStat = fs45.lstatSync(mp.linkPath);
18226
19331
  } catch (error) {
18227
19332
  if (error.code !== "ENOENT") throw error;
18228
19333
  }
18229
19334
  if (entryStat !== null) throw new Error("managed entry still present after uninstall");
18230
19335
  let releaseStat = null;
18231
19336
  try {
18232
- releaseStat = fs41.lstatSync(mp.releaseDir);
19337
+ releaseStat = fs45.lstatSync(mp.releaseDir);
18233
19338
  } catch (error) {
18234
19339
  if (error.code !== "ENOENT") throw error;
18235
19340
  }
18236
19341
  if (releaseStat !== null) throw new Error("managed release still present after uninstall");
18237
19342
  let receiptStat = null;
18238
19343
  try {
18239
- receiptStat = fs41.lstatSync(paths.receiptPath);
19344
+ receiptStat = fs45.lstatSync(paths.receiptPath);
18240
19345
  } catch (error) {
18241
19346
  if (error.code !== "ENOENT") throw error;
18242
19347
  }
18243
19348
  if (receiptStat !== null) throw new Error("receipt still present after uninstall");
18244
- if (fs41.readFileSync(settingsPath, "utf8") !== nextSettings) {
19349
+ if (fs45.readFileSync(settingsPath, "utf8") !== nextSettings) {
18245
19350
  throw new Error("settings mismatch after uninstall");
18246
19351
  }
18247
19352
  }
18248
19353
  });
18249
19354
  },
18250
- isPackageAbsent: () => !fs41.existsSync(packageRoot),
18251
- deleteReceipt: () => fs41.rmSync(paths.receiptPath, { force: true })
19355
+ isPackageAbsent: () => !fs45.existsSync(packageRoot),
19356
+ deleteReceipt: () => fs45.rmSync(paths.receiptPath, { force: true })
18252
19357
  }
18253
19358
  )
18254
19359
  };
@@ -18261,11 +19366,11 @@ init_pi();
18261
19366
  init_system_prompt_sections();
18262
19367
 
18263
19368
  // src/lib/pi-browser-contract.ts
18264
- import fs42 from "fs";
18265
- import path50 from "path";
19369
+ import fs46 from "fs";
19370
+ import path54 from "path";
18266
19371
  var CONTRACT_FILE = "browser-handoffs.v1.json";
18267
19372
  var MAX_CONTRACT_BYTES = 4 * 1024;
18268
- function fail11(message) {
19373
+ function fail13(message) {
18269
19374
  throw new Error(`Pi browser handoff contract: ${message}`);
18270
19375
  }
18271
19376
  function validSchemas(value) {
@@ -18273,53 +19378,135 @@ function validSchemas(value) {
18273
19378
  }
18274
19379
  function requirePiBrowserHandoffSchemas(packageRoot, required) {
18275
19380
  if (required.playwright === void 0 && required.devtools === void 0) return;
18276
- if (typeof packageRoot !== "string" || !path50.isAbsolute(packageRoot)) fail11("package root is not absolute");
19381
+ if (typeof packageRoot !== "string" || !path54.isAbsolute(packageRoot)) fail13("package root is not absolute");
18277
19382
  let realRoot;
18278
19383
  try {
18279
- realRoot = fs42.realpathSync(packageRoot);
18280
- if (!fs42.statSync(realRoot).isDirectory()) fail11("package root is not a directory");
19384
+ realRoot = fs46.realpathSync(packageRoot);
19385
+ if (!fs46.statSync(realRoot).isDirectory()) fail13("package root is not a directory");
18281
19386
  } catch {
18282
- fail11("installed Pi package root is unavailable");
19387
+ fail13("installed Pi package root is unavailable");
18283
19388
  }
18284
- const contractDir = path50.join(realRoot, "contract");
18285
- const file = path50.join(contractDir, CONTRACT_FILE);
19389
+ const contractDir = path54.join(realRoot, "contract");
19390
+ const file = path54.join(contractDir, CONTRACT_FILE);
18286
19391
  let raw;
18287
19392
  try {
18288
- const dirStat = fs42.lstatSync(contractDir);
18289
- const stat = fs42.lstatSync(file);
18290
- if (!dirStat.isDirectory() || dirStat.isSymbolicLink() || !stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_CONTRACT_BYTES) fail11("contract is not a bounded regular file");
18291
- if (fs42.realpathSync(file) !== file) fail11("contract path is not canonical");
18292
- raw = fs42.readFileSync(file);
19393
+ const dirStat = fs46.lstatSync(contractDir);
19394
+ const stat = fs46.lstatSync(file);
19395
+ if (!dirStat.isDirectory() || dirStat.isSymbolicLink() || !stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_CONTRACT_BYTES) fail13("contract is not a bounded regular file");
19396
+ if (fs46.realpathSync(file) !== file) fail13("contract path is not canonical");
19397
+ raw = fs46.readFileSync(file);
18293
19398
  if (raw.byteLength > MAX_CONTRACT_BYTES || !Buffer.from(raw.toString("utf8"), "utf8").equals(raw)) {
18294
- fail11("contract is not bounded UTF-8");
19399
+ fail13("contract is not bounded UTF-8");
18295
19400
  }
18296
19401
  } catch (error) {
18297
19402
  if (error instanceof Error && error.message.startsWith("Pi browser handoff contract: ")) throw error;
18298
- fail11("installed Pi does not provide a readable browser schema contract");
19403
+ fail13("installed Pi does not provide a readable browser schema contract");
18299
19404
  }
18300
19405
  let contract;
18301
19406
  try {
18302
19407
  contract = JSON.parse(raw.toString("utf8"));
18303
19408
  } catch {
18304
- fail11("contract is invalid JSON");
19409
+ fail13("contract is invalid JSON");
18305
19410
  }
18306
- if (contract === null || typeof contract !== "object" || Array.isArray(contract)) fail11("contract is invalid");
19411
+ if (contract === null || typeof contract !== "object" || Array.isArray(contract)) fail13("contract is invalid");
18307
19412
  const record = contract;
18308
19413
  if (Object.keys(record).sort().join("\0") !== ["devtools", "playwright", "schemaVersion"].join("\0") || record.schemaVersion !== 1 || !validSchemas(record.playwright) || !validSchemas(record.devtools)) {
18309
- fail11("contract schema is invalid");
19414
+ fail13("contract schema is invalid");
18310
19415
  }
18311
19416
  if (required.playwright !== void 0 && !record.playwright.includes(required.playwright)) {
18312
- fail11(`Playwright handoff schema ${required.playwright} is unsupported`);
19417
+ fail13(`Playwright handoff schema ${required.playwright} is unsupported`);
18313
19418
  }
18314
19419
  if (required.devtools !== void 0 && !record.devtools.includes(required.devtools)) {
18315
- fail11(`DevTools handoff schema ${required.devtools} is unsupported`);
19420
+ fail13(`DevTools handoff schema ${required.devtools} is unsupported`);
19421
+ }
19422
+ }
19423
+
19424
+ // src/lib/pi-browser-update.ts
19425
+ var PLAYWRIGHT_PACKAGE = "@playwright/cli";
19426
+ var BROWSER_INSTALL_TIMEOUT_MS = 6e5;
19427
+ function errorMessage(error) {
19428
+ return error instanceof Error ? error.message : String(error);
19429
+ }
19430
+ function assertSuccessfulProcess(result, action) {
19431
+ if (result.error !== void 0 || result.status !== 0) {
19432
+ throw new Error(`Playwright ${action} failed${result.error === void 0 ? "" : `: ${result.error.message}`}`);
19433
+ }
19434
+ }
19435
+ async function refreshPiPlaywright(input) {
19436
+ const previous = loadVerifiedManagedBrowserReceipt(input.stateDir, PLAYWRIGHT_PACKAGE);
19437
+ let activated;
19438
+ let observed;
19439
+ let capability;
19440
+ try {
19441
+ await prepareVerifiedBrowserRelease(PLAYWRIGHT_PACKAGE, {
19442
+ fetchImpl: input.fetchImpl ?? globalThis.fetch,
19443
+ withVerifiedArtifact: async (context) => {
19444
+ activated = await activateVerifiedBrowserArtifact(context, {
19445
+ stateDir: input.stateDir,
19446
+ pnpmBin: input.pnpmBin,
19447
+ fetchImpl: input.fetchImpl ?? globalThis.fetch
19448
+ });
19449
+ const browserInstall = runVerifiedManagedPlaywright(
19450
+ input.stateDir,
19451
+ ["install-browser", "chromium"],
19452
+ { timeoutMs: BROWSER_INSTALL_TIMEOUT_MS }
19453
+ );
19454
+ assertSuccessfulProcess(browserInstall, "Chromium installation");
19455
+ const versionProbe = runVerifiedManagedPlaywright(
19456
+ input.stateDir,
19457
+ ["--version"],
19458
+ { captureOutput: true, timeoutMs: 5e3 }
19459
+ );
19460
+ assertSuccessfulProcess(versionProbe, "version probe");
19461
+ const reportedVersion = (versionProbe.stdout ?? "").trim().replace(/^playwright-cli\s+/i, "");
19462
+ if (reportedVersion !== context.release.version) {
19463
+ throw new Error(
19464
+ `Playwright version probe returned ${reportedVersion || "no version"}; expected ${context.release.version}`
19465
+ );
19466
+ }
19467
+ const current = loadVerifiedManagedBrowserReceipt(input.stateDir, PLAYWRIGHT_PACKAGE);
19468
+ if (current === null || current.version !== context.release.version || current.integrity !== context.release.integrity || current.rootPath !== activated.rootPath) {
19469
+ throw new Error("Playwright managed receipt does not match the verified release");
19470
+ }
19471
+ const browserCache = isPlaywrightBrowserReady();
19472
+ if (browserCache.status !== "ready" || !verifyManagedPlaywrightBrowser(input.stateDir)) {
19473
+ throw new Error("Playwright Chromium smoke failed");
19474
+ }
19475
+ capability = {
19476
+ cli: { status: "current", binPath: current.launcherPath, detectedVersion: context.release.version },
19477
+ browserCache,
19478
+ browserVerified: true,
19479
+ effective: true
19480
+ };
19481
+ observed = {
19482
+ version: context.release.version,
19483
+ integrity: context.release.integrity
19484
+ };
19485
+ }
19486
+ });
19487
+ if (observed === void 0 || capability === void 0) {
19488
+ throw new Error("Playwright verified release did not complete activation and smoke");
19489
+ }
19490
+ return { observed, capability };
19491
+ } catch (error) {
19492
+ if (activated !== void 0) {
19493
+ try {
19494
+ await rollbackManagedBrowserActivation(input.stateDir, PLAYWRIGHT_PACKAGE, activated, previous);
19495
+ } catch (rollbackError) {
19496
+ throw new Error(
19497
+ `${errorMessage(error)}; Playwright rollback also failed: ${errorMessage(rollbackError)}`
19498
+ );
19499
+ }
19500
+ }
19501
+ throw error;
18316
19502
  }
18317
19503
  }
18318
19504
 
18319
19505
  // src/lib/pi-managed-runtime.ts
18320
19506
  init_paths();
18321
- import fs43 from "fs";
18322
- import path51 from "path";
19507
+ import fs47 from "fs";
19508
+ import os7 from "os";
19509
+ import path55 from "path";
18323
19510
  function projectionOperation(operation) {
18324
19511
  return operation === "update" ? "sync" : operation;
18325
19512
  }
@@ -18449,6 +19636,7 @@ async function runManagedPiSystem(input) {
18449
19636
  devtoolsMcpObservedVersion: injectedDevtoolsObserved,
18450
19637
  playwrightCliEnabled: explicitPlaywright,
18451
19638
  playwrightCapability,
19639
+ playwrightRefresh,
18452
19640
  packageOnly,
18453
19641
  writingStyle: suppliedStyle,
18454
19642
  writingStyleMode,
@@ -18473,15 +19661,18 @@ async function runManagedPiSystem(input) {
18473
19661
  const style = readsStyle ? suppliedStyle ?? preparedStyle : void 0;
18474
19662
  const mode = readsStyle ? writingStyleMode ?? (input.targetDir === void 0 ? loadInstallModePreference().mode : "human") : "human";
18475
19663
  const writingStyle = style && mode === "programmatic" ? { ...style, content: null } : style;
18476
- const devtoolsMcpEnabled = explicitDevtools ?? (input.targetDir === void 0 && loadDevtoolsMcpPreference(devtoolsMcpPreferenceFile(), "pi"));
19664
+ const persistedDevtools = explicitDevtools === void 0 && input.targetDir === void 0 && loadDevtoolsMcpPreference(devtoolsMcpPreferenceFile(), "pi");
19665
+ const devtoolsMcpEnabled = explicitDevtools ?? persistedDevtools;
18477
19666
  const needsDevtoolsObservation = devtoolsMcpEnabled === true && input.operation !== "uninstall" && input.operation !== "models";
18478
19667
  let devtoolsMcpObservedVersion = null;
18479
19668
  let devtoolsVerifiedForPersist;
18480
- const devtoolsManagedStateDir = input.targetDir === void 0 ? dataDir() : path51.join(input.targetDir, "home", ".jorgex-stack");
19669
+ const devtoolsManagedStateDir = input.targetDir === void 0 ? dataDir() : path55.join(input.targetDir, "home", ".jorgex-stack");
19670
+ const deliberateBrowserOperation = input.targetDir === void 0 && (input.operation === "install" || input.operation === "update");
19671
+ const devtoolsRefreshRequested = deliberateBrowserOperation && devtoolsMcpEnabled === true && explicitDevtools !== false;
18481
19672
  if (needsDevtoolsObservation) {
18482
19673
  if (input.targetDir !== void 0) {
18483
19674
  devtoolsMcpObservedVersion = isValidObservedVersion(injectedDevtoolsObserved) ? { version: injectedDevtoolsObserved.version, integrity: injectedDevtoolsObserved.integrity } : null;
18484
- } else if ((input.operation === "install" || input.operation === "update") && explicitDevtools === true) {
19675
+ } else if (devtoolsRefreshRequested) {
18485
19676
  if (isValidObservedVersion(injectedDevtoolsObserved)) {
18486
19677
  devtoolsMcpObservedVersion = { version: injectedDevtoolsObserved.version, integrity: injectedDevtoolsObserved.integrity };
18487
19678
  devtoolsVerifiedForPersist = devtoolsMcpObservedVersion;
@@ -18496,7 +19687,8 @@ async function runManagedPiSystem(input) {
18496
19687
  await activateVerifiedBrowserArtifact(context, {
18497
19688
  stateDir: devtoolsManagedStateDir,
18498
19689
  pnpmBin,
18499
- fetchImpl: globalThis.fetch
19690
+ fetchImpl: globalThis.fetch,
19691
+ browserExecutablePath: detectChromiumExecutable()
18500
19692
  });
18501
19693
  }
18502
19694
  });
@@ -18525,8 +19717,8 @@ async function runManagedPiSystem(input) {
18525
19717
  if (needsDevtoolsObservation) {
18526
19718
  try {
18527
19719
  if (input.targetDir !== void 0) {
18528
- const target = fs43.lstatSync(input.targetDir);
18529
- const home = fs43.lstatSync(path51.join(input.targetDir, "home"));
19720
+ const target = fs47.lstatSync(input.targetDir);
19721
+ const home = fs47.lstatSync(path55.join(input.targetDir, "home"));
18530
19722
  if (!target.isDirectory() || target.isSymbolicLink() || !home.isDirectory() || home.isSymbolicLink()) {
18531
19723
  throw new Error("HOME del target Pi no es un directorio real aislado");
18532
19724
  }
@@ -18543,14 +19735,56 @@ async function runManagedPiSystem(input) {
18543
19735
  };
18544
19736
  }
18545
19737
  }
19738
+ if (input.targetDir === void 0 && devtoolsVerifiedForPersist !== void 0) {
19739
+ saveDevtoolsMcpPreference(
19740
+ devtoolsMcpPreferenceFile(),
19741
+ "pi",
19742
+ loadDevtoolsMcpPreference(devtoolsMcpPreferenceFile(), "pi") === true,
19743
+ devtoolsVerifiedForPersist
19744
+ );
19745
+ }
18546
19746
  const devtoolsMcpVersion = devtoolsMcpObservedVersion?.version ?? null;
18547
19747
  const supportsPlaywright = PI_RUNTIME_CANDIDATE.contract.capabilities.includes("playwright-handoff-v1");
18548
19748
  const persistedPlaywright = input.targetDir === void 0 && loadPlaywrightCliPreference(void 0, "pi") === true;
18549
19749
  const selectedPlaywright = explicitPlaywright ?? persistedPlaywright;
18550
- const playwrightCapabilityEffective = playwrightCapability?.effective === true;
19750
+ let effectivePlaywrightCapability = playwrightCapability;
19751
+ let refreshedPlaywright;
19752
+ const skipPlaywrightRefresh = playwrightRefresh === false;
19753
+ const injectedPlaywrightCapability = input.operation === "install" && explicitPlaywright === true && playwrightCapability?.effective === true;
19754
+ if (deliberateBrowserOperation && input.operation === "install" && supportsPlaywright && explicitPlaywright === true && !skipPlaywrightRefresh && !injectedPlaywrightCapability) {
19755
+ return { kind: "blocked", reason: "playwright-capability-unverified", remedy: "El opt-in nuevo de Playwright requiere la adquisici\xF3n y verificaci\xF3n del CLI antes de proyectarse en Pi." };
19756
+ }
19757
+ const playwrightRefreshRequested = deliberateBrowserOperation && supportsPlaywright && selectedPlaywright === true && !skipPlaywrightRefresh && !injectedPlaywrightCapability;
19758
+ if (playwrightRefreshRequested) {
19759
+ const pnpmBin = resolvePnpmBin();
19760
+ if (pnpmBin === null) {
19761
+ return {
19762
+ kind: "blocked",
19763
+ reason: "playwright-refresh-failed",
19764
+ remedy: "pnpm no est\xE1 disponible para actualizar el Playwright gestionado de Pi; instala pnpm y reintenta."
19765
+ };
19766
+ }
19767
+ try {
19768
+ refreshedPlaywright = await refreshPiPlaywright({ stateDir: dataDir(), pnpmBin });
19769
+ effectivePlaywrightCapability = refreshedPlaywright.capability;
19770
+ savePlaywrightCliPreference(
19771
+ playwrightCliPreferenceFile(),
19772
+ true,
19773
+ { pi: persistedPlaywright },
19774
+ refreshedPlaywright.observed
19775
+ );
19776
+ } catch (error) {
19777
+ return {
19778
+ kind: "blocked",
19779
+ reason: "playwright-refresh-failed",
19780
+ remedy: error instanceof Error ? error.message : String(error)
19781
+ };
19782
+ }
19783
+ }
19784
+ const playwrightCapabilityEffective = effectivePlaywrightCapability?.effective === true;
18551
19785
  const playwrightCliEnabled = input.targetDir === void 0 && supportsPlaywright && selectedPlaywright && playwrightCapabilityEffective;
18552
19786
  const playwrightManagedStateDir = playwrightCliEnabled && input.operation !== "uninstall" && input.operation !== "models" ? dataDir() : void 0;
18553
- const playwrightDispatcherPath = playwrightManagedStateDir === void 0 ? void 0 : path51.join(path51.dirname(stackRoot()), "dist", "browser-playwright.js");
19787
+ const playwrightDispatcherPath = playwrightManagedStateDir === void 0 ? void 0 : path55.join(path55.dirname(stackRoot()), "dist", "browser-playwright.js");
18554
19788
  let effectivePackageSource = PI_RUNTIME_CANDIDATE.package.source;
18555
19789
  const projectionInput = {
18556
19790
  writingStyle,
@@ -18569,7 +19803,7 @@ async function runManagedPiSystem(input) {
18569
19803
  devtoolsMcpVersion
18570
19804
  };
18571
19805
  if (preparedStyle !== void 0 && input.operation !== "doctor") applyWritingStyle(preparedStyle);
18572
- const result = await runManagedPiOperation(input.operation, {
19806
+ let result = await runManagedPiOperation(input.operation, {
18573
19807
  installInitRemedy: input.targetDir === void 0 ? void 0 : INSTALL_INIT_TARGET_REMEDY,
18574
19808
  async runPackage(operation) {
18575
19809
  const raw = await runPiRuntimeSystem({
@@ -18579,7 +19813,7 @@ async function runManagedPiSystem(input) {
18579
19813
  operation,
18580
19814
  ...upgradePermissions ? { upgradePermissions: true } : {}
18581
19815
  });
18582
- if (effectiveCandidate !== void 0 && (operation === "install" && raw.kind === "installed" || operation === "update" && raw.kind === "updated")) {
19816
+ if (effectiveCandidate !== void 0 && ((operation === "install" || operation === "update") && (raw.kind === "installed" || raw.kind === "updated"))) {
18583
19817
  const expected = effectiveCandidate.package;
18584
19818
  const receipt = raw.receipt;
18585
19819
  const observed = receipt?.candidate?.package ?? receipt?.package;
@@ -18592,7 +19826,7 @@ async function runManagedPiSystem(input) {
18592
19826
  }
18593
19827
  effectivePackageSource = observed.source;
18594
19828
  }
18595
- if ((operation === "sync" && raw.kind === "synced" || operation === "doctor" && raw.kind === "healthy" || operation === "update" && raw.kind === "healthy") && "packageSource" in raw && raw.packageSource !== void 0) {
19829
+ if ((operation === "sync" && raw.kind === "synced" || operation === "doctor" && raw.kind === "healthy" || (operation === "update" || operation === "install") && raw.kind === "healthy") && "packageSource" in raw && raw.packageSource !== void 0) {
18596
19830
  const provided = raw.packageSource;
18597
19831
  if (typeof provided !== "string" || !/^npm:jorgex-pi@((0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*))$/.test(provided)) {
18598
19832
  return {
@@ -18607,7 +19841,7 @@ async function runManagedPiSystem(input) {
18607
19841
  },
18608
19842
  runProjection(operation) {
18609
19843
  if (playwrightCliEnabled || devtoolsMcpEnabled) {
18610
- const packageRoot = path51.join(path51.dirname(piSystemPromptFile(input.targetDir)), "npm", "node_modules", "jorgex-pi");
19844
+ const packageRoot = path55.join(path55.dirname(piSystemPromptFile(input.targetDir)), "npm", "node_modules", "jorgex-pi");
18611
19845
  try {
18612
19846
  requirePiBrowserHandoffSchemas(packageRoot, {
18613
19847
  ...playwrightCliEnabled ? { playwright: 2 } : {},
@@ -18640,15 +19874,38 @@ async function runManagedPiSystem(input) {
18640
19874
  return Promise.resolve(completePiProjectionUninstallSystem(token, { operation: "uninstall", ...projectionInput }));
18641
19875
  }
18642
19876
  });
18643
- if (input.targetDir === void 0 && explicitDevtools !== void 0 && (input.operation === "install" || input.operation === "sync" || input.operation === "update") && result.kind !== "blocked") {
18644
- if (explicitDevtools === true && devtoolsVerifiedForPersist !== void 0) {
19877
+ if (input.targetDir === void 0 && (input.operation === "install" || input.operation === "update") && result.kind !== "blocked") {
19878
+ try {
19879
+ if (input.engramBin === null) throw new Error("Engram no disponible para verificar los providers Pi");
19880
+ const providers = await updatePiProviderPackages({
19881
+ homeDir: os7.homedir(),
19882
+ agentDir: path55.dirname(piSystemPromptFile()),
19883
+ piExecutable: input.detected.executable,
19884
+ engramBin: input.engramBin
19885
+ });
19886
+ if (providers.kind === "updated" && result.kind === "healthy") result = { kind: "updated" };
19887
+ } catch (error) {
19888
+ return { kind: "blocked", reason: "provider-update-failed", remedy: `La fase principal de Pi termin\xF3, pero la actualizaci\xF3n de providers est\xE1 incompleta: ${error instanceof Error ? error.message : String(error)}` };
19889
+ }
19890
+ }
19891
+ if (input.targetDir === void 0 && (explicitDevtools !== void 0 || devtoolsVerifiedForPersist !== void 0) && (input.operation === "install" || input.operation === "sync" || input.operation === "update") && result.kind !== "blocked") {
19892
+ if (devtoolsVerifiedForPersist !== void 0) {
18645
19893
  saveDevtoolsMcpPreference(devtoolsMcpPreferenceFile(), "pi", true, devtoolsVerifiedForPersist);
18646
- } else {
19894
+ } else if (explicitDevtools !== void 0) {
18647
19895
  saveDevtoolsMcpPreference(devtoolsMcpPreferenceFile(), "pi", explicitDevtools);
18648
19896
  }
18649
19897
  }
18650
- if (input.targetDir === void 0 && supportsPlaywright && explicitPlaywright !== void 0 && (input.operation === "install" || input.operation === "sync") && result.kind !== "blocked") {
18651
- savePlaywrightCliPreference(playwrightCliPreferenceFile(), true, { pi: explicitPlaywright });
19898
+ if (input.targetDir === void 0 && supportsPlaywright && (explicitPlaywright !== void 0 || refreshedPlaywright !== void 0) && (input.operation === "install" || input.operation === "sync" || input.operation === "update") && result.kind !== "blocked") {
19899
+ if (refreshedPlaywright !== void 0) {
19900
+ savePlaywrightCliPreference(
19901
+ playwrightCliPreferenceFile(),
19902
+ true,
19903
+ { pi: true },
19904
+ refreshedPlaywright.observed
19905
+ );
19906
+ } else {
19907
+ savePlaywrightCliPreference(playwrightCliPreferenceFile(), true, { pi: explicitPlaywright ?? true });
19908
+ }
18652
19909
  }
18653
19910
  return result;
18654
19911
  }
@@ -18660,7 +19917,7 @@ init_fsx();
18660
19917
 
18661
19918
  // src/lib/quality-runner.ts
18662
19919
  init_detect();
18663
- import path52 from "path";
19920
+ import path56 from "path";
18664
19921
  import { spawn as spawn3, spawnSync as spawnSync6 } from "child_process";
18665
19922
 
18666
19923
  // src/lib/quality-policy.ts
@@ -18762,7 +20019,7 @@ var SENSITIVE_OUTPUT_PATTERN = /((?:authorization\s*:\s*bearer\s+|bearer\s+))[^\
18762
20019
  var SENSITIVE_KEY_VALUE_PATTERN = /((?:^|(?<=[^\w.-]))(?:-{0,2})(?:(?:[a-z][a-z0-9_.-]*[-_]?)?(?:access[-_]?token|api[-_]?key|auth(?:orization)?|client[-_]?secret|credential|pass(?:word|wd)?|refresh[-_]?token|secret|token))\s*[=:]\s*)(?:"[^"]*"|'[^']*'|[^\r\n,;]+)/gi;
18763
20020
  var SENSITIVE_SEPARATE_FLAG_PATTERN = /((?:^|(?<=[^\w.-]))-{1,2}(?:(?:[a-z][a-z0-9_.-]*[-_]?)?(?:access[-_]?token|api[-_]?key|auth(?:orization)?|client[-_]?secret|credential|pass(?:word|wd)?|refresh[-_]?token|secret|token))[ \t]+)(?:"[^"]*"|'[^']*'|[^\r\n,;]+)/gi;
18764
20021
  var SENSITIVE_STRUCTURED_VALUE_PATTERN = /((?:"|')?(?:token|password|api[-_]?key|access[-_]?token|_?auth(?:orization)?(?:[-_.]?token)?|aws[-_]?secret[-_]?access[-_]?key|private[-_]?key)(?:"|')?\s*:\s*)("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*')/gi;
18765
- function isRecord15(value) {
20022
+ function isRecord16(value) {
18766
20023
  return value !== null && typeof value === "object" && !Array.isArray(value);
18767
20024
  }
18768
20025
  function hasOwn(value, key) {
@@ -18783,7 +20040,7 @@ function assertExactKeys(value, allowed, label) {
18783
20040
  }
18784
20041
  }
18785
20042
  function requireRecord(value, label) {
18786
- if (!isRecord15(value)) throw new Error(`Invalid ${label}`);
20043
+ if (!isRecord16(value)) throw new Error(`Invalid ${label}`);
18787
20044
  return value;
18788
20045
  }
18789
20046
  function requireText(value, label) {
@@ -19141,7 +20398,7 @@ function normalizeTimeout(value) {
19141
20398
  return timeoutMs;
19142
20399
  }
19143
20400
  var COMMIT_SHA_PATTERN2 = /^[0-9a-f]{40}$/i;
19144
- function isRecord16(value) {
20401
+ function isRecord17(value) {
19145
20402
  return value !== null && typeof value === "object" && !Array.isArray(value);
19146
20403
  }
19147
20404
  function hasText3(value) {
@@ -19161,14 +20418,14 @@ function isQualityProfile3(value) {
19161
20418
  return typeof value === "string" && QUALITY_PROFILES.includes(value);
19162
20419
  }
19163
20420
  function assertValidEnvironment(value, label) {
19164
- if (!isRecord16(value)) throw new Error(`Invalid ${label}`);
20421
+ if (!isRecord17(value)) throw new Error(`Invalid ${label}`);
19165
20422
  for (const [key, entry] of Object.entries(value)) {
19166
20423
  if (typeof entry !== "string") throw new Error(`Invalid ${label}.${key}`);
19167
20424
  }
19168
20425
  }
19169
20426
  function assertQualityPlanInput(value) {
19170
- if (!isRecord16(value)) throw new Error("Invalid quality plan");
19171
- if (!isRecord16(value.identity)) throw new Error("Invalid quality plan identity");
20427
+ if (!isRecord17(value)) throw new Error("Invalid quality plan");
20428
+ if (!isRecord17(value.identity)) throw new Error("Invalid quality plan identity");
19172
20429
  if (typeof value.identity.baseSha !== "string" || !COMMIT_SHA_PATTERN2.test(value.identity.baseSha)) {
19173
20430
  throw new Error("Invalid quality plan identity.baseSha");
19174
20431
  }
@@ -19180,7 +20437,7 @@ function assertQualityPlanInput(value) {
19180
20437
  const controlIds = /* @__PURE__ */ new Set();
19181
20438
  for (let index = 0; index < value.controls.length; index += 1) {
19182
20439
  const control = value.controls[index];
19183
- if (!isRecord16(control) || !hasText3(control.id) || control.requirement !== "required" && control.requirement !== "optional") {
20440
+ if (!isRecord17(control) || !hasText3(control.id) || control.requirement !== "required" && control.requirement !== "optional") {
19184
20441
  throw new Error(`Invalid quality plan controls[${index}]`);
19185
20442
  }
19186
20443
  if (controlIds.has(control.id)) {
@@ -19196,7 +20453,7 @@ function assertQualityPlanInput(value) {
19196
20453
  const commandControlIds = /* @__PURE__ */ new Set();
19197
20454
  for (let index = 0; index < value.commands.length; index += 1) {
19198
20455
  const command = value.commands[index];
19199
- if (!isRecord16(command)) throw new Error(`Invalid quality plan commands[${index}]`);
20456
+ if (!isRecord17(command)) throw new Error(`Invalid quality plan commands[${index}]`);
19200
20457
  const timeoutMs = command.timeoutMs;
19201
20458
  if (!hasText3(command.controlId) || !hasText3(command.commandId) || !hasText3(command.executable) || !isDenseStringArray2(command.argv) || !isNonNegativeSafeInteger(timeoutMs)) {
19202
20459
  throw new Error(`Invalid quality plan commands[${index}]`);
@@ -19253,7 +20510,7 @@ function killProcessTree(child) {
19253
20510
  if (pid === void 0) return;
19254
20511
  if (process.platform === "win32") {
19255
20512
  const systemRoot = process.env.SystemRoot ?? process.env.WINDIR ?? "C:\\Windows";
19256
- const taskkill = path52.join(systemRoot, "System32", "taskkill.exe");
20513
+ const taskkill = path56.join(systemRoot, "System32", "taskkill.exe");
19257
20514
  const result = spawnSync6(taskkill, ["/pid", String(pid), "/t", "/f"], {
19258
20515
  shell: false,
19259
20516
  stdio: "ignore",
@@ -19499,9 +20756,9 @@ async function runQualityPlan(input) {
19499
20756
  }
19500
20757
 
19501
20758
  // src/lib/engram-install.ts
19502
- import fs44 from "fs";
19503
- import os7 from "os";
19504
- import path53 from "path";
20759
+ import fs48 from "fs";
20760
+ import os8 from "os";
20761
+ import path57 from "path";
19505
20762
  import { execFileSync as execFileSync8 } from "child_process";
19506
20763
  import { createHash as createHash11 } from "crypto";
19507
20764
  var MAX_ENGRAM_ARCHIVE_BYTES = 64 * 1024 * 1024;
@@ -19509,17 +20766,17 @@ var STABLE_TAG = /^v\d+\.\d+\.\d+$/;
19509
20766
  var DIGEST_RE = /^sha256:[0-9a-f]{64}$/;
19510
20767
  async function installMissingEngram(options = {}) {
19511
20768
  const platform = options.platform ?? process.platform;
19512
- const homeDir = options.homeDir ?? os7.homedir();
19513
- if (!path53.isAbsolute(homeDir)) return { ok: false, reason: "El directorio personal debe ser absoluto." };
20769
+ const homeDir = options.homeDir ?? os8.homedir();
20770
+ if (!path57.isAbsolute(homeDir)) return { ok: false, reason: "El directorio personal debe ser absoluto." };
19514
20771
  const binaryName = platform === "win32" ? "engram.exe" : "engram";
19515
- const bin = path53.join(homeDir, ".local", "bin", binaryName);
20772
+ const bin = path57.join(homeDir, ".local", "bin", binaryName);
19516
20773
  let staging;
19517
20774
  let outcome;
19518
20775
  try {
19519
- const existing = fs44.lstatSync(bin, { throwIfNoEntry: false });
20776
+ const existing = fs48.lstatSync(bin, { throwIfNoEntry: false });
19520
20777
  if (existing) {
19521
- if (!fs44.statSync(bin).isFile()) throw new Error(`El destino Engram no es un archivo: ${bin}`);
19522
- fs44.accessSync(bin, platform === "win32" ? fs44.constants.F_OK : fs44.constants.X_OK);
20778
+ if (!fs48.statSync(bin).isFile()) throw new Error(`El destino Engram no es un archivo: ${bin}`);
20779
+ fs48.accessSync(bin, platform === "win32" ? fs48.constants.F_OK : fs48.constants.X_OK);
19523
20780
  outcome = { ok: true, bin };
19524
20781
  } else {
19525
20782
  const fetchFn = options.fetch ?? globalThis.fetch;
@@ -19632,19 +20889,19 @@ async function installMissingEngram(options = {}) {
19632
20889
  if (size !== approvedSize || createHash11("sha256").update(archive).digest("hex") !== approvedSha) {
19633
20890
  throw new Error("Engram: tama\xF1o o hash SHA-256 no coincide con el artefacto aprobado.");
19634
20891
  }
19635
- fs44.mkdirSync(path53.dirname(bin), { recursive: true });
19636
- staging = fs44.mkdtempSync(path53.join(path53.dirname(bin), ".engram-install-"));
19637
- const archivePath = path53.join(staging, expectedName);
19638
- fs44.writeFileSync(archivePath, archive, { flag: "wx", mode: 384 });
20892
+ fs48.mkdirSync(path57.dirname(bin), { recursive: true });
20893
+ staging = fs48.mkdtempSync(path57.join(path57.dirname(bin), ".engram-install-"));
20894
+ const archivePath = path57.join(staging, expectedName);
20895
+ fs48.writeFileSync(archivePath, archive, { flag: "wx", mode: 384 });
19639
20896
  const binary = execFileSync8(resolveTarBin(), ["-xOf", archivePath, binaryName], {
19640
20897
  maxBuffer: 64 * 1024 * 1024,
19641
20898
  timeout: 3e4,
19642
20899
  stdio: ["ignore", "pipe", "pipe"]
19643
20900
  });
19644
20901
  if (binary.length === 0) throw new Error("El artefacto Engram no contiene un binario v\xE1lido.");
19645
- const stagedBin = path53.join(staging, binaryName);
19646
- fs44.writeFileSync(stagedBin, binary, { flag: "wx", mode: 493 });
19647
- fs44.linkSync(stagedBin, bin);
20902
+ const stagedBin = path57.join(staging, binaryName);
20903
+ fs48.writeFileSync(stagedBin, binary, { flag: "wx", mode: 493 });
20904
+ fs48.linkSync(stagedBin, bin);
19648
20905
  outcome = { ok: true, bin };
19649
20906
  }
19650
20907
  } catch (error) {
@@ -19652,7 +20909,7 @@ async function installMissingEngram(options = {}) {
19652
20909
  }
19653
20910
  if (staging !== void 0) {
19654
20911
  try {
19655
- fs44.rmSync(staging, { recursive: true, force: true });
20912
+ fs48.rmSync(staging, { recursive: true, force: true });
19656
20913
  } catch (cleanupError) {
19657
20914
  const cleanupMsg = cleanupError instanceof Error ? cleanupError.message : String(cleanupError);
19658
20915
  if (outcome !== void 0 && outcome.ok) {
@@ -20087,6 +21344,7 @@ async function runSelectedPi(options) {
20087
21344
  ...devtoolsMcpEnabled === void 0 ? {} : { devtoolsMcpEnabled },
20088
21345
  ...playwrightCliEnabled === void 0 ? {} : { playwrightCliEnabled },
20089
21346
  ...playwrightCapability === void 0 ? {} : { playwrightCapability },
21347
+ ...options.playwrightRefresh === void 0 ? {} : { playwrightRefresh: options.playwrightRefresh },
20090
21348
  ...(() => {
20091
21349
  if (options.upgradePermissions !== true) return {};
20092
21350
  const supports = PI_RUNTIME_CANDIDATE.contract.capabilities.includes("permissions-upgrade-v1");
@@ -20219,7 +21477,7 @@ Flags disponibles: jorgex-stack --help`
20219
21477
  return;
20220
21478
  }
20221
21479
  try {
20222
- const plan = JSON.parse(fs45.readFileSync(flags.positional[0], "utf8"));
21480
+ const plan = JSON.parse(fs49.readFileSync(flags.positional[0], "utf8"));
20223
21481
  const result = await runQualityPlan(plan);
20224
21482
  const serialized = serializeQualityReceipt(result.receipt);
20225
21483
  if (flags.receipt === void 0) process.stdout.write(`${serialized}
@@ -20338,6 +21596,7 @@ Flags disponibles: jorgex-stack --help`
20338
21596
  } else {
20339
21597
  const piExitCode = await runSelectedPi({
20340
21598
  operation: command,
21599
+ ...command === "install" && playwrightToolConsent.interactive && !flags.yes && flags.targetDir === void 0 && !playwrightToolConsent.confirmed ? { playwrightRefresh: false } : {},
20341
21600
  targetDir: flags.targetDir,
20342
21601
  yes: flags.yes,
20343
21602
  resolvedEngramBin: flags.targetDir === void 0 ? engramBin : void 0,
@@ -20345,7 +21604,7 @@ Flags disponibles: jorgex-stack --help`
20345
21604
  writingStyle,
20346
21605
  modePreference: mode,
20347
21606
  playwrightCliEnabled: flags.targetDir === void 0 && exitCode === 0 && playwrightToolPlan.actions.length > 0 ? playwrightToolConsent.runtimeSelection?.pi : void 0,
20348
- playwrightCapability: shouldInspectPlaywrightCapability(flags.targetDir, flags.dryRun) ? inspectManagedPlaywrightCapability() : void 0,
21607
+ playwrightCapability: playwrightCapability ?? (shouldInspectPlaywrightCapability(flags.targetDir, flags.dryRun) ? inspectManagedPlaywrightCapability() : void 0),
20349
21608
  ...flags.upgradePermissions ? { upgradePermissions: true } : {}
20350
21609
  });
20351
21610
  exitCode = Math.max(exitCode, piExitCode);