openfeel 1.0.9 → 1.1.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.
- package/README.en.md +11 -11
- package/README.md +2 -1
- package/README.zh-CN.md +13 -13
- package/bin/openfeel.js +2 -2
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +28 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/archive.d.ts +1 -1
- package/dist/commands/flow.d.ts +1 -1
- package/dist/commands/flow.d.ts.map +1 -1
- package/dist/commands/flow.js +20 -8
- package/dist/commands/flow.js.map +1 -1
- package/dist/commands/init.d.ts +2 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +7 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/migrate.d.ts +10 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +106 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/model.d.ts +10 -0
- package/dist/commands/model.d.ts.map +1 -0
- package/dist/commands/model.js +135 -0
- package/dist/commands/model.js.map +1 -0
- package/dist/commands/setup.d.ts +7 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +31 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/stage.d.ts.map +1 -1
- package/dist/commands/stage.js +50 -29
- package/dist/commands/stage.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +13 -10
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/view.d.ts +1 -1
- package/dist/commands/view.d.ts.map +1 -1
- package/dist/commands/view.js +2 -1
- package/dist/commands/view.js.map +1 -1
- package/dist/core/archive/merge.d.ts.map +1 -1
- package/dist/core/archive/merge.js +5 -4
- package/dist/core/archive/merge.js.map +1 -1
- package/dist/core/artifact-graph/resolver.js +2 -2
- package/dist/core/artifact-graph/resolver.js.map +1 -1
- package/dist/core/config.d.ts +8 -8
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +22 -17
- package/dist/core/config.js.map +1 -1
- package/dist/core/flow-manager.d.ts +25 -4
- package/dist/core/flow-manager.d.ts.map +1 -1
- package/dist/core/flow-manager.js +144 -79
- package/dist/core/flow-manager.js.map +1 -1
- package/dist/core/fs/atomic-write.d.ts +31 -0
- package/dist/core/fs/atomic-write.d.ts.map +1 -0
- package/dist/core/fs/atomic-write.js +80 -0
- package/dist/core/fs/atomic-write.js.map +1 -0
- package/dist/core/fs/file-lock.d.ts +34 -0
- package/dist/core/fs/file-lock.d.ts.map +1 -0
- package/dist/core/fs/file-lock.js +142 -0
- package/dist/core/fs/file-lock.js.map +1 -0
- package/dist/core/fs/sequence.d.ts +42 -0
- package/dist/core/fs/sequence.d.ts.map +1 -0
- package/dist/core/fs/sequence.js +69 -0
- package/dist/core/fs/sequence.js.map +1 -0
- package/dist/core/global-paths.d.ts +27 -0
- package/dist/core/global-paths.d.ts.map +1 -0
- package/dist/core/global-paths.js +61 -0
- package/dist/core/global-paths.js.map +1 -0
- package/dist/core/i18n-data/en.d.ts +3 -0
- package/dist/core/i18n-data/en.d.ts.map +1 -1
- package/dist/core/i18n-data/en.js +86 -1
- package/dist/core/i18n-data/en.js.map +1 -1
- package/dist/core/i18n-data/zh-CN.d.ts +3 -0
- package/dist/core/i18n-data/zh-CN.d.ts.map +1 -1
- package/dist/core/i18n-data/zh-CN.js +87 -1
- package/dist/core/i18n-data/zh-CN.js.map +1 -1
- package/dist/core/i18n.js +4 -4
- package/dist/core/i18n.js.map +1 -1
- package/dist/core/init.d.ts +10 -15
- package/dist/core/init.d.ts.map +1 -1
- package/dist/core/init.js +65 -134
- package/dist/core/init.js.map +1 -1
- package/dist/core/managed-region.d.ts +54 -0
- package/dist/core/managed-region.d.ts.map +1 -0
- package/dist/core/managed-region.js +171 -0
- package/dist/core/managed-region.js.map +1 -0
- package/dist/core/metrics.d.ts.map +1 -1
- package/dist/core/metrics.js +6 -3
- package/dist/core/metrics.js.map +1 -1
- package/dist/core/migrate.d.ts +115 -0
- package/dist/core/migrate.d.ts.map +1 -0
- package/dist/core/migrate.js +558 -0
- package/dist/core/migrate.js.map +1 -0
- package/dist/core/model-config.d.ts +62 -0
- package/dist/core/model-config.d.ts.map +1 -0
- package/dist/core/model-config.js +336 -0
- package/dist/core/model-config.js.map +1 -0
- package/dist/core/opencode-config.d.ts +17 -0
- package/dist/core/opencode-config.d.ts.map +1 -0
- package/dist/core/opencode-config.js +135 -0
- package/dist/core/opencode-config.js.map +1 -0
- package/dist/core/plan/roadmap.d.ts.map +1 -1
- package/dist/core/plan/roadmap.js +3 -2
- package/dist/core/plan/roadmap.js.map +1 -1
- package/dist/core/plan/scheme.d.ts.map +1 -1
- package/dist/core/plan/scheme.js +35 -36
- package/dist/core/plan/scheme.js.map +1 -1
- package/dist/core/plan/stage.d.ts.map +1 -1
- package/dist/core/plan/stage.js +4 -3
- package/dist/core/plan/stage.js.map +1 -1
- package/dist/core/public-logger.d.ts +3 -3
- package/dist/core/public-logger.d.ts.map +1 -1
- package/dist/core/public-logger.js +36 -40
- package/dist/core/public-logger.js.map +1 -1
- package/dist/core/setup.d.ts +10 -0
- package/dist/core/setup.d.ts.map +1 -0
- package/dist/core/setup.js +75 -0
- package/dist/core/setup.js.map +1 -0
- package/dist/core/template-loader.d.ts +4 -5
- package/dist/core/template-loader.d.ts.map +1 -1
- package/dist/core/template-loader.js +3974 -3274
- package/dist/core/template-loader.js.map +1 -1
- package/dist/core/templates.d.ts +2 -2
- package/dist/core/templates.d.ts.map +1 -1
- package/dist/core/templates.js +2 -2
- package/dist/core/templates.js.map +1 -1
- package/dist/core/update-infos.d.ts +32 -0
- package/dist/core/update-infos.d.ts.map +1 -0
- package/dist/core/update-infos.js +153 -0
- package/dist/core/update-infos.js.map +1 -0
- package/dist/core/update-state.d.ts +18 -0
- package/dist/core/update-state.d.ts.map +1 -1
- package/dist/core/update-state.js +66 -3
- package/dist/core/update-state.js.map +1 -1
- package/dist/core/update.d.ts +12 -6
- package/dist/core/update.d.ts.map +1 -1
- package/dist/core/update.js +400 -550
- package/dist/core/update.js.map +1 -1
- package/dist/core/view/entry.js +4 -4
- package/dist/core/view/entry.js.map +1 -1
- package/dist/core/workspace/identity.d.ts.map +1 -1
- package/dist/core/workspace/identity.js +13 -10
- package/dist/core/workspace/identity.js.map +1 -1
- package/dist/core/workspace/knowledge.d.ts.map +1 -1
- package/dist/core/workspace/knowledge.js +24 -14
- package/dist/core/workspace/knowledge.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 跨进程文件锁(建议性 / advisory lock)
|
|
3
|
+
*
|
|
4
|
+
* 协议:
|
|
5
|
+
* 1. openSync(lockPath, 'wx') 独占创建成功 → 持锁,写入 {pid, time, token};
|
|
6
|
+
* 2. 已存在(EEXIST)→ 指数退避重试,直至 timeoutMs;
|
|
7
|
+
* 3. 陈旧锁(mtime 年龄 > staleMs)→ 原子 rename 抢占后重试;
|
|
8
|
+
* 4. finally 释放:读回 token 校验归属,一致才 unlinkSync。
|
|
9
|
+
*
|
|
10
|
+
* TTL 定值依据(REV-006):实测最长临界区 P99 ≈ 8.14ms(本机 NTFS),
|
|
11
|
+
* staleMs = 3000ms ≈ 368×P99,且 staleMs < timeoutMs 保证崩溃残留可在等待窗口内被抢占。
|
|
12
|
+
* 心跳续期对同步临界区不可行(事件循环被阻塞),故采用静态大余量 TTL。
|
|
13
|
+
*
|
|
14
|
+
* 仅依赖 node:fs / node:path / node:crypto 与全局路径模块,不引入第三方依赖。
|
|
15
|
+
*/
|
|
16
|
+
import { closeSync, mkdirSync, openSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync, } from 'node:fs';
|
|
17
|
+
import { dirname, join } from 'node:path';
|
|
18
|
+
import { randomBytes } from 'node:crypto';
|
|
19
|
+
import { getGlobalLockPath } from '../global-paths.js';
|
|
20
|
+
/** 获取锁总超时(ms) */
|
|
21
|
+
export const LOCK_TIMEOUT_MS_DEFAULT = 5000;
|
|
22
|
+
/** 陈旧锁判定阈值(ms),依据实测临界区 P99 设定,见文件头说明 */
|
|
23
|
+
export const LOCK_STALE_MS_DEFAULT = 3000;
|
|
24
|
+
/** 退避初始间隔(ms) */
|
|
25
|
+
export const LOCK_INITIAL_BACKOFF_MS_DEFAULT = 10;
|
|
26
|
+
/** 退避上限(ms) */
|
|
27
|
+
export const LOCK_MAX_BACKOFF_MS_DEFAULT = 500;
|
|
28
|
+
/** 项目级锁文件路径:{projectPath}/.openfeel/tmp/locks/{name}.lock(S6) */
|
|
29
|
+
export function projectLockPath(projectPath, name) {
|
|
30
|
+
return join(projectPath, '.openfeel', 'tmp', 'locks', `${name}.lock`);
|
|
31
|
+
}
|
|
32
|
+
/** 全局锁文件路径:~/.openfeel/locks/{name}.lock(跨项目全局写入用) */
|
|
33
|
+
export function globalLockPath(name) {
|
|
34
|
+
// 委托 global-paths 作为全局路径唯一权威(N4 收口),签名不变供既有调用方复用
|
|
35
|
+
return getGlobalLockPath(name);
|
|
36
|
+
}
|
|
37
|
+
/** 同步睡眠(阻塞当前 JS 线程,不空转 CPU) */
|
|
38
|
+
function sleepSync(ms) {
|
|
39
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
40
|
+
}
|
|
41
|
+
/** 读取锁文件内容;读取/解析失败返回 null */
|
|
42
|
+
function readLockInfo(lockPath) {
|
|
43
|
+
try {
|
|
44
|
+
return JSON.parse(readFileSync(lockPath, 'utf-8'));
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 尝试抢占陈旧锁:rename 到唯一名(原子,只有一个进程成功),随后 best-effort 删除。
|
|
52
|
+
* @returns 是否成功抢占(false 表示未过期 / 已被他人抢占 / Windows 占用)
|
|
53
|
+
*/
|
|
54
|
+
function tryReclaimStale(lockPath, staleMs) {
|
|
55
|
+
let mtimeMs;
|
|
56
|
+
try {
|
|
57
|
+
mtimeMs = statSync(lockPath).mtimeMs;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return false; // 锁已被释放/抢占
|
|
61
|
+
}
|
|
62
|
+
if (Date.now() - mtimeMs <= staleMs) {
|
|
63
|
+
return false; // 未过期
|
|
64
|
+
}
|
|
65
|
+
const stalePath = `${lockPath}.stale.${process.pid}.${randomBytes(4).toString('hex')}`;
|
|
66
|
+
try {
|
|
67
|
+
renameSync(lockPath, stalePath); // 原子抢占:并发者只有一个成功
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// ENOENT(他人已抢占)/ EPERM、EBUSY(Windows 占用,持锁者仍活跃)→ 交回重试
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
unlinkSync(stalePath);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// 清理失败忽略(残留为唯一名,不阻塞后续加锁)
|
|
78
|
+
}
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* 在文件锁保护下同步执行 fn。
|
|
83
|
+
*
|
|
84
|
+
* @param lockPath 锁文件绝对路径(用 projectLockPath / globalLockPath 构造)
|
|
85
|
+
* @param fn 临界区函数
|
|
86
|
+
* @param options 超时 / TTL / 退避配置
|
|
87
|
+
* @returns fn 的返回值
|
|
88
|
+
* @throws 超过 timeoutMs 仍无法获取锁时抛出;fn 自身抛错原样透传
|
|
89
|
+
*/
|
|
90
|
+
export function withFileLock(lockPath, fn, options = {}) {
|
|
91
|
+
const { timeoutMs = LOCK_TIMEOUT_MS_DEFAULT, staleMs = LOCK_STALE_MS_DEFAULT, initialBackoffMs = LOCK_INITIAL_BACKOFF_MS_DEFAULT, maxBackoffMs = LOCK_MAX_BACKOFF_MS_DEFAULT, } = options;
|
|
92
|
+
mkdirSync(dirname(lockPath), { recursive: true });
|
|
93
|
+
const token = randomBytes(8).toString('hex');
|
|
94
|
+
const deadline = Date.now() + timeoutMs;
|
|
95
|
+
let backoff = initialBackoffMs;
|
|
96
|
+
// ── 获取锁 ──
|
|
97
|
+
for (;;) {
|
|
98
|
+
try {
|
|
99
|
+
const fd = openSync(lockPath, 'wx'); // O_EXCL 独占创建
|
|
100
|
+
try {
|
|
101
|
+
const info = { pid: process.pid, time: Date.now(), token };
|
|
102
|
+
writeFileSync(fd, JSON.stringify(info), 'utf-8');
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
closeSync(fd);
|
|
106
|
+
}
|
|
107
|
+
break; // 持锁成功
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
if (err.code !== 'EEXIST') {
|
|
111
|
+
throw err; // 权限/目录等错误直接抛出
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// 已存在:先尝试抢占陈旧锁
|
|
115
|
+
tryReclaimStale(lockPath, staleMs);
|
|
116
|
+
// 超时判定
|
|
117
|
+
if (Date.now() >= deadline) {
|
|
118
|
+
throw new Error(`获取文件锁超时(${timeoutMs}ms):${lockPath}`);
|
|
119
|
+
}
|
|
120
|
+
// 指数退避 + ±20% 抖动,避免多进程同频重试
|
|
121
|
+
const jitter = 1 + (Math.random() - 0.5) * 0.4;
|
|
122
|
+
sleepSync(Math.min(backoff, maxBackoffMs) * jitter);
|
|
123
|
+
backoff = Math.min(backoff * 2, maxBackoffMs);
|
|
124
|
+
}
|
|
125
|
+
// ── 执行临界区并释放 ──
|
|
126
|
+
try {
|
|
127
|
+
return fn();
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
// 归属校验:仅当锁文件仍属于本 token 时删除,避免误删他人/抢占后的新锁
|
|
131
|
+
const info = readLockInfo(lockPath);
|
|
132
|
+
if (info?.token === token) {
|
|
133
|
+
try {
|
|
134
|
+
unlinkSync(lockPath);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// Windows 占用/已删除:释放失败静默忽略,残留锁由 TTL 兜底(见方案说明)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=file-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-lock.js","sourceRoot":"","sources":["../../../src/core/fs/file-lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** 默认最大尝试次数 */
|
|
2
|
+
export declare const SEQUENCE_MAX_ATTEMPTS_DEFAULT = 1000;
|
|
3
|
+
/** 序号分配选项 */
|
|
4
|
+
export interface ReserveSequenceOptions {
|
|
5
|
+
/** 目标目录(不存在时自动创建) */
|
|
6
|
+
dir: string;
|
|
7
|
+
/** 候选文件名生成器:入参为序号(1 基),返回完整文件名(含扩展名) */
|
|
8
|
+
candidate: (seq: number) => string;
|
|
9
|
+
/**
|
|
10
|
+
* 从已有文件名解析序号,用于计算候选起点。
|
|
11
|
+
* 返回 null 表示该文件不匹配本序号空间。
|
|
12
|
+
* 注意:必须只解析文件名,禁止读取文件内容(保证空文件占位语义)。
|
|
13
|
+
*/
|
|
14
|
+
parse: (fileName: string) => number | null;
|
|
15
|
+
/** 起始序号;缺省时由 nextSequence(dir, parse) 计算(max+1 快速路径) */
|
|
16
|
+
start?: number;
|
|
17
|
+
/** 最大尝试次数,默认 1000 */
|
|
18
|
+
maxAttempts?: number;
|
|
19
|
+
}
|
|
20
|
+
/** 已分配的序号 */
|
|
21
|
+
export interface ReservedSequence {
|
|
22
|
+
/** 序号(1 基) */
|
|
23
|
+
seq: number;
|
|
24
|
+
/** 已独占创建的文件名 */
|
|
25
|
+
fileName: string;
|
|
26
|
+
/** 已独占创建的文件绝对路径(空文件,待调用方写入内容) */
|
|
27
|
+
path: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 纯计算:扫描目录,返回「最大已用序号 + 1」(空目录/不可读返回 1)。
|
|
31
|
+
* 仅作 reserveSequence 的候选起点,不承担分配职责。
|
|
32
|
+
*/
|
|
33
|
+
export declare function nextSequence(dir: string, parse: (fileName: string) => number | null): number;
|
|
34
|
+
/**
|
|
35
|
+
* 原子分配一个序号(O_EXCL 独占创建空文件占位)。
|
|
36
|
+
*
|
|
37
|
+
* @param options 目录 / 候选名生成器 / 解析器 / 起点 / 尝试上限
|
|
38
|
+
* @returns 已独占创建的序号、文件名与路径
|
|
39
|
+
* @throws 连续 maxAttempts 次候选均被占用时抛出;其它文件系统错误原样抛出
|
|
40
|
+
*/
|
|
41
|
+
export declare function reserveSequence(options: ReserveSequenceOptions): ReservedSequence;
|
|
42
|
+
//# sourceMappingURL=sequence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sequence.d.ts","sourceRoot":"","sources":["../../../src/core/fs/sequence.ts"],"names":[],"mappings":"AAkBA,eAAe;AACf,eAAO,MAAM,6BAA6B,OAAO,CAAC;AAElD,aAAa;AACb,MAAM,WAAW,sBAAsB;IACrC,qBAAqB;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,wCAAwC;IACxC,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IACnC;;;;OAIG;IACH,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC3C,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,aAAa;AACb,MAAM,WAAW,gBAAgB;IAC/B,cAAc;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GACzC,MAAM,CAaR;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,gBAAgB,CA+BjF"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 原子序号分配工具 — O_EXCL 独占创建 + 冲突递增重试
|
|
3
|
+
*
|
|
4
|
+
* 目的:消除「先算 max+1 再写」的跨进程竞态(公共日志 NNN、op-NNN 序号重号)。
|
|
5
|
+
* 原理:
|
|
6
|
+
* - 候选起点由 nextSequence(max+1)计算,仅作单进程快速路径;
|
|
7
|
+
* - 逐个候选文件名 openSync(path, 'wx') 独占创建,成功即占号;
|
|
8
|
+
* - 已存在(EEXIST)则递增重试,直至上限;
|
|
9
|
+
* - 调用方拿到已独占创建的空文件路径后,用原子写写入内容。
|
|
10
|
+
*
|
|
11
|
+
* 空文件占位语义(REV-009):若在占号与写内容之间崩溃会留下空文件,
|
|
12
|
+
* 但其文件名仍含有效序号,下游 max+1 扫描仍能取到并递增,不重号、不回退。
|
|
13
|
+
*
|
|
14
|
+
* 仅依赖 node:fs / node:path,不引入第三方依赖。
|
|
15
|
+
*/
|
|
16
|
+
import { closeSync, mkdirSync, openSync, readdirSync } from 'node:fs';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
/** 默认最大尝试次数 */
|
|
19
|
+
export const SEQUENCE_MAX_ATTEMPTS_DEFAULT = 1000;
|
|
20
|
+
/**
|
|
21
|
+
* 纯计算:扫描目录,返回「最大已用序号 + 1」(空目录/不可读返回 1)。
|
|
22
|
+
* 仅作 reserveSequence 的候选起点,不承担分配职责。
|
|
23
|
+
*/
|
|
24
|
+
export function nextSequence(dir, parse) {
|
|
25
|
+
let max = 0;
|
|
26
|
+
try {
|
|
27
|
+
for (const entry of readdirSync(dir)) {
|
|
28
|
+
const seq = parse(entry);
|
|
29
|
+
if (seq !== null && seq > max) {
|
|
30
|
+
max = seq;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return 1; // 目录不存在/不可读 → 从 1 开始
|
|
36
|
+
}
|
|
37
|
+
return max + 1;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 原子分配一个序号(O_EXCL 独占创建空文件占位)。
|
|
41
|
+
*
|
|
42
|
+
* @param options 目录 / 候选名生成器 / 解析器 / 起点 / 尝试上限
|
|
43
|
+
* @returns 已独占创建的序号、文件名与路径
|
|
44
|
+
* @throws 连续 maxAttempts 次候选均被占用时抛出;其它文件系统错误原样抛出
|
|
45
|
+
*/
|
|
46
|
+
export function reserveSequence(options) {
|
|
47
|
+
const { dir, candidate, parse, start, maxAttempts = SEQUENCE_MAX_ATTEMPTS_DEFAULT, } = options;
|
|
48
|
+
mkdirSync(dir, { recursive: true });
|
|
49
|
+
// 候选起点:显式 start 优先,否则 max+1(仅快速路径,最终以 O_EXCL 为准)
|
|
50
|
+
const base = start ?? nextSequence(dir, parse);
|
|
51
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
52
|
+
const seq = base + i;
|
|
53
|
+
const fileName = candidate(seq);
|
|
54
|
+
const path = join(dir, fileName);
|
|
55
|
+
try {
|
|
56
|
+
const fd = openSync(path, 'wx'); // O_EXCL:已存在则 EEXIST,不覆盖
|
|
57
|
+
closeSync(fd); // 先占位(空文件),内容由调用方原子写
|
|
58
|
+
return { seq, fileName, path };
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
if (err.code === 'EEXIST') {
|
|
62
|
+
continue; // 序号被占用,递增重试
|
|
63
|
+
}
|
|
64
|
+
throw err; // 权限/目录等错误直接抛出
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
throw new Error(`序号分配失败:连续 ${maxAttempts} 次候选均被占用(dir=${dir})`);
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=sequence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sequence.js","sourceRoot":"","sources":["../../../src/core/fs/sequence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,eAAe;AACf,MAAM,CAAC,MAAM,6BAA6B,GAAG,IAAI,CAAC;AA8BlD;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,GAAW,EACX,KAA0C;IAE1C,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,CAAC;QACH,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;YACrC,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;YACzB,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,GAAG,GAAG,EAAE,CAAC;gBAC9B,GAAG,GAAG,GAAG,CAAC;YACZ,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC,CAAC,qBAAqB;IACjC,CAAC;IACD,OAAO,GAAG,GAAG,CAAC,CAAC;AACjB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,OAA+B;IAC7D,MAAM,EACJ,GAAG,EACH,SAAS,EACT,KAAK,EACL,KAAK,EACL,WAAW,GAAG,6BAA6B,GAC5C,GAAG,OAAO,CAAC;IAEZ,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEpC,iDAAiD;IACjD,MAAM,IAAI,GAAG,KAAK,IAAI,YAAY,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAE/C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC;QACrB,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,yBAAyB;YAC1D,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,qBAAqB;YACpC,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QACjC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACrD,SAAS,CAAC,aAAa;YACzB,CAAC;YACD,MAAM,GAAG,CAAC,CAAC,eAAe;QAC5B,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,aAAa,WAAW,gBAAgB,GAAG,GAAG,CAAC,CAAC;AAClE,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** 全局 opencode 配置根目录(~/.config/opencode) */
|
|
2
|
+
export declare function getOpencodeGlobalDir(): string;
|
|
3
|
+
/** 全局 opencode agents 目录 */
|
|
4
|
+
export declare function getGlobalAgentsDir(): string;
|
|
5
|
+
/** 全局 opencode skills 目录 */
|
|
6
|
+
export declare function getGlobalSkillsDir(): string;
|
|
7
|
+
/** 全局 opencode.jsonc 路径 */
|
|
8
|
+
export declare function getGlobalOpencodeJsoncPath(): string;
|
|
9
|
+
/** 全局框架约束 core.md 路径(~/.config/opencode/openfeel/core.md;v1.1.1 起废弃,仅兼容检测/清理) */
|
|
10
|
+
export declare function getGlobalCoreMdPath(): string;
|
|
11
|
+
/** 全局 AGENTS.md 路径(~/.config/opencode/AGENTS.md;框架约束唯一权威,v1.1.1) */
|
|
12
|
+
export declare function getGlobalAgentsMdPath(): string;
|
|
13
|
+
/** 全局 update_state.json 路径(~/.openfeel/update_state.json) */
|
|
14
|
+
export declare function getGlobalUpdateStatePath(): string;
|
|
15
|
+
/** 全局 update_infos.md 路径(~/.openfeel/update_infos.md;读写属 stage-38,本阶段仅解析路径) */
|
|
16
|
+
export declare function getGlobalUpdateInfosPath(): string;
|
|
17
|
+
/** 全局 auth.json 路径(~/.local/share/opencode/auth.json;模型 provider 校验依据,REV-1505) */
|
|
18
|
+
export declare function getAuthJsonPath(): string;
|
|
19
|
+
/** 全局锁文件路径(~/.openfeel/locks/{name}.lock;跨项目全局写入用,REV-1801) */
|
|
20
|
+
export declare function getGlobalLockPath(name: string): string;
|
|
21
|
+
/** 全局配置文件路径(~/.openfeel/config.json,跨平台兼容,REV-1801) */
|
|
22
|
+
export declare function getGlobalOpenfeelConfigPath(): string;
|
|
23
|
+
/** 全局用户画像路径(~/.config/openfeel/profile.yaml,跨项目共享偏好,REV-1801) */
|
|
24
|
+
export declare function getGlobalProfilePath(): string;
|
|
25
|
+
/** 全局 Schema 目录(~/.openfeel/schemas,Schema 家目录查找层,REV-1801) */
|
|
26
|
+
export declare function getGlobalSchemasDir(): string;
|
|
27
|
+
//# sourceMappingURL=global-paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"global-paths.d.ts","sourceRoot":"","sources":["../../src/core/global-paths.ts"],"names":[],"mappings":"AASA,4CAA4C;AAC5C,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,4BAA4B;AAC5B,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,4BAA4B;AAC5B,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,2BAA2B;AAC3B,wBAAgB,0BAA0B,IAAI,MAAM,CAEnD;AAED,iFAAiF;AACjF,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,oEAAoE;AACpE,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED,6DAA6D;AAC7D,wBAAgB,wBAAwB,IAAI,MAAM,CAEjD;AAED,+EAA+E;AAC/E,wBAAgB,wBAAwB,IAAI,MAAM,CAEjD;AAED,mFAAmF;AACnF,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,+DAA+D;AAC/D,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,uDAAuD;AACvD,wBAAgB,2BAA2B,IAAI,MAAM,CAEpD;AAED,iEAAiE;AACjE,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,+DAA+D;AAC/D,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 全局路径模块
|
|
3
|
+
* 集中解析 opencode 与 openfeel 的全局路径(基于用户主目录),
|
|
4
|
+
* 作为 init/update 全局部署的路径基础。
|
|
5
|
+
* 仅此模块 import node:os 的 homedir(N4):测试 mock 一处即隔离全部全局路径。
|
|
6
|
+
*/
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { homedir } from 'node:os';
|
|
9
|
+
/** 全局 opencode 配置根目录(~/.config/opencode) */
|
|
10
|
+
export function getOpencodeGlobalDir() {
|
|
11
|
+
return join(homedir(), '.config', 'opencode');
|
|
12
|
+
}
|
|
13
|
+
/** 全局 opencode agents 目录 */
|
|
14
|
+
export function getGlobalAgentsDir() {
|
|
15
|
+
return join(getOpencodeGlobalDir(), 'agents');
|
|
16
|
+
}
|
|
17
|
+
/** 全局 opencode skills 目录 */
|
|
18
|
+
export function getGlobalSkillsDir() {
|
|
19
|
+
return join(getOpencodeGlobalDir(), 'skills');
|
|
20
|
+
}
|
|
21
|
+
/** 全局 opencode.jsonc 路径 */
|
|
22
|
+
export function getGlobalOpencodeJsoncPath() {
|
|
23
|
+
return join(getOpencodeGlobalDir(), 'opencode.jsonc');
|
|
24
|
+
}
|
|
25
|
+
/** 全局框架约束 core.md 路径(~/.config/opencode/openfeel/core.md;v1.1.1 起废弃,仅兼容检测/清理) */
|
|
26
|
+
export function getGlobalCoreMdPath() {
|
|
27
|
+
return join(getOpencodeGlobalDir(), 'openfeel', 'core.md');
|
|
28
|
+
}
|
|
29
|
+
/** 全局 AGENTS.md 路径(~/.config/opencode/AGENTS.md;框架约束唯一权威,v1.1.1) */
|
|
30
|
+
export function getGlobalAgentsMdPath() {
|
|
31
|
+
return join(getOpencodeGlobalDir(), 'AGENTS.md');
|
|
32
|
+
}
|
|
33
|
+
/** 全局 update_state.json 路径(~/.openfeel/update_state.json) */
|
|
34
|
+
export function getGlobalUpdateStatePath() {
|
|
35
|
+
return join(homedir(), '.openfeel', 'update_state.json');
|
|
36
|
+
}
|
|
37
|
+
/** 全局 update_infos.md 路径(~/.openfeel/update_infos.md;读写属 stage-38,本阶段仅解析路径) */
|
|
38
|
+
export function getGlobalUpdateInfosPath() {
|
|
39
|
+
return join(homedir(), '.openfeel', 'update_infos.md');
|
|
40
|
+
}
|
|
41
|
+
/** 全局 auth.json 路径(~/.local/share/opencode/auth.json;模型 provider 校验依据,REV-1505) */
|
|
42
|
+
export function getAuthJsonPath() {
|
|
43
|
+
return join(homedir(), '.local', 'share', 'opencode', 'auth.json');
|
|
44
|
+
}
|
|
45
|
+
/** 全局锁文件路径(~/.openfeel/locks/{name}.lock;跨项目全局写入用,REV-1801) */
|
|
46
|
+
export function getGlobalLockPath(name) {
|
|
47
|
+
return join(homedir(), '.openfeel', 'locks', `${name}.lock`);
|
|
48
|
+
}
|
|
49
|
+
/** 全局配置文件路径(~/.openfeel/config.json,跨平台兼容,REV-1801) */
|
|
50
|
+
export function getGlobalOpenfeelConfigPath() {
|
|
51
|
+
return join(homedir(), '.openfeel', 'config.json');
|
|
52
|
+
}
|
|
53
|
+
/** 全局用户画像路径(~/.config/openfeel/profile.yaml,跨项目共享偏好,REV-1801) */
|
|
54
|
+
export function getGlobalProfilePath() {
|
|
55
|
+
return join(homedir(), '.config', 'openfeel', 'profile.yaml');
|
|
56
|
+
}
|
|
57
|
+
/** 全局 Schema 目录(~/.openfeel/schemas,Schema 家目录查找层,REV-1801) */
|
|
58
|
+
export function getGlobalSchemasDir() {
|
|
59
|
+
return join(homedir(), '.openfeel', 'schemas');
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=global-paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"global-paths.js","sourceRoot":"","sources":["../../src/core/global-paths.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,4CAA4C;AAC5C,MAAM,UAAU,oBAAoB;IAClC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AAChD,CAAC;AAED,4BAA4B;AAC5B,MAAM,UAAU,kBAAkB;IAChC,OAAO,IAAI,CAAC,oBAAoB,EAAE,EAAE,QAAQ,CAAC,CAAC;AAChD,CAAC;AAED,4BAA4B;AAC5B,MAAM,UAAU,kBAAkB;IAChC,OAAO,IAAI,CAAC,oBAAoB,EAAE,EAAE,QAAQ,CAAC,CAAC;AAChD,CAAC;AAED,2BAA2B;AAC3B,MAAM,UAAU,0BAA0B;IACxC,OAAO,IAAI,CAAC,oBAAoB,EAAE,EAAE,gBAAgB,CAAC,CAAC;AACxD,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,mBAAmB;IACjC,OAAO,IAAI,CAAC,oBAAoB,EAAE,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;AAC7D,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,qBAAqB;IACnC,OAAO,IAAI,CAAC,oBAAoB,EAAE,EAAE,WAAW,CAAC,CAAC;AACnD,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,wBAAwB;IACtC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,mBAAmB,CAAC,CAAC;AAC3D,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,wBAAwB;IACtC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAC;AACzD,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,eAAe;IAC7B,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;AACrE,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,IAAI,OAAO,CAAC,CAAC;AAC/D,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,2BAA2B;IACzC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC;AACrD,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,oBAAoB;IAClC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,CAAC,CAAC;AAChE,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,mBAAmB;IACjC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;AACjD,CAAC"}
|
|
@@ -19,6 +19,9 @@ export declare const instructions: I18nDomain;
|
|
|
19
19
|
export declare const lint: I18nDomain;
|
|
20
20
|
export declare const help: I18nDomain;
|
|
21
21
|
export declare const config: I18nDomain;
|
|
22
|
+
export declare const migrate: I18nDomain;
|
|
23
|
+
export declare const model: I18nDomain;
|
|
24
|
+
export declare const setup: I18nDomain;
|
|
22
25
|
export declare const allDomains: Array<{
|
|
23
26
|
name: string;
|
|
24
27
|
domain: I18nDomain;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../src/core/i18n-data/en.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAa,UAAU,EAAE,MAAM,YAAY,CAAC;AAGxD,eAAO,MAAM,MAAM,EAAE,UAoBpB,CAAC;AAGF,eAAO,MAAM,IAAI,EAAE,UAiLlB,CAAC;AAGF,eAAO,MAAM,IAAI,EAAE,UAuBlB,CAAC;AAGF,eAAO,MAAM,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../src/core/i18n-data/en.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAa,UAAU,EAAE,MAAM,YAAY,CAAC;AAGxD,eAAO,MAAM,MAAM,EAAE,UAoBpB,CAAC;AAGF,eAAO,MAAM,IAAI,EAAE,UAiLlB,CAAC;AAGF,eAAO,MAAM,IAAI,EAAE,UAuBlB,CAAC;AAGF,eAAO,MAAM,MAAM,EAAE,UAoBpB,CAAC;AAGF,eAAO,MAAM,OAAO,EAAE,UA8CrB,CAAC;AAGF,eAAO,MAAM,KAAK,EAAE,UAmBnB,CAAC;AAGF,eAAO,MAAM,IAAI,EAAE,UAKlB,CAAC;AAGF,eAAO,MAAM,SAAS,EAAE,UAyBvB,CAAC;AAGF,eAAO,MAAM,OAAO,EAAE,UAOrB,CAAC;AAGF,eAAO,MAAM,OAAO,EAAE,UAAe,CAAC;AAGtC,eAAO,MAAM,IAAI,EAAE,UAQlB,CAAC;AAGF,eAAO,MAAM,YAAY,EAAE,UAAe,CAAC;AAG3C,eAAO,MAAM,IAAI,EAAE,UAalB,CAAC;AAGF,eAAO,MAAM,IAAI,EAAE,UA6IlB,CAAC;AAGF,eAAO,MAAM,MAAM,EAAE,UAoBpB,CAAC;AAGF,eAAO,MAAM,OAAO,EAAE,UA0BrB,CAAC;AAGF,eAAO,MAAM,KAAK,EAAE,UAqBnB,CAAC;AAGF,eAAO,MAAM,KAAK,EAAE,UAKnB,CAAC;AAGF,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAmBlE,CAAC"}
|
|
@@ -228,6 +228,9 @@ export const update = {
|
|
|
228
228
|
'langConflict': { key: 'update.langConflict', zh: '', en: '⚠️ AGENTS.md language mismatch: project is {projectLang}, requested is {requestedLang}. Use --force to override or adjust manually.' },
|
|
229
229
|
'conflictsTitle': { key: 'update.conflictsTitle', zh: '', en: 'Conflicts detected (files have been manually modified, overwrite refused)' },
|
|
230
230
|
'conflictsHint': { key: 'update.conflictsHint', zh: '', en: 'Conflict files written to .openfeel/update_conflicts/. Please merge manually, then re-run openfeel update.' },
|
|
231
|
+
'appendedTitle': { key: 'update.appendedTitle', zh: '', en: 'Appended {n} file(s) (no control-region marker; managed content appended to the end, pending session-start review)' },
|
|
232
|
+
'appendedHint': { key: 'update.appendedHint', zh: '', en: 'See ~/.openfeel/update_infos.md; each entry will be checked and prompted for repair at session start.' },
|
|
233
|
+
'appendedManyWarning': { key: 'update.appendedManyWarning', zh: '', en: '⚠️ Appending {n} files at once — possible update_state corruption or first migration. Run openfeel update then check ~/.openfeel/update_infos.md.' },
|
|
231
234
|
};
|
|
232
235
|
/* ==================== project ==================== */
|
|
233
236
|
export const project = {
|
|
@@ -371,6 +374,10 @@ export const help = {
|
|
|
371
374
|
'init': { key: 'help.init', zh: '', en: 'Initialize project workspace, create .openfeel/ directory structure and config files' },
|
|
372
375
|
'init.demo': { key: 'help.init.demo', zh: '', en: 'Create demo project skeleton (NumKit style)' },
|
|
373
376
|
'init.lang': { key: 'help.init.lang', zh: '', en: 'Agent prompt language (zh-CN or en), defaults to zh-CN in non-interactive mode' },
|
|
377
|
+
'init.workspaceOnly': { key: 'help.init.workspaceOnly', zh: '', en: 'Create the .openfeel/ workspace only (no AGENTS.md/opencode.jsonc); used by Feel to auto-setup blank projects' },
|
|
378
|
+
'init.nonInteractive': { key: 'help.init.nonInteractive', zh: '', en: 'Non-interactive mode (used together with --workspace-only)' },
|
|
379
|
+
'setup': { key: 'help.setup', zh: '', en: 'Deploy global OpenFeel framework config (global AGENTS.md + agents + skills + opencode.jsonc); does not create the project .openfeel/' },
|
|
380
|
+
'setup.lang': { key: 'help.setup.lang', zh: '', en: 'Agent prompt language (zh-CN or en), defaults to zh-CN' },
|
|
374
381
|
'update': { key: 'help.update', zh: '', en: 'Deploy OpenFeel adapter files to target project (interactive tool selection when no args)' },
|
|
375
382
|
'update.lang': { key: 'help.update.lang', zh: '', en: 'Agent prompt language (zh-CN or en)' },
|
|
376
383
|
'update.force': { key: 'help.update.force', zh: '', en: 'Skip AGENTS.md overwrite confirmation, force overwrite' },
|
|
@@ -378,7 +385,7 @@ export const help = {
|
|
|
378
385
|
'flow.status': { key: 'help.flow.status', zh: '', en: 'Show pipeline status summary' },
|
|
379
386
|
'flow.status.verbose': { key: 'help.flow.status.verbose', zh: '', en: 'Enhanced output: config cascade, recent changes, downstream agent readiness' },
|
|
380
387
|
'flow.status.lines': { key: 'help.flow.status.lines', zh: '', en: 'Number of recent status changes (default 5)' },
|
|
381
|
-
'flow.overview': { key: 'help.flow.overview', zh: '', en: 'Full status visualization (
|
|
388
|
+
'flow.overview': { key: 'help.flow.overview', zh: '', en: 'Full status visualization (openfeel flow overview backend)' },
|
|
382
389
|
'flow.current': { key: 'help.flow.current', zh: '', en: 'Show current stage and operation' },
|
|
383
390
|
'flow.metrics': { key: 'help.flow.metrics', zh: '', en: 'Show Agent performance metrics' },
|
|
384
391
|
'flow.stage': { key: 'help.flow.stage', zh: '', en: 'Stage management' },
|
|
@@ -469,6 +476,23 @@ export const help = {
|
|
|
469
476
|
'lint': { key: 'help.lint', zh: '', en: 'Project health checks (i18n key consistency, kb reference validity)' },
|
|
470
477
|
'lint.i18n': { key: 'help.lint.i18n', zh: '', en: 'Validate i18n key consistency (empty values / zh-CN-only / en-only keys)' },
|
|
471
478
|
'lint.kb': { key: 'help.lint.kb', zh: '', en: 'Detect stale file references in .openfeel/kb/' },
|
|
479
|
+
// migrate (legacy layout migration)
|
|
480
|
+
'migrate': { key: 'help.migrate', zh: '', en: 'Legacy layout migration (detect/backup/migrate/rollback existing project legacy layout)' },
|
|
481
|
+
'migrate.dryRun': { key: 'help.migrate.dryRun', zh: '', en: 'Detect and preview only, do not write' },
|
|
482
|
+
'migrate.remapAssignee': { key: 'help.migrate.remapAssignee', zh: '', en: 'Rewrite legacy flow.json assignee to new names (report only by default)' },
|
|
483
|
+
'migrate.rollback': { key: 'help.migrate.rollback', zh: '', en: 'Roll back the most recent migration' },
|
|
484
|
+
'migrate.rollback.dryRun': { key: 'help.migrate.rollback.dryRun', zh: '', en: 'Preview rollback plan only' },
|
|
485
|
+
'migrate.cleanGlobalCoreMd': { key: 'help.migrate.cleanGlobalCoreMd', zh: '', en: 'Delete the deprecated global core.md (~/.config/opencode/openfeel/core.md)' },
|
|
486
|
+
// model (model configuration)
|
|
487
|
+
'model': { key: 'help.model', zh: '', en: 'Model configuration (three scopes: set/get/list)' },
|
|
488
|
+
'model.set': { key: 'help.model.set', zh: '', en: 'Set the model of a given agent' },
|
|
489
|
+
'model.set.scope': { key: 'help.model.set.scope', zh: '', en: 'Scope (default|global|project, default project)' },
|
|
490
|
+
'model.set.build': { key: 'help.model.set.build', zh: '', en: 'Trigger npm run build after writing default scope to regenerate deploy artifacts' },
|
|
491
|
+
'model.set.force': { key: 'help.model.set.force', zh: '', en: 'Confirm modifying the framework default (required for --scope default in non-TTY)' },
|
|
492
|
+
'model.get': { key: 'help.model.get', zh: '', en: 'Read the model of a given agent' },
|
|
493
|
+
'model.get.scope': { key: 'help.model.get.scope', zh: '', en: 'Scope (omit to show effective value)' },
|
|
494
|
+
'model.list': { key: 'help.model.list', zh: '', en: 'List models of all agents' },
|
|
495
|
+
'model.list.scope': { key: 'help.model.list.scope', zh: '', en: 'Scope (omit to show effective value)' },
|
|
472
496
|
};
|
|
473
497
|
/* ==================== config ==================== */
|
|
474
498
|
export const config = {
|
|
@@ -492,6 +516,64 @@ export const config = {
|
|
|
492
516
|
'set.globalValueOk': { key: 'config.set.globalValueOk', zh: '', en: '✓ Global config set: {key} = {value}' },
|
|
493
517
|
'set.globalAllowedKeys': { key: 'config.set.globalAllowedKeys', zh: '', en: 'Supported global config keys: {keys}' },
|
|
494
518
|
};
|
|
519
|
+
/* ==================== migrate domain: legacy layout migration command ==================== */
|
|
520
|
+
export const migrate = {
|
|
521
|
+
// legacy layout general
|
|
522
|
+
'legacy.alreadyLatest': { key: 'migrate.legacy.alreadyLatest', zh: '', en: '✓ Already up-to-date layout, no migration needed' },
|
|
523
|
+
'legacy.done': { key: 'migrate.legacy.done', zh: '', en: 'Migration complete' },
|
|
524
|
+
// detect report
|
|
525
|
+
'detect.title': { key: 'migrate.detect.title', zh: '', en: 'Legacy layout detection report:' },
|
|
526
|
+
'detect.dryRunTitle': { key: 'migrate.detect.dryRunTitle', zh: '', en: '[DRY-RUN Mode] The following migration plan will be applied (not written):' },
|
|
527
|
+
'detect.dryRunNote': { key: 'migrate.detect.dryRunNote', zh: '', en: '(No files modified, run without --dry-run to apply the migration)' },
|
|
528
|
+
// backup
|
|
529
|
+
'backup.dirTmpl': { key: 'migrate.backup.dirTmpl', zh: '', en: 'Backup directory: {dir} (last 5 kept, older ones cleaned)' },
|
|
530
|
+
// clean
|
|
531
|
+
'clean.keptCustom': { key: 'migrate.clean.keptCustom', zh: '', en: '(project custom, kept in place)' },
|
|
532
|
+
// assignee
|
|
533
|
+
'assignee.title': { key: 'migrate.assignee.title', zh: '', en: 'Legacy flow.json assignee entries (not rewritten by default):' },
|
|
534
|
+
'assignee.hint': { key: 'migrate.assignee.hint', zh: '', en: 'To rewrite, re-run with --remap-assignee' },
|
|
535
|
+
// rollback
|
|
536
|
+
'rollback.dryRunTitle': { key: 'migrate.rollback.dryRunTitle', zh: '', en: '[DRY-RUN Mode] Rollback will restore the following files (not written):' },
|
|
537
|
+
'rollback.done': { key: 'migrate.rollback.done', zh: '', en: '✓ Rollback complete, restored:' },
|
|
538
|
+
'rollback.failed': { key: 'migrate.rollback.failed', zh: '', en: '✗ Rollback failed: {message}' },
|
|
539
|
+
// error
|
|
540
|
+
'error.pathNotExist': { key: 'migrate.error.pathNotExist', zh: '', en: 'Path does not exist: {path}' },
|
|
541
|
+
'error.aborted': { key: 'migrate.error.aborted', zh: '', en: 'Migration aborted; run `openfeel migrate rollback` to roll back: {message}' },
|
|
542
|
+
// deprecated (v1.1.1 compatibility transition)
|
|
543
|
+
'deprecated.globalCoreMd': { key: 'migrate.deprecated.globalCoreMd', zh: '', en: '? Detected deprecated global core.md (~/.config/opencode/openfeel/core.md); its constraints are now merged into the global AGENTS.md' },
|
|
544
|
+
'deprecated.globalCoreMdHint': { key: 'migrate.deprecated.globalCoreMdHint', zh: '', en: 'Run openfeel migrate --clean-global-core-md to delete, or remove it manually' },
|
|
545
|
+
'deprecated.projectAgentsMd': { key: 'migrate.deprecated.projectAgentsMd', zh: '', en: 'i Detected project AGENTS.md (legacy project constraints, now consolidated into the global ~/.config/opencode/AGENTS.md); keep or remove manually' },
|
|
546
|
+
};
|
|
547
|
+
/* ==================== model domain: model configuration (stage-40) ==================== */
|
|
548
|
+
export const model = {
|
|
549
|
+
// set
|
|
550
|
+
'set.ok': { key: 'model.set.ok', zh: '', en: '✓ Wrote model {model} for {agentId} (scope={scope})' },
|
|
551
|
+
'set.warning': { key: 'model.set.warning', zh: '', en: 'Note: {warning}' },
|
|
552
|
+
'set.needsBuild': { key: 'model.set.needsBuild', zh: '', en: 'default scope changed the framework source; run npm run build to regenerate deploy artifacts' },
|
|
553
|
+
'set.buildTriggered': { key: 'model.set.buildTriggered', zh: '', en: 'Running npm run build ...' },
|
|
554
|
+
'set.needConfirm': { key: 'model.set.needConfirm', zh: '', en: 'Modifying the framework default affects all future deployments. In non-TTY you must explicitly pass --force or --build to confirm; execution refused' },
|
|
555
|
+
'set.noSourceRepo': { key: 'model.set.noSourceRepo', zh: '', en: 'default scope is only available in the OpenFeel source repo (src/core/templates-data not found). Use --scope global/project' },
|
|
556
|
+
'set.failed': { key: 'model.set.failed', zh: '', en: '✗ Write failed: {message}' },
|
|
557
|
+
'set.example': { key: 'model.set.example', zh: '', en: 'Example: openfeel model set openfeel-vision deepseek/deepseek-flash --scope global' },
|
|
558
|
+
// get
|
|
559
|
+
'get.effective': { key: 'model.get.effective', zh: '', en: 'Effective model: {value}' },
|
|
560
|
+
'get.byScope': { key: 'model.get.byScope', zh: '', en: ' {scope}: {value}' },
|
|
561
|
+
'get.none': { key: 'model.get.none', zh: '', en: '(not explicitly set, using opencode default)' },
|
|
562
|
+
'get.inconsistent': { key: 'model.get.inconsistent', zh: '', en: '⚠ default layer multi-source mismatch (frontmatter differs from opencode-config.ts); frontmatter takes precedence' },
|
|
563
|
+
'get.shadowed': { key: 'model.get.shadowed', zh: '', en: '⚠ project/global setting is shadowed by the framework default frontmatter (default takes precedence); use --scope default to change the effective value' },
|
|
564
|
+
'get.failed': { key: 'model.get.failed', zh: '', en: '✗ Read failed: {message}' },
|
|
565
|
+
// list
|
|
566
|
+
'list.title': { key: 'model.list.title', zh: '', en: 'Agent model list:' },
|
|
567
|
+
// error
|
|
568
|
+
'error.scope': { key: 'model.error.scope', zh: '', en: '✗ {message}' },
|
|
569
|
+
};
|
|
570
|
+
/* ==================== setup domain: global deployment (v1.1.1) ==================== */
|
|
571
|
+
export const setup = {
|
|
572
|
+
'created': { key: 'setup.created', zh: '', en: 'Created global files:' },
|
|
573
|
+
'updated': { key: 'setup.updated', zh: '', en: 'Updated global files:' },
|
|
574
|
+
'appended': { key: 'setup.appended', zh: '', en: 'Global files needing manual confirmation (already exist and are unmanaged; not overwritten):' },
|
|
575
|
+
'complete': { key: 'setup.complete', zh: '', en: '? Global OpenFeel framework config deployed' },
|
|
576
|
+
};
|
|
495
577
|
/* ==================== 聚合导出 ==================== */
|
|
496
578
|
export const allDomains = [
|
|
497
579
|
{ name: 'common', domain: common },
|
|
@@ -509,5 +591,8 @@ export const allDomains = [
|
|
|
509
591
|
{ name: 'instructions', domain: instructions },
|
|
510
592
|
{ name: 'lint', domain: lint },
|
|
511
593
|
{ name: 'config', domain: config },
|
|
594
|
+
{ name: 'migrate', domain: migrate },
|
|
595
|
+
{ name: 'model', domain: model },
|
|
596
|
+
{ name: 'setup', domain: setup },
|
|
512
597
|
];
|
|
513
598
|
//# sourceMappingURL=en.js.map
|