@lark-project/cli
Version:
飞书项目插件开发工具
125 lines (124 loc) • 6.14 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.snapshotLocalConfigBackup = exports.isLocalConfigDirty = exports.checkLocalConfigDrift = exports.writeLocalConfigLastSet = void 0;
const crypto_1 = require("crypto");
const fs_extra_1 = require("fs-extra");
const path_1 = __importDefault(require("path"));
const write_local_point_config_1 = require("./write-local-point-config");
const workspace_1 = require("../v2/utils/workspace");
// canonical hash 用于 drift 比较——JSON.stringify 不带 indent,规避 indent / 行尾差异。
// 不规范 key 顺序:用户手动调换 key 顺序也算"改了文件",提示是合理的。
function canonicalHash(raw) {
return (0, crypto_1.createHash)('sha256').update(JSON.stringify(raw)).digest('hex');
}
/**
* `lpm local-config set` 成功后调用:从磁盘 point.config.local.json 读一次最终落盘内容、
* 算 canonical hash 写到 .lpm-cache/config/last-set.json,作为后续 drift 检测的基线。
*
* **必须从磁盘读、不能传内存对象**:writeJSONSync 写磁盘时会过滤 undefined 字段,
* 内存对象与磁盘读出来的 hash 可能不一致——若拿内存对象算 baseline,set 完
* 立刻跑 start 会误报 drift,反向训练 AI 忽略 stderr 信号。
*
* 写入失败静默——drift 检查只是辅助信号,不能拖累 set 的主流程。
*/
function writeLocalConfigLastSet() {
try {
const localPath = (0, write_local_point_config_1.getLocalConfigPath)();
if (!(0, fs_extra_1.existsSync)(localPath))
return;
const parsed = JSON.parse((0, fs_extra_1.readFileSync)(localPath, 'utf8'));
(0, workspace_1.ensureWorkspace)();
const paths = (0, workspace_1.workspacePaths)();
(0, fs_extra_1.writeJSONSync)(paths.configLastSet, {
syncedAt: new Date().toISOString(),
hash: canonicalHash(parsed),
}, { spaces: 2 });
}
catch (_a) {
// intentional swallow
}
}
exports.writeLocalConfigLastSet = writeLocalConfigLastSet;
/**
* Stage Config plan → apply 边界守护:检测 point.config.local.json 是否
* 在上次 `lpm local-config set` 之后被改动过、还没同步到 backend。
*
* 触发条件:本地配置文件存在 + last-set sentinel 存在 + canonical hash 不匹配
* → 输出一行 stderr hint。**不阻断主流程**——挂在 start / build / diff 前
* 是为了把"agent 改完文件就停"的信号显式化,而非强制 gate。
*
* 设计 fail-safe:所有 IO / parse 异常静默吞掉——drift 检查只是辅助提示,
* 不能让它把 start / build / diff 弄挂。永不调 process.exit()。
*/
function checkLocalConfigDrift() {
try {
const localPath = (0, write_local_point_config_1.getLocalConfigPath)();
if (!(0, fs_extra_1.existsSync)(localPath))
return;
const paths = (0, workspace_1.workspacePaths)();
if (!(0, fs_extra_1.existsSync)(paths.configLastSet))
return;
const current = JSON.parse((0, fs_extra_1.readFileSync)(localPath, 'utf8'));
const sentinel = JSON.parse((0, fs_extra_1.readFileSync)(paths.configLastSet, 'utf8'));
if (!(sentinel === null || sentinel === void 0 ? void 0 : sentinel.hash))
return;
if (canonicalHash(current) === sentinel.hash)
return;
process.stderr.write('[hint] point.config.local.json 已修改但未同步到 backend。\n' +
' Run `lpm local-config set --from point.config.local.json` first.\n');
}
catch (_a) {
// intentional swallow — drift check is best-effort
}
}
exports.checkLocalConfigDrift = checkLocalConfigDrift;
/**
* 三态 dirty 判定(供 set 处「本地未推改动保护」闸口用):
* true = point.config.local.json 自上次 `set` 后被手改过(hash 不匹配)
* false = 与上次 set 落盘一致(本地只是相对远端旧,不算被改)
* null = 无法判定(无本地文件 / 无 sentinel / IO 异常)——调用方据此跳过检查(fail-open)
* 与 checkLocalConfigDrift 同源(复用 canonicalHash + 同一 sentinel),只是返回值而非打 hint。
*/
function isLocalConfigDirty() {
try {
const localPath = (0, write_local_point_config_1.getLocalConfigPath)();
if (!(0, fs_extra_1.existsSync)(localPath))
return null;
const paths = (0, workspace_1.workspacePaths)();
if (!(0, fs_extra_1.existsSync)(paths.configLastSet))
return null;
const current = JSON.parse((0, fs_extra_1.readFileSync)(localPath, 'utf8'));
const sentinel = JSON.parse((0, fs_extra_1.readFileSync)(paths.configLastSet, 'utf8'));
if (!(sentinel === null || sentinel === void 0 ? void 0 : sentinel.hash))
return null;
return canonicalHash(current) !== sentinel.hash;
}
catch (_a) {
return null; // fail-open:判不出就不拦
}
}
exports.isLocalConfigDirty = isLocalConfigDirty;
/**
* 覆盖前把当前 point.config.local.json 快照到 .lpm-cache/config/local-pre-set-<时间戳>.json,
* 返回相对路径;无本地文件 / 异常 → null。供带 --overwrite-local-edits 的有损覆盖前调用(双保险)。
*/
function snapshotLocalConfigBackup() {
try {
const localPath = (0, write_local_point_config_1.getLocalConfigPath)();
if (!(0, fs_extra_1.existsSync)(localPath))
return null;
(0, workspace_1.ensureWorkspace)();
const paths = (0, workspace_1.workspacePaths)();
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
const dest = path_1.default.join(paths.configDir, `local-pre-set-${stamp}.json`);
(0, fs_extra_1.copyFileSync)(localPath, dest);
return paths.relative(dest);
}
catch (_a) {
return null;
}
}
exports.snapshotLocalConfigBackup = snapshotLocalConfigBackup;