wangchuan 1.0.0
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/.wangchuan/config.example.json +43 -0
- package/README.md +210 -0
- package/dist/bin/wangchuan.d.ts +9 -0
- package/dist/bin/wangchuan.d.ts.map +1 -0
- package/dist/bin/wangchuan.js +93 -0
- package/dist/bin/wangchuan.js.map +1 -0
- package/dist/src/commands/diff.d.ts +9 -0
- package/dist/src/commands/diff.d.ts.map +1 -0
- package/dist/src/commands/diff.js +96 -0
- package/dist/src/commands/diff.js.map +1 -0
- package/dist/src/commands/init.d.ts +6 -0
- package/dist/src/commands/init.d.ts.map +1 -0
- package/dist/src/commands/init.js +49 -0
- package/dist/src/commands/init.js.map +1 -0
- package/dist/src/commands/list.d.ts +9 -0
- package/dist/src/commands/list.d.ts.map +1 -0
- package/dist/src/commands/list.js +45 -0
- package/dist/src/commands/list.js.map +1 -0
- package/dist/src/commands/pull.d.ts +6 -0
- package/dist/src/commands/pull.d.ts.map +1 -0
- package/dist/src/commands/pull.js +53 -0
- package/dist/src/commands/pull.js.map +1 -0
- package/dist/src/commands/push.d.ts +9 -0
- package/dist/src/commands/push.d.ts.map +1 -0
- package/dist/src/commands/push.js +72 -0
- package/dist/src/commands/push.js.map +1 -0
- package/dist/src/commands/status.d.ts +6 -0
- package/dist/src/commands/status.d.ts.map +1 -0
- package/dist/src/commands/status.js +86 -0
- package/dist/src/commands/status.js.map +1 -0
- package/dist/src/core/config.d.ts +27 -0
- package/dist/src/core/config.d.ts.map +1 -0
- package/dist/src/core/config.js +110 -0
- package/dist/src/core/config.js.map +1 -0
- package/dist/src/core/crypto.d.ts +24 -0
- package/dist/src/core/crypto.d.ts.map +1 -0
- package/dist/src/core/crypto.js +89 -0
- package/dist/src/core/crypto.js.map +1 -0
- package/dist/src/core/git.d.ts +18 -0
- package/dist/src/core/git.d.ts.map +1 -0
- package/dist/src/core/git.js +79 -0
- package/dist/src/core/git.js.map +1 -0
- package/dist/src/core/sync.d.ts +27 -0
- package/dist/src/core/sync.d.ts.map +1 -0
- package/dist/src/core/sync.js +253 -0
- package/dist/src/core/sync.js.map +1 -0
- package/dist/src/types.d.ts +95 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +5 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/linediff.d.ts +22 -0
- package/dist/src/utils/linediff.d.ts.map +1 -0
- package/dist/src/utils/linediff.js +80 -0
- package/dist/src/utils/linediff.js.map +1 -0
- package/dist/src/utils/logger.d.ts +13 -0
- package/dist/src/utils/logger.d.ts.map +1 -0
- package/dist/src/utils/logger.js +34 -0
- package/dist/src/utils/logger.js.map +1 -0
- package/dist/src/utils/prompt.d.ts +14 -0
- package/dist/src/utils/prompt.d.ts.map +1 -0
- package/dist/src/utils/prompt.js +37 -0
- package/dist/src/utils/prompt.js.map +1 -0
- package/dist/src/utils/validator.d.ts +17 -0
- package/dist/src/utils/validator.d.ts.map +1 -0
- package/dist/src/utils/validator.js +33 -0
- package/dist/src/utils/validator.js.map +1 -0
- package/dist/test/crypto.test.d.ts +5 -0
- package/dist/test/crypto.test.d.ts.map +1 -0
- package/dist/test/crypto.test.js +93 -0
- package/dist/test/crypto.test.js.map +1 -0
- package/package.json +56 -0
- package/skill/SKILL.md +67 -0
- package/skill/wangchuan-skill.sh +54 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pull.ts — wangchuan pull 命令
|
|
3
|
+
*/
|
|
4
|
+
import { config } from '../core/config.js';
|
|
5
|
+
import { gitEngine } from '../core/git.js';
|
|
6
|
+
import { syncEngine } from '../core/sync.js';
|
|
7
|
+
import { validator } from '../utils/validator.js';
|
|
8
|
+
import { logger } from '../utils/logger.js';
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
import ora from 'ora';
|
|
11
|
+
export async function cmdPull({ agent } = {}) {
|
|
12
|
+
logger.banner('忘川 · 拉取配置');
|
|
13
|
+
const cfg = config.load();
|
|
14
|
+
validator.requireInit(cfg);
|
|
15
|
+
const repoPath = syncEngine.expandHome(cfg.localRepoPath);
|
|
16
|
+
if (agent)
|
|
17
|
+
logger.info(`过滤智能体: ${chalk.cyan(agent)}`);
|
|
18
|
+
// ── 1. git pull ────────────────────────────────────────────
|
|
19
|
+
let spinner = ora(`从 ${cfg.repo} 拉取最新配置 …`).start();
|
|
20
|
+
try {
|
|
21
|
+
await gitEngine.pull(repoPath, cfg.branch);
|
|
22
|
+
spinner.succeed('远端配置已拉取');
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
spinner.fail('Git 拉取失败');
|
|
26
|
+
throw new Error(`Git pull 失败: ${err.message}`);
|
|
27
|
+
}
|
|
28
|
+
// ── 2. 还原文件 ───────────────────────────────────────────
|
|
29
|
+
spinner.stop(); // 冲突提示前停止 spinner
|
|
30
|
+
let result;
|
|
31
|
+
try {
|
|
32
|
+
result = await syncEngine.restoreFromRepo(cfg, agent);
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
throw new Error(`还原失败: ${err.message}`);
|
|
36
|
+
}
|
|
37
|
+
// ── 3. 输出结果 ───────────────────────────────────────────
|
|
38
|
+
console.log();
|
|
39
|
+
if (result.synced.length === 0 && result.skipped.length > 0) {
|
|
40
|
+
logger.info('仓库中暂无配置文件,请先执行 wangchuan push');
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
for (const f of result.synced) {
|
|
44
|
+
const label = result.decrypted.includes(f) ? chalk.gray('[已解密]') : '';
|
|
45
|
+
logger.ok(` ${chalk.green(f)} ${label}`);
|
|
46
|
+
}
|
|
47
|
+
if (result.skipped.length > 0) {
|
|
48
|
+
logger.info(`\n跳过(仓库中不存在): ${result.skipped.length} 个文件`);
|
|
49
|
+
}
|
|
50
|
+
logger.ok(`\n共同步 ${result.synced.length} 个文件(含 ${result.decrypted.length} 个加密文件,${result.conflicts.length} 个冲突)`);
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=pull.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull.js","sourceRoot":"","sources":["../../../src/commands/pull.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* push.ts — wangchuan push 命令
|
|
3
|
+
*/
|
|
4
|
+
import type { PushOptions, CommitResult, StageResult } from '../types.js';
|
|
5
|
+
export interface PushCommandResult extends CommitResult {
|
|
6
|
+
readonly stageResult?: StageResult;
|
|
7
|
+
}
|
|
8
|
+
export declare function cmdPush({ message, agent }?: PushOptions): Promise<PushCommandResult>;
|
|
9
|
+
//# sourceMappingURL=push.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../../src/commands/push.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI1E,MAAM,WAAW,iBAAkB,SAAQ,YAAY;IACrD,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC;CACpC;AAED,wBAAsB,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,GAAE,WAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA4D9F"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* push.ts — wangchuan push 命令
|
|
3
|
+
*/
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import { config } from '../core/config.js';
|
|
6
|
+
import { gitEngine } from '../core/git.js';
|
|
7
|
+
import { syncEngine } from '../core/sync.js';
|
|
8
|
+
import { validator } from '../utils/validator.js';
|
|
9
|
+
import { logger } from '../utils/logger.js';
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import ora from 'ora';
|
|
12
|
+
export async function cmdPush({ message, agent } = {}) {
|
|
13
|
+
logger.banner('忘川 · 推送配置');
|
|
14
|
+
const cfg = config.load();
|
|
15
|
+
validator.requireInit(cfg);
|
|
16
|
+
const repoPath = syncEngine.expandHome(cfg.localRepoPath);
|
|
17
|
+
const hostname = cfg.hostname || os.hostname();
|
|
18
|
+
if (agent)
|
|
19
|
+
logger.info(`过滤智能体: ${chalk.cyan(agent)}`);
|
|
20
|
+
const agentTag = agent ? `[${agent}]` : '';
|
|
21
|
+
const msg = message
|
|
22
|
+
? `sync: ${message} ${agentTag}[${hostname}]`.trimEnd()
|
|
23
|
+
: `sync: update configs ${agentTag}[${hostname}]`.trimEnd();
|
|
24
|
+
// ── 1. 工作区 → 仓库目录 ────────────────────────────────────
|
|
25
|
+
let spinner = ora('加密并准备配置文件 …').start();
|
|
26
|
+
let stageResult;
|
|
27
|
+
try {
|
|
28
|
+
stageResult = await syncEngine.stageToRepo(cfg, agent);
|
|
29
|
+
spinner.succeed(`已准备 ${stageResult.synced.length} 个文件`);
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
spinner.fail('暂存文件失败');
|
|
33
|
+
throw new Error(`准备文件失败: ${err.message}`);
|
|
34
|
+
}
|
|
35
|
+
if (stageResult.synced.length === 0) {
|
|
36
|
+
logger.info('没有找到任何可同步的文件,请检查工作区路径是否正确');
|
|
37
|
+
return { committed: false, pushed: false };
|
|
38
|
+
}
|
|
39
|
+
// ── 2. commit + push ────────────────────────────────────────
|
|
40
|
+
spinner = ora('提交并推送到远端仓库 …').start();
|
|
41
|
+
let pushResult;
|
|
42
|
+
try {
|
|
43
|
+
pushResult = await gitEngine.commitAndPush(repoPath, msg, cfg.branch);
|
|
44
|
+
if (pushResult.committed) {
|
|
45
|
+
spinner.succeed(`已推送到 ${cfg.repo}`);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
spinner.info('没有变更需要提交(仓库已是最新)');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
spinner.fail('推送失败,正在回滚 …');
|
|
53
|
+
try {
|
|
54
|
+
await gitEngine.rollback(repoPath);
|
|
55
|
+
}
|
|
56
|
+
catch (re) {
|
|
57
|
+
logger.error(`回滚失败: ${re.message}`);
|
|
58
|
+
}
|
|
59
|
+
throw new Error(`推送失败: ${err.message}`);
|
|
60
|
+
}
|
|
61
|
+
// ── 3. 输出汇总 ─────────────────────────────────────────────
|
|
62
|
+
if (pushResult.committed) {
|
|
63
|
+
console.log();
|
|
64
|
+
for (const f of stageResult.synced) {
|
|
65
|
+
const label = stageResult.encrypted.includes(f) ? chalk.gray('[已加密]') : '';
|
|
66
|
+
logger.ok(` ${chalk.green(f)} ${label}`);
|
|
67
|
+
}
|
|
68
|
+
logger.ok(`\n推送完成:${stageResult.synced.length} 个文件,commit: ${pushResult.sha ?? '-'}`);
|
|
69
|
+
}
|
|
70
|
+
return { ...pushResult, stageResult };
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=push.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push.js","sourceRoot":"","sources":["../../../src/commands/push.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/commands/status.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,wBAAsB,SAAS,CAAC,EAAE,KAAK,EAAE,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkF5E"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* status.ts — wangchuan status 命令
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import { config } from '../core/config.js';
|
|
6
|
+
import { gitEngine } from '../core/git.js';
|
|
7
|
+
import { syncEngine } from '../core/sync.js';
|
|
8
|
+
import { validator } from '../utils/validator.js';
|
|
9
|
+
import { logger } from '../utils/logger.js';
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
export async function cmdStatus({ agent } = {}) {
|
|
12
|
+
logger.banner('忘川 · 同步状态');
|
|
13
|
+
const cfg = config.load();
|
|
14
|
+
validator.requireInit(cfg);
|
|
15
|
+
const repoPath = syncEngine.expandHome(cfg.localRepoPath);
|
|
16
|
+
console.log(chalk.bold(' 仓库地址:') + chalk.cyan(cfg.repo));
|
|
17
|
+
console.log(chalk.bold(' 本地路径:') + repoPath);
|
|
18
|
+
console.log(chalk.bold(' 分 支: ') + chalk.yellow(cfg.branch));
|
|
19
|
+
if (agent)
|
|
20
|
+
console.log(chalk.bold(' 过滤智能体:') + chalk.cyan(agent));
|
|
21
|
+
console.log();
|
|
22
|
+
// ── 最近提交 ────────────────────────────────────────────────
|
|
23
|
+
try {
|
|
24
|
+
const logs = await gitEngine.log(repoPath, 3);
|
|
25
|
+
if (logs.length > 0) {
|
|
26
|
+
console.log(chalk.bold(' 最近提交:'));
|
|
27
|
+
for (const c of logs) {
|
|
28
|
+
const date = new Date(c.date).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' });
|
|
29
|
+
console.log(` ${chalk.gray(c.hash.slice(0, 7))} ${chalk.white(c.message.slice(0, 60))} ${chalk.gray(date)}`);
|
|
30
|
+
}
|
|
31
|
+
console.log();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
logger.warn('无法读取 git 日志(仓库可能尚未克隆)');
|
|
36
|
+
}
|
|
37
|
+
// ── Git 工作树状态 ──────────────────────────────────────────
|
|
38
|
+
const gitStatus = await gitEngine.status(repoPath);
|
|
39
|
+
if (gitStatus !== null) {
|
|
40
|
+
const { modified, created, deleted, not_added } = gitStatus;
|
|
41
|
+
const hasPending = modified.length + created.length + deleted.length + not_added.length > 0;
|
|
42
|
+
if (hasPending) {
|
|
43
|
+
console.log(chalk.bold(' 本地仓库(未提交变更):'));
|
|
44
|
+
modified.forEach(f => console.log(` ${chalk.yellow('M')} ${f}`));
|
|
45
|
+
created.forEach(f => console.log(` ${chalk.green('A')} ${f}`));
|
|
46
|
+
deleted.forEach(f => console.log(` ${chalk.red('D')} ${f}`));
|
|
47
|
+
not_added.forEach(f => console.log(` ${chalk.gray('?')} ${f}`));
|
|
48
|
+
console.log();
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
logger.ok(' 本地仓库与远端保持一致');
|
|
52
|
+
console.log();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// ── 工作区差异 ──────────────────────────────────────────────
|
|
56
|
+
try {
|
|
57
|
+
const diff = await syncEngine.diff(cfg, agent);
|
|
58
|
+
const total = diff.added.length + diff.modified.length + diff.missing.length;
|
|
59
|
+
if (total === 0) {
|
|
60
|
+
logger.ok(' 工作区与仓库一致,无需同步');
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
console.log(chalk.bold(' 工作区差异:'));
|
|
64
|
+
diff.added.forEach(f => console.log(` ${chalk.green('+')} ${f} ${chalk.gray('(本地新增,推送后将同步)')}`));
|
|
65
|
+
diff.modified.forEach(f => console.log(` ${chalk.yellow('~')} ${f} ${chalk.gray('(已修改)')}`));
|
|
66
|
+
diff.missing.forEach(f => console.log(` ${chalk.red('-')} ${f} ${chalk.gray('(本地缺失,拉取后将还原)')}`));
|
|
67
|
+
console.log();
|
|
68
|
+
console.log(` ${chalk.yellow('+')} ${diff.added.length} 新增 ` +
|
|
69
|
+
`${chalk.yellow('~')} ${diff.modified.length} 修改 ` +
|
|
70
|
+
`${chalk.red('-')} ${diff.missing.length} 缺失`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
logger.warn(`差异分析失败: ${err.message}`);
|
|
75
|
+
}
|
|
76
|
+
// ── 文件清单 ────────────────────────────────────────────────
|
|
77
|
+
console.log();
|
|
78
|
+
const entries = syncEngine.buildFileEntries(cfg, undefined, agent);
|
|
79
|
+
console.log(chalk.bold(` 配置文件清单(共 ${entries.length} 项):`));
|
|
80
|
+
for (const e of entries) {
|
|
81
|
+
const mark = fs.existsSync(e.srcAbs) ? chalk.green('✔') : chalk.red('✖');
|
|
82
|
+
const encLabel = e.encrypt ? chalk.gray('[enc]') : '';
|
|
83
|
+
console.log(` ${mark} ${e.repoRel} ${encLabel}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/commands/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* config.ts — 全局配置管理
|
|
3
|
+
*
|
|
4
|
+
* 配置文件:~/.wangchuan/config.json
|
|
5
|
+
* 密钥文件:~/.wangchuan/master.key
|
|
6
|
+
*/
|
|
7
|
+
import type { WangchuanConfig } from '../types.js';
|
|
8
|
+
export declare const config: {
|
|
9
|
+
/** 读取配置,不存在时返回 null */
|
|
10
|
+
readonly load: () => WangchuanConfig | null;
|
|
11
|
+
/** 保存配置到磁盘 */
|
|
12
|
+
readonly save: (cfg: WangchuanConfig) => void;
|
|
13
|
+
/** 探测远程仓库的默认分支 */
|
|
14
|
+
readonly detectDefaultBranch: (repo: string) => Promise<string>;
|
|
15
|
+
/** 初始化目录和配置文件,返回新建的配置对象 */
|
|
16
|
+
readonly initialize: (repo: string) => Promise<WangchuanConfig>;
|
|
17
|
+
/** 浅合并两个配置(override 优先) */
|
|
18
|
+
readonly merge: (base: WangchuanConfig, override: Partial<WangchuanConfig>) => WangchuanConfig;
|
|
19
|
+
/** 常用路径常量 */
|
|
20
|
+
readonly paths: {
|
|
21
|
+
readonly dir: string;
|
|
22
|
+
readonly config: string;
|
|
23
|
+
readonly key: string;
|
|
24
|
+
readonly example: string;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/core/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,OAAO,KAAK,EAAE,eAAe,EAAiB,MAAM,aAAa,CAAC;AA0ClE,eAAO,MAAM,MAAM;IACjB,uBAAuB;yBACf,eAAe,GAAG,IAAI;IAU9B,cAAc;yBACJ,eAAe,KAAG,IAAI;IAMhC,kBAAkB;yCACc,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;IAaxD,2BAA2B;gCACJ,MAAM,KAAG,OAAO,CAAC,eAAe,CAAC;IAexD,2BAA2B;2BACf,eAAe,YAAY,OAAO,CAAC,eAAe,CAAC,KAAG,eAAe;IAIjF,aAAa;;;;;;;CAOL,CAAC"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* config.ts — 全局配置管理
|
|
3
|
+
*
|
|
4
|
+
* 配置文件:~/.wangchuan/config.json
|
|
5
|
+
* 密钥文件:~/.wangchuan/master.key
|
|
6
|
+
*/
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import os from 'os';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
import { logger } from '../utils/logger.js';
|
|
12
|
+
const WANGCHUAN_DIR = path.join(os.homedir(), '.wangchuan');
|
|
13
|
+
const CONFIG_PATH = path.join(WANGCHUAN_DIR, 'config.json');
|
|
14
|
+
const KEY_PATH = path.join(WANGCHUAN_DIR, 'master.key');
|
|
15
|
+
const EXAMPLE_PATH = fileURLToPath(new URL('../../.wangchuan/config.example.json', import.meta.url));
|
|
16
|
+
/** 默认配置骨架(不含敏感值) */
|
|
17
|
+
const DEFAULT_PROFILES = {
|
|
18
|
+
openclaw: {
|
|
19
|
+
enabled: true,
|
|
20
|
+
workspacePath: path.join(os.homedir(), '.openclaw', 'workspace'),
|
|
21
|
+
syncFiles: [
|
|
22
|
+
{ src: 'MEMORY.md', encrypt: true },
|
|
23
|
+
{ src: 'AGENTS.md', encrypt: false },
|
|
24
|
+
{ src: 'SOUL.md', encrypt: false },
|
|
25
|
+
{ src: 'USER.md', encrypt: true },
|
|
26
|
+
{ src: 'TOOLS.md', encrypt: false },
|
|
27
|
+
{ src: 'config/mcporter.json', encrypt: true },
|
|
28
|
+
],
|
|
29
|
+
syncDirs: [
|
|
30
|
+
{ src: 'skills/', encrypt: false },
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
claude: {
|
|
34
|
+
enabled: false,
|
|
35
|
+
workspacePath: path.join(os.homedir(), '.claude'),
|
|
36
|
+
syncFiles: [
|
|
37
|
+
{ src: '.claude.json', encrypt: true },
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
gemini: {
|
|
41
|
+
enabled: false,
|
|
42
|
+
workspacePath: path.join(os.homedir(), '.gemini'),
|
|
43
|
+
syncFiles: [
|
|
44
|
+
{ src: 'settings.internal.json', encrypt: true },
|
|
45
|
+
{ src: 'projects.json', encrypt: false },
|
|
46
|
+
{ src: 'trustedFolders.json', encrypt: false },
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
export const config = {
|
|
51
|
+
/** 读取配置,不存在时返回 null */
|
|
52
|
+
load() {
|
|
53
|
+
if (!fs.existsSync(CONFIG_PATH))
|
|
54
|
+
return null;
|
|
55
|
+
try {
|
|
56
|
+
const raw = fs.readFileSync(CONFIG_PATH, 'utf-8');
|
|
57
|
+
return JSON.parse(raw);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
throw new Error(`读取配置失败: ${err.message}`);
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
/** 保存配置到磁盘 */
|
|
64
|
+
save(cfg) {
|
|
65
|
+
fs.mkdirSync(WANGCHUAN_DIR, { recursive: true });
|
|
66
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(cfg, null, 2), 'utf-8');
|
|
67
|
+
logger.debug(`配置已保存到 ${CONFIG_PATH}`);
|
|
68
|
+
},
|
|
69
|
+
/** 探测远程仓库的默认分支 */
|
|
70
|
+
async detectDefaultBranch(repo) {
|
|
71
|
+
try {
|
|
72
|
+
const { execSync } = await import('child_process');
|
|
73
|
+
const result = execSync(`git ls-remote --symref ${repo} HEAD`, {
|
|
74
|
+
encoding: 'utf-8'
|
|
75
|
+
});
|
|
76
|
+
const match = result.match(/ref: refs\/heads\/(\S+)\s+HEAD/);
|
|
77
|
+
return match?.[1] ?? 'main'; // 默认回退到 main
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return 'main'; // 探测失败时用 main
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
/** 初始化目录和配置文件,返回新建的配置对象 */
|
|
84
|
+
async initialize(repo) {
|
|
85
|
+
fs.mkdirSync(WANGCHUAN_DIR, { recursive: true });
|
|
86
|
+
const branch = await this.detectDefaultBranch(repo);
|
|
87
|
+
const cfg = {
|
|
88
|
+
repo,
|
|
89
|
+
branch,
|
|
90
|
+
localRepoPath: path.join(WANGCHUAN_DIR, 'repo'),
|
|
91
|
+
keyPath: KEY_PATH,
|
|
92
|
+
hostname: os.hostname(),
|
|
93
|
+
profiles: { default: DEFAULT_PROFILES },
|
|
94
|
+
};
|
|
95
|
+
this.save(cfg);
|
|
96
|
+
return cfg;
|
|
97
|
+
},
|
|
98
|
+
/** 浅合并两个配置(override 优先) */
|
|
99
|
+
merge(base, override) {
|
|
100
|
+
return { ...base, ...override };
|
|
101
|
+
},
|
|
102
|
+
/** 常用路径常量 */
|
|
103
|
+
paths: {
|
|
104
|
+
dir: WANGCHUAN_DIR,
|
|
105
|
+
config: CONFIG_PATH,
|
|
106
|
+
key: KEY_PATH,
|
|
107
|
+
example: EXAMPLE_PATH,
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/core/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAQ,IAAI,CAAC;AACtB,OAAO,EAAE,MAAQ,IAAI,CAAC;AACtB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAG5C,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;AAC5D,MAAM,WAAW,GAAK,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AAC9D,MAAM,QAAQ,GAAQ,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;AAC7D,MAAM,YAAY,GAAI,aAAa,CAAC,IAAI,GAAG,CAAC,sCAAsC,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAEtG,oBAAoB;AACpB,MAAM,gBAAgB,GAAkB;IACtC,QAAQ,EAAE;QACR,OAAO,EAAE,IAAI;QACb,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,WAAW,CAAC;QAChE,SAAS,EAAE;YACT,EAAE,GAAG,EAAE,WAAW,EAAa,OAAO,EAAE,IAAI,EAAG;YAC/C,EAAE,GAAG,EAAE,WAAW,EAAa,OAAO,EAAE,KAAK,EAAE;YAC/C,EAAE,GAAG,EAAE,SAAS,EAAe,OAAO,EAAE,KAAK,EAAE;YAC/C,EAAE,GAAG,EAAE,SAAS,EAAe,OAAO,EAAE,IAAI,EAAG;YAC/C,EAAE,GAAG,EAAE,UAAU,EAAc,OAAO,EAAE,KAAK,EAAE;YAC/C,EAAE,GAAG,EAAE,sBAAsB,EAAE,OAAO,EAAE,IAAI,EAAG;SAChD;QACD,QAAQ,EAAE;YACR,EAAE,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;SACnC;KACF;IACD,MAAM,EAAE;QACN,OAAO,EAAE,KAAK;QACd,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC;QACjD,SAAS,EAAE;YACT,EAAE,GAAG,EAAE,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE;SACvC;KACF;IACD,MAAM,EAAE;QACN,OAAO,EAAE,KAAK;QACd,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC;QACjD,SAAS,EAAE;YACT,EAAE,GAAG,EAAE,wBAAwB,EAAE,OAAO,EAAE,IAAI,EAAG;YACjD,EAAE,GAAG,EAAE,eAAe,EAAW,OAAO,EAAE,KAAK,EAAE;YACjD,EAAE,GAAG,EAAE,qBAAqB,EAAK,OAAO,EAAE,KAAK,EAAE;SAClD;KACF;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,uBAAuB;IACvB,IAAI;QACF,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC;YAAE,OAAO,IAAI,CAAC;QAC7C,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;YAClD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAoB,CAAC;QAC5C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,WAAY,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED,cAAc;IACd,IAAI,CAAC,GAAoB;QACvB,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QACrE,MAAM,CAAC,KAAK,CAAC,UAAU,WAAW,EAAE,CAAC,CAAC;IACxC,CAAC;IAED,kBAAkB;IAClB,KAAK,CAAC,mBAAmB,CAAC,IAAY;QACpC,IAAI,CAAC;YACH,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;YACnD,MAAM,MAAM,GAAG,QAAQ,CAAC,0BAA0B,IAAI,OAAO,EAAE;gBAC7D,QAAQ,EAAE,OAAyB;aACpC,CAAC,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;YAC7D,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAE,aAAa;QAC7C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,MAAM,CAAC,CAAE,cAAc;QAChC,CAAC;IACH,CAAC;IAED,2BAA2B;IAC3B,KAAK,CAAC,UAAU,CAAC,IAAY;QAC3B,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QACpD,MAAM,GAAG,GAAoB;YAC3B,IAAI;YACJ,MAAM;YACN,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC;YAC/C,OAAO,EAAQ,QAAQ;YACvB,QAAQ,EAAO,EAAE,CAAC,QAAQ,EAAE;YAC5B,QAAQ,EAAE,EAAE,OAAO,EAAE,gBAAgB,EAAE;SACxC,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,OAAO,GAAG,CAAC;IACb,CAAC;IAED,2BAA2B;IAC3B,KAAK,CAAC,IAAqB,EAAE,QAAkC;QAC7D,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,QAAQ,EAAE,CAAC;IAClC,CAAC;IAED,aAAa;IACb,KAAK,EAAE;QACL,GAAG,EAAM,aAAa;QACtB,MAAM,EAAG,WAAW;QACpB,GAAG,EAAM,QAAQ;QACjB,OAAO,EAAE,YAAY;KACtB;CACO,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* crypto.ts — AES-256-GCM 加解密模块
|
|
3
|
+
*
|
|
4
|
+
* 密钥文件格式:32 字节随机数据,以十六进制字符串存储(64 chars)
|
|
5
|
+
* 密文文件格式:IV(12B) | AuthTag(16B) | CipherText → Base64 写入 .enc 文件
|
|
6
|
+
*/
|
|
7
|
+
export declare const cryptoEngine: {
|
|
8
|
+
/**
|
|
9
|
+
* 生成新主密钥并写入文件(mode 0o600)
|
|
10
|
+
* 返回原始 32 字节 Buffer,供测试断言使用
|
|
11
|
+
*/
|
|
12
|
+
readonly generateKey: (keyPath: string) => Buffer;
|
|
13
|
+
/** 密钥文件是否存在 */
|
|
14
|
+
readonly hasKey: (keyPath: string) => boolean;
|
|
15
|
+
/** 加密源文件,密文写入 destPath(通常以 .enc 结尾) */
|
|
16
|
+
readonly encryptFile: (srcPath: string, destPath: string, keyPath: string) => void;
|
|
17
|
+
/** 解密 .enc 文件,明文写入 destPath */
|
|
18
|
+
readonly decryptFile: (srcPath: string, destPath: string, keyPath: string) => void;
|
|
19
|
+
/** 加密任意字符串,返回 Base64 密文 */
|
|
20
|
+
readonly encryptString: (plaintext: string, keyPath: string) => string;
|
|
21
|
+
/** 解密 Base64 密文,返回原始字符串 */
|
|
22
|
+
readonly decryptString: (b64: string, keyPath: string) => string;
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=crypto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../../src/core/crypto.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA6CH,eAAO,MAAM,YAAY;IACvB;;;OAGG;oCACkB,MAAM,KAAG,MAAM;IAQpC,eAAe;+BACC,MAAM,KAAG,OAAO;IAIhC,uCAAuC;oCAClB,MAAM,YAAY,MAAM,WAAW,MAAM,KAAG,IAAI;IASrE,+BAA+B;oCACV,MAAM,YAAY,MAAM,WAAW,MAAM,KAAG,IAAI;IASrE,2BAA2B;wCACF,MAAM,WAAW,MAAM,KAAG,MAAM;IAKzD,2BAA2B;kCACR,MAAM,WAAW,MAAM,KAAG,MAAM;CAI3C,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* crypto.ts — AES-256-GCM 加解密模块
|
|
3
|
+
*
|
|
4
|
+
* 密钥文件格式:32 字节随机数据,以十六进制字符串存储(64 chars)
|
|
5
|
+
* 密文文件格式:IV(12B) | AuthTag(16B) | CipherText → Base64 写入 .enc 文件
|
|
6
|
+
*/
|
|
7
|
+
import crypto from 'crypto';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { logger } from '../utils/logger.js';
|
|
11
|
+
const ALGO = 'aes-256-gcm';
|
|
12
|
+
const KEY_BYTES = 32; // 256 bit
|
|
13
|
+
const IV_BYTES = 12; // 96 bit (GCM 推荐)
|
|
14
|
+
const TAG_BYTES = 16; // 128 bit auth tag
|
|
15
|
+
/** 从文件加载并验证主密钥 */
|
|
16
|
+
function loadKey(keyPath) {
|
|
17
|
+
if (!fs.existsSync(keyPath)) {
|
|
18
|
+
throw new Error(`密钥文件不存在: ${keyPath}\n请先运行 wangchuan init 生成密钥。`);
|
|
19
|
+
}
|
|
20
|
+
const hex = fs.readFileSync(keyPath, 'utf-8').trim();
|
|
21
|
+
if (hex.length !== KEY_BYTES * 2) {
|
|
22
|
+
throw new Error(`密钥文件格式无效,期望 ${KEY_BYTES * 2} 个十六进制字符`);
|
|
23
|
+
}
|
|
24
|
+
return Buffer.from(hex, 'hex');
|
|
25
|
+
}
|
|
26
|
+
/** 加密 Buffer → Base64 字符串(格式:IV + AuthTag + CipherText) */
|
|
27
|
+
function encryptBuffer(plaintext, key) {
|
|
28
|
+
const iv = crypto.randomBytes(IV_BYTES);
|
|
29
|
+
const cipher = crypto.createCipheriv(ALGO, key, iv);
|
|
30
|
+
const enc = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
31
|
+
const tag = cipher.getAuthTag();
|
|
32
|
+
return Buffer.concat([iv, tag, enc]).toString('base64');
|
|
33
|
+
}
|
|
34
|
+
/** Base64 密文 → 原始 Buffer */
|
|
35
|
+
function decryptBuffer(b64, key) {
|
|
36
|
+
const data = Buffer.from(b64, 'base64');
|
|
37
|
+
const iv = data.subarray(0, IV_BYTES);
|
|
38
|
+
const tag = data.subarray(IV_BYTES, IV_BYTES + TAG_BYTES);
|
|
39
|
+
const enc = data.subarray(IV_BYTES + TAG_BYTES);
|
|
40
|
+
const decipher = crypto.createDecipheriv(ALGO, key, iv);
|
|
41
|
+
decipher.setAuthTag(tag);
|
|
42
|
+
return Buffer.concat([decipher.update(enc), decipher.final()]);
|
|
43
|
+
}
|
|
44
|
+
export const cryptoEngine = {
|
|
45
|
+
/**
|
|
46
|
+
* 生成新主密钥并写入文件(mode 0o600)
|
|
47
|
+
* 返回原始 32 字节 Buffer,供测试断言使用
|
|
48
|
+
*/
|
|
49
|
+
generateKey(keyPath) {
|
|
50
|
+
fs.mkdirSync(path.dirname(keyPath), { recursive: true });
|
|
51
|
+
const key = crypto.randomBytes(KEY_BYTES);
|
|
52
|
+
fs.writeFileSync(keyPath, key.toString('hex'), { mode: 0o600, encoding: 'utf-8' });
|
|
53
|
+
logger.ok(`主密钥已生成: ${keyPath}`);
|
|
54
|
+
return key;
|
|
55
|
+
},
|
|
56
|
+
/** 密钥文件是否存在 */
|
|
57
|
+
hasKey(keyPath) {
|
|
58
|
+
return fs.existsSync(keyPath);
|
|
59
|
+
},
|
|
60
|
+
/** 加密源文件,密文写入 destPath(通常以 .enc 结尾) */
|
|
61
|
+
encryptFile(srcPath, destPath, keyPath) {
|
|
62
|
+
const key = loadKey(keyPath);
|
|
63
|
+
const plaintext = fs.readFileSync(srcPath);
|
|
64
|
+
const encrypted = encryptBuffer(plaintext, key);
|
|
65
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
66
|
+
fs.writeFileSync(destPath, encrypted, 'utf-8');
|
|
67
|
+
logger.debug(`已加密: ${srcPath} → ${destPath}`);
|
|
68
|
+
},
|
|
69
|
+
/** 解密 .enc 文件,明文写入 destPath */
|
|
70
|
+
decryptFile(srcPath, destPath, keyPath) {
|
|
71
|
+
const key = loadKey(keyPath);
|
|
72
|
+
const encrypted = fs.readFileSync(srcPath, 'utf-8').trim();
|
|
73
|
+
const plaintext = decryptBuffer(encrypted, key);
|
|
74
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
75
|
+
fs.writeFileSync(destPath, plaintext);
|
|
76
|
+
logger.debug(`已解密: ${srcPath} → ${destPath}`);
|
|
77
|
+
},
|
|
78
|
+
/** 加密任意字符串,返回 Base64 密文 */
|
|
79
|
+
encryptString(plaintext, keyPath) {
|
|
80
|
+
const key = loadKey(keyPath);
|
|
81
|
+
return encryptBuffer(Buffer.from(plaintext, 'utf-8'), key);
|
|
82
|
+
},
|
|
83
|
+
/** 解密 Base64 密文,返回原始字符串 */
|
|
84
|
+
decryptString(b64, keyPath) {
|
|
85
|
+
const key = loadKey(keyPath);
|
|
86
|
+
return decryptBuffer(b64, key).toString('utf-8');
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../../src/core/crypto.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* git.ts — simple-git 封装,提供幂等的 Git 操作
|
|
3
|
+
*
|
|
4
|
+
* simple-git v3 在 ESM 下必须使用具名导入 { simpleGit } 而非默认导入。
|
|
5
|
+
*/
|
|
6
|
+
import type { DefaultLogFields } from 'simple-git';
|
|
7
|
+
import type { CommitResult } from '../types.js';
|
|
8
|
+
export declare const gitEngine: {
|
|
9
|
+
readonly cloneOrFetch: (remoteUrl: string, localPath: string, branch?: string) => Promise<void>;
|
|
10
|
+
readonly pull: (localPath: string, branch?: string) => Promise<import("simple-git").PullResult>;
|
|
11
|
+
readonly commitAndPush: (localPath: string, message: string, branch?: string) => Promise<CommitResult>;
|
|
12
|
+
readonly status: (localPath: string) => Promise<import("simple-git").StatusResult | null>;
|
|
13
|
+
readonly log: (localPath: string, n?: number) => Promise<readonly DefaultLogFields[]>;
|
|
14
|
+
readonly getRemoteUrl: (localPath: string) => Promise<string | null>;
|
|
15
|
+
readonly rollback: (localPath: string) => Promise<void>;
|
|
16
|
+
readonly isGitAvailable: () => Promise<boolean>;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../../src/core/git.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAa,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAI9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAShD,eAAO,MAAM,SAAS;uCACU,MAAM,aAAa,MAAM,sBAAoB,OAAO,CAAC,IAAI,CAAC;+BAalE,MAAM;wCAOG,MAAM,WAAW,MAAM,sBAAoB,OAAO,CAAC,YAAY,CAAC;iCAqBvE,MAAM;8BAKT,MAAM,iBAAU,OAAO,CAAC,SAAS,gBAAgB,EAAE,CAAC;uCAK3C,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;mCAKnC,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;mCAMxB,OAAO,CAAC,OAAO,CAAC;CAQhC,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* git.ts — simple-git 封装,提供幂等的 Git 操作
|
|
3
|
+
*
|
|
4
|
+
* simple-git v3 在 ESM 下必须使用具名导入 { simpleGit } 而非默认导入。
|
|
5
|
+
*/
|
|
6
|
+
import { simpleGit } from 'simple-git';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { logger } from '../utils/logger.js';
|
|
10
|
+
function createGit(repoPath) {
|
|
11
|
+
return simpleGit(repoPath, {
|
|
12
|
+
maxConcurrentProcesses: 1,
|
|
13
|
+
timeout: { block: 30_000 },
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
export const gitEngine = {
|
|
17
|
+
async cloneOrFetch(remoteUrl, localPath, branch = 'main') {
|
|
18
|
+
if (fs.existsSync(path.join(localPath, '.git'))) {
|
|
19
|
+
logger.debug(`仓库已存在,执行 fetch: ${localPath}`);
|
|
20
|
+
const git = createGit(localPath);
|
|
21
|
+
await git.fetch('origin', branch);
|
|
22
|
+
await git.reset(['--hard', `origin/${branch}`]);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
logger.debug(`克隆仓库: ${remoteUrl} → ${localPath}`);
|
|
26
|
+
fs.mkdirSync(localPath, { recursive: true });
|
|
27
|
+
await simpleGit().clone(remoteUrl, localPath, ['--branch', branch, '--single-branch']);
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
async pull(localPath, branch = 'main') {
|
|
31
|
+
const git = createGit(localPath);
|
|
32
|
+
const result = await git.pull('origin', branch, { '--rebase': 'false' });
|
|
33
|
+
logger.debug(`git pull: ${JSON.stringify(result.summary)}`);
|
|
34
|
+
return result;
|
|
35
|
+
},
|
|
36
|
+
async commitAndPush(localPath, message, branch = 'main') {
|
|
37
|
+
const git = createGit(localPath);
|
|
38
|
+
await git.add('.');
|
|
39
|
+
const status = await git.status();
|
|
40
|
+
if (status.isClean()) {
|
|
41
|
+
logger.info('没有新的变更需要提交');
|
|
42
|
+
return { committed: false, pushed: false };
|
|
43
|
+
}
|
|
44
|
+
logger.debug(`暂存文件: ${status.staged.join(', ')}`);
|
|
45
|
+
const commitResult = await git.commit(message);
|
|
46
|
+
logger.debug(`commit: ${commitResult.commit}`);
|
|
47
|
+
await git.push('origin', branch);
|
|
48
|
+
logger.debug(`已推送到 origin/${branch}`);
|
|
49
|
+
return { committed: true, pushed: true, sha: commitResult.commit };
|
|
50
|
+
},
|
|
51
|
+
async status(localPath) {
|
|
52
|
+
if (!fs.existsSync(path.join(localPath, '.git')))
|
|
53
|
+
return null;
|
|
54
|
+
return createGit(localPath).status();
|
|
55
|
+
},
|
|
56
|
+
async log(localPath, n = 5) {
|
|
57
|
+
const result = await createGit(localPath).log({ maxCount: n });
|
|
58
|
+
return result.all;
|
|
59
|
+
},
|
|
60
|
+
async getRemoteUrl(localPath) {
|
|
61
|
+
const remotes = await createGit(localPath).getRemotes(true);
|
|
62
|
+
return remotes.find(r => r.name === 'origin')?.refs?.fetch ?? null;
|
|
63
|
+
},
|
|
64
|
+
async rollback(localPath) {
|
|
65
|
+
logger.warn('正在回滚最近一次 commit …');
|
|
66
|
+
await createGit(localPath).reset(['--soft', 'HEAD~1']);
|
|
67
|
+
logger.warn('回滚完成,本地变更已保留在暂存区');
|
|
68
|
+
},
|
|
69
|
+
async isGitAvailable() {
|
|
70
|
+
try {
|
|
71
|
+
await simpleGit().version();
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=git.js.map
|