larkin 0.4.29 → 0.5.1

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.
Files changed (57) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +5 -9
  3. package/artifacts/release/THIRD_PARTY_NOTICES.txt +751 -4425
  4. package/dist/app/agent-cli.mjs +14 -2
  5. package/dist/app/agent-config.mjs +26 -86
  6. package/dist/app/binary-entry.mjs +0 -20
  7. package/dist/app/cli.mjs +6 -22
  8. package/dist/app/internal-command.cjs +0 -2
  9. package/dist/app/internal-command.mjs +0 -2
  10. package/dist/app/runtime-model-directory.mjs +2 -16
  11. package/dist/app/runtime-process.mjs +0 -1
  12. package/dist/app/setup.mjs +52 -57
  13. package/dist/dashboard/dashboard-config-controller.mjs +43 -109
  14. package/dist/dashboard/dashboard-view-model.mjs +4 -7
  15. package/dist/dashboard/web/assets/dashboard.css +1 -1
  16. package/dist/dashboard/web/assets/dashboard.js +15 -15
  17. package/dist/feishu/host-shell.cjs +1 -2
  18. package/dist/feishu/host-shell.mjs +1 -2
  19. package/dist/platform/config.cjs +168 -133
  20. package/dist/platform/config.mjs +168 -133
  21. package/dist/runtime/pi-artifact-provenance.mjs +3 -3
  22. package/dist/runtime/pi-bash-timeout-extension.mjs +4 -5
  23. package/dist/runtime/pi-compaction-recovery.cjs +45 -15
  24. package/dist/runtime/pi-compaction-recovery.mjs +43 -14
  25. package/dist/runtime/pi-extension-api.mjs +1 -0
  26. package/dist/runtime/pi-model-catalog.cjs +7 -8
  27. package/dist/runtime/pi-model-catalog.mjs +7 -8
  28. package/dist/runtime/pi-subagent-injection.mjs +17 -4
  29. package/dist/runtime/runtime-adapters.mjs +26 -43
  30. package/dist/runtime/runtime-host.cjs +21 -43
  31. package/dist/runtime/runtime-host.mjs +22 -44
  32. package/dist/runtime/runtime-model-catalog.cjs +1 -1
  33. package/dist/runtime/runtime-model-catalog.mjs +1 -1
  34. package/dist/runtime/runtime-readiness.cjs +62 -98
  35. package/dist/runtime/runtime-readiness.mjs +60 -97
  36. package/dist/runtime/user-runtime.cjs +7 -24
  37. package/dist/runtime/user-runtime.mjs +7 -23
  38. package/dist/setup/bot-register.mjs +15 -123
  39. package/dist/setup/setup-agent-choice.mjs +48 -74
  40. package/dist/setup/setup-bind.mjs +51 -151
  41. package/dist/setup/setup-binding.mjs +6 -9
  42. package/package.json +3 -2
  43. package/dist/app/pi-auth-cli.mjs +0 -277
  44. package/dist/platform/process-boundary-trace.cjs +0 -108
  45. package/dist/runtime/builtin-pi-assets.cjs +0 -117
  46. package/dist/runtime/builtin-pi-assets.mjs +0 -105
  47. package/dist/runtime/pi-artifact-provenance.cjs +0 -147
  48. package/dist/runtime/pi-distribution-label.cjs +0 -6
  49. package/dist/runtime/pi-distribution-label.mjs +0 -3
  50. package/dist/runtime/pi-inline-extensions.mjs +0 -78
  51. package/dist/runtime/pi-official-auth.cjs +0 -594
  52. package/dist/runtime/pi-official-auth.mjs +0 -544
  53. package/dist/runtime/pi-profile-migration.cjs +0 -764
  54. package/dist/runtime/pi-profile-migration.mjs +0 -719
  55. package/dist/runtime/pi-provider-config.cjs +0 -354
  56. package/dist/runtime/pi-provider-config.mjs +0 -334
  57. package/dist/runtime/pi-provider-login.mjs +0 -240
