larkin 0.4.17 → 0.4.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/runtime/builtin-pi-assets.cjs +117 -0
- package/dist/runtime/builtin-pi-assets.mjs +72 -0
- package/dist/runtime/pi-model-catalog.cjs +16 -2
- package/dist/runtime/pi-model-catalog.mjs +16 -2
- package/dist/runtime/pi-profile-migration.cjs +113 -13
- package/dist/runtime/pi-profile-migration.mjs +113 -13
- package/dist/runtime/runtime-adapters.mjs +16 -10
- package/dist/runtime/runtime-readiness.cjs +9 -2
- package/dist/runtime/runtime-readiness.mjs +9 -2
- package/dist/setup/setup-bind.mjs +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.prepareBuiltinPiPackageAssets = prepareBuiltinPiPackageAssets;
|
|
7
|
+
exports.piChildDistribution = piChildDistribution;
|
|
8
|
+
exports.piChildDistributionFromOverrides = piChildDistributionFromOverrides;
|
|
9
|
+
exports.resolveExternalPiPackageDir = resolveExternalPiPackageDir;
|
|
10
|
+
exports.isLarkinBuiltinPiPackageDir = isLarkinBuiltinPiPackageDir;
|
|
11
|
+
exports.catalogPiChildDistribution = catalogPiChildDistribution;
|
|
12
|
+
exports.applyPiPackageDirForChild = applyPiPackageDirForChild;
|
|
13
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
14
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
15
|
+
function privateDirectory(directory) {
|
|
16
|
+
node_fs_1.default.mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
17
|
+
const stat = node_fs_1.default.lstatSync(directory);
|
|
18
|
+
if (!stat.isDirectory() || stat.isSymbolicLink())
|
|
19
|
+
throw new Error(`unsafe bundled Pi asset directory: ${directory}`);
|
|
20
|
+
node_fs_1.default.chmodSync(directory, 0o700);
|
|
21
|
+
}
|
|
22
|
+
function writeEmbeddedFile(file, contents) {
|
|
23
|
+
const temporary = `${file}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
|
|
24
|
+
node_fs_1.default.writeFileSync(temporary, contents, { mode: 0o600, flag: "wx" });
|
|
25
|
+
node_fs_1.default.renameSync(temporary, file);
|
|
26
|
+
node_fs_1.default.chmodSync(file, 0o600);
|
|
27
|
+
}
|
|
28
|
+
/** Materialize upstream runtime assets that Bun cannot serve through ordinary fs paths. */
|
|
29
|
+
function prepareBuiltinPiPackageAssets() {
|
|
30
|
+
const assets = globalThis.__LARKIN_EMBEDDED_BUILTIN_PI_ASSETS__;
|
|
31
|
+
if (!assets)
|
|
32
|
+
return;
|
|
33
|
+
const agentDir = process.env.PI_CODING_AGENT_DIR;
|
|
34
|
+
if (!agentDir)
|
|
35
|
+
throw new Error("bundled Pi requires PI_CODING_AGENT_DIR");
|
|
36
|
+
privateDirectory(agentDir);
|
|
37
|
+
const packageDir = node_path_1.default.join(agentDir, ".larkin-official-pi-package");
|
|
38
|
+
const themeDir = node_path_1.default.join(packageDir, "theme");
|
|
39
|
+
privateDirectory(packageDir);
|
|
40
|
+
privateDirectory(themeDir);
|
|
41
|
+
writeEmbeddedFile(node_path_1.default.join(packageDir, "package.json"), assets.packageJson);
|
|
42
|
+
writeEmbeddedFile(node_path_1.default.join(themeDir, "dark.json"), assets.darkTheme);
|
|
43
|
+
writeEmbeddedFile(node_path_1.default.join(themeDir, "light.json"), assets.lightTheme);
|
|
44
|
+
process.env.PI_PACKAGE_DIR = packageDir;
|
|
45
|
+
}
|
|
46
|
+
function piChildDistribution(env) {
|
|
47
|
+
return env?.LARKIN_PI_DISTRIBUTION === "builtin" ? "builtin" : "external";
|
|
48
|
+
}
|
|
49
|
+
/** Host process env may be builtin Pi; only explicit child overrides select bundled assets. */
|
|
50
|
+
function piChildDistributionFromOverrides(...overrides) {
|
|
51
|
+
for (let index = overrides.length - 1; index >= 0; index -= 1) {
|
|
52
|
+
const value = overrides[index]?.LARKIN_PI_DISTRIBUTION;
|
|
53
|
+
if (value === "builtin" || value === "external")
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
return "external";
|
|
57
|
+
}
|
|
58
|
+
function existingPiThemeFile(directory) {
|
|
59
|
+
const theme = node_path_1.default.join(directory, "dist", "modes", "interactive", "theme", "dark.json");
|
|
60
|
+
let fd;
|
|
61
|
+
try {
|
|
62
|
+
fd = node_fs_1.default.openSync(theme, node_fs_1.default.constants.O_RDONLY);
|
|
63
|
+
return node_fs_1.default.fstatSync(fd).isFile() ? theme : undefined;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
if (fd !== undefined) {
|
|
70
|
+
try {
|
|
71
|
+
node_fs_1.default.closeSync(fd);
|
|
72
|
+
}
|
|
73
|
+
catch { /* already closed or unreadable */ }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function resolveExternalPiPackageDir(directory) {
|
|
78
|
+
if (!directory?.trim())
|
|
79
|
+
return undefined;
|
|
80
|
+
try {
|
|
81
|
+
const resolved = node_fs_1.default.realpathSync(directory);
|
|
82
|
+
return existingPiThemeFile(resolved) ? resolved : undefined;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function isLarkinBuiltinPiPackageDir(directory) {
|
|
89
|
+
if (!directory)
|
|
90
|
+
return false;
|
|
91
|
+
try {
|
|
92
|
+
const resolved = node_fs_1.default.realpathSync(directory);
|
|
93
|
+
if (node_path_1.default.basename(resolved).toLowerCase() !== ".larkin-official-pi-package")
|
|
94
|
+
return false;
|
|
95
|
+
return !existingPiThemeFile(resolved);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return node_path_1.default.basename(directory).toLowerCase() === ".larkin-official-pi-package";
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function catalogPiChildDistribution(commandArgs) {
|
|
102
|
+
return commandArgs?.includes("pi-rpc") ? "builtin" : "external";
|
|
103
|
+
}
|
|
104
|
+
/** External/npm Pi resolves PI_PACKAGE_DIR as a Node package root and looks for dist/themes. */
|
|
105
|
+
function applyPiPackageDirForChild(env, distribution = piChildDistribution(env)) {
|
|
106
|
+
const options = typeof distribution === "string" ? { distribution } : distribution;
|
|
107
|
+
const resolved = options.distribution ?? piChildDistribution(env);
|
|
108
|
+
const next = { ...env };
|
|
109
|
+
if (resolved === "builtin")
|
|
110
|
+
return next;
|
|
111
|
+
const kept = resolveExternalPiPackageDir(options.explicitPackageDir) ?? resolveExternalPiPackageDir(next.PI_PACKAGE_DIR);
|
|
112
|
+
if (kept)
|
|
113
|
+
next.PI_PACKAGE_DIR = kept;
|
|
114
|
+
else
|
|
115
|
+
delete next.PI_PACKAGE_DIR;
|
|
116
|
+
return next;
|
|
117
|
+
}
|
|
@@ -31,3 +31,75 @@ export function prepareBuiltinPiPackageAssets() {
|
|
|
31
31
|
writeEmbeddedFile(path.join(themeDir, "light.json"), assets.lightTheme);
|
|
32
32
|
process.env.PI_PACKAGE_DIR = packageDir;
|
|
33
33
|
}
|
|
34
|
+
export function piChildDistribution(env) {
|
|
35
|
+
return env?.LARKIN_PI_DISTRIBUTION === "builtin" ? "builtin" : "external";
|
|
36
|
+
}
|
|
37
|
+
/** Host process env may be builtin Pi; only explicit child overrides select bundled assets. */
|
|
38
|
+
export function piChildDistributionFromOverrides(...overrides) {
|
|
39
|
+
for (let index = overrides.length - 1; index >= 0; index -= 1) {
|
|
40
|
+
const value = overrides[index]?.LARKIN_PI_DISTRIBUTION;
|
|
41
|
+
if (value === "builtin" || value === "external")
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
return "external";
|
|
45
|
+
}
|
|
46
|
+
function existingPiThemeFile(directory) {
|
|
47
|
+
const theme = path.join(directory, "dist", "modes", "interactive", "theme", "dark.json");
|
|
48
|
+
let fd;
|
|
49
|
+
try {
|
|
50
|
+
fd = fs.openSync(theme, fs.constants.O_RDONLY);
|
|
51
|
+
return fs.fstatSync(fd).isFile() ? theme : undefined;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
if (fd !== undefined) {
|
|
58
|
+
try {
|
|
59
|
+
fs.closeSync(fd);
|
|
60
|
+
}
|
|
61
|
+
catch { /* already closed or unreadable */ }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export function resolveExternalPiPackageDir(directory) {
|
|
66
|
+
if (!directory?.trim())
|
|
67
|
+
return undefined;
|
|
68
|
+
try {
|
|
69
|
+
const resolved = fs.realpathSync(directory);
|
|
70
|
+
return existingPiThemeFile(resolved) ? resolved : undefined;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export function isLarkinBuiltinPiPackageDir(directory) {
|
|
77
|
+
if (!directory)
|
|
78
|
+
return false;
|
|
79
|
+
try {
|
|
80
|
+
const resolved = fs.realpathSync(directory);
|
|
81
|
+
if (path.basename(resolved).toLowerCase() !== ".larkin-official-pi-package")
|
|
82
|
+
return false;
|
|
83
|
+
return !existingPiThemeFile(resolved);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return path.basename(directory).toLowerCase() === ".larkin-official-pi-package";
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export function catalogPiChildDistribution(commandArgs) {
|
|
90
|
+
return commandArgs?.includes("pi-rpc") ? "builtin" : "external";
|
|
91
|
+
}
|
|
92
|
+
/** External/npm Pi resolves PI_PACKAGE_DIR as a Node package root and looks for dist/themes. */
|
|
93
|
+
export function applyPiPackageDirForChild(env, distribution = piChildDistribution(env)) {
|
|
94
|
+
const options = typeof distribution === "string" ? { distribution } : distribution;
|
|
95
|
+
const resolved = options.distribution ?? piChildDistribution(env);
|
|
96
|
+
const next = { ...env };
|
|
97
|
+
if (resolved === "builtin")
|
|
98
|
+
return next;
|
|
99
|
+
const kept = resolveExternalPiPackageDir(options.explicitPackageDir) ?? resolveExternalPiPackageDir(next.PI_PACKAGE_DIR);
|
|
100
|
+
if (kept)
|
|
101
|
+
next.PI_PACKAGE_DIR = kept;
|
|
102
|
+
else
|
|
103
|
+
delete next.PI_PACKAGE_DIR;
|
|
104
|
+
return next;
|
|
105
|
+
}
|
|
@@ -41,6 +41,7 @@ exports.findExactPiModel = findExactPiModel;
|
|
|
41
41
|
exports.discoverPiModelCatalog = discoverPiModelCatalog;
|
|
42
42
|
const node_child_process_1 = require("node:child_process");
|
|
43
43
|
const path = __importStar(require("node:path"));
|
|
44
|
+
const builtin_pi_assets_js_1 = require("./builtin-pi-assets.cjs");
|
|
44
45
|
const pi_rpc_client_js_1 = require("./pi-rpc-client.cjs");
|
|
45
46
|
const runtime_readiness_js_1 = require("./runtime-readiness.cjs");
|
|
46
47
|
exports.PI_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
@@ -71,10 +72,23 @@ function findExactPiModel(reference, models) {
|
|
|
71
72
|
*/
|
|
72
73
|
const discoveryCache = new Map();
|
|
73
74
|
/** Discover only through Pi's structured RPC protocol; no table parsing or static fallback. */
|
|
75
|
+
function piCatalogChildEnv(options) {
|
|
76
|
+
const mergedEnv = {
|
|
77
|
+
...process.env,
|
|
78
|
+
...options.env,
|
|
79
|
+
...(options.agentDir ? { PI_CODING_AGENT_DIR: path.resolve(options.agentDir) } : {}),
|
|
80
|
+
NO_COLOR: "1",
|
|
81
|
+
};
|
|
82
|
+
return (0, builtin_pi_assets_js_1.applyPiPackageDirForChild)(mergedEnv, {
|
|
83
|
+
distribution: (0, builtin_pi_assets_js_1.catalogPiChildDistribution)(options.commandArgs),
|
|
84
|
+
explicitPackageDir: options.packageDir,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
74
87
|
async function discoverPiModelCatalog(options) {
|
|
75
88
|
if (!options.spawn) {
|
|
76
89
|
const command = options.command ?? options.env?.LARKIN_PI_COMMAND ?? process.env.LARKIN_PI_COMMAND ?? "pi";
|
|
77
|
-
const
|
|
90
|
+
const childEnv = piCatalogChildEnv(options);
|
|
91
|
+
const key = `${command}|${(options.commandArgs ?? []).join("\0")}|${childEnv.PI_CODING_AGENT_DIR ?? ""}|${options.agentDir ?? ""}|${childEnv.PI_PACKAGE_DIR ?? ""}|${options.packageDir ?? ""}`;
|
|
78
92
|
const cached = discoveryCache.get(key);
|
|
79
93
|
if (cached)
|
|
80
94
|
return cached;
|
|
@@ -93,7 +107,7 @@ async function discoverPiModelCatalogUncached(options) {
|
|
|
93
107
|
const command = options.command ?? options.env?.LARKIN_PI_COMMAND ?? process.env.LARKIN_PI_COMMAND ?? "pi";
|
|
94
108
|
const child = spawn(command, [...(options.commandArgs ?? []), "--mode", "rpc", "--no-session"], {
|
|
95
109
|
cwd: options.cwd,
|
|
96
|
-
env:
|
|
110
|
+
env: piCatalogChildEnv(options),
|
|
97
111
|
stdio: ["pipe", "pipe", "pipe"],
|
|
98
112
|
});
|
|
99
113
|
const client = new pi_rpc_client_js_1.PiRpcClient(child, { requestTimeoutMs: options.timeoutMs ?? 10_000 });
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawn as nodeSpawn } from "node:child_process";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
+
import { applyPiPackageDirForChild, catalogPiChildDistribution } from "./builtin-pi-assets.mjs";
|
|
3
4
|
import { PiRpcClient } from "./pi-rpc-client.mjs";
|
|
4
5
|
import { classifyRuntimePrerequisite, RuntimePrerequisiteError } from "./runtime-readiness.mjs";
|
|
5
6
|
export const PI_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
@@ -30,10 +31,23 @@ export function findExactPiModel(reference, models) {
|
|
|
30
31
|
*/
|
|
31
32
|
const discoveryCache = new Map();
|
|
32
33
|
/** Discover only through Pi's structured RPC protocol; no table parsing or static fallback. */
|
|
34
|
+
function piCatalogChildEnv(options) {
|
|
35
|
+
const mergedEnv = {
|
|
36
|
+
...process.env,
|
|
37
|
+
...options.env,
|
|
38
|
+
...(options.agentDir ? { PI_CODING_AGENT_DIR: path.resolve(options.agentDir) } : {}),
|
|
39
|
+
NO_COLOR: "1",
|
|
40
|
+
};
|
|
41
|
+
return applyPiPackageDirForChild(mergedEnv, {
|
|
42
|
+
distribution: catalogPiChildDistribution(options.commandArgs),
|
|
43
|
+
explicitPackageDir: options.packageDir,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
33
46
|
export async function discoverPiModelCatalog(options) {
|
|
34
47
|
if (!options.spawn) {
|
|
35
48
|
const command = options.command ?? options.env?.LARKIN_PI_COMMAND ?? process.env.LARKIN_PI_COMMAND ?? "pi";
|
|
36
|
-
const
|
|
49
|
+
const childEnv = piCatalogChildEnv(options);
|
|
50
|
+
const key = `${command}|${(options.commandArgs ?? []).join("\0")}|${childEnv.PI_CODING_AGENT_DIR ?? ""}|${options.agentDir ?? ""}|${childEnv.PI_PACKAGE_DIR ?? ""}|${options.packageDir ?? ""}`;
|
|
37
51
|
const cached = discoveryCache.get(key);
|
|
38
52
|
if (cached)
|
|
39
53
|
return cached;
|
|
@@ -52,7 +66,7 @@ async function discoverPiModelCatalogUncached(options) {
|
|
|
52
66
|
const command = options.command ?? options.env?.LARKIN_PI_COMMAND ?? process.env.LARKIN_PI_COMMAND ?? "pi";
|
|
53
67
|
const child = spawn(command, [...(options.commandArgs ?? []), "--mode", "rpc", "--no-session"], {
|
|
54
68
|
cwd: options.cwd,
|
|
55
|
-
env:
|
|
69
|
+
env: piCatalogChildEnv(options),
|
|
56
70
|
stdio: ["pipe", "pipe", "pipe"],
|
|
57
71
|
});
|
|
58
72
|
const client = new PiRpcClient(child, { requestTimeoutMs: options.timeoutMs ?? 10_000 });
|
|
@@ -49,6 +49,7 @@ const os = __importStar(require("node:os"));
|
|
|
49
49
|
const path = __importStar(require("node:path"));
|
|
50
50
|
const node_child_process_1 = require("node:child_process");
|
|
51
51
|
const process_inspect_cjs_1 = __importDefault(require("../platform/process-inspect.cjs"));
|
|
52
|
+
const builtin_pi_assets_js_1 = require("./builtin-pi-assets.cjs");
|
|
52
53
|
const runtime_readiness_js_1 = require("./runtime-readiness.cjs");
|
|
53
54
|
const pi_compaction_recovery_js_1 = require("./pi-compaction-recovery.cjs");
|
|
54
55
|
const pi_provider_config_js_1 = require("./pi-provider-config.cjs");
|
|
@@ -61,6 +62,60 @@ const EXECUTABLE_LIMIT = 256 * 1024 * 1024;
|
|
|
61
62
|
const SOURCE_MODES = new Set([0o600, 0o644, 0o640]);
|
|
62
63
|
const TARGET_LOCK_SUFFIX = ".larkin-pi-import.lock";
|
|
63
64
|
function hash(bytes) { return crypto.createHash("sha256").update(bytes).digest("hex"); }
|
|
65
|
+
function isPiOrLarkinEnvKey(key) {
|
|
66
|
+
return key === "PATH" || key.startsWith("PI_") || key.startsWith("LARKIN_");
|
|
67
|
+
}
|
|
68
|
+
function isolatePlannedReplayEnv(source) {
|
|
69
|
+
const next = {};
|
|
70
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
71
|
+
if (isPiOrLarkinEnvKey(key))
|
|
72
|
+
continue;
|
|
73
|
+
next[key] = value;
|
|
74
|
+
}
|
|
75
|
+
if (source.PATH)
|
|
76
|
+
next.PATH = source.PATH;
|
|
77
|
+
if (source.LARKIN_PI_COMMAND)
|
|
78
|
+
next.LARKIN_PI_COMMAND = source.LARKIN_PI_COMMAND;
|
|
79
|
+
if (source.PI_PACKAGE_DIR)
|
|
80
|
+
next.PI_PACKAGE_DIR = source.PI_PACKAGE_DIR;
|
|
81
|
+
return next;
|
|
82
|
+
}
|
|
83
|
+
function capturePackageRootProof(directory) {
|
|
84
|
+
const resolved = fs.realpathSync(directory);
|
|
85
|
+
const rootStat = fs.statSync(resolved);
|
|
86
|
+
const theme = path.join(resolved, "dist", "modes", "interactive", "theme", "dark.json");
|
|
87
|
+
let fd;
|
|
88
|
+
try {
|
|
89
|
+
fd = fs.openSync(theme, fs.constants.O_RDONLY);
|
|
90
|
+
const themeStat = fs.fstatSync(fd);
|
|
91
|
+
if (!themeStat.isFile())
|
|
92
|
+
throw new Error("external Pi package root changed; refusing migration");
|
|
93
|
+
const bytes = fs.readFileSync(fd);
|
|
94
|
+
return { dir: resolved, dev: rootStat.dev, ino: rootStat.ino, themeSha256: hash(bytes) };
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
if (fd !== undefined)
|
|
98
|
+
try {
|
|
99
|
+
fs.closeSync(fd);
|
|
100
|
+
}
|
|
101
|
+
catch { /* ignore */ }
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function assertPackageRootProof(state) {
|
|
105
|
+
if (!state.sourcePackageDir)
|
|
106
|
+
return;
|
|
107
|
+
let current;
|
|
108
|
+
try {
|
|
109
|
+
current = capturePackageRootProof(state.sourcePackageDir);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
throw new Error("external Pi package root changed; refusing migration");
|
|
113
|
+
}
|
|
114
|
+
if (current.dir !== state.sourcePackageDir || current.dev !== state.sourcePackageDev
|
|
115
|
+
|| current.ino !== state.sourcePackageIno || current.themeSha256 !== state.sourcePackageThemeSha256) {
|
|
116
|
+
throw new Error("external Pi package root changed; refusing migration");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
64
119
|
function targetLockFile(state) { return path.join(path.dirname(state.targetDir), `${state.agentId}${TARGET_LOCK_SUFFIX}`); }
|
|
65
120
|
function acquireTargetLock(state) {
|
|
66
121
|
const file = targetLockFile(state);
|
|
@@ -314,13 +369,20 @@ function targetDirectory(configDir, agentId) {
|
|
|
314
369
|
throw new Error("Pi Agent ID 格式无效");
|
|
315
370
|
return path.join(path.resolve(configDir), "providers", "pi", agentId);
|
|
316
371
|
}
|
|
317
|
-
function sourceVersion(env, cwd) {
|
|
372
|
+
function sourceVersion(env, cwd, alreadySanitized = false) {
|
|
318
373
|
const command = String(env.LARKIN_PI_COMMAND || "pi");
|
|
319
374
|
const executable = (0, runtime_readiness_js_1.resolveRuntimeExecutable)(command, env);
|
|
320
375
|
if (!executable)
|
|
321
376
|
throw new Error("外部 Pi 0.84.2 profile import requires an installed Pi executable");
|
|
322
377
|
const executableState = readExecutable(executable, "external Pi executable");
|
|
323
|
-
const
|
|
378
|
+
const probeEnv = alreadySanitized ? env : (0, builtin_pi_assets_js_1.applyPiPackageDirForChild)(env, { distribution: "external" });
|
|
379
|
+
const result = (0, node_child_process_1.spawnSync)(executableState.path, ["--version"], {
|
|
380
|
+
cwd,
|
|
381
|
+
env: probeEnv,
|
|
382
|
+
encoding: "utf8",
|
|
383
|
+
timeout: 5_000,
|
|
384
|
+
maxBuffer: 64 * 1024,
|
|
385
|
+
});
|
|
324
386
|
if (result.status !== 0)
|
|
325
387
|
throw new Error("external Pi version probe failed");
|
|
326
388
|
try {
|
|
@@ -366,10 +428,11 @@ function targetPrior(directory) {
|
|
|
366
428
|
return { stat, entries, files };
|
|
367
429
|
}
|
|
368
430
|
function preparePiProfileMigration(env, configDir, agentId, distribution = "builtin") {
|
|
369
|
-
const
|
|
431
|
+
const sanitized = (0, builtin_pi_assets_js_1.applyPiPackageDirForChild)({ ...env }, { distribution: "external" });
|
|
432
|
+
const sourceDir = sourceDirectory(sanitized);
|
|
370
433
|
verifySourceProfile(sourceDir);
|
|
371
434
|
const sourceDirectoryState = assertDirectory(sourceDir, "external Pi profile");
|
|
372
|
-
const sourceExecutable = sourceVersion(
|
|
435
|
+
const sourceExecutable = sourceVersion(sanitized, sourceDir, true);
|
|
373
436
|
const sourceFiles = Object.fromEntries(FILES.map((name) => {
|
|
374
437
|
const file = readRegular(path.join(sourceDir, name), `external Pi ${name}`, false);
|
|
375
438
|
if (!file)
|
|
@@ -391,14 +454,28 @@ function preparePiProfileMigration(env, configDir, agentId, distribution = "buil
|
|
|
391
454
|
const afterFiles = Object.fromEntries(FILES.map((name) => [name, safeState({ bytes: imported[name], mode: 0o600 }, false)]));
|
|
392
455
|
return {
|
|
393
456
|
state: { version: 1, agentId, sourceDir, sourceDirMode: sourceDirectoryState.mode & 0o777,
|
|
394
|
-
sourceCommand: String(
|
|
457
|
+
sourceCommand: String(sanitized.LARKIN_PI_COMMAND || "pi"), sourceExecutable,
|
|
395
458
|
sourceFiles: Object.fromEntries(FILES.map((name) => [name, safeState(sourceFiles[name], false)])),
|
|
396
459
|
targetDir, targetDirExisted: Boolean(target.stat), targetDirMode: target.stat ? target.stat.mode & 0o777 : 0o700,
|
|
397
460
|
...(target.stat ? { targetDirDevice: target.stat.dev, targetDirInode: target.stat.ino } : {}), targetEntries: target.entries,
|
|
398
|
-
priorFiles: target.files, afterFiles
|
|
461
|
+
priorFiles: target.files, afterFiles,
|
|
462
|
+
...(sanitized.PATH ? { sourcePath: sanitized.PATH } : {}),
|
|
463
|
+
...(sanitized.PI_PACKAGE_DIR ? (() => {
|
|
464
|
+
const proof = capturePackageRootProof(sanitized.PI_PACKAGE_DIR);
|
|
465
|
+
return {
|
|
466
|
+
sourcePackageDir: proof.dir,
|
|
467
|
+
sourcePackageDev: proof.dev,
|
|
468
|
+
sourcePackageIno: proof.ino,
|
|
469
|
+
sourcePackageThemeSha256: proof.themeSha256,
|
|
470
|
+
};
|
|
471
|
+
})() : {}) },
|
|
399
472
|
sourceBytes: { "auth.json": sourceFiles["auth.json"].bytes, "models.json": sourceFiles["models.json"].bytes,
|
|
400
473
|
"settings.json": imported["settings.json"] },
|
|
401
|
-
sourceEnvironment: {
|
|
474
|
+
sourceEnvironment: {
|
|
475
|
+
PATH: sanitized.PATH,
|
|
476
|
+
LARKIN_PI_COMMAND: String(sanitized.LARKIN_PI_COMMAND || "pi"),
|
|
477
|
+
...(sanitized.PI_PACKAGE_DIR ? { PI_PACKAGE_DIR: capturePackageRootProof(sanitized.PI_PACKAGE_DIR).dir } : {}),
|
|
478
|
+
},
|
|
402
479
|
};
|
|
403
480
|
}
|
|
404
481
|
function validatePiProfileMigrationState(value) {
|
|
@@ -439,12 +516,26 @@ function validatePiProfileMigrationState(value) {
|
|
|
439
516
|
}
|
|
440
517
|
}
|
|
441
518
|
}
|
|
519
|
+
function replayExternalPiPackageEnv(env) {
|
|
520
|
+
const next = { ...env };
|
|
521
|
+
const planned = env.PI_PACKAGE_DIR;
|
|
522
|
+
if (!planned) {
|
|
523
|
+
delete next.PI_PACKAGE_DIR;
|
|
524
|
+
return next;
|
|
525
|
+
}
|
|
526
|
+
const resolved = (0, builtin_pi_assets_js_1.applyPiPackageDirForChild)({ PI_PACKAGE_DIR: planned }, { distribution: "external", explicitPackageDir: planned }).PI_PACKAGE_DIR;
|
|
527
|
+
if (!resolved)
|
|
528
|
+
throw new Error("external Pi package root changed; refusing migration");
|
|
529
|
+
next.PI_PACKAGE_DIR = resolved;
|
|
530
|
+
return next;
|
|
531
|
+
}
|
|
442
532
|
function assertSourceUnchanged(state, resolutionEnvironment = process.env, verifyResolution = true) {
|
|
443
533
|
const directory = assertDirectory(state.sourceDir, "external Pi profile");
|
|
444
534
|
if ((directory.mode & 0o777) !== state.sourceDirMode)
|
|
445
535
|
throw new Error("external Pi profile changed; refusing migration");
|
|
446
536
|
verifySourceProfile(state.sourceDir);
|
|
447
|
-
|
|
537
|
+
assertPackageRootProof(state);
|
|
538
|
+
const probeEnvironment = replayExternalPiPackageEnv(resolutionEnvironment);
|
|
448
539
|
const resolved = verifyResolution ? (0, runtime_readiness_js_1.resolveRuntimeExecutable)(state.sourceCommand, probeEnvironment) : state.sourceExecutable.path;
|
|
449
540
|
if (!resolved)
|
|
450
541
|
throw new Error("external Pi executable changed; refusing migration");
|
|
@@ -453,7 +544,13 @@ function assertSourceUnchanged(state, resolutionEnvironment = process.env, verif
|
|
|
453
544
|
|| executable.bytes !== state.sourceExecutable.bytes || executable.mode !== state.sourceExecutable.mode) {
|
|
454
545
|
throw new Error("external Pi executable changed; refusing migration");
|
|
455
546
|
}
|
|
456
|
-
const result = (0, node_child_process_1.spawnSync)(executable.path, ["--version"], {
|
|
547
|
+
const result = (0, node_child_process_1.spawnSync)(executable.path, ["--version"], {
|
|
548
|
+
cwd: state.sourceDir,
|
|
549
|
+
env: probeEnvironment,
|
|
550
|
+
encoding: "utf8",
|
|
551
|
+
timeout: 5_000,
|
|
552
|
+
maxBuffer: 64 * 1024,
|
|
553
|
+
});
|
|
457
554
|
if (result.status !== 0)
|
|
458
555
|
throw new Error("external Pi version probe failed");
|
|
459
556
|
try {
|
|
@@ -565,9 +662,8 @@ function restoreFile(file, prior) {
|
|
|
565
662
|
fs.chmodSync(file, prior.mode);
|
|
566
663
|
}
|
|
567
664
|
function applyPiProfileMigration(plan) {
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
assertSourceUnchanged(plan.state, sameEnvironment ? process.env : plan.sourceEnvironment);
|
|
665
|
+
const replayEnv = isolatePlannedReplayEnv(plan.sourceEnvironment);
|
|
666
|
+
assertSourceUnchanged(plan.state, replayEnv);
|
|
571
667
|
const parent = path.dirname(plan.state.targetDir);
|
|
572
668
|
assertNoSymlinkAncestors(parent);
|
|
573
669
|
fs.mkdirSync(parent, { recursive: true, mode: 0o700 });
|
|
@@ -613,7 +709,11 @@ function fsyncDirectory(directory) {
|
|
|
613
709
|
}
|
|
614
710
|
function rollbackPiProfileMigration(state) {
|
|
615
711
|
validatePiProfileMigrationState(state);
|
|
616
|
-
assertSourceUnchanged(state,
|
|
712
|
+
assertSourceUnchanged(state, isolatePlannedReplayEnv({
|
|
713
|
+
PATH: state.sourcePath,
|
|
714
|
+
LARKIN_PI_COMMAND: state.sourceCommand,
|
|
715
|
+
...(state.sourcePackageDir ? { PI_PACKAGE_DIR: state.sourcePackageDir } : {}),
|
|
716
|
+
}), false);
|
|
617
717
|
const target = assertDirectory(state.targetDir, "Pi provider target", false);
|
|
618
718
|
if (!target) {
|
|
619
719
|
if (state.targetDirExisted)
|
|
@@ -4,6 +4,7 @@ import * as os from "node:os";
|
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
6
6
|
import processInspect from "../platform/process-inspect.cjs";
|
|
7
|
+
import { applyPiPackageDirForChild } from "./builtin-pi-assets.mjs";
|
|
7
8
|
import { resolveRuntimeExecutable } from "./runtime-readiness.mjs";
|
|
8
9
|
import { mergeOwnedPiSettings, parsePiExecutableVersion } from "./pi-compaction-recovery.mjs";
|
|
9
10
|
import { BUNDLED_PI_VERSION } from "./pi-provider-config.mjs";
|
|
@@ -16,6 +17,60 @@ const EXECUTABLE_LIMIT = 256 * 1024 * 1024;
|
|
|
16
17
|
const SOURCE_MODES = new Set([0o600, 0o644, 0o640]);
|
|
17
18
|
const TARGET_LOCK_SUFFIX = ".larkin-pi-import.lock";
|
|
18
19
|
function hash(bytes) { return crypto.createHash("sha256").update(bytes).digest("hex"); }
|
|
20
|
+
function isPiOrLarkinEnvKey(key) {
|
|
21
|
+
return key === "PATH" || key.startsWith("PI_") || key.startsWith("LARKIN_");
|
|
22
|
+
}
|
|
23
|
+
function isolatePlannedReplayEnv(source) {
|
|
24
|
+
const next = {};
|
|
25
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
26
|
+
if (isPiOrLarkinEnvKey(key))
|
|
27
|
+
continue;
|
|
28
|
+
next[key] = value;
|
|
29
|
+
}
|
|
30
|
+
if (source.PATH)
|
|
31
|
+
next.PATH = source.PATH;
|
|
32
|
+
if (source.LARKIN_PI_COMMAND)
|
|
33
|
+
next.LARKIN_PI_COMMAND = source.LARKIN_PI_COMMAND;
|
|
34
|
+
if (source.PI_PACKAGE_DIR)
|
|
35
|
+
next.PI_PACKAGE_DIR = source.PI_PACKAGE_DIR;
|
|
36
|
+
return next;
|
|
37
|
+
}
|
|
38
|
+
function capturePackageRootProof(directory) {
|
|
39
|
+
const resolved = fs.realpathSync(directory);
|
|
40
|
+
const rootStat = fs.statSync(resolved);
|
|
41
|
+
const theme = path.join(resolved, "dist", "modes", "interactive", "theme", "dark.json");
|
|
42
|
+
let fd;
|
|
43
|
+
try {
|
|
44
|
+
fd = fs.openSync(theme, fs.constants.O_RDONLY);
|
|
45
|
+
const themeStat = fs.fstatSync(fd);
|
|
46
|
+
if (!themeStat.isFile())
|
|
47
|
+
throw new Error("external Pi package root changed; refusing migration");
|
|
48
|
+
const bytes = fs.readFileSync(fd);
|
|
49
|
+
return { dir: resolved, dev: rootStat.dev, ino: rootStat.ino, themeSha256: hash(bytes) };
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
if (fd !== undefined)
|
|
53
|
+
try {
|
|
54
|
+
fs.closeSync(fd);
|
|
55
|
+
}
|
|
56
|
+
catch { /* ignore */ }
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function assertPackageRootProof(state) {
|
|
60
|
+
if (!state.sourcePackageDir)
|
|
61
|
+
return;
|
|
62
|
+
let current;
|
|
63
|
+
try {
|
|
64
|
+
current = capturePackageRootProof(state.sourcePackageDir);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
throw new Error("external Pi package root changed; refusing migration");
|
|
68
|
+
}
|
|
69
|
+
if (current.dir !== state.sourcePackageDir || current.dev !== state.sourcePackageDev
|
|
70
|
+
|| current.ino !== state.sourcePackageIno || current.themeSha256 !== state.sourcePackageThemeSha256) {
|
|
71
|
+
throw new Error("external Pi package root changed; refusing migration");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
19
74
|
function targetLockFile(state) { return path.join(path.dirname(state.targetDir), `${state.agentId}${TARGET_LOCK_SUFFIX}`); }
|
|
20
75
|
function acquireTargetLock(state) {
|
|
21
76
|
const file = targetLockFile(state);
|
|
@@ -269,13 +324,20 @@ function targetDirectory(configDir, agentId) {
|
|
|
269
324
|
throw new Error("Pi Agent ID 格式无效");
|
|
270
325
|
return path.join(path.resolve(configDir), "providers", "pi", agentId);
|
|
271
326
|
}
|
|
272
|
-
function sourceVersion(env, cwd) {
|
|
327
|
+
function sourceVersion(env, cwd, alreadySanitized = false) {
|
|
273
328
|
const command = String(env.LARKIN_PI_COMMAND || "pi");
|
|
274
329
|
const executable = resolveRuntimeExecutable(command, env);
|
|
275
330
|
if (!executable)
|
|
276
331
|
throw new Error("外部 Pi 0.84.2 profile import requires an installed Pi executable");
|
|
277
332
|
const executableState = readExecutable(executable, "external Pi executable");
|
|
278
|
-
const
|
|
333
|
+
const probeEnv = alreadySanitized ? env : applyPiPackageDirForChild(env, { distribution: "external" });
|
|
334
|
+
const result = spawnSync(executableState.path, ["--version"], {
|
|
335
|
+
cwd,
|
|
336
|
+
env: probeEnv,
|
|
337
|
+
encoding: "utf8",
|
|
338
|
+
timeout: 5_000,
|
|
339
|
+
maxBuffer: 64 * 1024,
|
|
340
|
+
});
|
|
279
341
|
if (result.status !== 0)
|
|
280
342
|
throw new Error("external Pi version probe failed");
|
|
281
343
|
try {
|
|
@@ -321,10 +383,11 @@ function targetPrior(directory) {
|
|
|
321
383
|
return { stat, entries, files };
|
|
322
384
|
}
|
|
323
385
|
export function preparePiProfileMigration(env, configDir, agentId, distribution = "builtin") {
|
|
324
|
-
const
|
|
386
|
+
const sanitized = applyPiPackageDirForChild({ ...env }, { distribution: "external" });
|
|
387
|
+
const sourceDir = sourceDirectory(sanitized);
|
|
325
388
|
verifySourceProfile(sourceDir);
|
|
326
389
|
const sourceDirectoryState = assertDirectory(sourceDir, "external Pi profile");
|
|
327
|
-
const sourceExecutable = sourceVersion(
|
|
390
|
+
const sourceExecutable = sourceVersion(sanitized, sourceDir, true);
|
|
328
391
|
const sourceFiles = Object.fromEntries(FILES.map((name) => {
|
|
329
392
|
const file = readRegular(path.join(sourceDir, name), `external Pi ${name}`, false);
|
|
330
393
|
if (!file)
|
|
@@ -346,14 +409,28 @@ export function preparePiProfileMigration(env, configDir, agentId, distribution
|
|
|
346
409
|
const afterFiles = Object.fromEntries(FILES.map((name) => [name, safeState({ bytes: imported[name], mode: 0o600 }, false)]));
|
|
347
410
|
return {
|
|
348
411
|
state: { version: 1, agentId, sourceDir, sourceDirMode: sourceDirectoryState.mode & 0o777,
|
|
349
|
-
sourceCommand: String(
|
|
412
|
+
sourceCommand: String(sanitized.LARKIN_PI_COMMAND || "pi"), sourceExecutable,
|
|
350
413
|
sourceFiles: Object.fromEntries(FILES.map((name) => [name, safeState(sourceFiles[name], false)])),
|
|
351
414
|
targetDir, targetDirExisted: Boolean(target.stat), targetDirMode: target.stat ? target.stat.mode & 0o777 : 0o700,
|
|
352
415
|
...(target.stat ? { targetDirDevice: target.stat.dev, targetDirInode: target.stat.ino } : {}), targetEntries: target.entries,
|
|
353
|
-
priorFiles: target.files, afterFiles
|
|
416
|
+
priorFiles: target.files, afterFiles,
|
|
417
|
+
...(sanitized.PATH ? { sourcePath: sanitized.PATH } : {}),
|
|
418
|
+
...(sanitized.PI_PACKAGE_DIR ? (() => {
|
|
419
|
+
const proof = capturePackageRootProof(sanitized.PI_PACKAGE_DIR);
|
|
420
|
+
return {
|
|
421
|
+
sourcePackageDir: proof.dir,
|
|
422
|
+
sourcePackageDev: proof.dev,
|
|
423
|
+
sourcePackageIno: proof.ino,
|
|
424
|
+
sourcePackageThemeSha256: proof.themeSha256,
|
|
425
|
+
};
|
|
426
|
+
})() : {}) },
|
|
354
427
|
sourceBytes: { "auth.json": sourceFiles["auth.json"].bytes, "models.json": sourceFiles["models.json"].bytes,
|
|
355
428
|
"settings.json": imported["settings.json"] },
|
|
356
|
-
sourceEnvironment: {
|
|
429
|
+
sourceEnvironment: {
|
|
430
|
+
PATH: sanitized.PATH,
|
|
431
|
+
LARKIN_PI_COMMAND: String(sanitized.LARKIN_PI_COMMAND || "pi"),
|
|
432
|
+
...(sanitized.PI_PACKAGE_DIR ? { PI_PACKAGE_DIR: capturePackageRootProof(sanitized.PI_PACKAGE_DIR).dir } : {}),
|
|
433
|
+
},
|
|
357
434
|
};
|
|
358
435
|
}
|
|
359
436
|
export function validatePiProfileMigrationState(value) {
|
|
@@ -394,12 +471,26 @@ export function validatePiProfileMigrationState(value) {
|
|
|
394
471
|
}
|
|
395
472
|
}
|
|
396
473
|
}
|
|
474
|
+
function replayExternalPiPackageEnv(env) {
|
|
475
|
+
const next = { ...env };
|
|
476
|
+
const planned = env.PI_PACKAGE_DIR;
|
|
477
|
+
if (!planned) {
|
|
478
|
+
delete next.PI_PACKAGE_DIR;
|
|
479
|
+
return next;
|
|
480
|
+
}
|
|
481
|
+
const resolved = applyPiPackageDirForChild({ PI_PACKAGE_DIR: planned }, { distribution: "external", explicitPackageDir: planned }).PI_PACKAGE_DIR;
|
|
482
|
+
if (!resolved)
|
|
483
|
+
throw new Error("external Pi package root changed; refusing migration");
|
|
484
|
+
next.PI_PACKAGE_DIR = resolved;
|
|
485
|
+
return next;
|
|
486
|
+
}
|
|
397
487
|
function assertSourceUnchanged(state, resolutionEnvironment = process.env, verifyResolution = true) {
|
|
398
488
|
const directory = assertDirectory(state.sourceDir, "external Pi profile");
|
|
399
489
|
if ((directory.mode & 0o777) !== state.sourceDirMode)
|
|
400
490
|
throw new Error("external Pi profile changed; refusing migration");
|
|
401
491
|
verifySourceProfile(state.sourceDir);
|
|
402
|
-
|
|
492
|
+
assertPackageRootProof(state);
|
|
493
|
+
const probeEnvironment = replayExternalPiPackageEnv(resolutionEnvironment);
|
|
403
494
|
const resolved = verifyResolution ? resolveRuntimeExecutable(state.sourceCommand, probeEnvironment) : state.sourceExecutable.path;
|
|
404
495
|
if (!resolved)
|
|
405
496
|
throw new Error("external Pi executable changed; refusing migration");
|
|
@@ -408,7 +499,13 @@ function assertSourceUnchanged(state, resolutionEnvironment = process.env, verif
|
|
|
408
499
|
|| executable.bytes !== state.sourceExecutable.bytes || executable.mode !== state.sourceExecutable.mode) {
|
|
409
500
|
throw new Error("external Pi executable changed; refusing migration");
|
|
410
501
|
}
|
|
411
|
-
const result = spawnSync(executable.path, ["--version"], {
|
|
502
|
+
const result = spawnSync(executable.path, ["--version"], {
|
|
503
|
+
cwd: state.sourceDir,
|
|
504
|
+
env: probeEnvironment,
|
|
505
|
+
encoding: "utf8",
|
|
506
|
+
timeout: 5_000,
|
|
507
|
+
maxBuffer: 64 * 1024,
|
|
508
|
+
});
|
|
412
509
|
if (result.status !== 0)
|
|
413
510
|
throw new Error("external Pi version probe failed");
|
|
414
511
|
try {
|
|
@@ -520,9 +617,8 @@ function restoreFile(file, prior) {
|
|
|
520
617
|
fs.chmodSync(file, prior.mode);
|
|
521
618
|
}
|
|
522
619
|
export function applyPiProfileMigration(plan) {
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
assertSourceUnchanged(plan.state, sameEnvironment ? process.env : plan.sourceEnvironment);
|
|
620
|
+
const replayEnv = isolatePlannedReplayEnv(plan.sourceEnvironment);
|
|
621
|
+
assertSourceUnchanged(plan.state, replayEnv);
|
|
526
622
|
const parent = path.dirname(plan.state.targetDir);
|
|
527
623
|
assertNoSymlinkAncestors(parent);
|
|
528
624
|
fs.mkdirSync(parent, { recursive: true, mode: 0o700 });
|
|
@@ -568,7 +664,11 @@ function fsyncDirectory(directory) {
|
|
|
568
664
|
}
|
|
569
665
|
export function rollbackPiProfileMigration(state) {
|
|
570
666
|
validatePiProfileMigrationState(state);
|
|
571
|
-
assertSourceUnchanged(state,
|
|
667
|
+
assertSourceUnchanged(state, isolatePlannedReplayEnv({
|
|
668
|
+
PATH: state.sourcePath,
|
|
669
|
+
LARKIN_PI_COMMAND: state.sourceCommand,
|
|
670
|
+
...(state.sourcePackageDir ? { PI_PACKAGE_DIR: state.sourcePackageDir } : {}),
|
|
671
|
+
}), false);
|
|
572
672
|
const target = assertDirectory(state.targetDir, "Pi provider target", false);
|
|
573
673
|
if (!target) {
|
|
574
674
|
if (state.targetDirExisted)
|
|
@@ -3,6 +3,7 @@ import crypto from "node:crypto";
|
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import { createInterface } from "node:readline";
|
|
6
|
+
import { applyPiPackageDirForChild, piChildDistributionFromOverrides } from "./builtin-pi-assets.mjs";
|
|
6
7
|
import { isPiThinkingLevel } from "./pi-model-catalog.mjs";
|
|
7
8
|
import { PiRpcClient } from "./pi-rpc-client.mjs";
|
|
8
9
|
import { internalCommandSpec } from "../app/internal-command.mjs";
|
|
@@ -967,23 +968,29 @@ async function createPiRpcBackend(input, dependencies, spawn, productionSpawn =
|
|
|
967
968
|
...(session.sessionFile ? ["--session", session.sessionFile] : []),
|
|
968
969
|
...(requestedModel ? ["--model", requestedModel] : []),
|
|
969
970
|
...(requestedEffort ? ["--thinking", requestedEffort] : [])];
|
|
970
|
-
const builtin =
|
|
971
|
+
const builtin = piChildDistributionFromOverrides(dependencies.env, input.env) === "builtin";
|
|
972
|
+
if (!builtin && mergedEnv.LARKIN_PI_DISTRIBUTION === "builtin")
|
|
973
|
+
delete mergedEnv.LARKIN_PI_DISTRIBUTION;
|
|
971
974
|
const builtinSpec = builtin ? internalCommandSpec("pi-rpc", [], mergedEnv) : null;
|
|
972
975
|
const command = builtinSpec?.command ?? dependencies.piCommand ?? dependencies.env?.LARKIN_PI_COMMAND ?? process.env.LARKIN_PI_COMMAND ?? "pi";
|
|
973
976
|
const commandPrefix = builtinSpec?.args ?? dependencies.piCommandArgs ?? [];
|
|
974
977
|
const commandArgs = [...commandPrefix, ...args];
|
|
978
|
+
mergedEnv.PI_CODING_AGENT_DIR = ownedPiDirectory;
|
|
979
|
+
if (builtin)
|
|
980
|
+
mergedEnv.PI_TELEMETRY = "0";
|
|
981
|
+
const childEnv = applyPiPackageDirForChild(mergedEnv, {
|
|
982
|
+
distribution: builtin ? "builtin" : "external",
|
|
983
|
+
explicitPackageDir: input.env?.PI_PACKAGE_DIR ?? dependencies.env?.PI_PACKAGE_DIR,
|
|
984
|
+
});
|
|
975
985
|
const reportedVersion = productionSpawn && !builtin
|
|
976
986
|
? parsePiExecutableVersion(String(spawnSync(command, [...commandPrefix, "--version"], {
|
|
977
|
-
cwd: input.workspaceDir, env:
|
|
987
|
+
cwd: input.workspaceDir, env: childEnv, encoding: "utf8", timeout: 5_000,
|
|
978
988
|
}).stdout || ""))
|
|
979
989
|
: BUNDLED_PI_VERSION;
|
|
980
|
-
mergedEnv.PI_CODING_AGENT_DIR = ownedPiDirectory;
|
|
981
|
-
if (builtin)
|
|
982
|
-
mergedEnv.PI_TELEMETRY = "0";
|
|
983
990
|
const extensionArgs = (dependencies.resolvePiProcessExtensionArgs ?? resolvePiProcessExtensionArgs)({
|
|
984
991
|
distribution: builtin ? "builtin" : "external",
|
|
985
992
|
piCommand: command,
|
|
986
|
-
env:
|
|
993
|
+
env: childEnv,
|
|
987
994
|
platform: process.platform,
|
|
988
995
|
});
|
|
989
996
|
commandArgs.push(...extensionArgs);
|
|
@@ -993,14 +1000,14 @@ async function createPiRpcBackend(input, dependencies, spawn, productionSpawn =
|
|
|
993
1000
|
// requested model first, then write the matching 15% reserve before the
|
|
994
1001
|
// real session starts; no prompt, provider request, package, or extension
|
|
995
1002
|
// is loaded by the probe.
|
|
996
|
-
probedModel = await discoverEffectivePiContextWindow(input, command, commandPrefix, requestedModel,
|
|
1003
|
+
probedModel = await discoverEffectivePiContextWindow(input, command, commandPrefix, requestedModel, childEnv, spawn, dependencies.piRpcClientOptions);
|
|
997
1004
|
writeOwnedPiSettings(ownedPiDirectory, calculatePiCompactionSettings(probedModel.contextWindow));
|
|
998
1005
|
}
|
|
999
1006
|
const artifactEntriesBeforeSpawn = new Set(fs.readdirSync(ownedPiDirectory));
|
|
1000
1007
|
const artifactSpawnBoundary = Date.now();
|
|
1001
1008
|
const child = spawn(command, commandArgs, {
|
|
1002
1009
|
cwd: input.workspaceDir,
|
|
1003
|
-
env:
|
|
1010
|
+
env: childEnv,
|
|
1004
1011
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1005
1012
|
});
|
|
1006
1013
|
traceProcessBoundary(mergedEnv, "pi-rpc:child-spawned", { configDir: mergedEnv.LARKIN_CONFIG_DIR, agentId: input.agentId, targetDir: ownedPiDirectory, childPid: child.pid ?? null });
|
|
@@ -1164,8 +1171,7 @@ export function createNativeRuntimeAdapter(id, dependencies = {}) {
|
|
|
1164
1171
|
throw new RuntimePrerequisiteError(readiness);
|
|
1165
1172
|
}
|
|
1166
1173
|
if (id === "pi") {
|
|
1167
|
-
const distribution = (
|
|
1168
|
-
? "builtin" : "external";
|
|
1174
|
+
const distribution = piChildDistributionFromOverrides(dependencies.env, input.env);
|
|
1169
1175
|
return new PiSession(await (dependencies.createPiSession
|
|
1170
1176
|
? dependencies.createPiSession(input)
|
|
1171
1177
|
: createPiRpcBackend(input, { ...dependencies, piCommand: resolvedExecutable }, spawn, productionSpawn)), distribution);
|
|
@@ -44,6 +44,7 @@ exports.probeNativeRuntimeReadiness = probeNativeRuntimeReadiness;
|
|
|
44
44
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
45
45
|
const node_path_1 = __importDefault(require("node:path"));
|
|
46
46
|
const node_child_process_1 = require("node:child_process");
|
|
47
|
+
const builtin_pi_assets_js_1 = require("./builtin-pi-assets.cjs");
|
|
47
48
|
const pi_provider_config_js_1 = require("./pi-provider-config.cjs");
|
|
48
49
|
const process_boundary_trace_js_1 = require("../platform/process-boundary-trace.cjs");
|
|
49
50
|
function safeProviderLabel(value) {
|
|
@@ -131,8 +132,14 @@ function executableVersion(executable, env, commandArgs = []) {
|
|
|
131
132
|
}
|
|
132
133
|
/** Resolve and handshake through each runtime's structured native control protocol. */
|
|
133
134
|
async function probeNativeRuntimeReadiness(options) {
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
let env = { ...process.env, ...options.env };
|
|
136
|
+
const piDistribution = options.runtime === "pi" ? (0, builtin_pi_assets_js_1.piChildDistributionFromOverrides)(options.env) : "external";
|
|
137
|
+
if (options.runtime === "pi" && piDistribution !== "builtin") {
|
|
138
|
+
if (env.LARKIN_PI_DISTRIBUTION === "builtin")
|
|
139
|
+
delete env.LARKIN_PI_DISTRIBUTION;
|
|
140
|
+
env = (0, builtin_pi_assets_js_1.applyPiPackageDirForChild)(env, { distribution: "external" });
|
|
141
|
+
}
|
|
142
|
+
if (options.runtime === "pi" && piDistribution === "builtin") {
|
|
136
143
|
try {
|
|
137
144
|
if (!options.agentId || !env.LARKIN_CONFIG_DIR)
|
|
138
145
|
throw new Error("内置 Pi readiness 缺少 Agent/config identity");
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { applyPiPackageDirForChild, piChildDistributionFromOverrides } from "./builtin-pi-assets.mjs";
|
|
4
5
|
import { assertBuiltinPiAgentDirectory, BUNDLED_PI_VERSION, piAgentDirectory } from "./pi-provider-config.mjs";
|
|
5
6
|
import { traceProcessBoundary } from "../platform/process-boundary-trace.mjs";
|
|
6
7
|
function safeProviderLabel(value) {
|
|
@@ -87,8 +88,14 @@ function executableVersion(executable, env, commandArgs = []) {
|
|
|
87
88
|
}
|
|
88
89
|
/** Resolve and handshake through each runtime's structured native control protocol. */
|
|
89
90
|
export async function probeNativeRuntimeReadiness(options) {
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
let env = { ...process.env, ...options.env };
|
|
92
|
+
const piDistribution = options.runtime === "pi" ? piChildDistributionFromOverrides(options.env) : "external";
|
|
93
|
+
if (options.runtime === "pi" && piDistribution !== "builtin") {
|
|
94
|
+
if (env.LARKIN_PI_DISTRIBUTION === "builtin")
|
|
95
|
+
delete env.LARKIN_PI_DISTRIBUTION;
|
|
96
|
+
env = applyPiPackageDirForChild(env, { distribution: "external" });
|
|
97
|
+
}
|
|
98
|
+
if (options.runtime === "pi" && piDistribution === "builtin") {
|
|
92
99
|
try {
|
|
93
100
|
if (!options.agentId || !env.LARKIN_CONFIG_DIR)
|
|
94
101
|
throw new Error("内置 Pi readiness 缺少 Agent/config identity");
|
|
@@ -330,7 +330,7 @@ export async function main() {
|
|
|
330
330
|
const catalog = await discoverPiModelCatalog({
|
|
331
331
|
cwd: CFG_DIR,
|
|
332
332
|
agentDir: piAgentDirectory(CFG_DIR, profile.appId),
|
|
333
|
-
env: process.env,
|
|
333
|
+
env: { ...process.env, LARKIN_PI_DISTRIBUTION: "external" },
|
|
334
334
|
});
|
|
335
335
|
const effective = catalog.effectiveModel;
|
|
336
336
|
const entry = effective ? catalog.models.find((model) => model.id === effective) : undefined;
|