openfeel 1.3.3 → 1.3.5
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.md +1 -1
- package/dist/cli/deploy-check-output.d.ts.map +1 -1
- package/dist/cli/deploy-check-output.js +7 -1
- package/dist/cli/deploy-check-output.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +7 -0
- package/dist/commands/setup.js.map +1 -1
- package/dist/core/adapter-hash.d.ts +7 -0
- package/dist/core/adapter-hash.d.ts.map +1 -0
- package/dist/core/adapter-hash.js +64 -0
- package/dist/core/adapter-hash.js.map +1 -0
- package/dist/core/config.js +2 -2
- package/dist/core/deployment-check.d.ts +17 -4
- package/dist/core/deployment-check.d.ts.map +1 -1
- package/dist/core/deployment-check.js +59 -7
- package/dist/core/deployment-check.js.map +1 -1
- package/dist/core/global-paths.d.ts +5 -0
- package/dist/core/global-paths.d.ts.map +1 -1
- package/dist/core/global-paths.js +7 -0
- package/dist/core/global-paths.js.map +1 -1
- package/dist/core/i18n-data/en.d.ts.map +1 -1
- package/dist/core/i18n-data/en.js +2 -0
- package/dist/core/i18n-data/en.js.map +1 -1
- package/dist/core/i18n-data/zh-CN.d.ts.map +1 -1
- package/dist/core/i18n-data/zh-CN.js +2 -0
- package/dist/core/i18n-data/zh-CN.js.map +1 -1
- package/dist/core/setup.d.ts.map +1 -1
- package/dist/core/setup.js +8 -3
- package/dist/core/setup.js.map +1 -1
- package/dist/core/template-loader.d.ts.map +1 -1
- package/dist/core/template-loader.js +33 -6
- package/dist/core/template-loader.js.map +1 -1
- package/dist/core/update-state.d.ts +20 -0
- package/dist/core/update-state.d.ts.map +1 -1
- package/dist/core/update-state.js +26 -0
- package/dist/core/update-state.js.map +1 -1
- package/dist/core/update.d.ts.map +1 -1
- package/dist/core/update.js +21 -2
- package/dist/core/update.js.map +1 -1
- package/dsh/ADAPTER.md +9 -3
- package/dsh/README.md +9 -3
- package/dsh/lib/adapters/host-state.js +111 -0
- package/dsh/lib/index.js +21 -0
- package/dsh/lib/surface/prompt.js +10 -0
- package/dsh/lib/tools/index.js +8 -3
- package/dsh/package.json +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 宿主已加载适配器标记(v1.3.4)
|
|
3
|
+
*
|
|
4
|
+
* 目的:让 `openfeel setup --check` 能区分
|
|
5
|
+
* - 「磁盘适配器旧」→ mismatch(应重跑 setup)
|
|
6
|
+
* - 「磁盘已新,但运行中的宿主仍加载旧模块」→ restart-required(应完全重启宿主)
|
|
7
|
+
*
|
|
8
|
+
* 本文件在 DSH 宿主 `apply()`(启动)时由适配器调用 `writeHostMarker()` 写入;
|
|
9
|
+
* 指纹算法必须与 CLI 侧 `src/core/adapter-hash.ts` **逐字节一致**(见该文件注释):
|
|
10
|
+
* `lib/**` 全部文件按相对 POSIX 路径排序 → 逐文件 CRLF→LF 后 sha256 → 拼接 `${rel}\n${hex}\n` → 再 sha256。
|
|
11
|
+
*
|
|
12
|
+
* 零依赖、只读/写一个 JSON;任何异常都吞掉(标记缺失时 CLI 会落到 restart-required,
|
|
13
|
+
* 属可操作提示,不应让宿主挂载失败)。
|
|
14
|
+
*
|
|
15
|
+
* @module openfeel-dsh/host-state
|
|
16
|
+
*/
|
|
17
|
+
import { createHash } from 'node:crypto';
|
|
18
|
+
import { mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
19
|
+
import { homedir } from 'node:os';
|
|
20
|
+
import { dirname, join, relative, sep } from 'node:path';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 默认标记路径:`~/.openfeel/adapter-host-dsh.json`。
|
|
24
|
+
* `OPENFEEL_ADAPTER_HOST_PATH` 可覆盖(测试隔离用)。
|
|
25
|
+
* @returns {string}
|
|
26
|
+
*/
|
|
27
|
+
export function defaultMarkerPath() {
|
|
28
|
+
const override = process.env.OPENFEEL_ADAPTER_HOST_PATH;
|
|
29
|
+
if (typeof override === 'string' && override.trim() !== '') return override;
|
|
30
|
+
return join(homedir(), '.openfeel', 'adapter-host-dsh.json');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** CRLF / CR → LF */
|
|
34
|
+
function normalizeEol(content) {
|
|
35
|
+
return content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 递归收集普通文件(不跟随符号链接目录)。
|
|
40
|
+
* @param {string} dir
|
|
41
|
+
* @param {string[]} out
|
|
42
|
+
*/
|
|
43
|
+
function collectFiles(dir, out) {
|
|
44
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
45
|
+
const full = join(dir, entry.name);
|
|
46
|
+
if (entry.isDirectory()) collectFiles(full, out);
|
|
47
|
+
else if (entry.isFile()) out.push(full);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 计算适配器 `lib/` 目录指纹(与 CLI 侧 `hashAdapterFiles` 同算法)。
|
|
53
|
+
* @param {string} adapterRoot 适配器根目录(含 `lib/`)
|
|
54
|
+
* @returns {string} 64 位 hex;不可读时返回空串
|
|
55
|
+
*/
|
|
56
|
+
export function hashLoadedAdapter(adapterRoot) {
|
|
57
|
+
try {
|
|
58
|
+
const libDir = join(adapterRoot, 'lib');
|
|
59
|
+
const files = [];
|
|
60
|
+
collectFiles(libDir, files);
|
|
61
|
+
if (files.length === 0) return '';
|
|
62
|
+
const entries = files
|
|
63
|
+
.map((file) => ({ rel: relative(adapterRoot, file).split(sep).join('/'), abs: file }))
|
|
64
|
+
.sort((a, b) => (a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0));
|
|
65
|
+
const sha = createHash('sha256');
|
|
66
|
+
for (const entry of entries) {
|
|
67
|
+
const content = normalizeEol(readFileSync(entry.abs, 'utf-8'));
|
|
68
|
+
const fileHash = createHash('sha256').update(content, 'utf-8').digest('hex');
|
|
69
|
+
sha.update(`${entry.rel}\n${fileHash}\n`, 'utf-8');
|
|
70
|
+
}
|
|
71
|
+
return sha.digest('hex');
|
|
72
|
+
} catch {
|
|
73
|
+
return '';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 写入宿主已加载标记(best-effort)。
|
|
79
|
+
* @param {{ adapterRoot: string, version: string, markerPath?: string }} options
|
|
80
|
+
* @returns {{ adapter: string, version: string, loaded_hash: string, started_at: string } | null}
|
|
81
|
+
*/
|
|
82
|
+
export function writeHostMarker(options) {
|
|
83
|
+
try {
|
|
84
|
+
const markerPath = options.markerPath ?? defaultMarkerPath();
|
|
85
|
+
const marker = {
|
|
86
|
+
adapter: 'dsh',
|
|
87
|
+
version: options.version,
|
|
88
|
+
loaded_hash: hashLoadedAdapter(options.adapterRoot),
|
|
89
|
+
started_at: new Date().toISOString(),
|
|
90
|
+
};
|
|
91
|
+
mkdirSync(dirname(markerPath), { recursive: true });
|
|
92
|
+
writeFileSync(markerPath, JSON.stringify(marker, null, 2) + '\n', 'utf-8');
|
|
93
|
+
return marker;
|
|
94
|
+
} catch {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* 读取宿主已加载标记(只读;缺失/损坏 → null)。
|
|
101
|
+
* @param {string} [markerPath]
|
|
102
|
+
* @returns {{ adapter?: string, version?: string, loaded_hash?: string, started_at?: string } | null}
|
|
103
|
+
*/
|
|
104
|
+
export function readHostMarker(markerPath) {
|
|
105
|
+
try {
|
|
106
|
+
const raw = JSON.parse(readFileSync(markerPath ?? defaultMarkerPath(), 'utf-8'));
|
|
107
|
+
return typeof raw === 'object' && raw !== null ? raw : null;
|
|
108
|
+
} catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
package/dsh/lib/index.js
CHANGED
|
@@ -22,8 +22,13 @@
|
|
|
22
22
|
* @module openfeel-dsh
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
+
import { readFileSync } from 'node:fs';
|
|
26
|
+
import { dirname } from 'node:path';
|
|
27
|
+
import { fileURLToPath } from 'node:url';
|
|
28
|
+
|
|
25
29
|
import { FeelCli } from './adapters/cli.js';
|
|
26
30
|
import { resolveWorkspace } from './adapters/workspace.js';
|
|
31
|
+
import { writeHostMarker } from './adapters/host-state.js';
|
|
27
32
|
import { createOpenFeelGuard } from './guard.js';
|
|
28
33
|
import { createFeelCommandHandler } from './surface/command.js';
|
|
29
34
|
import { FEEL_PROMPT_NAME, FEEL_PROMPT_ORDER, renderBootstrapPrompt, renderFeelPrompt } from './surface/prompt.js';
|
|
@@ -110,6 +115,22 @@ function currentAgent(ctx) {
|
|
|
110
115
|
*/
|
|
111
116
|
export function apply(ctx, rawConfig) {
|
|
112
117
|
const config = normalizeConfig(rawConfig);
|
|
118
|
+
|
|
119
|
+
// v1.3.4:记录「本宿主实际加载的适配器指纹」,供 `openfeel setup --check` 区分
|
|
120
|
+
// 「文件旧」与「文件已新但宿主未重启」。best-effort:失败不影响挂载。
|
|
121
|
+
try {
|
|
122
|
+
const adapterRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
123
|
+
let adapterVersion = '0.0.0';
|
|
124
|
+
try {
|
|
125
|
+
const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));
|
|
126
|
+
if (typeof pkg.version === 'string' && pkg.version !== '') adapterVersion = pkg.version;
|
|
127
|
+
} catch {
|
|
128
|
+
/* package.json 不可读时保持占位版本 */
|
|
129
|
+
}
|
|
130
|
+
writeHostMarker({ adapterRoot, version: adapterVersion });
|
|
131
|
+
} catch {
|
|
132
|
+
/* 标记写入失败 → CLI check 会提示 restart-required,可操作,不阻塞 */
|
|
133
|
+
}
|
|
113
134
|
const log = typeof ctx.logger === 'function'
|
|
114
135
|
? ctx.logger('openfeel-dsh')
|
|
115
136
|
: {
|
|
@@ -146,6 +146,11 @@ Feel(你)只做编排、决策与门禁判断。
|
|
|
146
146
|
- \`fast\`:跳过 \`scheme_*\` 与 \`test_*\`;**计划审查与代码审查各保留一轮**(fast 不等于免审)。
|
|
147
147
|
- \`general\`:无流水线(空转移表),按需直接调度通用工作者;**仅需求级合法**,阶段级/部署级会拒绝。
|
|
148
148
|
|
|
149
|
+
## 命令执行
|
|
150
|
+
- **Windows:优先 \`bash\`(Git Bash),其次 \`pwsh\`(PowerShell 7)**;默认不用 \`cmd.exe\`。
|
|
151
|
+
- macOS / Linux:\`bash\`。
|
|
152
|
+
- 同一任务内保持同一 shell;路径与转义按所选 shell 书写。
|
|
153
|
+
|
|
149
154
|
## 需求分发
|
|
150
155
|
需求是排期层(\`REQ-NNN\`,7 状态:draft/scheduled/in_progress/done/blocked/cancelled/rejected)。
|
|
151
156
|
接取流程:\`feel_req action=next\` → \`claim\` → \`link\` 到阶段 → 阶段推进驱动聚合 → 全部关联阶段 done 时需求自动收口。
|
|
@@ -196,6 +201,11 @@ planner / schemer / executor / reviewer / feel-tester / archiver. **The authorit
|
|
|
196
201
|
- \`fast\`: skips \`scheme_*\` and \`test_*\`; **plan review and code review each keep one round** (fast is not review-free).
|
|
197
202
|
- \`general\`: no pipeline (empty transition table), dispatch general workers on demand; requirement-level only.
|
|
198
203
|
|
|
204
|
+
## Command execution
|
|
205
|
+
- **Windows: prefer \`bash\` (Git Bash), then \`pwsh\` (PowerShell 7)**; do not default to \`cmd.exe\`.
|
|
206
|
+
- macOS / Linux: \`bash\`.
|
|
207
|
+
- Keep one shell per task; write paths/escaping for that shell.
|
|
208
|
+
|
|
199
209
|
## Requirement distribution
|
|
200
210
|
Requirements are the scheduling layer (\`REQ-NNN\`, 7 statuses: draft/scheduled/in_progress/done/blocked/cancelled/rejected).
|
|
201
211
|
Flow: \`feel_req action=next\` → \`claim\` → \`link\` to a stage → stage advancement drives aggregation → auto-close when every linked stage is done.
|
package/dsh/lib/tools/index.js
CHANGED
|
@@ -207,9 +207,9 @@ export function createTools(context) {
|
|
|
207
207
|
'操作。',
|
|
208
208
|
{ required: true },
|
|
209
209
|
),
|
|
210
|
-
stage: string('阶段 ID(advance/retry 必需;phases/mode 可选用于按阶段派生模式)。'),
|
|
210
|
+
stage: string('阶段 ID(advance/retry 必需;review_add 与 op 二选一;phases/mode 可选用于按阶段派生模式)。'),
|
|
211
211
|
to: stringEnum(PHASES, 'advance 的目标相位。'),
|
|
212
|
-
op: string('操作 ID(如 v1.2.1-stage-93.op-001
|
|
212
|
+
op: string('操作 ID(如 v1.2.1-stage-93.op-001);review_add 与 stage 二选一。'),
|
|
213
213
|
force: boolean('advance 时跳过相位合法性与跳跃检查(不能绕过 blocking REV 门)。'),
|
|
214
214
|
dry_run: boolean('仅校验/预览,不写盘。'),
|
|
215
215
|
review_tier: stringEnum(TIERS, '审查档位:full=零裁剪 / lean=裁 scheme_review / skip=再裁 plan_review。缺省不裁剪(=full)。'),
|
|
@@ -294,7 +294,12 @@ export function createTools(context) {
|
|
|
294
294
|
}
|
|
295
295
|
case 'review_add': {
|
|
296
296
|
const argv = ['flow', 'review', 'add'];
|
|
297
|
-
|
|
297
|
+
// v1.3.4:op / stage 二选一透传(fast 无 op 场景必须能挂阶段级 REV)
|
|
298
|
+
if (!args.op && !args.stage) {
|
|
299
|
+
throw new FeelError('review_add 需要 op 或 stage 之一', { kind: 'usage', code: 'FEEL_BAD_USAGE' });
|
|
300
|
+
}
|
|
301
|
+
if (args.op) pushOption(argv, '--op', args.op);
|
|
302
|
+
else pushOption(argv, '--stage', args.stage);
|
|
298
303
|
pushOption(argv, '--title', requireArg(args, 'title', action));
|
|
299
304
|
pushOption(argv, '--auto-fix', args.auto_fix);
|
|
300
305
|
if (args.blocking !== undefined) argv.push('--blocking', args.blocking === true ? 'true' : 'false');
|
package/dsh/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openfeel-dsh",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.5",
|
|
4
4
|
"description": "OpenFeel × DSH — Feel 模式适配器:把 OpenFeel 的流水线、需求分发、版本归档与知识库接入 DeepSeek Harness",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": true,
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"openfeel": {
|
|
34
34
|
"adapter": "dsh",
|
|
35
|
-
"requiresCli": ">=1.3.
|
|
35
|
+
"requiresCli": ">=1.3.5"
|
|
36
36
|
},
|
|
37
37
|
"keywords": [
|
|
38
38
|
"openfeel",
|