@@ -1,277 +0,0 @@
1
- import fs from "node:fs";
2
- import * as larkinConfig from "../platform/config.mjs";
3
- import { createOfficialPiCredentialRuntime, officialPiAuthStatus, } from "../runtime/pi-official-auth.mjs";
4
- import { configureBuiltinPiProvider, listBuiltinPiProviderCatalog, logoutBuiltinPiProvider, } from "../runtime/pi-provider-login.mjs";
5
- import { MAX_BUILTIN_PI_API_KEY_LENGTH } from "../runtime/pi-provider-config.mjs";
6
- const APP_ID = /^cli_[A-Za-z0-9]+$/;
7
- const USAGE = "Usage: larkin pi-auth status [--agent <App ID>] [--json] | larkin pi-auth providers [--json] | larkin pi-auth login <preset|custom> --agent <App ID> [--model <model>] [--base-url <url>] [--api-key-stdin] [--json] | larkin pi-auth logout <provider> [--agent <App ID>]";
8
- const API_KEY_ARGV_MESSAGE = "existing-Agent login does not accept --api-key. Use --api-key-stdin or an interactive TTY prompt.";
9
- const API_KEY_BUDGET_MESSAGE = "API Key 不能为空或包含控制字符";
10
- const STDIN_CHUNK_BYTES = 1024;
11
- const STDIN_BOM_BYTES = 3;
12
- const STDIN_TRAILING_NEWLINE_SLACK = 32;
13
- const MAX_STDIN_API_KEY_BYTES = MAX_BUILTIN_PI_API_KEY_LENGTH + STDIN_BOM_BYTES + STDIN_TRAILING_NEWLINE_SLACK;
14
- function flag(args, name) {
15
- const index = args.indexOf(name);
16
- return index >= 0 ? args[index + 1] : undefined;
17
- }
18
- function hasApiKeyArgv(args) {
19
- return args.some((argument) => argument === "--api-key" || argument.startsWith("--api-key="));
20
- }
21
- function consumeFlags(args, allowed, unary) {
22
- let index = 0;
23
- while (index < args.length) {
24
- const argument = args[index];
25
- if (!argument.startsWith("--"))
26
- return false;
27
- if (unary.has(argument)) {
28
- index += 1;
29
- continue;
30
- }
31
- if (allowed.has(argument) && args[index + 1] && !args[index + 1].startsWith("--")) {
32
- index += 2;
33
- continue;
34
- }
35
- return false;
36
- }
37
- return true;
38
- }
39
- /** 去掉 BOM 与全部尾部换行;保留内部控制字符给后续校验拒绝。 */
40
- export function normalizeStdinApiKey(raw) {
41
- return raw.replace(/^\uFEFF/, "").replace(/[\r\n]+$/g, "");
42
- }
43
- function assertStdinApiKeyBudget(size) {
44
- if (size > MAX_STDIN_API_KEY_BYTES)
45
- throw new Error(API_KEY_BUDGET_MESSAGE);
46
- }
47
- function finishStdinApiKey(chunks) {
48
- const normalized = normalizeStdinApiKey(Buffer.concat(chunks).toString("utf8"));
49
- if (normalized.length > MAX_BUILTIN_PI_API_KEY_LENGTH)
50
- throw new Error(API_KEY_BUDGET_MESSAGE);
51
- return normalized;
52
- }
53
- function readFdChunk(fd, buffer) {
54
- return new Promise((resolve, reject) => {
55
- fs.read(fd, buffer, 0, buffer.length, null, (error, bytesRead) => {
56
- if (error)
57
- reject(error);
58
- else
59
- resolve(bytesRead);
60
- });
61
- });
62
- }
63
- /** 按块读取 fd,超过 key 上限立即停止,不等待管道 EOF。 */
64
- async function readBoundedFdSecret(fd) {
65
- const chunks = [];
66
- let size = 0;
67
- const buffer = Buffer.alloc(STDIN_CHUNK_BYTES);
68
- while (true) {
69
- const bytesRead = await readFdChunk(fd, buffer);
70
- if (bytesRead === 0)
71
- break;
72
- size += bytesRead;
73
- assertStdinApiKeyBudget(size);
74
- chunks.push(Buffer.from(buffer.subarray(0, bytesRead)));
75
- }
76
- return finishStdinApiKey(chunks);
77
- }
78
- async function readBoundedStreamSecret(stdin) {
79
- const chunks = [];
80
- let size = 0;
81
- for await (const chunk of stdin) {
82
- const bytes = Buffer.from(chunk);
83
- size += bytes.length;
84
- assertStdinApiKeyBudget(size);
85
- chunks.push(bytes);
86
- }
87
- return finishStdinApiKey(chunks);
88
- }
89
- export async function readStdinSecret(stdin) {
90
- const stream = stdin;
91
- if (typeof stream.fd === "number" && stream.fd >= 0 && !stream.isTTY) {
92
- if (typeof stream.pause === "function")
93
- stream.pause();
94
- try {
95
- return await readBoundedFdSecret(stream.fd);
96
- }
97
- catch (error) {
98
- if (error instanceof Error && error.message === API_KEY_BUDGET_MESSAGE)
99
- throw error;
100
- // 回退到异步收集,例如测试传入的假 stream。
101
- }
102
- }
103
- return readBoundedStreamSecret(stdin);
104
- }
105
- function createNoEchoQuestioner(stdin, stdout) {
106
- return {
107
- close() { },
108
- secret(prompt) {
109
- if (!stdin.isTTY || typeof stdin.setRawMode !== "function") {
110
- throw new Error("non-interactive API-key login requires --api-key-stdin");
111
- }
112
- stdout.write(prompt);
113
- const wasRaw = stdin.isRaw;
114
- stdin.setRawMode(true);
115
- stdin.resume();
116
- return new Promise((resolve, reject) => {
117
- let value = "";
118
- const finish = (error) => {
119
- stdin.off("data", onData);
120
- stdin.setRawMode(Boolean(wasRaw));
121
- stdin.pause();
122
- stdout.write("\n");
123
- if (error)
124
- reject(error);
125
- else
126
- resolve(value);
127
- };
128
- const onData = (chunk) => {
129
- const bytes = Buffer.from(chunk);
130
- for (const byte of bytes) {
131
- if (byte === 3)
132
- return finish(new Error("pi-auth login cancelled"));
133
- if (byte === 10 || byte === 13)
134
- return finish();
135
- if (byte === 8 || byte === 127) {
136
- value = value.slice(0, -1);
137
- continue;
138
- }
139
- if (byte >= 32 && byte <= 126)
140
- value += String.fromCharCode(byte);
141
- }
142
- };
143
- stdin.on("data", onData);
144
- });
145
- },
146
- };
147
- }
148
- function resolveAgentId(args, env) {
149
- const loaded = larkinConfig.loadConfig(env);
150
- const agentId = flag(args, "--agent") || loaded.config.activeAgent || undefined;
151
- if (!agentId || !APP_ID.test(agentId))
152
- throw new Error("请用 --agent <App ID> 选择 Agent");
153
- const agent = loaded.config.agents[agentId];
154
- if (!agent)
155
- throw new Error(`Agent ${agentId} 不存在`);
156
- if (agent.runtime !== "pi" || agent.piDistribution !== "builtin")
157
- throw new Error(`Agent ${agentId} 不是内置 Pi`);
158
- return agentId;
159
- }
160
- export async function main(args = process.argv.slice(2), env = process.env, io = { stdin: process.stdin, stdout: process.stdout, isTTY: Boolean(process.stdin.isTTY) }) {
161
- const action = args[0] || "status";
162
- const json = args.includes("--json");
163
- if (hasApiKeyArgv(args))
164
- throw new Error(API_KEY_ARGV_MESSAGE);
165
- if (action === "providers") {
166
- if (!consumeFlags(args.slice(1), new Set(), new Set(["--json"])))
167
- throw new Error(USAGE);
168
- const providers = listBuiltinPiProviderCatalog();
169
- if (json) {
170
- console.log(JSON.stringify({ providers }));
171
- return;
172
- }
173
- console.log("Builtin Pi providers:");
174
- for (const entry of providers) {
175
- const model = entry.defaultModel ? ` default=${entry.defaultModel}` : " (requires --base-url and --model)";
176
- console.log(` ${entry.id} ${entry.name}${model}`);
177
- }
178
- return;
179
- }
180
- if (action === "login") {
181
- const preset = args[1];
182
- const rest = args.slice(2);
183
- if (!preset || preset.startsWith("--")
184
- || !consumeFlags(rest, new Set(["--agent", "--model", "--base-url"]), new Set(["--json", "--api-key-stdin"]))) {
185
- throw new Error(USAGE);
186
- }
187
- const agentId = resolveAgentId(rest, env);
188
- const stdinKey = rest.includes("--api-key-stdin");
189
- let apiKey = "";
190
- if (stdinKey)
191
- apiKey = await readStdinSecret(io.stdin);
192
- else if (io.isTTY) {
193
- const questioner = createNoEchoQuestioner(io.stdin, io.stdout);
194
- try {
195
- apiKey = await questioner.secret("API Key:\n> ");
196
- }
197
- finally {
198
- questioner.close();
199
- }
200
- }
201
- else {
202
- throw new Error("non-interactive API-key login requires --api-key-stdin");
203
- }
204
- const result = await configureBuiltinPiProvider({
205
- agentId,
206
- preset,
207
- apiKey,
208
- model: flag(rest, "--model"),
209
- baseUrl: flag(rest, "--base-url"),
210
- env,
211
- });
212
- const payload = {
213
- agentId: result.agentId,
214
- provider: result.provider,
215
- model: result.model,
216
- credentialType: result.credentialType,
217
- applyState: result.applyState,
218
- ...(result.applyError ? { applyError: result.applyError } : {}),
219
- };
220
- if (json)
221
- console.log(JSON.stringify(payload));
222
- else {
223
- const pending = result.applyState === "applied"
224
- ? "applied"
225
- : result.applyState === "pending"
226
- ? `pending${result.applyError ? ` (${result.applyError})` : ""}`
227
- : "saved; will apply on larkin start";
228
- console.log(`Agent ${result.agentId} provider=${result.provider} model=${result.model} credential=${result.credentialType} apply=${pending}`);
229
- }
230
- return;
231
- }
232
- const requestedAgent = flag(args, "--agent");
233
- let index = action === "logout" ? 2 : 1;
234
- let valid = action === "status" || action === "logout";
235
- while (valid && index < args.length) {
236
- const argument = args[index];
237
- if (argument === "--json" && action === "status") {
238
- index += 1;
239
- continue;
240
- }
241
- if (argument === "--agent" && args[index + 1]) {
242
- index += 2;
243
- continue;
244
- }
245
- valid = false;
246
- }
247
- if (!valid)
248
- throw new Error(USAGE);
249
- const loaded = larkinConfig.loadConfig(env);
250
- const agentId = requestedAgent || loaded.config.activeAgent || undefined;
251
- if (!agentId || !APP_ID.test(agentId))
252
- throw new Error("请用 --agent <App ID> 选择 Agent");
253
- const agent = loaded.config.agents[agentId];
254
- if (!agent)
255
- throw new Error(`Agent ${agentId} 不存在`);
256
- if (agent.runtime !== "pi" || agent.piDistribution !== "builtin")
257
- throw new Error(`Agent ${agentId} 不是内置 Pi`);
258
- if (action === "status") {
259
- const runtime = await createOfficialPiCredentialRuntime(loaded.configDir, agentId);
260
- const statuses = await officialPiAuthStatus(runtime);
261
- if (json)
262
- console.log(JSON.stringify({ agentId, credentials: statuses }));
263
- else if (statuses.length === 0)
264
- console.log(`Agent ${agentId} 尚无已配置的官方 Pi credential`);
265
- else {
266
- console.log(`Agent ${agentId} 官方 Pi credential:`);
267
- for (const entry of statuses)
268
- console.log(` ${entry.providerName} (${entry.providerId}): ${entry.credentialType}/${entry.source}${entry.stored ? " [stored]" : " [ambient]"}`);
269
- }
270
- return;
271
- }
272
- const providerId = args[1];
273
- if (!providerId || providerId.startsWith("--"))
274
- throw new Error("logout 需要安全的 provider ID");
275
- await logoutBuiltinPiProvider({ agentId, providerId, env });
276
- console.log(`Agent ${agentId} 已退出 ${providerId};其他 provider 未修改`);
277
- }
@@ -1,108 +0,0 @@
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.traceProcessBoundary = traceProcessBoundary;
7
- const node_fs_1 = __importDefault(require("node:fs"));
8
- const node_path_1 = __importDefault(require("node:path"));
9
- function inspect(role, file) {
10
- try {
11
- const stat = node_fs_1.default.lstatSync(file);
12
- return {
13
- role,
14
- exists: true,
15
- regularFile: stat.isFile(),
16
- directory: stat.isDirectory(),
17
- symlink: stat.isSymbolicLink(),
18
- mode: stat.mode & 0o777,
19
- };
20
- }
21
- catch (error) {
22
- const code = error.code;
23
- return { role, exists: false, ...(code === "ELOOP" ? { symlink: true } : {}) };
24
- }
25
- }
26
- function errorCategory(error) {
27
- const code = error && typeof error === "object" && "code" in error
28
- ? String(error.code || "") : "";
29
- if (code === "ENOENT")
30
- return "missing";
31
- if (code === "EACCES" || code === "EPERM")
32
- return "permission";
33
- if (code === "ELOOP")
34
- return "symlink";
35
- if (code === "EEXIST")
36
- return "conflict";
37
- if (code === "EINVAL" || code === "EBADMSG" || code === "ERR_INVALID_ARG_VALUE")
38
- return "invalid";
39
- return "runtime";
40
- }
41
- function safePhase(value) {
42
- const phase = typeof value === "string" ? value : "unknown";
43
- return /^[a-z0-9:_-]{1,80}$/i.test(phase) ? phase : "unknown";
44
- }
45
- function safeEpoch(value) {
46
- if (typeof value !== "string" && typeof value !== "number" && !(value instanceof Date))
47
- return null;
48
- const text = value instanceof Date ? value.toISOString() : String(value);
49
- const time = Date.parse(text);
50
- return Number.isFinite(time) ? new Date(time).toISOString() : null;
51
- }
52
- /**
53
- * Opt-in, content-free diagnostics for process-boundary startup investigations.
54
- * The trace intentionally emits roles and metadata only: never a cwd, home,
55
- * config/profile/target path, or exception text. Diagnostics are inert unless an
56
- * absolute 0600 destination is explicitly supplied by a test or operator.
57
- */
58
- function traceProcessBoundary(env, phase, fields = {}) {
59
- const file = env.LARKIN_PROCESS_BOUNDARY_TRACE_FILE;
60
- if (!file || !node_path_1.default.isAbsolute(file) || /[\0\r\n]/.test(file))
61
- return;
62
- try {
63
- const configRoot = fields.configDir || env.LARKIN_CONFIG_DIR || env.LARKIN_HOME || null;
64
- const record = {
65
- at: new Date().toISOString(),
66
- epoch: safeEpoch(fields.epoch ?? env.LARKIN_DAEMON_EPOCH ?? new Date().toISOString()),
67
- pid: process.pid,
68
- ppid: process.ppid,
69
- phase: safePhase(phase),
70
- configuredRoots: {
71
- configDir: Boolean(env.LARKIN_CONFIG_DIR || fields.configDir),
72
- larkinHome: Boolean(env.LARKIN_HOME),
73
- piAgentDir: Boolean(env.PI_CODING_AGENT_DIR),
74
- },
75
- pathRoles: [
76
- ...(configRoot ? [inspect("config-root", configRoot)] : []),
77
- ...(fields.targetDir ? [inspect("provider-target", fields.targetDir)] : []),
78
- ],
79
- };
80
- if (typeof fields.agentId === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(fields.agentId))
81
- record.agentId = fields.agentId;
82
- if (Number.isInteger(fields.childPid) && Number(fields.childPid) > 0)
83
- record.childPid = Number(fields.childPid);
84
- if (typeof fields.distribution === "string" && /^(builtin|external)$/.test(fields.distribution))
85
- record.distribution = fields.distribution;
86
- if (typeof fields.requested === "string" && /^(builtin|external|migration|startup)$/.test(fields.requested))
87
- record.requested = fields.requested;
88
- if (typeof fields.targetDirExisted === "boolean")
89
- record.targetDirExisted = fields.targetDirExisted;
90
- if (fields.error !== undefined)
91
- record.errorCategory = errorCategory(fields.error);
92
- const bytes = Buffer.from(`${JSON.stringify(record)}\n`);
93
- const fd = node_fs_1.default.openSync(file, node_fs_1.default.constants.O_WRONLY | node_fs_1.default.constants.O_APPEND | node_fs_1.default.constants.O_CREAT | (node_fs_1.default.constants.O_NOFOLLOW || 0), 0o600);
94
- try {
95
- const stat = node_fs_1.default.fstatSync(fd);
96
- if (!stat.isFile() || (typeof process.getuid === "function" && stat.uid !== process.getuid()) || (stat.mode & 0o077) !== 0)
97
- return;
98
- node_fs_1.default.writeSync(fd, bytes);
99
- node_fs_1.default.fchmodSync(fd, 0o600);
100
- }
101
- finally {
102
- node_fs_1.default.closeSync(fd);
103
- }
104
- }
105
- catch {
106
- // Diagnostics must never alter startup behavior.
107
- }
108
- }
@@ -1,117 +0,0 @@
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
- }
@@ -1,105 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- function privateDirectory(directory) {
4
- fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
5
- const stat = fs.lstatSync(directory);
6
- if (!stat.isDirectory() || stat.isSymbolicLink())
7
- throw new Error(`unsafe bundled Pi asset directory: ${directory}`);
8
- fs.chmodSync(directory, 0o700);
9
- }
10
- function writeEmbeddedFile(file, contents) {
11
- const temporary = `${file}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
12
- fs.writeFileSync(temporary, contents, { mode: 0o600, flag: "wx" });
13
- fs.renameSync(temporary, file);
14
- fs.chmodSync(file, 0o600);
15
- }
16
- /** Materialize upstream runtime assets that Bun cannot serve through ordinary fs paths. */
17
- export function prepareBuiltinPiPackageAssets() {
18
- const assets = globalThis.__LARKIN_EMBEDDED_BUILTIN_PI_ASSETS__;
19
- if (!assets)
20
- return;
21
- const agentDir = process.env.PI_CODING_AGENT_DIR;
22
- if (!agentDir)
23
- throw new Error("bundled Pi requires PI_CODING_AGENT_DIR");
24
- privateDirectory(agentDir);
25
- const packageDir = path.join(agentDir, ".larkin-official-pi-package");
26
- const themeDir = path.join(packageDir, "theme");
27
- privateDirectory(packageDir);
28
- privateDirectory(themeDir);
29
- writeEmbeddedFile(path.join(packageDir, "package.json"), assets.packageJson);
30
- writeEmbeddedFile(path.join(themeDir, "dark.json"), assets.darkTheme);
31
- writeEmbeddedFile(path.join(themeDir, "light.json"), assets.lightTheme);
32
- process.env.PI_PACKAGE_DIR = packageDir;
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
- }