jorgex-stack 1.9.58 → 1.9.60
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 +9 -9
- package/dist/chunk-2CBCBOP7.js +245 -0
- package/dist/{chunk-NNPFC6QV.js → chunk-4USZMJ3I.js} +6 -15
- package/dist/chunk-66WVKOU4.js +693 -0
- package/dist/chunk-B76ZJXXD.js +989 -0
- package/dist/{chunk-M6QX2F3A.js → chunk-BSHTDTJL.js} +5 -6
- package/dist/{chunk-GOLJ5PA3.js → chunk-KKN4OYHD.js} +5 -12
- package/dist/{claude-code-WY4JJAXP.js → claude-code-BBHLN5TD.js} +3 -2
- package/dist/cli.js +409 -310
- package/dist/{codex-WSWHXYJ3.js → codex-ALGP2YBC.js} +3 -2
- package/dist/official-engram-setup-4WIXM4EF.js +34 -0
- package/dist/{opencode-NUDGK6J7.js → opencode-VG2IZRRF.js} +3 -2
- package/dist/pi-CKFSPGJU.js +13 -0
- package/package.json +1 -1
- package/dist/chunk-KB5IQEKL.js +0 -1400
- package/stack/system-prompt/engram-protocol.md +0 -55
package/dist/cli.js
CHANGED
|
@@ -12,32 +12,27 @@ import {
|
|
|
12
12
|
inspectOpencodePluginFile,
|
|
13
13
|
opencodeAdapter,
|
|
14
14
|
shouldRetireLegacyEngram
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-BSHTDTJL.js";
|
|
16
16
|
import {
|
|
17
17
|
claudeCodeAdapter
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-4USZMJ3I.js";
|
|
19
19
|
import {
|
|
20
20
|
codexAdapter
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-KKN4OYHD.js";
|
|
22
22
|
import {
|
|
23
23
|
DEFAULT_MODEL_MAP,
|
|
24
24
|
DEVTOOLS_MCP_SERVER,
|
|
25
|
+
LEGACY_SYSTEM_PROMPT_SECTIONS,
|
|
25
26
|
SYSTEM_PROMPT_SECTIONS,
|
|
26
27
|
assertSystemPromptFile,
|
|
27
28
|
assertSystemPromptMarkers,
|
|
28
|
-
copyFile,
|
|
29
|
-
createBackup,
|
|
30
29
|
detectClaudeCode,
|
|
31
30
|
detectCodex,
|
|
32
31
|
detectEngram,
|
|
33
32
|
detectOpenCode,
|
|
34
33
|
engramVersion,
|
|
35
|
-
ensureDir,
|
|
36
34
|
ensureModelMapFile,
|
|
37
35
|
hasHealthyManagedMarkdownMarkers,
|
|
38
|
-
isContainedIn,
|
|
39
|
-
listBackups,
|
|
40
|
-
listFilesRecursive,
|
|
41
36
|
loadCanonicalAgents,
|
|
42
37
|
loadCanonicalHooks,
|
|
43
38
|
loadCanonicalMcp,
|
|
@@ -46,26 +41,37 @@ import {
|
|
|
46
41
|
lookPath,
|
|
47
42
|
modelMapFile,
|
|
48
43
|
planDetectedBinCommand,
|
|
49
|
-
pruneEmptyDirs,
|
|
50
44
|
readSystemPromptFile,
|
|
51
|
-
readTextIfExists,
|
|
52
45
|
removeMarkdownSection,
|
|
53
46
|
removeSystemPromptSections,
|
|
54
47
|
resolveAgentModel,
|
|
55
|
-
restoreBackup,
|
|
56
48
|
runDetectedBin,
|
|
49
|
+
stripLeadingHtmlComments,
|
|
50
|
+
upsertMarkdownSection
|
|
51
|
+
} from "./chunk-66WVKOU4.js";
|
|
52
|
+
import {
|
|
53
|
+
piAdapter,
|
|
54
|
+
piSystemPromptFile
|
|
55
|
+
} from "./chunk-2CBCBOP7.js";
|
|
56
|
+
import {
|
|
57
|
+
copyFile,
|
|
58
|
+
createBackup,
|
|
59
|
+
ensureDir,
|
|
60
|
+
isContainedIn,
|
|
61
|
+
listBackups,
|
|
62
|
+
listFilesRecursive,
|
|
63
|
+
pruneEmptyDirs,
|
|
64
|
+
readTextIfExists,
|
|
65
|
+
restoreBackup,
|
|
57
66
|
runOfficialSetupIfNeeded,
|
|
58
67
|
sameFileContent,
|
|
59
68
|
shouldRunOfficialSetup,
|
|
60
|
-
stripLeadingHtmlComments,
|
|
61
|
-
upsertMarkdownSection,
|
|
62
69
|
validateOfficialSetupDestination,
|
|
63
70
|
writeText
|
|
64
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-B76ZJXXD.js";
|
|
65
72
|
import {
|
|
66
73
|
HOME,
|
|
67
74
|
dataDir,
|
|
68
|
-
samePath,
|
|
69
75
|
stackRoot
|
|
70
76
|
} from "./chunk-NQZVJFV2.js";
|
|
71
77
|
|
|
@@ -355,7 +361,6 @@ function planSystemPrompt(adapter, ctx) {
|
|
|
355
361
|
);
|
|
356
362
|
const modules = {
|
|
357
363
|
"system-prompt": composeProgrammaticSystemPrompt(ctx.stackDir, readModule("AGENTS.md"), ctx.mode),
|
|
358
|
-
"engram-protocol": adapter.injectEngramProtocol(ctx) ? readModule("engram-protocol.md") : void 0,
|
|
359
364
|
context7: readModule("context7.md"),
|
|
360
365
|
playwright: ctx.playwrightCliEnabled ? readModule("browser-playwright.md") : void 0,
|
|
361
366
|
"chrome-devtools": ctx.enabledMcpServers?.has(DEVTOOLS_MCP_SERVER) ? readModule("browser-chrome-devtools.md") : void 0,
|
|
@@ -367,6 +372,9 @@ function planSystemPrompt(adapter, ctx) {
|
|
|
367
372
|
const body = sections[section];
|
|
368
373
|
content = body ? upsertMarkdownSection(content, section, body) : removeMarkdownSection(content, section);
|
|
369
374
|
}
|
|
375
|
+
for (const section of LEGACY_SYSTEM_PROMPT_SECTIONS) {
|
|
376
|
+
content = removeMarkdownSection(content, section);
|
|
377
|
+
}
|
|
370
378
|
return [{ kind: "write", target, content }];
|
|
371
379
|
}
|
|
372
380
|
|
|
@@ -457,12 +465,6 @@ function planPlugins(adapter, ctx) {
|
|
|
457
465
|
if (content.includes('"{{ENGRAM_BIN}}"')) {
|
|
458
466
|
content = content.replace(/"\{\{ENGRAM_BIN\}\}"/g, JSON.stringify(ctx.engramBin ?? "engram"));
|
|
459
467
|
}
|
|
460
|
-
if (content.includes('"{{ENGRAM_PROTOCOL}}"')) {
|
|
461
|
-
const protocol = stripLeadingHtmlComments(
|
|
462
|
-
fs7.readFileSync(path9.join(ctx.stackDir, "system-prompt", "engram-protocol.md"), "utf8").replace(/\r\n/g, "\n")
|
|
463
|
-
);
|
|
464
|
-
content = content.replace(/"\{\{ENGRAM_PROTOCOL\}\}"/g, JSON.stringify(protocol));
|
|
465
|
-
}
|
|
466
468
|
content = content.replace(/(from\s+["'])(\.{1,2}\/[^"']+?)\.js(["'])/g, "$1$2.ts$3");
|
|
467
469
|
if (content === raw) return { kind: "copy", source: sourceFile, target };
|
|
468
470
|
return { kind: "write", target, content };
|
|
@@ -1491,52 +1493,16 @@ async function runInstall(opts) {
|
|
|
1491
1493
|
|
|
1492
1494
|
// src/uninstall.ts
|
|
1493
1495
|
import fs12 from "fs";
|
|
1494
|
-
import
|
|
1496
|
+
import path15 from "path";
|
|
1495
1497
|
import * as p2 from "@clack/prompts";
|
|
1496
1498
|
|
|
1497
1499
|
// src/lib/pi-projection-lifecycle.ts
|
|
1498
1500
|
import fs11 from "fs";
|
|
1499
|
-
import
|
|
1501
|
+
import path14 from "path";
|
|
1500
1502
|
import { createHash } from "crypto";
|
|
1501
1503
|
|
|
1502
|
-
// src/adapters/pi.ts
|
|
1503
|
-
import path13 from "path";
|
|
1504
|
-
function piSystemPromptFile(targetDir) {
|
|
1505
|
-
const configDir = targetDir === void 0 ? process.env.PI_CODING_AGENT_DIR ?? path13.join(HOME, ".pi", "agent") : path13.join(path13.resolve(targetDir), "pi-agent");
|
|
1506
|
-
return path13.join(configDir, "AGENTS.md");
|
|
1507
|
-
}
|
|
1508
|
-
var piAdapter = {
|
|
1509
|
-
id: "pi",
|
|
1510
|
-
paths(configDir) {
|
|
1511
|
-
const piConfigDir = path13.dirname(piSystemPromptFile());
|
|
1512
|
-
const agentsHome = samePath(configDir, piConfigDir) ? HOME : path13.join(path13.dirname(configDir), "home");
|
|
1513
|
-
return {
|
|
1514
|
-
systemPromptFile: path13.join(configDir, "AGENTS.md"),
|
|
1515
|
-
agentsDir: path13.join(configDir, "agents"),
|
|
1516
|
-
skillsDir: path13.join(agentsHome, ".agents", "skills"),
|
|
1517
|
-
commandsDir: path13.join(configDir, "prompts"),
|
|
1518
|
-
pluginsDir: null,
|
|
1519
|
-
scriptsDir: path13.join(configDir, "scripts"),
|
|
1520
|
-
outputStylesDir: null,
|
|
1521
|
-
profilesDir: null
|
|
1522
|
-
};
|
|
1523
|
-
},
|
|
1524
|
-
renderCommand(file, content) {
|
|
1525
|
-
return { file, content: content.replace(/\{\{input\}\}/g, "$ARGUMENTS") };
|
|
1526
|
-
},
|
|
1527
|
-
injectEngramProtocol() {
|
|
1528
|
-
return true;
|
|
1529
|
-
},
|
|
1530
|
-
// La guía Context7 se habilita al adoptar su registro HTTP en Pi.
|
|
1531
|
-
adaptSystemPromptSections(sections) {
|
|
1532
|
-
const modular = { ...sections };
|
|
1533
|
-
delete modular.context7;
|
|
1534
|
-
return modular;
|
|
1535
|
-
}
|
|
1536
|
-
};
|
|
1537
|
-
|
|
1538
1504
|
// src/lib/pi-package-lifecycle.ts
|
|
1539
|
-
import
|
|
1505
|
+
import path13 from "path";
|
|
1540
1506
|
var REQUIRED_CAPABILITIES = /* @__PURE__ */ new Set([
|
|
1541
1507
|
"foundation-contract-v1",
|
|
1542
1508
|
"runner-json-v1",
|
|
@@ -1688,7 +1654,7 @@ function parseReceiptShape(receiptJson) {
|
|
|
1688
1654
|
return null;
|
|
1689
1655
|
}
|
|
1690
1656
|
if (engram === void 0) return "upgrade-required";
|
|
1691
|
-
if (engram === null || typeof engram !== "object" || Array.isArray(engram) || typeof Reflect.get(engram, "binary") !== "string" || !
|
|
1657
|
+
if (engram === null || typeof engram !== "object" || Array.isArray(engram) || typeof Reflect.get(engram, "binary") !== "string" || !path13.isAbsolute(Reflect.get(engram, "binary"))) {
|
|
1692
1658
|
return null;
|
|
1693
1659
|
}
|
|
1694
1660
|
return parsed;
|
|
@@ -1725,7 +1691,7 @@ function planPiPackageLifecycle(input) {
|
|
|
1725
1691
|
if (input.receiptJson !== null) {
|
|
1726
1692
|
const parsedReceipt = parseReceipt(input.receiptJson, input.candidate, {
|
|
1727
1693
|
kind: input.scope.kind,
|
|
1728
|
-
codingAgentDir:
|
|
1694
|
+
codingAgentDir: path13.resolve(input.scope.codingAgentDir)
|
|
1729
1695
|
}, input.engramBin);
|
|
1730
1696
|
if (parsedReceipt === "upgrade-required") return blocked(input, "receipt-upgrade-required");
|
|
1731
1697
|
if (parsedReceipt === null) return blocked(input, "receipt-corrupt");
|
|
@@ -1760,7 +1726,7 @@ function planPiPackageLifecycle(input) {
|
|
|
1760
1726
|
},
|
|
1761
1727
|
receipt: expectedReceipt(input.candidate, "installing", {
|
|
1762
1728
|
kind: input.scope.kind,
|
|
1763
|
-
codingAgentDir:
|
|
1729
|
+
codingAgentDir: path13.resolve(input.scope.codingAgentDir)
|
|
1764
1730
|
}, input.engramBin),
|
|
1765
1731
|
ownership: ownership(true)
|
|
1766
1732
|
};
|
|
@@ -1775,7 +1741,7 @@ function parseRunnerRecord(stdout, stderr, command, candidate, packageRunner) {
|
|
|
1775
1741
|
const parsed = JSON.parse(body);
|
|
1776
1742
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
1777
1743
|
const record = parsed;
|
|
1778
|
-
if (record.schemaVersion !== candidate.contract.runner.schemaVersion || record.command !== command || record.ok !== true || record.package === null || typeof record.package !== "object" || record.package.name !== candidate.package.name || record.package.version !== candidate.package.version || typeof record.package.root !== "string" || !
|
|
1744
|
+
if (record.schemaVersion !== candidate.contract.runner.schemaVersion || record.command !== command || record.ok !== true || record.package === null || typeof record.package !== "object" || record.package.name !== candidate.package.name || record.package.version !== candidate.package.version || typeof record.package.root !== "string" || !path13.isAbsolute(record.package.root) || path13.resolve(packageRunner) !== path13.resolve(record.package.root, "bin", "jorgex-pi.mjs")) {
|
|
1779
1745
|
return null;
|
|
1780
1746
|
}
|
|
1781
1747
|
return record;
|
|
@@ -1923,10 +1889,10 @@ function validateOwnedOperationState(input) {
|
|
|
1923
1889
|
}))) {
|
|
1924
1890
|
return { kind: "blocked", reason: "receipt-untrusted" };
|
|
1925
1891
|
}
|
|
1926
|
-
if (receipt.scope.kind !== (input.paths.targetDir ? "target-dir" : "real") ||
|
|
1892
|
+
if (receipt.scope.kind !== (input.paths.targetDir ? "target-dir" : "real") || path13.resolve(receipt.scope.codingAgentDir) !== path13.resolve(input.paths.codingAgentDir)) {
|
|
1927
1893
|
return { kind: "blocked", reason: "source-divergent" };
|
|
1928
1894
|
}
|
|
1929
|
-
if (input.engramBin !== null &&
|
|
1895
|
+
if (input.engramBin !== null && path13.resolve(receipt.engram.binary) !== path13.resolve(input.engramBin)) {
|
|
1930
1896
|
return { kind: "blocked", reason: "receipt-corrupt" };
|
|
1931
1897
|
}
|
|
1932
1898
|
const source = receipt.candidate.package.source;
|
|
@@ -2033,7 +1999,7 @@ var RETIRED_PLAYWRIGHT_FILES = [
|
|
|
2033
1999
|
"references/video-recording.md"
|
|
2034
2000
|
];
|
|
2035
2001
|
function retiredPlaywrightFiles(scope) {
|
|
2036
|
-
return RETIRED_PLAYWRIGHT_FILES.map((file) =>
|
|
2002
|
+
return RETIRED_PLAYWRIGHT_FILES.map((file) => path14.resolve(scope.home, ".agents", "skills", "playwright-cli", file));
|
|
2037
2003
|
}
|
|
2038
2004
|
function handoffEnabled(input, kind) {
|
|
2039
2005
|
return (kind === "devtools" ? input.devtoolsMcpEnabled : input.playwrightHandoffEnabled) === true;
|
|
@@ -2042,18 +2008,18 @@ var preparedPiProjectionUninstalls = /* @__PURE__ */ new WeakMap();
|
|
|
2042
2008
|
function projectionScope(scope) {
|
|
2043
2009
|
return {
|
|
2044
2010
|
...scope,
|
|
2045
|
-
settingsFile:
|
|
2011
|
+
settingsFile: path14.join(scope.codingAgentDir, "settings.json")
|
|
2046
2012
|
};
|
|
2047
2013
|
}
|
|
2048
2014
|
function isInside(root, file) {
|
|
2049
|
-
const relative =
|
|
2050
|
-
return relative === "" || relative !== ".." && !relative.startsWith(`..${
|
|
2015
|
+
const relative = path14.relative(path14.resolve(root), path14.resolve(file));
|
|
2016
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path14.sep}`) && !path14.isAbsolute(relative);
|
|
2051
2017
|
}
|
|
2052
2018
|
function isManagedPath(scope, file) {
|
|
2053
2019
|
return isInside(scope.home, file) || isInside(scope.codingAgentDir, file);
|
|
2054
2020
|
}
|
|
2055
2021
|
function isAllowedPath(scope, file) {
|
|
2056
|
-
return isManagedPath(scope, file) ||
|
|
2022
|
+
return isManagedPath(scope, file) || path14.resolve(file) === path14.resolve(scope.receiptFile);
|
|
2057
2023
|
}
|
|
2058
2024
|
function projectedPiAdapter(scope) {
|
|
2059
2025
|
const paths = piAdapter.paths(scope.codingAgentDir);
|
|
@@ -2061,7 +2027,7 @@ function projectedPiAdapter(scope) {
|
|
|
2061
2027
|
...piAdapter,
|
|
2062
2028
|
paths: () => ({
|
|
2063
2029
|
...paths,
|
|
2064
|
-
skillsDir:
|
|
2030
|
+
skillsDir: path14.join(scope.home, ".agents", "skills")
|
|
2065
2031
|
})
|
|
2066
2032
|
};
|
|
2067
2033
|
}
|
|
@@ -2105,7 +2071,7 @@ function projectionPlan(input, scope, includePrompt = true) {
|
|
|
2105
2071
|
return actions;
|
|
2106
2072
|
}
|
|
2107
2073
|
function handoffPath(scope, kind) {
|
|
2108
|
-
return
|
|
2074
|
+
return path14.resolve(scope.codingAgentDir, "jorgex-pi", `${kind}.v1.json`);
|
|
2109
2075
|
}
|
|
2110
2076
|
function contentHash(content) {
|
|
2111
2077
|
return createHash("sha256").update(content).digest("hex");
|
|
@@ -2118,7 +2084,7 @@ function hasActionDrift(action, deps) {
|
|
|
2118
2084
|
}
|
|
2119
2085
|
function applyActions(actions, deps, handoffs = []) {
|
|
2120
2086
|
for (const action of actions) {
|
|
2121
|
-
const handoff = handoffs.find((entry) =>
|
|
2087
|
+
const handoff = handoffs.find((entry) => path14.resolve(action.target) === entry.file);
|
|
2122
2088
|
if (handoff) {
|
|
2123
2089
|
try {
|
|
2124
2090
|
if (deps.readText(handoff.file) !== handoff.previous) return handoffConflict(handoff.kind, handoff.file);
|
|
@@ -2138,9 +2104,9 @@ function assertPlanContained(plan, scope) {
|
|
|
2138
2104
|
}
|
|
2139
2105
|
}
|
|
2140
2106
|
function receiptFor(plan, scope) {
|
|
2141
|
-
const systemPrompt =
|
|
2107
|
+
const systemPrompt = path14.join(scope.codingAgentDir, "AGENTS.md");
|
|
2142
2108
|
const owned = [...new Set(
|
|
2143
|
-
plan.map((action) =>
|
|
2109
|
+
plan.map((action) => path14.resolve(action.target)).filter((target) => target !== systemPrompt)
|
|
2144
2110
|
)];
|
|
2145
2111
|
const receipt = {
|
|
2146
2112
|
schemaVersion: 1,
|
|
@@ -2153,7 +2119,7 @@ function receiptFor(plan, scope) {
|
|
|
2153
2119
|
owned
|
|
2154
2120
|
};
|
|
2155
2121
|
for (const kind of HANDOFF_KINDS) {
|
|
2156
|
-
const action = plan.find((entry) =>
|
|
2122
|
+
const action = plan.find((entry) => path14.resolve(entry.target) === handoffPath(scope, kind));
|
|
2157
2123
|
if (action) receipt[kind] = { sha256: contentHash(canonicalActionContent(action)) };
|
|
2158
2124
|
}
|
|
2159
2125
|
return receipt;
|
|
@@ -2194,7 +2160,7 @@ function parseReceipt2(raw, expected) {
|
|
|
2194
2160
|
if (matches(expectedOwned)) return { schemaVersion: 1, scope: expected.scope, owned: expectedOwned, ...digests };
|
|
2195
2161
|
const retired = retiredPlaywrightFiles(expected.scope);
|
|
2196
2162
|
if (retired.some((file) => expectedOwned.includes(file))) return null;
|
|
2197
|
-
const skillsRoot = `${
|
|
2163
|
+
const skillsRoot = `${path14.resolve(expected.scope.home, ".agents", "skills")}${path14.sep}`;
|
|
2198
2164
|
const insertion = expectedOwned.findIndex((file) => !file.startsWith(skillsRoot) || file > retired[0]);
|
|
2199
2165
|
const legacyOwned = [...expectedOwned];
|
|
2200
2166
|
legacyOwned.splice(insertion === -1 ? legacyOwned.length : insertion, 0, ...retired);
|
|
@@ -2213,8 +2179,8 @@ function realPiProjectionScope() {
|
|
|
2213
2179
|
return {
|
|
2214
2180
|
kind: "real",
|
|
2215
2181
|
home: HOME,
|
|
2216
|
-
codingAgentDir:
|
|
2217
|
-
receiptFile:
|
|
2182
|
+
codingAgentDir: path14.dirname(piSystemPromptFile()),
|
|
2183
|
+
receiptFile: path14.join(dataDir(), "pi-projection-receipt.json")
|
|
2218
2184
|
};
|
|
2219
2185
|
}
|
|
2220
2186
|
function readTextOnlyIfMissing(file) {
|
|
@@ -2252,13 +2218,13 @@ function manifestOwned(manifest) {
|
|
|
2252
2218
|
return new Set([
|
|
2253
2219
|
...manifest.runtimes.codex?.owned ?? [],
|
|
2254
2220
|
...manifest.runtimes.opencode?.owned ?? []
|
|
2255
|
-
].map((file) =>
|
|
2221
|
+
].map((file) => path14.resolve(file)));
|
|
2256
2222
|
}
|
|
2257
2223
|
function withoutManagedPromptSections(content) {
|
|
2258
2224
|
return removeSystemPromptSections(content);
|
|
2259
2225
|
}
|
|
2260
2226
|
function uniquePaths(paths) {
|
|
2261
|
-
return [...new Set(paths.map((file) =>
|
|
2227
|
+
return [...new Set(paths.map((file) => path14.resolve(file)))];
|
|
2262
2228
|
}
|
|
2263
2229
|
function blocked2(reason, paths, remedy) {
|
|
2264
2230
|
return { kind: "blocked", reason, paths: uniquePaths(paths), remedy };
|
|
@@ -2365,7 +2331,7 @@ function preparePiProjectionUninstall(input, deps) {
|
|
|
2365
2331
|
}
|
|
2366
2332
|
const scope = projectionScope(input.scope);
|
|
2367
2333
|
const expectedReceipt2 = expectedProjectionReceipt(input, scope);
|
|
2368
|
-
const prompt =
|
|
2334
|
+
const prompt = path14.join(scope.codingAgentDir, "AGENTS.md");
|
|
2369
2335
|
let existingPrompt;
|
|
2370
2336
|
try {
|
|
2371
2337
|
existingPrompt = deps.readText(prompt);
|
|
@@ -2378,7 +2344,7 @@ function preparePiProjectionUninstall(input, deps) {
|
|
|
2378
2344
|
return blocked2("projection-prompt-markers", [prompt], error instanceof Error ? error.message : String(error));
|
|
2379
2345
|
}
|
|
2380
2346
|
const promptContent = existingPrompt === null ? null : withoutManagedPromptSections(existingPrompt);
|
|
2381
|
-
const promptUpdate = promptContent === null || promptContent === existingPrompt ? null : { file:
|
|
2347
|
+
const promptUpdate = promptContent === null || promptContent === existingPrompt ? null : { file: path14.resolve(prompt), content: promptContent };
|
|
2382
2348
|
const receiptRead = readProjectionReceipt(scope.receiptFile, deps);
|
|
2383
2349
|
if (receiptRead.kind === "unreadable") return receiptUnreadable(scope.receiptFile);
|
|
2384
2350
|
let ownedToRemove = [];
|
|
@@ -2396,7 +2362,7 @@ function preparePiProjectionUninstall(input, deps) {
|
|
|
2396
2362
|
return cleanupFailure([scope.receiptFile]);
|
|
2397
2363
|
}
|
|
2398
2364
|
ownedToRemove = receipt.owned.filter((owned) => !retained.has(owned));
|
|
2399
|
-
receiptFile =
|
|
2365
|
+
receiptFile = path14.resolve(scope.receiptFile);
|
|
2400
2366
|
backupTargets = [...backupTargets, ...receipt.owned, receiptFile];
|
|
2401
2367
|
}
|
|
2402
2368
|
const handoffs = [];
|
|
@@ -2474,16 +2440,16 @@ function runPiProjectionLifecycle(input, deps) {
|
|
|
2474
2440
|
const prepared = preparePiProjectionUninstall(input, deps);
|
|
2475
2441
|
return prepared.kind === "blocked" ? prepared : completePiProjectionUninstall(prepared.plan, deps);
|
|
2476
2442
|
}
|
|
2477
|
-
const prompt =
|
|
2443
|
+
const prompt = path14.join(scope.codingAgentDir, "AGENTS.md");
|
|
2478
2444
|
try {
|
|
2479
2445
|
assertSystemPromptMarkers(deps.readText(prompt), prompt);
|
|
2480
2446
|
} catch (error) {
|
|
2481
2447
|
return blocked2("projection-prompt-markers", [prompt], error instanceof Error ? error.message : String(error));
|
|
2482
2448
|
}
|
|
2483
|
-
if (input.devtoolsMcpEnabled && (!input.pnpmBin || !
|
|
2449
|
+
if (input.devtoolsMcpEnabled && (!input.pnpmBin || !path14.isAbsolute(input.pnpmBin))) {
|
|
2484
2450
|
return blocked2("projection-devtools-command", [handoffPath(scope, "devtools")], "DevTools requiere un ejecutable pnpm absoluto disponible en PATH.");
|
|
2485
2451
|
}
|
|
2486
|
-
if (input.playwrightHandoffEnabled && (!input.playwrightCliCommand || !
|
|
2452
|
+
if (input.playwrightHandoffEnabled && (!input.playwrightCliCommand || !path14.isAbsolute(input.playwrightCliCommand) || /[\u0000-\u001f\u007f]/.test(input.playwrightCliCommand))) {
|
|
2487
2453
|
return blocked2("projection-playwright-command", [handoffPath(scope, "playwright")], "Playwright requiere un ejecutable absoluto verificado. Abre una terminal nueva tras pnpm setup y reintenta install --playwright.");
|
|
2488
2454
|
}
|
|
2489
2455
|
const plan = projectionPlan(input, scope);
|
|
@@ -2496,12 +2462,12 @@ function runPiProjectionLifecycle(input, deps) {
|
|
|
2496
2462
|
const expectedReceipt2 = receiptContent(receipt);
|
|
2497
2463
|
const drifted = plan.filter((action) => hasActionDrift(action, deps));
|
|
2498
2464
|
for (const { kind, file } of checked.handoffs) {
|
|
2499
|
-
if (handoffEnabled(input, kind) && checked.previous?.[kind]?.sha256 !== receipt[kind]?.sha256 && !drifted.some((action) =>
|
|
2465
|
+
if (handoffEnabled(input, kind) && checked.previous?.[kind]?.sha256 !== receipt[kind]?.sha256 && !drifted.some((action) => path14.resolve(action.target) === file)) {
|
|
2500
2466
|
return input.operation === "doctor" ? { kind: "drift", paths: [file] } : handoffConflict(kind, file);
|
|
2501
2467
|
}
|
|
2502
2468
|
}
|
|
2503
2469
|
if (input.operation === "doctor") {
|
|
2504
|
-
const paths = drifted.map((action) =>
|
|
2470
|
+
const paths = drifted.map((action) => path14.resolve(action.target));
|
|
2505
2471
|
paths.push(...removedHandoffs.map(({ file }) => file), ...retiredSkills);
|
|
2506
2472
|
const currentSettings2 = deps.readText(scope.settingsFile);
|
|
2507
2473
|
if (currentSettings2 === null || filterProjectedPiPackage(currentSettings2, input.packageSource) !== currentSettings2) {
|
|
@@ -2560,12 +2526,12 @@ function runPiProjectionLifecycle(input, deps) {
|
|
|
2560
2526
|
return { kind: "synced", changed: drifted.length > 0 || packageWillChange || receiptChanged || removedHandoffs.length > 0 };
|
|
2561
2527
|
}
|
|
2562
2528
|
function systemProjectionLifecycle(input) {
|
|
2563
|
-
const targetRoot = input.targetDir === void 0 ? null :
|
|
2529
|
+
const targetRoot = input.targetDir === void 0 ? null : path14.resolve(input.targetDir);
|
|
2564
2530
|
const scope = targetRoot === null ? realPiProjectionScope() : {
|
|
2565
2531
|
kind: "target-dir",
|
|
2566
|
-
home:
|
|
2567
|
-
codingAgentDir:
|
|
2568
|
-
receiptFile:
|
|
2532
|
+
home: path14.join(targetRoot, "home"),
|
|
2533
|
+
codingAgentDir: path14.dirname(piSystemPromptFile(targetRoot)),
|
|
2534
|
+
receiptFile: path14.join(targetRoot, "state", "pi-projection-receipt.json")
|
|
2569
2535
|
};
|
|
2570
2536
|
return {
|
|
2571
2537
|
input: {
|
|
@@ -2586,14 +2552,14 @@ function systemProjectionLifecycle(input) {
|
|
|
2586
2552
|
backup: (paths) => createBackup(
|
|
2587
2553
|
paths,
|
|
2588
2554
|
`pi-projection-${input.operation}`,
|
|
2589
|
-
targetRoot === null ? void 0 :
|
|
2555
|
+
targetRoot === null ? void 0 : path14.join(targetRoot, "backups")
|
|
2590
2556
|
),
|
|
2591
2557
|
writeText,
|
|
2592
2558
|
copyFile,
|
|
2593
2559
|
removeFile: (file) => {
|
|
2594
2560
|
fs11.rmSync(file, { force: true });
|
|
2595
|
-
if (retiredPlaywrightFiles(scope).includes(
|
|
2596
|
-
pruneEmptyDirs(file,
|
|
2561
|
+
if (retiredPlaywrightFiles(scope).includes(path14.resolve(file))) {
|
|
2562
|
+
pruneEmptyDirs(file, path14.join(scope.home, ".agents", "skills"));
|
|
2597
2563
|
}
|
|
2598
2564
|
},
|
|
2599
2565
|
readManifest: targetRoot === null ? readManifest : () => ({ runtimes: {} })
|
|
@@ -2696,7 +2662,7 @@ async function runUninstall(opts) {
|
|
|
2696
2662
|
if (!detection.installed) continue;
|
|
2697
2663
|
const keepCtx = makeContext(keep, detection.configDir);
|
|
2698
2664
|
if (!keepCtx) continue;
|
|
2699
|
-
for (const action of buildContentPlan(keep, keepCtx)) retained.add(
|
|
2665
|
+
for (const action of buildContentPlan(keep, keepCtx)) retained.add(path15.resolve(action.target));
|
|
2700
2666
|
}
|
|
2701
2667
|
}
|
|
2702
2668
|
for (const id of opts.runtimes) {
|
|
@@ -2722,16 +2688,16 @@ async function runUninstall(opts) {
|
|
|
2722
2688
|
exitCode = 1;
|
|
2723
2689
|
continue;
|
|
2724
2690
|
}
|
|
2725
|
-
const mergedTargets = new Set(unmerge.map((a) =>
|
|
2691
|
+
const mergedTargets = new Set(unmerge.map((a) => path15.resolve(a.target)));
|
|
2726
2692
|
const usingRealConfig = opts.targetDir === void 0;
|
|
2727
2693
|
const prevOwned = usingRealConfig ? readManifest().runtimes[id]?.owned ?? [] : [];
|
|
2728
|
-
const pruneRoot = usingRealConfig ? HOME :
|
|
2694
|
+
const pruneRoot = usingRealConfig ? HOME : path15.dirname(configDir);
|
|
2729
2695
|
const planTargets = [
|
|
2730
|
-
.../* @__PURE__ */ new Set([...buildContentPlan(adapter, ctx).map((a) =>
|
|
2696
|
+
.../* @__PURE__ */ new Set([...buildContentPlan(adapter, ctx).map((a) => path15.resolve(a.target)), ...prevOwned.map((t) => path15.resolve(t))])
|
|
2731
2697
|
].filter((t) => !mergedTargets.has(t) && fs12.existsSync(t));
|
|
2732
2698
|
const deleteTargets = planTargets.filter((t) => {
|
|
2733
2699
|
if (retained.has(t) || !isContainedIn(t, pruneRoot)) return false;
|
|
2734
|
-
if (
|
|
2700
|
+
if (path15.basename(t) !== "engram.ts") return true;
|
|
2735
2701
|
const state = inspectOpencodePluginFile(t);
|
|
2736
2702
|
if (state === "official" || state === "unknown") return false;
|
|
2737
2703
|
return !ctx.preserveEngram;
|
|
@@ -2814,7 +2780,7 @@ async function runUninstall(opts) {
|
|
|
2814
2780
|
}
|
|
2815
2781
|
|
|
2816
2782
|
// src/doctor.ts
|
|
2817
|
-
import
|
|
2783
|
+
import path16 from "path";
|
|
2818
2784
|
import fs13 from "fs";
|
|
2819
2785
|
import * as p3 from "@clack/prompts";
|
|
2820
2786
|
function readDoctorTextIfExists(file) {
|
|
@@ -2828,12 +2794,12 @@ function readDoctorTextIfExists(file) {
|
|
|
2828
2794
|
var STALE_PERMISSIONS_MARKER = "differs from the stack default and was left untouched";
|
|
2829
2795
|
var UPGRADE_PERMISSIONS_REMEDY = "jorgex-stack sync --upgrade-permissions --dry-run";
|
|
2830
2796
|
function piAgentDir(targetDir) {
|
|
2831
|
-
return targetDir === void 0 ? process.env.PI_CODING_AGENT_DIR ??
|
|
2797
|
+
return targetDir === void 0 ? process.env.PI_CODING_AGENT_DIR ?? path16.join(HOME, ".pi", "agent") : path16.join(targetDir, "pi-agent");
|
|
2832
2798
|
}
|
|
2833
2799
|
function reportPiPermissions(targetDir) {
|
|
2834
2800
|
const agentDir = piAgentDir(targetDir);
|
|
2835
|
-
const configFile =
|
|
2836
|
-
const receiptFile =
|
|
2801
|
+
const configFile = path16.join(agentDir, "extensions", "pi-permission-system", "config.json");
|
|
2802
|
+
const receiptFile = path16.join(agentDir, "jorgex-pi", "permissions-lifecycle.v1.json");
|
|
2837
2803
|
let config;
|
|
2838
2804
|
let receipt;
|
|
2839
2805
|
try {
|
|
@@ -2870,7 +2836,7 @@ function resolvePlaywrightDoctorState(input) {
|
|
|
2870
2836
|
return { status: "healthy" };
|
|
2871
2837
|
}
|
|
2872
2838
|
function context7KeyConfigured(id, configDir) {
|
|
2873
|
-
const file = id === "codex" ?
|
|
2839
|
+
const file = id === "codex" ? path16.join(configDir, "config.toml") : id === "claude-code" ? path16.join(path16.dirname(configDir), `${path16.basename(configDir)}.json`) : path16.join(configDir, "opencode.json");
|
|
2874
2840
|
const content = readTextIfExists(file);
|
|
2875
2841
|
if (content === null) return null;
|
|
2876
2842
|
const match = /CONTEXT7_API_KEY"?\s*[:=]\s*"([^"]*)"/.exec(content);
|
|
@@ -2890,7 +2856,7 @@ function reportWritingStyle(options, style, mode) {
|
|
|
2890
2856
|
for (const id of runtimes) {
|
|
2891
2857
|
const adapter = id === "pi" ? piAdapter : ADAPTERS[id];
|
|
2892
2858
|
if (adapter === void 0) continue;
|
|
2893
|
-
const configDir = id === "pi" ? options.targetDir === void 0 ? process.env.PI_CODING_AGENT_DIR ??
|
|
2859
|
+
const configDir = id === "pi" ? options.targetDir === void 0 ? process.env.PI_CODING_AGENT_DIR ?? path16.join(HOME, ".pi", "agent") : path16.join(options.targetDir, "pi-agent") : options.targetDir ?? ADAPTERS[id].detect().configDir;
|
|
2894
2860
|
const file = adapter.paths(configDir).systemPromptFile;
|
|
2895
2861
|
try {
|
|
2896
2862
|
const content = readDoctorTextIfExists(file) ?? "";
|
|
@@ -2905,7 +2871,7 @@ function reportWritingStyle(options, style, mode) {
|
|
|
2905
2871
|
problems++;
|
|
2906
2872
|
}
|
|
2907
2873
|
if (id === "codex") {
|
|
2908
|
-
const override =
|
|
2874
|
+
const override = path16.join(configDir, "AGENTS.override.md");
|
|
2909
2875
|
if ((readDoctorTextIfExists(override) ?? "").trim() !== "") {
|
|
2910
2876
|
p3.log.warn(`Codex: ${override} no vac\xEDo puede ocultar el AGENTS.md gestionado; rev\xEDsalo sin modificarlo autom\xE1ticamente.`);
|
|
2911
2877
|
problems++;
|
|
@@ -2920,7 +2886,7 @@ function reportWritingStyle(options, style, mode) {
|
|
|
2920
2886
|
}
|
|
2921
2887
|
async function verifyOfficialForRuntime(runtime, configDir, engramBin, homeDir, isExplicitClaudeConfigDir) {
|
|
2922
2888
|
if (runtime === "claude-code") {
|
|
2923
|
-
const { verifyOfficialSetup: verifyOfficialSetup2 } = await import("./claude-code-
|
|
2889
|
+
const { verifyOfficialSetup: verifyOfficialSetup2 } = await import("./claude-code-BBHLN5TD.js");
|
|
2924
2890
|
const explicit = isExplicitClaudeConfigDir ?? process.env.CLAUDE_CONFIG_DIR !== void 0;
|
|
2925
2891
|
const report2 = await verifyOfficialSetup2({ configDir, engramBin, homeDir, isExplicitClaudeConfigDir: explicit });
|
|
2926
2892
|
return {
|
|
@@ -2930,7 +2896,7 @@ async function verifyOfficialForRuntime(runtime, configDir, engramBin, homeDir,
|
|
|
2930
2896
|
};
|
|
2931
2897
|
}
|
|
2932
2898
|
if (runtime === "codex") {
|
|
2933
|
-
const { verifyOfficialSetup: verifyOfficialSetup2 } = await import("./codex-
|
|
2899
|
+
const { verifyOfficialSetup: verifyOfficialSetup2 } = await import("./codex-ALGP2YBC.js");
|
|
2934
2900
|
const report2 = await verifyOfficialSetup2({ configDir, engramBin });
|
|
2935
2901
|
return {
|
|
2936
2902
|
ok: report2.ok,
|
|
@@ -2938,7 +2904,16 @@ async function verifyOfficialForRuntime(runtime, configDir, engramBin, homeDir,
|
|
|
2938
2904
|
...report2.reason === void 0 ? {} : { reason: report2.reason }
|
|
2939
2905
|
};
|
|
2940
2906
|
}
|
|
2941
|
-
|
|
2907
|
+
if (runtime === "pi") {
|
|
2908
|
+
const { verifyOfficialSetup: verifyOfficialSetup2 } = await import("./pi-CKFSPGJU.js");
|
|
2909
|
+
const report2 = await verifyOfficialSetup2({ configDir, engramBin, homeDir });
|
|
2910
|
+
return {
|
|
2911
|
+
ok: report2.ok,
|
|
2912
|
+
layers: report2.layers,
|
|
2913
|
+
...report2.reason === void 0 ? {} : { reason: report2.reason }
|
|
2914
|
+
};
|
|
2915
|
+
}
|
|
2916
|
+
const { verifyOfficialSetup } = await import("./opencode-VG2IZRRF.js");
|
|
2942
2917
|
const report = await verifyOfficialSetup({ configDir, engramBin });
|
|
2943
2918
|
return {
|
|
2944
2919
|
ok: report.ok,
|
|
@@ -2981,8 +2956,8 @@ async function runDoctor(options = {}) {
|
|
|
2981
2956
|
engramBinAvailable = true;
|
|
2982
2957
|
}
|
|
2983
2958
|
}
|
|
2984
|
-
const engramDataDir = process.env.ENGRAM_DATA_DIR ??
|
|
2985
|
-
const engramDb =
|
|
2959
|
+
const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path16.join(HOME, ".engram");
|
|
2960
|
+
const engramDb = path16.join(engramDataDir, "engram.db");
|
|
2986
2961
|
if (fs13.existsSync(engramDb)) {
|
|
2987
2962
|
const sizeMb = (fs13.statSync(engramDb).size / 1024 / 1024).toFixed(1);
|
|
2988
2963
|
p3.log.info(`Engram DB: ${engramDb} (${sizeMb} MB de memorias \u2014 el stack no la toca JAM\xC1S).`);
|
|
@@ -3087,7 +3062,7 @@ async function runDoctor(options = {}) {
|
|
|
3087
3062
|
p3.log.warn(`${adapter.name}: ${orphans.length} archivos hu\xE9rfanos de versiones previas \u2192 ejecuta 'sync'.`);
|
|
3088
3063
|
problems++;
|
|
3089
3064
|
}
|
|
3090
|
-
if (adapter.id === "codex" && fs13.existsSync(
|
|
3065
|
+
if (adapter.id === "codex" && fs13.existsSync(path16.join(detection.configDir, "hooks.json"))) {
|
|
3091
3066
|
p3.log.info("Codex: recuerda que los hooks requieren aprobaci\xF3n manual \u2014 verifica con /hooks dentro de codex.");
|
|
3092
3067
|
}
|
|
3093
3068
|
const key = context7KeyConfigured(adapter.id, detection.configDir);
|
|
@@ -3136,14 +3111,14 @@ async function runDoctor(options = {}) {
|
|
|
3136
3111
|
|
|
3137
3112
|
// src/update.ts
|
|
3138
3113
|
import fs16 from "fs";
|
|
3139
|
-
import
|
|
3114
|
+
import path19 from "path";
|
|
3140
3115
|
import os3 from "os";
|
|
3141
3116
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
3142
3117
|
import * as p4 from "@clack/prompts";
|
|
3143
3118
|
|
|
3144
3119
|
// src/lib/github.ts
|
|
3145
3120
|
import fs14 from "fs";
|
|
3146
|
-
import
|
|
3121
|
+
import path17 from "path";
|
|
3147
3122
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
3148
3123
|
import os2 from "os";
|
|
3149
3124
|
import { Readable } from "stream";
|
|
@@ -3180,16 +3155,28 @@ function githubHeaders() {
|
|
|
3180
3155
|
...authHeaders()
|
|
3181
3156
|
};
|
|
3182
3157
|
}
|
|
3158
|
+
var ENGRAM_REPO = "Gentleman-Programming/engram";
|
|
3159
|
+
var ENGRAM_RELEASES_LATEST_URL = `https://api.github.com/repos/${ENGRAM_REPO}/releases/latest`;
|
|
3160
|
+
var ENGRAM_DOWNLOAD_PREFIX = `https://github.com/${ENGRAM_REPO}/releases/download/`;
|
|
3161
|
+
function expectedEngramAssetName(version, platform, arch) {
|
|
3162
|
+
const osPart = platform === "win32" ? "windows" : platform === "linux" ? "linux" : platform === "darwin" ? "darwin" : null;
|
|
3163
|
+
const archPart = arch === "x64" ? "amd64" : arch === "arm64" ? "arm64" : null;
|
|
3164
|
+
if (osPart === null || archPart === null) return null;
|
|
3165
|
+
const ext = osPart === "windows" ? "zip" : "tar.gz";
|
|
3166
|
+
return `engram_${version}_${osPart}_${archPart}.${ext}`;
|
|
3167
|
+
}
|
|
3168
|
+
async function fetchLatestGithubRelease(repo, fetchFn = globalThis.fetch) {
|
|
3169
|
+
const res = await fetchFn(`https://api.github.com/repos/${repo}/releases/latest`, {
|
|
3170
|
+
headers: githubHeaders(),
|
|
3171
|
+
signal: AbortSignal.timeout(1e4)
|
|
3172
|
+
});
|
|
3173
|
+
if (!res.ok && (res.status === 403 || res.status === 429)) rateLimitHit = true;
|
|
3174
|
+
return res;
|
|
3175
|
+
}
|
|
3183
3176
|
async function latestGithubRelease(repo) {
|
|
3184
3177
|
try {
|
|
3185
|
-
const res = await
|
|
3186
|
-
|
|
3187
|
-
signal: AbortSignal.timeout(1e4)
|
|
3188
|
-
});
|
|
3189
|
-
if (!res.ok) {
|
|
3190
|
-
if (res.status === 403 || res.status === 429) rateLimitHit = true;
|
|
3191
|
-
return null;
|
|
3192
|
-
}
|
|
3178
|
+
const res = await fetchLatestGithubRelease(repo);
|
|
3179
|
+
if (!res.ok) return null;
|
|
3193
3180
|
const data = await res.json();
|
|
3194
3181
|
return data.tag_name?.replace(/^v/, "") ?? null;
|
|
3195
3182
|
} catch {
|
|
@@ -3213,7 +3200,7 @@ async function latestGithubCommit(repo) {
|
|
|
3213
3200
|
}
|
|
3214
3201
|
}
|
|
3215
3202
|
function validateExtractedTree(destDir) {
|
|
3216
|
-
const resolved =
|
|
3203
|
+
const resolved = path17.resolve(destDir);
|
|
3217
3204
|
const walk = (dir) => {
|
|
3218
3205
|
let entries;
|
|
3219
3206
|
try {
|
|
@@ -3222,7 +3209,7 @@ function validateExtractedTree(destDir) {
|
|
|
3222
3209
|
return false;
|
|
3223
3210
|
}
|
|
3224
3211
|
for (const entry of entries) {
|
|
3225
|
-
const full =
|
|
3212
|
+
const full = path17.join(dir, entry.name);
|
|
3226
3213
|
let stat;
|
|
3227
3214
|
try {
|
|
3228
3215
|
stat = fs14.lstatSync(full);
|
|
@@ -3241,12 +3228,12 @@ function validateExtractedTree(destDir) {
|
|
|
3241
3228
|
}
|
|
3242
3229
|
function resolveTarBin() {
|
|
3243
3230
|
if (process.platform !== "win32") return "tar";
|
|
3244
|
-
const winTar =
|
|
3231
|
+
const winTar = path17.join(process.env["SystemRoot"] ?? "C:\\Windows", "System32", "tar.exe");
|
|
3245
3232
|
return fs14.existsSync(winTar) ? winTar : "tar";
|
|
3246
3233
|
}
|
|
3247
3234
|
async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
|
|
3248
3235
|
const url = `https://codeload.github.com/${repo}/tar.gz/${sha}`;
|
|
3249
|
-
const tmp =
|
|
3236
|
+
const tmp = path17.join(os2.tmpdir(), `jorgex-tarball-${Date.now()}.tar.gz`);
|
|
3250
3237
|
const fail = (reason) => {
|
|
3251
3238
|
try {
|
|
3252
3239
|
fs14.rmSync(destDir, { recursive: true, force: true });
|
|
@@ -3279,8 +3266,8 @@ async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
|
|
|
3279
3266
|
const detail = (e.stderr?.toString().trim() || e.message || "").split("\n")[0];
|
|
3280
3267
|
return fail(detail ? `tar fall\xF3: ${detail}` : "tar no disponible o fall\xF3 la extracci\xF3n");
|
|
3281
3268
|
}
|
|
3282
|
-
const resolvedDest =
|
|
3283
|
-
const validateRoot = validateSubdir ?
|
|
3269
|
+
const resolvedDest = path17.resolve(destDir);
|
|
3270
|
+
const validateRoot = validateSubdir ? path17.resolve(resolvedDest, validateSubdir) : resolvedDest;
|
|
3284
3271
|
if (validateRoot !== resolvedDest && !isContainedIn(validateRoot, resolvedDest)) {
|
|
3285
3272
|
return fail(`la ruta de validaci\xF3n "${validateSubdir}" escapa del destino`);
|
|
3286
3273
|
}
|
|
@@ -3302,7 +3289,7 @@ async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
|
|
|
3302
3289
|
|
|
3303
3290
|
// src/lib/skill-update.ts
|
|
3304
3291
|
import fs15 from "fs";
|
|
3305
|
-
import
|
|
3292
|
+
import path18 from "path";
|
|
3306
3293
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
3307
3294
|
var PROTECTED_SKILLS = /* @__PURE__ */ new Set([
|
|
3308
3295
|
"agent-delegation",
|
|
@@ -3322,10 +3309,10 @@ function sameTextContentNormalized(a, b) {
|
|
|
3322
3309
|
}
|
|
3323
3310
|
function diffSkillDirs(upstreamDir, localDir) {
|
|
3324
3311
|
const upstreamFiles = new Set(
|
|
3325
|
-
listFilesRecursive(upstreamDir).map((f) =>
|
|
3312
|
+
listFilesRecursive(upstreamDir).map((f) => path18.relative(upstreamDir, f))
|
|
3326
3313
|
);
|
|
3327
3314
|
const localFiles = new Set(
|
|
3328
|
-
listFilesRecursive(localDir).map((f) =>
|
|
3315
|
+
listFilesRecursive(localDir).map((f) => path18.relative(localDir, f))
|
|
3329
3316
|
);
|
|
3330
3317
|
const added = [];
|
|
3331
3318
|
const modified = [];
|
|
@@ -3333,7 +3320,7 @@ function diffSkillDirs(upstreamDir, localDir) {
|
|
|
3333
3320
|
for (const rel of upstreamFiles) {
|
|
3334
3321
|
if (!localFiles.has(rel)) {
|
|
3335
3322
|
added.push(rel);
|
|
3336
|
-
} else if (!sameTextContentNormalized(
|
|
3323
|
+
} else if (!sameTextContentNormalized(path18.join(upstreamDir, rel), path18.join(localDir, rel))) {
|
|
3337
3324
|
modified.push(rel);
|
|
3338
3325
|
}
|
|
3339
3326
|
}
|
|
@@ -3395,7 +3382,7 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
|
|
|
3395
3382
|
if (PROTECTED_SKILLS.has(name)) {
|
|
3396
3383
|
throw new Error(`La skill "${name}" es propia del stack y no se actualiza desde upstream.`);
|
|
3397
3384
|
}
|
|
3398
|
-
const upstreamsFile = upstreamsFilePath ??
|
|
3385
|
+
const upstreamsFile = upstreamsFilePath ?? path18.join(path18.dirname(stackRoot()), "upstreams.json");
|
|
3399
3386
|
const raw = fs15.readFileSync(upstreamsFile, "utf8");
|
|
3400
3387
|
const data = JSON.parse(raw);
|
|
3401
3388
|
const skillEntry = data?.skills?.[name];
|
|
@@ -3405,8 +3392,8 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
|
|
|
3405
3392
|
if (skillEntry.kind === "release") {
|
|
3406
3393
|
throw new Error(`La skill "${name}" es de tipo release y no se actualiza con replaceSkill.`);
|
|
3407
3394
|
}
|
|
3408
|
-
const skillsRoot = localSkillsRoot ??
|
|
3409
|
-
const localSkillDir =
|
|
3395
|
+
const skillsRoot = localSkillsRoot ?? path18.join(stackRoot(), "skills");
|
|
3396
|
+
const localSkillDir = path18.join(skillsRoot, name);
|
|
3410
3397
|
const localFiles = listFilesRecursive(localSkillDir);
|
|
3411
3398
|
if (localFiles.length > 0) {
|
|
3412
3399
|
createBackup(localFiles, `skill-update-${name}`, backupsRoot);
|
|
@@ -3419,9 +3406,9 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
|
|
|
3419
3406
|
if (st.isSymbolicLink()) {
|
|
3420
3407
|
throw new Error(`Symlink rechazado en upstream de skill "${name}": ${src}`);
|
|
3421
3408
|
}
|
|
3422
|
-
const rel =
|
|
3423
|
-
const dest =
|
|
3424
|
-
ensureDir(
|
|
3409
|
+
const rel = path18.relative(upstreamSkillDir, src);
|
|
3410
|
+
const dest = path18.join(stagingDir, rel);
|
|
3411
|
+
ensureDir(path18.dirname(dest));
|
|
3425
3412
|
copyFile(src, dest);
|
|
3426
3413
|
}
|
|
3427
3414
|
const oldDir = `${localSkillDir}.old-${process.pid}`;
|
|
@@ -3446,7 +3433,7 @@ function rateLimitHint(prefix) {
|
|
|
3446
3433
|
return ghPresentButTokenFailed() ? `${prefix} Tienes gh instalado pero \`gh auth token\` no devolvi\xF3 credencial (\xBFsesi\xF3n caducada?) \u2014 prueba \`gh auth login\` o define GH_TOKEN.` : `${prefix} Define GH_TOKEN o inicia sesi\xF3n en gh CLI.`;
|
|
3447
3434
|
}
|
|
3448
3435
|
function loadUpstreams() {
|
|
3449
|
-
const file =
|
|
3436
|
+
const file = path19.join(path19.dirname(stackRoot()), "upstreams.json");
|
|
3450
3437
|
return JSON.parse(fs16.readFileSync(file, "utf8"));
|
|
3451
3438
|
}
|
|
3452
3439
|
function skillsToScan(maintainer, upstreams) {
|
|
@@ -3678,8 +3665,8 @@ function isEngramRunning() {
|
|
|
3678
3665
|
return null;
|
|
3679
3666
|
}
|
|
3680
3667
|
}
|
|
3681
|
-
function isGitClone(projectRoot =
|
|
3682
|
-
return fs16.existsSync(
|
|
3668
|
+
function isGitClone(projectRoot = path19.dirname(stackRoot())) {
|
|
3669
|
+
return fs16.existsSync(path19.join(projectRoot, ".git"));
|
|
3683
3670
|
}
|
|
3684
3671
|
var STACK_METHOD_CLONE = "git pull + pnpm install + pnpm build";
|
|
3685
3672
|
function resolvePnpm() {
|
|
@@ -3694,7 +3681,7 @@ function cleanupTmp(dir) {
|
|
|
3694
3681
|
}
|
|
3695
3682
|
}
|
|
3696
3683
|
function updateStackGitClone() {
|
|
3697
|
-
const projectRoot =
|
|
3684
|
+
const projectRoot = path19.dirname(stackRoot());
|
|
3698
3685
|
const git = lookPath("git");
|
|
3699
3686
|
if (!git) throw new Error("git no encontrado en PATH.");
|
|
3700
3687
|
const pnpm = resolvePnpm();
|
|
@@ -3711,7 +3698,7 @@ function updateStackGlobal() {
|
|
|
3711
3698
|
execFileSync4(pnpm, ["add", "-g", "jorgex-stack@latest"], { stdio: "inherit" });
|
|
3712
3699
|
}
|
|
3713
3700
|
async function downloadSkillToTemp(repo, sha, skillPath) {
|
|
3714
|
-
const root = fs16.mkdtempSync(
|
|
3701
|
+
const root = fs16.mkdtempSync(path19.join(os3.tmpdir(), "jorgex-skill-"));
|
|
3715
3702
|
try {
|
|
3716
3703
|
const result = await downloadRepoTarball(repo, sha, root, skillPath);
|
|
3717
3704
|
if (!result.ok) {
|
|
@@ -3719,14 +3706,14 @@ async function downloadSkillToTemp(repo, sha, skillPath) {
|
|
|
3719
3706
|
return { error: result.reason };
|
|
3720
3707
|
}
|
|
3721
3708
|
if (skillPath) {
|
|
3722
|
-
const sub =
|
|
3709
|
+
const sub = path19.resolve(path19.join(root, skillPath));
|
|
3723
3710
|
if (!isContainedIn(sub, root)) {
|
|
3724
3711
|
cleanupTmp(root);
|
|
3725
3712
|
return { error: `la ruta "${skillPath}" escapa del directorio temporal` };
|
|
3726
3713
|
}
|
|
3727
3714
|
if (fs16.existsSync(sub)) return { dir: sub, root };
|
|
3728
3715
|
const lastSeg = skillPath.split("/").pop();
|
|
3729
|
-
const sub2 =
|
|
3716
|
+
const sub2 = path19.resolve(path19.join(root, lastSeg));
|
|
3730
3717
|
if (isContainedIn(sub2, root) && fs16.existsSync(sub2)) {
|
|
3731
3718
|
if (!validateExtractedTree(sub2)) {
|
|
3732
3719
|
cleanupTmp(root);
|
|
@@ -3747,10 +3734,10 @@ function pruneEngramDbBackups() {
|
|
|
3747
3734
|
try {
|
|
3748
3735
|
const dir = dataDir();
|
|
3749
3736
|
if (!fs16.existsSync(dir)) return;
|
|
3750
|
-
const backups = fs16.readdirSync(dir).filter((f) => f.startsWith("engram-db-backup-") && f.endsWith(".db")).map((f) => ({ name: f, mtime: fs16.statSync(
|
|
3737
|
+
const backups = fs16.readdirSync(dir).filter((f) => f.startsWith("engram-db-backup-") && f.endsWith(".db")).map((f) => ({ name: f, mtime: fs16.statSync(path19.join(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
3751
3738
|
for (const old of backups.slice(3)) {
|
|
3752
3739
|
try {
|
|
3753
|
-
fs16.rmSync(
|
|
3740
|
+
fs16.rmSync(path19.join(dir, old.name));
|
|
3754
3741
|
} catch {
|
|
3755
3742
|
}
|
|
3756
3743
|
}
|
|
@@ -3759,17 +3746,17 @@ function pruneEngramDbBackups() {
|
|
|
3759
3746
|
}
|
|
3760
3747
|
function rotateLockedBinary(binPath, sweepRoot = HOME) {
|
|
3761
3748
|
if (!fs16.existsSync(binPath)) return null;
|
|
3762
|
-
const dir =
|
|
3763
|
-
const base =
|
|
3749
|
+
const dir = path19.dirname(binPath);
|
|
3750
|
+
const base = path19.basename(binPath);
|
|
3764
3751
|
const escapedBase = base.replace(/[.*+?^$()|[\]{}\\]/g, "\\$&");
|
|
3765
3752
|
const oldPattern = new RegExp("^" + escapedBase + "\\.old-\\d+$");
|
|
3766
|
-
const resolvedDir =
|
|
3767
|
-
if (resolvedDir ===
|
|
3753
|
+
const resolvedDir = path19.resolve(dir);
|
|
3754
|
+
if (resolvedDir === path19.resolve(sweepRoot) || isContainedIn(resolvedDir, sweepRoot)) {
|
|
3768
3755
|
try {
|
|
3769
3756
|
for (const entry of fs16.readdirSync(dir)) {
|
|
3770
3757
|
if (oldPattern.test(entry)) {
|
|
3771
3758
|
try {
|
|
3772
|
-
fs16.rmSync(
|
|
3759
|
+
fs16.rmSync(path19.join(dir, entry), { force: true });
|
|
3773
3760
|
} catch {
|
|
3774
3761
|
}
|
|
3775
3762
|
}
|
|
@@ -3777,7 +3764,7 @@ function rotateLockedBinary(binPath, sweepRoot = HOME) {
|
|
|
3777
3764
|
} catch {
|
|
3778
3765
|
}
|
|
3779
3766
|
}
|
|
3780
|
-
const rotated =
|
|
3767
|
+
const rotated = path19.join(dir, `${base}.old-${Date.now()}`);
|
|
3781
3768
|
fs16.renameSync(binPath, rotated);
|
|
3782
3769
|
return rotated;
|
|
3783
3770
|
}
|
|
@@ -3787,8 +3774,8 @@ async function updateEngram(engramRepo, latestVersion) {
|
|
|
3787
3774
|
"Engram est\xE1 en ejecuci\xF3n: los procesos vivos seguir\xE1n usando la versi\xF3n antigua hasta que reinicies los clientes (Claude Code/OpenCode/Codex)."
|
|
3788
3775
|
);
|
|
3789
3776
|
}
|
|
3790
|
-
const engramDataDir = process.env.ENGRAM_DATA_DIR ??
|
|
3791
|
-
const engramDb =
|
|
3777
|
+
const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path19.join(HOME, ".engram");
|
|
3778
|
+
const engramDb = path19.join(engramDataDir, "engram.db");
|
|
3792
3779
|
if (fs16.existsSync(engramDb)) {
|
|
3793
3780
|
const doBackup = await p4.confirm({
|
|
3794
3781
|
message: `\xBFHacer backup de la DB de Engram antes de actualizar? (${engramDb})`,
|
|
@@ -3796,7 +3783,7 @@ async function updateEngram(engramRepo, latestVersion) {
|
|
|
3796
3783
|
});
|
|
3797
3784
|
if (!p4.isCancel(doBackup) && doBackup) {
|
|
3798
3785
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3799
|
-
const dest =
|
|
3786
|
+
const dest = path19.join(dataDir(), `engram-db-backup-${ts}.db`);
|
|
3800
3787
|
try {
|
|
3801
3788
|
if (!fs16.existsSync(dataDir())) fs16.mkdirSync(dataDir(), { recursive: true });
|
|
3802
3789
|
fs16.copyFileSync(engramDb, dest);
|
|
@@ -4134,7 +4121,7 @@ async function runInteractiveUpdate(localVersion, yes, dryRun = false, includeBr
|
|
|
4134
4121
|
continue;
|
|
4135
4122
|
}
|
|
4136
4123
|
const { dir: tmpDir, root: tmpRoot } = tmpResult;
|
|
4137
|
-
const localSkillDir =
|
|
4124
|
+
const localSkillDir = path19.join(stackRoot(), "skills", skillInfo.name);
|
|
4138
4125
|
const diff = renderSkillDiff(tmpDir, localSkillDir);
|
|
4139
4126
|
if (diff) {
|
|
4140
4127
|
p4.log.info(`Diff de ${skillInfo.name}:
|
|
@@ -4226,7 +4213,7 @@ ${diff}`);
|
|
|
4226
4213
|
}
|
|
4227
4214
|
|
|
4228
4215
|
// src/models-picker.ts
|
|
4229
|
-
import
|
|
4216
|
+
import path20 from "path";
|
|
4230
4217
|
import * as p5 from "@clack/prompts";
|
|
4231
4218
|
var TIERS = ["strong", "standard", "cheap"];
|
|
4232
4219
|
var EFFORTS = ["low", "medium", "high", "xhigh"];
|
|
@@ -4252,7 +4239,7 @@ function opencodeLiveModels(binPath) {
|
|
|
4252
4239
|
}
|
|
4253
4240
|
function agentsByTier() {
|
|
4254
4241
|
const grouped = { strong: [], standard: [], cheap: [] };
|
|
4255
|
-
for (const agent of loadCanonicalAgents(
|
|
4242
|
+
for (const agent of loadCanonicalAgents(path20.join(stackRoot(), "agents"))) {
|
|
4256
4243
|
if (agent.mode === "subagent") grouped[agent.tier].push(agent.name);
|
|
4257
4244
|
}
|
|
4258
4245
|
return grouped;
|
|
@@ -4438,15 +4425,15 @@ function cancelled() {
|
|
|
4438
4425
|
|
|
4439
4426
|
// src/lib/release.ts
|
|
4440
4427
|
import fs17 from "fs";
|
|
4441
|
-
import
|
|
4428
|
+
import path21 from "path";
|
|
4442
4429
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
4443
4430
|
import { fileURLToPath } from "url";
|
|
4444
4431
|
function findPackageJson() {
|
|
4445
|
-
let dir =
|
|
4432
|
+
let dir = path21.dirname(fileURLToPath(import.meta.url));
|
|
4446
4433
|
for (let i = 0; i < 6; i++) {
|
|
4447
|
-
const candidate =
|
|
4434
|
+
const candidate = path21.join(dir, "package.json");
|
|
4448
4435
|
if (fs17.existsSync(candidate)) return candidate;
|
|
4449
|
-
dir =
|
|
4436
|
+
dir = path21.dirname(dir);
|
|
4450
4437
|
}
|
|
4451
4438
|
throw new Error("No se encontr\xF3 package.json cerca del CLI.");
|
|
4452
4439
|
}
|
|
@@ -4467,7 +4454,7 @@ function readPackageMetadata() {
|
|
|
4467
4454
|
|
|
4468
4455
|
// src/lib/pi-runtime.ts
|
|
4469
4456
|
import os4 from "os";
|
|
4470
|
-
import
|
|
4457
|
+
import path22 from "path";
|
|
4471
4458
|
import fs18 from "fs";
|
|
4472
4459
|
import { spawnSync } from "child_process";
|
|
4473
4460
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -4476,16 +4463,16 @@ import { createHash as createHash2 } from "crypto";
|
|
|
4476
4463
|
var pi_runtime_pin_default = {
|
|
4477
4464
|
package: {
|
|
4478
4465
|
name: "jorgex-pi",
|
|
4479
|
-
version: "0.8.
|
|
4480
|
-
source: "npm:jorgex-pi@0.8.
|
|
4466
|
+
version: "0.8.28",
|
|
4467
|
+
source: "npm:jorgex-pi@0.8.28"
|
|
4481
4468
|
},
|
|
4482
4469
|
provenance: {
|
|
4483
|
-
commit: "
|
|
4470
|
+
commit: "056fbc7eeea07466c3ce3a6f85723e94bcf38577"
|
|
4484
4471
|
},
|
|
4485
4472
|
tarball: {
|
|
4486
|
-
bytes:
|
|
4487
|
-
sha256: "
|
|
4488
|
-
sha512: "
|
|
4473
|
+
bytes: 17993987,
|
|
4474
|
+
sha256: "3490845ad6d67c82cb0828847d0c9510a58958f068c4e8408161aadfe6f28ed9",
|
|
4475
|
+
sha512: "7ec1c46ccbafe8052effca13ef5e2bdff2ad09a2624bbe4ad7884622e231bbe25119b7124b6ba8f96916be34d99e511bfd83a9c16708d6f828ccf9f9d705bd7c"
|
|
4489
4476
|
}
|
|
4490
4477
|
};
|
|
4491
4478
|
|
|
@@ -4506,7 +4493,7 @@ var PI_RUNTIME_CANDIDATE = {
|
|
|
4506
4493
|
"structured-questions-v1",
|
|
4507
4494
|
"web-access-v1",
|
|
4508
4495
|
"goal-continuation-v1",
|
|
4509
|
-
"
|
|
4496
|
+
"engram-official-bridge-v1",
|
|
4510
4497
|
"engram-runtime-tools-v1",
|
|
4511
4498
|
"context7-http-v1",
|
|
4512
4499
|
"permissions-policy-v1",
|
|
@@ -4603,16 +4590,16 @@ async function resolvePiEngramRequirement(input, deps) {
|
|
|
4603
4590
|
};
|
|
4604
4591
|
}
|
|
4605
4592
|
const accepted = await deps.confirm({
|
|
4606
|
-
message: "Engram es obligatorio para JorgeX Pi. \xBFInstalar ahora el binario
|
|
4593
|
+
message: "Engram es obligatorio para JorgeX Pi. \xBFInstalar ahora el binario oficial verificado?",
|
|
4607
4594
|
initialValue: false
|
|
4608
4595
|
});
|
|
4609
4596
|
if (!accepted) return { kind: "offer", accepted: false };
|
|
4610
|
-
const installed = await deps.
|
|
4611
|
-
if (!installed) {
|
|
4597
|
+
const installed = await deps.installShared();
|
|
4598
|
+
if (!installed.ok) {
|
|
4612
4599
|
return {
|
|
4613
4600
|
kind: "blocked",
|
|
4614
4601
|
reason: "engram-install-failed",
|
|
4615
|
-
remedy:
|
|
4602
|
+
remedy: `La instalaci\xF3n de Engram fall\xF3: ${installed.reason} Instala Engram manualmente o configura ENGRAM_BIN antes de reintentar.`
|
|
4616
4603
|
};
|
|
4617
4604
|
}
|
|
4618
4605
|
const detected = deps.detectHost();
|
|
@@ -4665,7 +4652,7 @@ function healthyDoctor(stdout, stderr, packageRunner, candidate) {
|
|
|
4665
4652
|
if (record === null || typeof record !== "object" || Array.isArray(record)) return false;
|
|
4666
4653
|
const packageValue = Reflect.get(record, "package");
|
|
4667
4654
|
const result = Reflect.get(record, "result");
|
|
4668
|
-
return Reflect.get(record, "schemaVersion") === 1 && Reflect.get(record, "command") === "doctor" && Reflect.get(record, "ok") === true && packageValue !== null && typeof packageValue === "object" && Reflect.get(packageValue, "name") === "jorgex-pi" && Reflect.get(packageValue, "version") === (candidate.package?.version ?? /^npm:jorgex-pi@([^\s]+)$/.exec(candidate.source)?.[1]) &&
|
|
4655
|
+
return Reflect.get(record, "schemaVersion") === 1 && Reflect.get(record, "command") === "doctor" && Reflect.get(record, "ok") === true && packageValue !== null && typeof packageValue === "object" && Reflect.get(packageValue, "name") === "jorgex-pi" && Reflect.get(packageValue, "version") === (candidate.package?.version ?? /^npm:jorgex-pi@([^\s]+)$/.exec(candidate.source)?.[1]) && path22.resolve(packageRunner) === path22.resolve(String(Reflect.get(packageValue, "root")), "bin", "jorgex-pi.mjs") && result !== null && typeof result === "object" && Reflect.get(result, "healthy") === true;
|
|
4669
4656
|
} catch {
|
|
4670
4657
|
return false;
|
|
4671
4658
|
}
|
|
@@ -4686,7 +4673,7 @@ function provisionalPendingDoctor(exitCode, stdout, stderr, packageRunner, candi
|
|
|
4686
4673
|
if (JSON.stringify(Object.keys(packageValue).sort()) !== JSON.stringify(["name", "root", "version"])) return false;
|
|
4687
4674
|
if (Reflect.get(packageValue, "name") !== "jorgex-pi") return false;
|
|
4688
4675
|
if (Reflect.get(packageValue, "version") !== (candidate.package?.version ?? /^npm:jorgex-pi@([^\s]+)$/.exec(candidate.source)?.[1])) return false;
|
|
4689
|
-
if (
|
|
4676
|
+
if (path22.resolve(packageRunner) !== path22.resolve(String(Reflect.get(packageValue, "root")), "bin", "jorgex-pi.mjs")) return false;
|
|
4690
4677
|
const result = Reflect.get(record, "result");
|
|
4691
4678
|
if (result === null || typeof result !== "object" || Array.isArray(result)) return false;
|
|
4692
4679
|
if (JSON.stringify(Object.keys(result).sort()) !== JSON.stringify(["checks", "healthy"])) return false;
|
|
@@ -4715,7 +4702,7 @@ function provisionalPendingDoctor(exitCode, stdout, stderr, packageRunner, candi
|
|
|
4715
4702
|
}
|
|
4716
4703
|
function installPiFromVerifiedTarball(input, deps) {
|
|
4717
4704
|
const paths = input.targetDir === void 0 ? userPaths(input.engramBin, input.piExecutable) : targetPaths(input.targetDir, input.engramBin, input.piExecutable);
|
|
4718
|
-
const destination = input.targetDir === void 0 ?
|
|
4705
|
+
const destination = input.targetDir === void 0 ? path22.join(dataDir(), "packages", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`) : path22.join(path22.resolve(input.targetDir), "downloads", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`);
|
|
4719
4706
|
const artifact = deps.download(destination);
|
|
4720
4707
|
if (artifact.bytes !== input.candidate.bytes || artifact.sha256 !== input.candidate.sha256 || artifact.sha512 !== input.candidate.sha512) {
|
|
4721
4708
|
return { kind: "blocked", reason: "tarball-integrity" };
|
|
@@ -4723,7 +4710,7 @@ function installPiFromVerifiedTarball(input, deps) {
|
|
|
4723
4710
|
deps.backupSettings();
|
|
4724
4711
|
const scope = {
|
|
4725
4712
|
kind: input.targetDir === void 0 ? "real" : "target-dir",
|
|
4726
|
-
codingAgentDir:
|
|
4713
|
+
codingAgentDir: path22.resolve(paths.codingAgentDir)
|
|
4727
4714
|
};
|
|
4728
4715
|
const installing = flatCandidateReceipt(input.candidate, scope, "installing", input.engramBin);
|
|
4729
4716
|
deps.writeReceiptAtomic(`${JSON.stringify(installing)}
|
|
@@ -4754,30 +4741,30 @@ function installPiFromVerifiedTarball(input, deps) {
|
|
|
4754
4741
|
return { kind: "installed", receipt };
|
|
4755
4742
|
}
|
|
4756
4743
|
function runtimePath(piExecutable) {
|
|
4757
|
-
const entries = process.platform === "win32" ? [piExecutable === void 0 ? null :
|
|
4758
|
-
return [...new Set(entries.filter((entry) => entry !== null))].join(
|
|
4744
|
+
const entries = process.platform === "win32" ? [piExecutable === void 0 ? null : path22.dirname(piExecutable), path22.dirname(process.execPath), process.env.SystemRoot ? path22.join(process.env.SystemRoot, "System32") : null] : [piExecutable === void 0 ? null : path22.dirname(piExecutable), path22.dirname(process.execPath), "/usr/local/bin", "/usr/bin", "/bin"];
|
|
4745
|
+
return [...new Set(entries.filter((entry) => entry !== null))].join(path22.delimiter);
|
|
4759
4746
|
}
|
|
4760
4747
|
function targetPaths(targetDir, engramBin, piExecutable) {
|
|
4761
|
-
const root =
|
|
4762
|
-
const codingAgentDir =
|
|
4763
|
-
const home =
|
|
4764
|
-
const temporary =
|
|
4748
|
+
const root = path22.resolve(targetDir);
|
|
4749
|
+
const codingAgentDir = path22.join(root, "pi-agent");
|
|
4750
|
+
const home = path22.join(root, "home");
|
|
4751
|
+
const temporary = path22.join(root, "tmp");
|
|
4765
4752
|
return {
|
|
4766
4753
|
codingAgentDir,
|
|
4767
|
-
receiptPath:
|
|
4768
|
-
packageRunner:
|
|
4754
|
+
receiptPath: path22.join(root, "state", "pi-receipt.json"),
|
|
4755
|
+
packageRunner: path22.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
|
|
4769
4756
|
environment: {
|
|
4770
4757
|
HOME: home,
|
|
4771
4758
|
USERPROFILE: home,
|
|
4772
|
-
APPDATA:
|
|
4773
|
-
LOCALAPPDATA:
|
|
4774
|
-
XDG_CONFIG_HOME:
|
|
4775
|
-
XDG_DATA_HOME:
|
|
4776
|
-
XDG_CACHE_HOME:
|
|
4759
|
+
APPDATA: path22.join(root, "appdata"),
|
|
4760
|
+
LOCALAPPDATA: path22.join(root, "localappdata"),
|
|
4761
|
+
XDG_CONFIG_HOME: path22.join(root, "xdg-config"),
|
|
4762
|
+
XDG_DATA_HOME: path22.join(root, "xdg-data"),
|
|
4763
|
+
XDG_CACHE_HOME: path22.join(root, "xdg-cache"),
|
|
4777
4764
|
TEMP: temporary,
|
|
4778
4765
|
TMP: temporary,
|
|
4779
4766
|
TMPDIR: temporary,
|
|
4780
|
-
npm_config_cache:
|
|
4767
|
+
npm_config_cache: path22.join(root, "npm-cache"),
|
|
4781
4768
|
NPM_CONFIG_IGNORE_SCRIPTS: "true",
|
|
4782
4769
|
NPM_CONFIG_UPDATE_NOTIFIER: "false",
|
|
4783
4770
|
PI_CODING_AGENT_DIR: codingAgentDir,
|
|
@@ -4788,15 +4775,15 @@ function targetPaths(targetDir, engramBin, piExecutable) {
|
|
|
4788
4775
|
}
|
|
4789
4776
|
function userPaths(engramBin, piExecutable) {
|
|
4790
4777
|
const home = os4.homedir();
|
|
4791
|
-
const codingAgentDir = process.env.PI_CODING_AGENT_DIR ??
|
|
4778
|
+
const codingAgentDir = process.env.PI_CODING_AGENT_DIR ?? path22.join(home, ".pi", "agent");
|
|
4792
4779
|
return {
|
|
4793
4780
|
codingAgentDir,
|
|
4794
|
-
receiptPath:
|
|
4795
|
-
packageRunner:
|
|
4781
|
+
receiptPath: path22.join(dataDir(), "pi-receipt.json"),
|
|
4782
|
+
packageRunner: path22.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
|
|
4796
4783
|
environment: {
|
|
4797
4784
|
HOME: home,
|
|
4798
|
-
XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME ??
|
|
4799
|
-
XDG_CACHE_HOME: process.env.XDG_CACHE_HOME ??
|
|
4785
|
+
XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME ?? path22.join(home, ".config"),
|
|
4786
|
+
XDG_CACHE_HOME: process.env.XDG_CACHE_HOME ?? path22.join(home, ".cache"),
|
|
4800
4787
|
TMPDIR: os4.tmpdir(),
|
|
4801
4788
|
NPM_CONFIG_IGNORE_SCRIPTS: "true",
|
|
4802
4789
|
NPM_CONFIG_UPDATE_NOTIFIER: "false",
|
|
@@ -4824,7 +4811,7 @@ function runPiRuntime(input, deps) {
|
|
|
4824
4811
|
return { kind: "blocked", reason: "tarball-integrity" };
|
|
4825
4812
|
}
|
|
4826
4813
|
const paths = input.targetDir === void 0 ? userPaths(input.engramBin, input.detected.executable) : targetPaths(input.targetDir, input.engramBin, input.detected.executable);
|
|
4827
|
-
const settingsJson = deps.readSettings(
|
|
4814
|
+
const settingsJson = deps.readSettings(path22.join(paths.codingAgentDir, "settings.json"));
|
|
4828
4815
|
const receiptJson = deps.readReceipt(paths.receiptPath);
|
|
4829
4816
|
const lifecycleInput = {
|
|
4830
4817
|
candidate: PI_RUNTIME_CANDIDATE,
|
|
@@ -4888,14 +4875,14 @@ function readJsonFile(file) {
|
|
|
4888
4875
|
function packageVersionFromExecutable(executable) {
|
|
4889
4876
|
let current;
|
|
4890
4877
|
try {
|
|
4891
|
-
current =
|
|
4878
|
+
current = path22.dirname(fs18.realpathSync(executable));
|
|
4892
4879
|
} catch {
|
|
4893
4880
|
return null;
|
|
4894
4881
|
}
|
|
4895
4882
|
for (let depth = 0; depth < 8; depth++) {
|
|
4896
4883
|
const manifests = [
|
|
4897
|
-
|
|
4898
|
-
|
|
4884
|
+
path22.join(current, "package.json"),
|
|
4885
|
+
path22.join(current, "node_modules", "@earendil-works", "pi-coding-agent", "package.json")
|
|
4899
4886
|
];
|
|
4900
4887
|
for (const manifest of manifests) {
|
|
4901
4888
|
try {
|
|
@@ -4906,7 +4893,7 @@ function packageVersionFromExecutable(executable) {
|
|
|
4906
4893
|
} catch {
|
|
4907
4894
|
}
|
|
4908
4895
|
}
|
|
4909
|
-
const parent =
|
|
4896
|
+
const parent = path22.dirname(current);
|
|
4910
4897
|
if (parent === current) break;
|
|
4911
4898
|
current = parent;
|
|
4912
4899
|
}
|
|
@@ -4921,16 +4908,16 @@ function detectPiRuntime() {
|
|
|
4921
4908
|
installed: executable !== null,
|
|
4922
4909
|
executable,
|
|
4923
4910
|
version: executable === null ? null : packageVersionFromExecutable(executable),
|
|
4924
|
-
codingAgentDir: process.env.PI_CODING_AGENT_DIR ??
|
|
4911
|
+
codingAgentDir: process.env.PI_CODING_AGENT_DIR ?? path22.join(home, ".pi", "agent")
|
|
4925
4912
|
};
|
|
4926
4913
|
}
|
|
4927
4914
|
function hasManagedPiRuntime(targetDir) {
|
|
4928
|
-
const receipt = targetDir === void 0 ?
|
|
4915
|
+
const receipt = targetDir === void 0 ? path22.join(dataDir(), "pi-receipt.json") : path22.join(path22.resolve(targetDir), "state", "pi-receipt.json");
|
|
4929
4916
|
return fs18.statSync(receipt, { throwIfNoEntry: false })?.isFile() === true;
|
|
4930
4917
|
}
|
|
4931
4918
|
function resolvePiEngramBin(targetDir) {
|
|
4932
4919
|
if (targetDir === void 0) return detectEngram();
|
|
4933
|
-
const candidate =
|
|
4920
|
+
const candidate = path22.join(path22.resolve(targetDir), "bin", process.platform === "win32" ? "engram.exe" : "engram");
|
|
4934
4921
|
return fs18.statSync(candidate, { throwIfNoEntry: false })?.isFile() ? candidate : null;
|
|
4935
4922
|
}
|
|
4936
4923
|
function readOptional(file, fallback) {
|
|
@@ -4970,7 +4957,7 @@ async function acquirePiTarball(destination) {
|
|
|
4970
4957
|
}
|
|
4971
4958
|
fs18.rmSync(destination, { force: true });
|
|
4972
4959
|
}
|
|
4973
|
-
fs18.mkdirSync(
|
|
4960
|
+
fs18.mkdirSync(path22.dirname(destination), { recursive: true });
|
|
4974
4961
|
const partial = `${destination}.partial-${process.pid}`;
|
|
4975
4962
|
const response = await fetch(`https://registry.npmjs.org/jorgex-pi/-/jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`, {
|
|
4976
4963
|
redirect: "error",
|
|
@@ -5013,6 +5000,21 @@ function runProcess(invocation) {
|
|
|
5013
5000
|
stderr: result.stderr ?? (result.error?.message ?? "")
|
|
5014
5001
|
};
|
|
5015
5002
|
}
|
|
5003
|
+
function setupPiFailedRemedy(setup) {
|
|
5004
|
+
const detail = setup.reason ?? setup.stderr ?? "setup oficial Pi fall\xF3";
|
|
5005
|
+
const recovery = setup.recovery ?? "none";
|
|
5006
|
+
const backupId = setup.backupId ?? null;
|
|
5007
|
+
if (recovery === "complete") {
|
|
5008
|
+
return backupId !== null ? `${detail}. Se restaur\xF3 el backup ${backupId}; Pi no qued\xF3 activado.` : `${detail}. Se restaur\xF3 el backup previo; Pi no qued\xF3 activado.`;
|
|
5009
|
+
}
|
|
5010
|
+
if (recovery === "incomplete") {
|
|
5011
|
+
const id2 = backupId !== null ? ` (backup ${backupId})` : " (sin backup v\xE1lido)";
|
|
5012
|
+
const cause = setup.restoreError !== void 0 ? ` ${setup.restoreError}.` : "";
|
|
5013
|
+
return `${detail}. Recuperaci\xF3n incompleta${id2};${cause} revisa manualmente el estado y corrige la causa antes de reintentar; Pi no qued\xF3 activado.`;
|
|
5014
|
+
}
|
|
5015
|
+
const id = backupId !== null ? ` (backup ${backupId})` : " (sin backup v\xE1lido)";
|
|
5016
|
+
return `${detail}. Sin recuperaci\xF3n autom\xE1tica${id}; revisa manualmente el estado y corrige la causa antes de reintentar; Pi no qued\xF3 activado.`;
|
|
5017
|
+
}
|
|
5016
5018
|
async function runPiRuntimeSystem(input) {
|
|
5017
5019
|
if (input.engramBin === null && input.operation !== "uninstall") return runPiRuntime(input, {
|
|
5018
5020
|
readSettings: () => {
|
|
@@ -5043,7 +5045,32 @@ async function runPiRuntimeSystem(input) {
|
|
|
5043
5045
|
remedy: "Instala Engram o configura un ENGRAM_BIN absoluto antes de reintentar."
|
|
5044
5046
|
};
|
|
5045
5047
|
}
|
|
5046
|
-
|
|
5048
|
+
if (input.targetDir === void 0) {
|
|
5049
|
+
const { runOfficialSetupIfNeeded: runOfficialSetupIfNeeded2 } = await import("./official-engram-setup-4WIXM4EF.js");
|
|
5050
|
+
const setup = await runOfficialSetupIfNeeded2("pi", {
|
|
5051
|
+
command: "install",
|
|
5052
|
+
dryRun: false,
|
|
5053
|
+
targetDir: void 0,
|
|
5054
|
+
engramBin: input.engramBin,
|
|
5055
|
+
configDir: paths.codingAgentDir,
|
|
5056
|
+
homeDir: os4.homedir()
|
|
5057
|
+
});
|
|
5058
|
+
if (!setup.ran || !setup.ok) {
|
|
5059
|
+
if (!setup.ran) {
|
|
5060
|
+
return {
|
|
5061
|
+
kind: "blocked",
|
|
5062
|
+
reason: "setup-pi-failed",
|
|
5063
|
+
remedy: "setup oficial Pi omitido en install real. Sin recuperaci\xF3n autom\xE1tica (sin backup v\xE1lido); revisa manualmente el estado y corrige la causa antes de reintentar; Pi no qued\xF3 activado."
|
|
5064
|
+
};
|
|
5065
|
+
}
|
|
5066
|
+
return {
|
|
5067
|
+
kind: "blocked",
|
|
5068
|
+
reason: "setup-pi-failed",
|
|
5069
|
+
remedy: setupPiFailedRemedy(setup)
|
|
5070
|
+
};
|
|
5071
|
+
}
|
|
5072
|
+
}
|
|
5073
|
+
const destination = input.targetDir === void 0 ? path22.join(dataDir(), "packages", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`) : path22.join(path22.resolve(input.targetDir), "downloads", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`);
|
|
5047
5074
|
let artifact;
|
|
5048
5075
|
try {
|
|
5049
5076
|
artifact = await acquirePiTarball(destination);
|
|
@@ -5064,18 +5091,18 @@ async function runPiRuntimeSystem(input) {
|
|
|
5064
5091
|
}, {
|
|
5065
5092
|
download: () => artifact,
|
|
5066
5093
|
backupSettings: () => createBackup(
|
|
5067
|
-
[
|
|
5094
|
+
[path22.join(paths.codingAgentDir, "settings.json")],
|
|
5068
5095
|
"pi-package-install",
|
|
5069
|
-
input.targetDir === void 0 ? void 0 :
|
|
5096
|
+
input.targetDir === void 0 ? void 0 : path22.join(path22.resolve(input.targetDir), "backups")
|
|
5070
5097
|
),
|
|
5071
5098
|
run: runProcess,
|
|
5072
|
-
readSettings: () => readOptional(
|
|
5073
|
-
rewriteSettings: (content) => writeText(
|
|
5099
|
+
readSettings: () => readOptional(path22.join(paths.codingAgentDir, "settings.json"), '{"packages":[]}'),
|
|
5100
|
+
rewriteSettings: (content) => writeText(path22.join(paths.codingAgentDir, "settings.json"), `${content}
|
|
5074
5101
|
`),
|
|
5075
5102
|
writeReceiptAtomic: (content) => writeText(paths.receiptPath, content)
|
|
5076
5103
|
});
|
|
5077
5104
|
}
|
|
5078
|
-
const packageRoot =
|
|
5105
|
+
const packageRoot = path22.dirname(path22.dirname(paths.packageRunner));
|
|
5079
5106
|
const writeReceipt = (receipt) => {
|
|
5080
5107
|
writeText(paths.receiptPath, `${JSON.stringify(receipt, null, 2)}
|
|
5081
5108
|
`);
|
|
@@ -5093,9 +5120,9 @@ async function runPiRuntimeSystem(input) {
|
|
|
5093
5120
|
value,
|
|
5094
5121
|
{
|
|
5095
5122
|
backupSettings: () => createBackup(
|
|
5096
|
-
[
|
|
5123
|
+
[path22.join(paths.codingAgentDir, "settings.json")],
|
|
5097
5124
|
"pi-package-uninstall",
|
|
5098
|
-
input.targetDir === void 0 ? void 0 :
|
|
5125
|
+
input.targetDir === void 0 ? void 0 : path22.join(path22.resolve(input.targetDir), "backups")
|
|
5099
5126
|
),
|
|
5100
5127
|
run: runProcess,
|
|
5101
5128
|
isPackageAbsent: () => !fs18.existsSync(packageRoot),
|
|
@@ -5274,7 +5301,7 @@ async function runManagedPiSystem(input) {
|
|
|
5274
5301
|
}
|
|
5275
5302
|
|
|
5276
5303
|
// src/lib/quality-runner.ts
|
|
5277
|
-
import
|
|
5304
|
+
import path23 from "path";
|
|
5278
5305
|
import { spawn, spawnSync as spawnSync2 } from "child_process";
|
|
5279
5306
|
function normalizeLimit(value, name) {
|
|
5280
5307
|
if (value === void 0) return void 0;
|
|
@@ -5406,7 +5433,7 @@ function killProcessTree(child) {
|
|
|
5406
5433
|
if (pid === void 0) return;
|
|
5407
5434
|
if (process.platform === "win32") {
|
|
5408
5435
|
const systemRoot = process.env.SystemRoot ?? process.env.WINDIR ?? "C:\\Windows";
|
|
5409
|
-
const taskkill =
|
|
5436
|
+
const taskkill = path23.join(systemRoot, "System32", "taskkill.exe");
|
|
5410
5437
|
const result = spawnSync2(taskkill, ["/pid", String(pid), "/t", "/f"], {
|
|
5411
5438
|
shell: false,
|
|
5412
5439
|
stdio: "ignore",
|
|
@@ -5654,107 +5681,172 @@ async function runQualityPlan(input) {
|
|
|
5654
5681
|
// src/lib/engram-install.ts
|
|
5655
5682
|
import fs19 from "fs";
|
|
5656
5683
|
import os5 from "os";
|
|
5657
|
-
import
|
|
5684
|
+
import path24 from "path";
|
|
5658
5685
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
5659
5686
|
import { createHash as createHash3 } from "crypto";
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
var
|
|
5663
|
-
assets: {
|
|
5664
|
-
darwin_arm64: {
|
|
5665
|
-
name: "engram_2.0.0_darwin_arm64.tar.gz",
|
|
5666
|
-
sha256: "793c695153e0e76144dc53b006a7039771bd24115385856578bc4d6506d46d7f",
|
|
5667
|
-
size: 7210321
|
|
5668
|
-
},
|
|
5669
|
-
darwin_x64: {
|
|
5670
|
-
name: "engram_2.0.0_darwin_amd64.tar.gz",
|
|
5671
|
-
sha256: "f1dcc343ac241c5001617113279b49dec698005550b538250ccf21655fc728ac",
|
|
5672
|
-
size: 7678428
|
|
5673
|
-
},
|
|
5674
|
-
linux_arm64: {
|
|
5675
|
-
name: "engram_2.0.0_linux_arm64.tar.gz",
|
|
5676
|
-
sha256: "a942e73ab424faaa6e2785d1563e0d9d7f20739944dae0c50071223301d44333",
|
|
5677
|
-
size: 6926960
|
|
5678
|
-
},
|
|
5679
|
-
linux_x64: {
|
|
5680
|
-
name: "engram_2.0.0_linux_amd64.tar.gz",
|
|
5681
|
-
sha256: "23be1c2ce9739c455097ff864736213717b925b3e8821a988dfc619685a5abd5",
|
|
5682
|
-
size: 7544949
|
|
5683
|
-
},
|
|
5684
|
-
windows_arm64: {
|
|
5685
|
-
name: "engram_2.0.0_windows_arm64.zip",
|
|
5686
|
-
sha256: "e4d9074ebf8a839573f62d7bd2719421fc428ab25a9affe82ca4b2d5555183d1",
|
|
5687
|
-
size: 6980876
|
|
5688
|
-
},
|
|
5689
|
-
windows_x64: {
|
|
5690
|
-
name: "engram_2.0.0_windows_amd64.zip",
|
|
5691
|
-
sha256: "fba31e7221702f213954c2081aa83b38833e0e9b824a981f976f8cee2c74548c",
|
|
5692
|
-
size: 7738896
|
|
5693
|
-
}
|
|
5694
|
-
},
|
|
5695
|
-
version: "2.0.0"
|
|
5696
|
-
};
|
|
5697
|
-
|
|
5698
|
-
// src/lib/engram-install.ts
|
|
5687
|
+
var MAX_ENGRAM_ARCHIVE_BYTES = 64 * 1024 * 1024;
|
|
5688
|
+
var STABLE_TAG = /^v\d+\.\d+\.\d+$/;
|
|
5689
|
+
var DIGEST_RE = /^sha256:[0-9a-f]{64}$/;
|
|
5699
5690
|
async function installMissingEngram(options = {}) {
|
|
5700
5691
|
const platform = options.platform ?? process.platform;
|
|
5701
5692
|
const homeDir = options.homeDir ?? os5.homedir();
|
|
5702
|
-
if (!
|
|
5693
|
+
if (!path24.isAbsolute(homeDir)) return { ok: false, reason: "El directorio personal debe ser absoluto." };
|
|
5703
5694
|
const binaryName = platform === "win32" ? "engram.exe" : "engram";
|
|
5704
|
-
const bin =
|
|
5695
|
+
const bin = path24.join(homeDir, ".local", "bin", binaryName);
|
|
5705
5696
|
let staging;
|
|
5697
|
+
let outcome;
|
|
5706
5698
|
try {
|
|
5707
5699
|
const existing = fs19.lstatSync(bin, { throwIfNoEntry: false });
|
|
5708
5700
|
if (existing) {
|
|
5709
5701
|
if (!fs19.statSync(bin).isFile()) throw new Error(`El destino Engram no es un archivo: ${bin}`);
|
|
5710
5702
|
fs19.accessSync(bin, platform === "win32" ? fs19.constants.F_OK : fs19.constants.X_OK);
|
|
5711
|
-
|
|
5712
|
-
}
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
const chunks = [];
|
|
5723
|
-
let size = 0;
|
|
5724
|
-
try {
|
|
5725
|
-
while (true) {
|
|
5726
|
-
const chunk = await reader.read();
|
|
5727
|
-
if (chunk.done) break;
|
|
5728
|
-
size += chunk.value.byteLength;
|
|
5729
|
-
if (size > asset.size) throw new Error("El tama\xF1o descargado de Engram supera el aprobado.");
|
|
5730
|
-
chunks.push(chunk.value);
|
|
5703
|
+
outcome = { ok: true, bin };
|
|
5704
|
+
} else {
|
|
5705
|
+
const fetchFn = options.fetch ?? globalThis.fetch;
|
|
5706
|
+
let releaseRes;
|
|
5707
|
+
try {
|
|
5708
|
+
releaseRes = await fetchLatestGithubRelease(ENGRAM_REPO, fetchFn);
|
|
5709
|
+
} catch (error) {
|
|
5710
|
+
const cause = error instanceof Error ? error.message : String(error);
|
|
5711
|
+
throw new Error(
|
|
5712
|
+
`Engram: error de red al consultar los metadatos del release (latest): ${cause}. Reintenta la instalaci\xF3n.`
|
|
5713
|
+
);
|
|
5731
5714
|
}
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5715
|
+
if (!releaseRes.ok) {
|
|
5716
|
+
if (releaseRes.status === 429) {
|
|
5717
|
+
throw new Error(
|
|
5718
|
+
"Engram: metadatos del release no disponibles (HTTP 429 rate limit en la consulta latest/metadatos). Espera y reintenta; define GH_TOKEN o inicia sesi\xF3n con gh auth login para elevar el l\xEDmite."
|
|
5719
|
+
);
|
|
5720
|
+
}
|
|
5721
|
+
if (releaseRes.status === 403) {
|
|
5722
|
+
throw new Error(
|
|
5723
|
+
"Engram: metadatos del release no disponibles (HTTP 403 en la consulta latest/metadatos). Posible rate limit o falta de autenticaci\xF3n; define GH_TOKEN o usa gh auth login y reintenta."
|
|
5724
|
+
);
|
|
5725
|
+
}
|
|
5726
|
+
throw new Error(
|
|
5727
|
+
`Engram: metadatos del release no disponibles (HTTP ${releaseRes.status} en la consulta latest).`
|
|
5728
|
+
);
|
|
5729
|
+
}
|
|
5730
|
+
let raw;
|
|
5731
|
+
try {
|
|
5732
|
+
raw = await releaseRes.json();
|
|
5733
|
+
} catch (error) {
|
|
5734
|
+
if (error instanceof SyntaxError) {
|
|
5735
|
+
throw new Error("Engram: metadatos del release inv\xE1lidos: JSON no parseable.");
|
|
5736
|
+
}
|
|
5737
|
+
const cause = error instanceof Error ? error.message : String(error);
|
|
5738
|
+
const hint = error instanceof Error && error.name === "AbortError" ? "interrumpida (red/cancelada)" : "de red";
|
|
5739
|
+
throw new Error(
|
|
5740
|
+
`Engram: lectura de metadatos del release ${hint}: ${cause}. Reintenta la instalaci\xF3n.`
|
|
5741
|
+
);
|
|
5742
|
+
}
|
|
5743
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
5744
|
+
throw new Error("Engram: metadatos del release inv\xE1lidos: se esperaba un objeto JSON.");
|
|
5745
|
+
}
|
|
5746
|
+
const releaseJson = raw;
|
|
5747
|
+
const tag = releaseJson.tag_name;
|
|
5748
|
+
if (typeof tag !== "string" || !STABLE_TAG.test(tag)) {
|
|
5749
|
+
throw new Error(`Engram: tag del release inv\xE1lido: ${String(tag)}.`);
|
|
5750
|
+
}
|
|
5751
|
+
const version = tag.slice(1);
|
|
5752
|
+
const expectedName = expectedEngramAssetName(version, platform, options.arch ?? process.arch);
|
|
5753
|
+
if (expectedName === null) {
|
|
5754
|
+
throw new Error(`Engram no dispone de un binario aprobado para ${platform}_${options.arch ?? process.arch}.`);
|
|
5755
|
+
}
|
|
5756
|
+
if (!Array.isArray(releaseJson.assets)) throw new Error("Engram: metadatos del release sin assets.");
|
|
5757
|
+
const matches = releaseJson.assets.filter((asset2) => asset2?.name === expectedName);
|
|
5758
|
+
if (matches.length !== 1) throw new Error("Engram: asset exacto ausente o ambiguo en el release.");
|
|
5759
|
+
const asset = matches[0];
|
|
5760
|
+
if (asset.state !== "uploaded") throw new Error("Engram: asset no publicado.");
|
|
5761
|
+
if (!Number.isInteger(asset.size) || asset.size <= 0) {
|
|
5762
|
+
throw new Error("Engram: tama\xF1o del asset inv\xE1lido.");
|
|
5763
|
+
}
|
|
5764
|
+
const expectedUrl = `${ENGRAM_DOWNLOAD_PREFIX}${tag}/${expectedName}`;
|
|
5765
|
+
if (typeof asset.browser_download_url !== "string" || asset.browser_download_url !== expectedUrl) {
|
|
5766
|
+
throw new Error("Engram: URL del asset no oficial.");
|
|
5767
|
+
}
|
|
5768
|
+
if (typeof asset.digest !== "string" || !DIGEST_RE.test(asset.digest)) {
|
|
5769
|
+
throw new Error("Engram: digest del asset inv\xE1lido.");
|
|
5770
|
+
}
|
|
5771
|
+
const approvedSize = asset.size;
|
|
5772
|
+
if (approvedSize > MAX_ENGRAM_ARCHIVE_BYTES) {
|
|
5773
|
+
throw new Error(
|
|
5774
|
+
`Engram: tama\xF1o del asset aprobado (${approvedSize} bytes) excede el l\xEDmite de 64 MiB (${MAX_ENGRAM_ARCHIVE_BYTES} bytes).`
|
|
5775
|
+
);
|
|
5776
|
+
}
|
|
5777
|
+
const approvedSha = asset.digest.slice("sha256:".length);
|
|
5778
|
+
const response = await fetchFn(asset.browser_download_url, {
|
|
5779
|
+
signal: AbortSignal.timeout(12e4)
|
|
5780
|
+
});
|
|
5781
|
+
if (!response.ok || !response.body) throw new Error(`Descarga Engram fallida: HTTP ${response.status}.`);
|
|
5782
|
+
const reader = response.body.getReader();
|
|
5783
|
+
const chunks = [];
|
|
5784
|
+
let size = 0;
|
|
5785
|
+
let readError;
|
|
5786
|
+
try {
|
|
5787
|
+
try {
|
|
5788
|
+
while (true) {
|
|
5789
|
+
const chunk = await reader.read();
|
|
5790
|
+
if (chunk.done) break;
|
|
5791
|
+
size += chunk.value.byteLength;
|
|
5792
|
+
if (size > approvedSize) throw new Error("El tama\xF1o descargado de Engram supera el aprobado.");
|
|
5793
|
+
chunks.push(chunk.value);
|
|
5794
|
+
}
|
|
5795
|
+
} catch (error) {
|
|
5796
|
+
readError = error;
|
|
5797
|
+
throw error;
|
|
5798
|
+
}
|
|
5799
|
+
} finally {
|
|
5800
|
+
try {
|
|
5801
|
+
await reader.cancel();
|
|
5802
|
+
} catch (cancelError) {
|
|
5803
|
+
const cancelMsg = cancelError instanceof Error ? cancelError.message : String(cancelError);
|
|
5804
|
+
if (readError !== void 0) {
|
|
5805
|
+
const primaryMsg = readError instanceof Error ? readError.message : String(readError);
|
|
5806
|
+
throw new Error(`${primaryMsg} (adem\xE1s fall\xF3 la cancelaci\xF3n del stream: ${cancelMsg})`);
|
|
5807
|
+
}
|
|
5808
|
+
throw cancelError;
|
|
5809
|
+
}
|
|
5810
|
+
}
|
|
5811
|
+
const archive = Buffer.concat(chunks);
|
|
5812
|
+
if (size !== approvedSize || createHash3("sha256").update(archive).digest("hex") !== approvedSha) {
|
|
5813
|
+
throw new Error("Engram: tama\xF1o o hash SHA-256 no coincide con el artefacto aprobado.");
|
|
5814
|
+
}
|
|
5815
|
+
fs19.mkdirSync(path24.dirname(bin), { recursive: true });
|
|
5816
|
+
staging = fs19.mkdtempSync(path24.join(path24.dirname(bin), ".engram-install-"));
|
|
5817
|
+
const archivePath = path24.join(staging, expectedName);
|
|
5818
|
+
fs19.writeFileSync(archivePath, archive, { flag: "wx", mode: 384 });
|
|
5819
|
+
const binary = execFileSync6(resolveTarBin(), ["-xOf", archivePath, binaryName], {
|
|
5820
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
5821
|
+
timeout: 3e4,
|
|
5822
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
5823
|
+
});
|
|
5824
|
+
if (binary.length === 0) throw new Error("El artefacto Engram no contiene un binario v\xE1lido.");
|
|
5825
|
+
const stagedBin = path24.join(staging, binaryName);
|
|
5826
|
+
fs19.writeFileSync(stagedBin, binary, { flag: "wx", mode: 493 });
|
|
5827
|
+
fs19.linkSync(stagedBin, bin);
|
|
5828
|
+
outcome = { ok: true, bin };
|
|
5829
|
+
}
|
|
5753
5830
|
} catch (error) {
|
|
5754
|
-
|
|
5755
|
-
} finally {
|
|
5756
|
-
if (staging) fs19.rmSync(staging, { recursive: true, force: true });
|
|
5831
|
+
outcome = { ok: false, reason: error instanceof Error ? error.message : String(error) };
|
|
5757
5832
|
}
|
|
5833
|
+
if (staging !== void 0) {
|
|
5834
|
+
try {
|
|
5835
|
+
fs19.rmSync(staging, { recursive: true, force: true });
|
|
5836
|
+
} catch (cleanupError) {
|
|
5837
|
+
const cleanupMsg = cleanupError instanceof Error ? cleanupError.message : String(cleanupError);
|
|
5838
|
+
if (outcome !== void 0 && outcome.ok) {
|
|
5839
|
+
outcome = {
|
|
5840
|
+
ok: true,
|
|
5841
|
+
bin: outcome.bin,
|
|
5842
|
+
warning: `Limpieza del directorio temporal ${staging} fallida tras instalar Engram: ${cleanupMsg}. El binario qued\xF3 instalado en ${outcome.bin}.`
|
|
5843
|
+
};
|
|
5844
|
+
} else if (outcome !== void 0 && !outcome.ok) {
|
|
5845
|
+
outcome = { ok: false, reason: `${outcome.reason} (adem\xE1s fall\xF3 la limpieza temporal: ${cleanupMsg})` };
|
|
5846
|
+
}
|
|
5847
|
+
}
|
|
5848
|
+
}
|
|
5849
|
+
return outcome ?? { ok: false, reason: "Engram: instalaci\xF3n finaliz\xF3 sin resultado verificable." };
|
|
5758
5850
|
}
|
|
5759
5851
|
|
|
5760
5852
|
// src/cli.ts
|
|
@@ -6088,7 +6180,10 @@ async function resolveHostEngramForInstall(flags) {
|
|
|
6088
6180
|
}
|
|
6089
6181
|
try {
|
|
6090
6182
|
const result = await installMissingEngram();
|
|
6091
|
-
if (result.ok)
|
|
6183
|
+
if (result.ok) {
|
|
6184
|
+
if (result.warning) p6.log.warn(result.warning);
|
|
6185
|
+
return { ok: true, bin: result.bin };
|
|
6186
|
+
}
|
|
6092
6187
|
return { ok: false, message: `Engram: ${result.reason}` };
|
|
6093
6188
|
} catch (error) {
|
|
6094
6189
|
return { ok: false, message: `Engram: ${error instanceof Error ? error.message : String(error)}` };
|
|
@@ -6143,7 +6238,11 @@ async function runSelectedPi(options) {
|
|
|
6143
6238
|
const answer = await p6.confirm({ message, initialValue });
|
|
6144
6239
|
return !p6.isCancel(answer) && answer;
|
|
6145
6240
|
},
|
|
6146
|
-
|
|
6241
|
+
installShared: async () => {
|
|
6242
|
+
const result2 = await installMissingEngram();
|
|
6243
|
+
if (result2.ok && result2.warning) p6.log.warn(result2.warning);
|
|
6244
|
+
return result2;
|
|
6245
|
+
}
|
|
6147
6246
|
});
|
|
6148
6247
|
if (requirement.kind !== "existing") {
|
|
6149
6248
|
console.error(requirement.kind === "offer" ? "Pi: instalaci\xF3n cancelada; Engram sigue siendo obligatorio." : `Pi: ${requirement.reason}. ${requirement.remedy}`);
|