Files
my-pi/pi-compaction/src/config.ts
T
云服务部-叶林立andDevin <158243242+devin-ai-integration[bot]@users.noreply.github.com> 2aa1be0b54 feat: 组合包配置以包内 config/ 为唯一来源,并新增受保护写入人工审批
配置统一:权限策略、硬防御、Tool Search、提示词封面以及新迁入的
RTK、pi-compaction、pi-worktree、pi-hermes-memory 配置全部由扩展
直接读取已安装组合包内的 config/,pi install / pi update 即完成部署;
删除 rebuild-bundle-config.mjs 与 bundle-manifest.json,my-pi/ 只保留
日志与缓存。update.sh 成功升级后将默认 git 来源的已安装副本
reset 回发布版本。AGENTS.md 新增「组合包配置原则」。

脚本整合:search_config.sh / ssh_config.sh 合并为 configure.sh
search|ssh;install.sh / update.sh 共享函数抽到 scripts/lib.sh,远程
安装器改为先下载到临时文件再执行,下载失败可被检测。

权限:hard-guards 新增 protectedWrites,写入 Pi 凭据/设置、已安装
组合包与扩展目录、pi-ssh 凭据库时一律人工确认(跳过 AutoReview、
yolo 与 Session grant,无 UI 时拒绝);放行 ls、LSP 查询、ctx 只读、
web_search、Chrome 只读与 git 只读命令,skill_manage 改为 ask。

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-20 01:05:31 -07:00

96 lines
3.7 KiB
TypeScript

import { readFile } from "node:fs/promises";
import { fileURLToPath } from "node:url";
import type { PiCompactionConfig } from "./types.js";
// Bundle-level `config/` is the single source of truth: the extension reads
// `config/pi-compaction.json` from inside the installed bundle (resolved
// relative to this source file). It never reads `settings.json` and never
// writes config at runtime.
export const CONFIG_PATH = fileURLToPath(
new URL("../../config/pi-compaction.json", import.meta.url),
);
export const DEFAULT_CONFIG: PiCompactionConfig = {
enabled: true,
softRatio: 0.72,
hardRatio: 0.9,
targetRatio: 0.55,
deterministic: {
enabled: true,
maxStubTokens: 1_024,
},
semantic: {
enabled: true,
model: "default",
thinking: "low",
concurrency: 1,
minInputChars: 4_000,
maxInputChars: 24_000,
maxRetries: 2,
},
native: {
enabled: true,
},
};
function ratio(value: unknown, fallback: number): number {
return typeof value === "number" && Number.isFinite(value) && value > 0 && value < 1 ? value : fallback;
}
function integer(value: unknown, fallback: number, min = 0, max = Number.MAX_SAFE_INTEGER): number {
return typeof value === "number" && Number.isInteger(value) && value >= min && value <= max ? value : fallback;
}
function object(value: unknown): Record<string, unknown> {
return value !== null && typeof value === "object" && !Array.isArray(value)
? value as Record<string, unknown>
: {};
}
export function normalizeConfig(value: unknown): PiCompactionConfig {
const input = object(value);
const deterministic = object(input.deterministic);
const semantic = object(input.semantic);
const native = object(input.native);
const softRatio = ratio(input.softRatio, DEFAULT_CONFIG.softRatio);
const hardRatio = Math.max(softRatio + 0.01, ratio(input.hardRatio, DEFAULT_CONFIG.hardRatio));
const targetRatio = Math.min(softRatio - 0.01, ratio(input.targetRatio, DEFAULT_CONFIG.targetRatio));
const thinking = ["off", "minimal", "low", "medium", "high", "xhigh"].includes(String(semantic.thinking))
? semantic.thinking as PiCompactionConfig["semantic"]["thinking"]
: DEFAULT_CONFIG.semantic.thinking;
return {
enabled: typeof input.enabled === "boolean" ? input.enabled : DEFAULT_CONFIG.enabled,
softRatio,
hardRatio: Math.min(0.99, hardRatio),
targetRatio: Math.max(0.1, targetRatio),
deterministic: {
enabled: typeof deterministic.enabled === "boolean" ? deterministic.enabled : DEFAULT_CONFIG.deterministic.enabled,
maxStubTokens: integer(deterministic.maxStubTokens, DEFAULT_CONFIG.deterministic.maxStubTokens, 32, 4_096),
},
semantic: {
enabled: typeof semantic.enabled === "boolean" ? semantic.enabled : DEFAULT_CONFIG.semantic.enabled,
model: typeof semantic.model === "string" && semantic.model.trim() ? semantic.model.trim() : DEFAULT_CONFIG.semantic.model,
thinking,
concurrency: integer(semantic.concurrency, DEFAULT_CONFIG.semantic.concurrency, 1, 4),
minInputChars: integer(semantic.minInputChars, DEFAULT_CONFIG.semantic.minInputChars, 0, 1_000_000),
maxInputChars: integer(semantic.maxInputChars, DEFAULT_CONFIG.semantic.maxInputChars, 1_000, 1_000_000),
maxRetries: integer(semantic.maxRetries, DEFAULT_CONFIG.semantic.maxRetries, 0, 5),
},
native: {
enabled: typeof native.enabled === "boolean" ? native.enabled : DEFAULT_CONFIG.native.enabled,
},
};
}
export async function loadConfig(
configPath: string = CONFIG_PATH,
): Promise<PiCompactionConfig> {
try {
const parsed = JSON.parse(await readFile(configPath, "utf8"));
return normalizeConfig(parsed);
} catch {
return structuredClone(DEFAULT_CONFIG);
}
}