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/README.md +3 -1
- package/dist/browser-playwright.js +3 -2
- package/dist/cli.js +1598 -339
- package/package.json +1 -1
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
|
|
503
|
+
const resolved3 = fs4.realpathSync(probe);
|
|
504
504
|
const root = fs4.realpathSync(rootDir);
|
|
505
|
-
if (
|
|
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
|
|
1280
|
-
const stat = fs7.lstatSync(
|
|
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: ${
|
|
1282
|
+
throw new Error(`Pi MCP config directory is not a real directory: ${resolved3}`);
|
|
1283
1283
|
}
|
|
1284
|
-
return
|
|
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
|
|
1637
|
-
if (path12.basename(
|
|
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
|
|
1652
|
+
const resolved3 = path12.resolve(linkPath);
|
|
1643
1653
|
for (const root of trustedRoots) {
|
|
1644
|
-
if (isContainedIn(
|
|
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
|
|
1878
|
+
const resolved3 = path12.resolve(target);
|
|
1869
1879
|
try {
|
|
1870
|
-
if (fs8.lstatSync(
|
|
1871
|
-
return `${
|
|
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 `${
|
|
1885
|
+
return `${resolved3}: ilegible (${errnoCode2(error)}, no se puede descartar alias)`;
|
|
1876
1886
|
}
|
|
1877
1887
|
}
|
|
1878
|
-
let dir = path12.dirname(
|
|
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(
|
|
1895
|
-
const bad = findSymlinkInTree(
|
|
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
|
|
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 =
|
|
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
|
-
...
|
|
2093
|
-
recovery:
|
|
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
|
|
2182
|
+
const resolved3 = path12.resolve(configDir);
|
|
2173
2183
|
const homeResolved = path12.resolve(homeDir);
|
|
2174
|
-
if (
|
|
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
|
|
3307
|
-
if (
|
|
3308
|
-
return { ok: false, detail: `installPath:foreign (${
|
|
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 =
|
|
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(
|
|
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:
|
|
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
|
|
3447
|
+
const resolved3 = resolveStrictInstallPath(args.configDir);
|
|
3438
3448
|
const enabled = checkStrictEnabled(args.configDir);
|
|
3439
|
-
const hasPlugin =
|
|
3440
|
-
const pluginDetail = !
|
|
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 =
|
|
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
|
|
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
|
|
5370
|
+
let resolved3;
|
|
5360
5371
|
try {
|
|
5361
|
-
|
|
5372
|
+
resolved3 = fs19.realpathSync(candidate);
|
|
5362
5373
|
} catch {
|
|
5363
5374
|
fail2(`cannot resolve ${label}: ${candidate}`);
|
|
5364
5375
|
}
|
|
5365
|
-
if (
|
|
5376
|
+
if (resolved3 !== realRoot && !resolved3.startsWith(realRoot + path25.sep)) {
|
|
5366
5377
|
fail2(`${label} escapes the stage`);
|
|
5367
5378
|
}
|
|
5368
|
-
return
|
|
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
|
|
5535
|
-
return
|
|
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
|
|
6257
|
-
if (!isContained(root,
|
|
6258
|
-
return
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
7362
|
-
if (
|
|
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
|
|
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
|
-
|
|
7464
|
-
|
|
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
|
|
7696
|
-
return process.platform === "win32" ?
|
|
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 (
|
|
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
|
|
8822
|
-
const st = lstatOrNull(
|
|
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
|
|
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
|
|
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 && !
|
|
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 && !
|
|
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
|
|
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,
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
11961
|
+
return fail14(`la ruta de validaci\xF3n "${validateSubdir}" escapa del destino`);
|
|
11911
11962
|
}
|
|
11912
11963
|
if (fs30.existsSync(validateRoot) && !validateExtractedTree(validateRoot)) {
|
|
11913
|
-
return
|
|
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
|
|
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
|
|
13153
|
-
import
|
|
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
|
|
13631
|
-
const st = lstatOrNull3(
|
|
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
|
|
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,
|
|
14555
|
-
const binDir = path45.dirname(
|
|
14556
|
-
if (path45.basename(
|
|
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(
|
|
14597
|
-
const binDir = path45.dirname(
|
|
14598
|
-
if (path45.basename(
|
|
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
|
|
14663
|
+
let resolved3;
|
|
14609
14664
|
try {
|
|
14610
|
-
|
|
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(
|
|
14615
|
-
let current = path45.dirname(
|
|
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(
|
|
14647
|
-
const st = lstatOrNull5(
|
|
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: ${
|
|
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-
|
|
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
|
-
|
|
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
|
|
17606
|
+
function isRecord15(value) {
|
|
16492
17607
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
16493
17608
|
}
|
|
16494
|
-
function
|
|
17609
|
+
function lstatOrNull9(p7) {
|
|
16495
17610
|
try {
|
|
16496
|
-
return
|
|
17611
|
+
return fs44.lstatSync(p7);
|
|
16497
17612
|
} catch {
|
|
16498
17613
|
return null;
|
|
16499
17614
|
}
|
|
16500
17615
|
}
|
|
16501
|
-
function
|
|
16502
|
-
const rel =
|
|
16503
|
-
return rel !== "" && rel !== ".." && !rel.startsWith(`..${
|
|
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 (!
|
|
17633
|
+
if (!isRecord15(input)) return false;
|
|
16519
17634
|
const { receipt, homeDir, downloadsDir } = input;
|
|
16520
|
-
if (typeof homeDir !== "string" || homeDir === "" || !
|
|
16521
|
-
if (typeof downloadsDir !== "string" || 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 =
|
|
16525
|
-
const downloadsResolved =
|
|
16526
|
-
const homeStat =
|
|
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 =
|
|
17643
|
+
const downloadsStat = lstatOrNull9(downloadsResolved);
|
|
16529
17644
|
if (downloadsStat === null || !downloadsStat.isDirectory() || downloadsStat.isSymbolicLink()) {
|
|
16530
17645
|
return false;
|
|
16531
17646
|
}
|
|
16532
|
-
if (!
|
|
16533
|
-
const rel =
|
|
16534
|
-
const parts = rel.split(
|
|
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 =
|
|
16539
|
-
const st2 =
|
|
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 (!
|
|
17657
|
+
if (!isRecord15(receipt)) return false;
|
|
16543
17658
|
if (receipt["schemaVersion"] !== 1) return false;
|
|
16544
|
-
if (!
|
|
17659
|
+
if (!isRecord15(receipt["managedPackage"])) return false;
|
|
16545
17660
|
const candidate = receipt["candidate"];
|
|
16546
|
-
if (!
|
|
17661
|
+
if (!isRecord15(candidate)) return false;
|
|
16547
17662
|
const pkg = candidate["package"];
|
|
16548
|
-
if (!
|
|
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 (!
|
|
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 =
|
|
16565
|
-
if (
|
|
16566
|
-
const st =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
16761
|
-
const entries = process.platform === "win32" ? [piExecutable === void 0 ? null :
|
|
16762
|
-
return [...new Set(entries.filter((entry) => entry !== null))].join(
|
|
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 =
|
|
16766
|
-
const codingAgentDir =
|
|
16767
|
-
const home =
|
|
16768
|
-
const temporary =
|
|
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:
|
|
16772
|
-
packageRunner:
|
|
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:
|
|
16777
|
-
LOCALAPPDATA:
|
|
16778
|
-
XDG_CONFIG_HOME:
|
|
16779
|
-
XDG_DATA_HOME:
|
|
16780
|
-
XDG_CACHE_HOME:
|
|
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:
|
|
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:
|
|
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 ??
|
|
17910
|
+
const codingAgentDir = process.env.PI_CODING_AGENT_DIR ?? path53.join(home, ".pi", "agent");
|
|
16796
17911
|
return {
|
|
16797
17912
|
codingAgentDir,
|
|
16798
|
-
receiptPath:
|
|
16799
|
-
packageRunner:
|
|
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 ??
|
|
16803
|
-
XDG_CACHE_HOME: process.env.XDG_CACHE_HOME ??
|
|
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:
|
|
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(
|
|
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 ??
|
|
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 ?
|
|
16920
|
-
return
|
|
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 =
|
|
16925
|
-
return
|
|
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
|
|
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(
|
|
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 =
|
|
17089
|
-
const npmDir =
|
|
17090
|
-
const managedRoot =
|
|
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 =
|
|
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" || !
|
|
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" || !
|
|
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 =
|
|
17157
|
-
if (linkResolved !==
|
|
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" || !
|
|
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 =
|
|
17165
|
-
const expectedRelease =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
17181
|
-
if (target === "" ||
|
|
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 =
|
|
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
|
|
17204
|
-
const rel =
|
|
17205
|
-
return rel !== "" && rel !== ".." && !rel.startsWith(`..${
|
|
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 === "" || !
|
|
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 === "" || !
|
|
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 === "" || !
|
|
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 ??
|
|
17254
|
-
const updateReceiptPath =
|
|
17255
|
-
const updateDownloadsDir =
|
|
17256
|
-
if (!
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 === "" || !
|
|
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 =
|
|
17375
|
-
if (!
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
17557
|
-
if (
|
|
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 :
|
|
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() :
|
|
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:
|
|
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:
|
|
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 || !
|
|
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 =
|
|
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() :
|
|
17776
|
-
const updateDownloadsDir = input.targetDir === void 0 ?
|
|
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 =
|
|
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:
|
|
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:
|
|
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 =
|
|
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(
|
|
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() :
|
|
18040
|
-
downloadsDir: input.targetDir === void 0 ?
|
|
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: () => !
|
|
18043
|
-
deleteReceipt: () =>
|
|
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(
|
|
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() :
|
|
18107
|
-
downloadsDir: input.targetDir === void 0 ?
|
|
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: () => !
|
|
18110
|
-
deleteReceipt: () =>
|
|
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
|
-
[
|
|
19232
|
+
[path53.join(paths.codingAgentDir, "settings.json")],
|
|
18128
19233
|
"pi-package-uninstall",
|
|
18129
|
-
input.targetDir === void 0 ? void 0 :
|
|
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() :
|
|
18135
|
-
downloadsDir: input.targetDir === void 0 ?
|
|
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: () =>
|
|
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 === "" || !
|
|
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 =
|
|
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 =
|
|
18178
|
-
const legacyEntry =
|
|
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() :
|
|
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 =
|
|
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 =
|
|
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 (
|
|
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 =
|
|
19320
|
+
const settingsPath = path53.join(paths.codingAgentDir, "settings.json");
|
|
18216
19321
|
return deactivateVerifiedPiRelease({
|
|
18217
|
-
homeDir: input.targetDir === void 0 ? os6.homedir() :
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 (
|
|
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: () => !
|
|
18251
|
-
deleteReceipt: () =>
|
|
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
|
|
18265
|
-
import
|
|
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
|
|
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" || !
|
|
19381
|
+
if (typeof packageRoot !== "string" || !path54.isAbsolute(packageRoot)) fail13("package root is not absolute");
|
|
18277
19382
|
let realRoot;
|
|
18278
19383
|
try {
|
|
18279
|
-
realRoot =
|
|
18280
|
-
if (!
|
|
19384
|
+
realRoot = fs46.realpathSync(packageRoot);
|
|
19385
|
+
if (!fs46.statSync(realRoot).isDirectory()) fail13("package root is not a directory");
|
|
18281
19386
|
} catch {
|
|
18282
|
-
|
|
19387
|
+
fail13("installed Pi package root is unavailable");
|
|
18283
19388
|
}
|
|
18284
|
-
const contractDir =
|
|
18285
|
-
const 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 =
|
|
18289
|
-
const stat =
|
|
18290
|
-
if (!dirStat.isDirectory() || dirStat.isSymbolicLink() || !stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_CONTRACT_BYTES)
|
|
18291
|
-
if (
|
|
18292
|
-
raw =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
19409
|
+
fail13("contract is invalid JSON");
|
|
18305
19410
|
}
|
|
18306
|
-
if (contract === null || typeof contract !== "object" || Array.isArray(contract))
|
|
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
|
-
|
|
19414
|
+
fail13("contract schema is invalid");
|
|
18310
19415
|
}
|
|
18311
19416
|
if (required.playwright !== void 0 && !record.playwright.includes(required.playwright)) {
|
|
18312
|
-
|
|
19417
|
+
fail13(`Playwright handoff schema ${required.playwright} is unsupported`);
|
|
18313
19418
|
}
|
|
18314
19419
|
if (required.devtools !== void 0 && !record.devtools.includes(required.devtools)) {
|
|
18315
|
-
|
|
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
|
|
18322
|
-
import
|
|
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
|
|
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() :
|
|
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 (
|
|
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 =
|
|
18529
|
-
const 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
|
-
|
|
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 :
|
|
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
|
-
|
|
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"
|
|
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 =
|
|
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 &&
|
|
18644
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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 (!
|
|
19171
|
-
if (!
|
|
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 (!
|
|
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 (!
|
|
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 =
|
|
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
|
|
19503
|
-
import
|
|
19504
|
-
import
|
|
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 ??
|
|
19513
|
-
if (!
|
|
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 =
|
|
20772
|
+
const bin = path57.join(homeDir, ".local", "bin", binaryName);
|
|
19516
20773
|
let staging;
|
|
19517
20774
|
let outcome;
|
|
19518
20775
|
try {
|
|
19519
|
-
const existing =
|
|
20776
|
+
const existing = fs48.lstatSync(bin, { throwIfNoEntry: false });
|
|
19520
20777
|
if (existing) {
|
|
19521
|
-
if (!
|
|
19522
|
-
|
|
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
|
-
|
|
19636
|
-
staging =
|
|
19637
|
-
const archivePath =
|
|
19638
|
-
|
|
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 =
|
|
19646
|
-
|
|
19647
|
-
|
|
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
|
-
|
|
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(
|
|
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);
|