openfeel 1.2.4 → 1.3.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 +40 -74
- package/README.md +39 -62
- package/README.zh-CN.md +40 -74
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +90 -5
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +7 -2
- package/dist/commands/update.js.map +1 -1
- package/dist/core/config.js +2 -2
- package/dist/core/dsh-profile.d.ts +93 -0
- package/dist/core/dsh-profile.d.ts.map +1 -0
- package/dist/core/dsh-profile.js +400 -0
- package/dist/core/dsh-profile.js.map +1 -0
- package/dist/core/i18n-data/en.d.ts.map +1 -1
- package/dist/core/i18n-data/en.js +21 -2
- 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 +21 -2
- package/dist/core/i18n-data/zh-CN.js.map +1 -1
- package/dist/core/setup.d.ts +12 -1
- package/dist/core/setup.d.ts.map +1 -1
- package/dist/core/setup.js +25 -10
- package/dist/core/setup.js.map +1 -1
- package/dist/core/template-loader.d.ts.map +1 -1
- package/dist/core/template-loader.js +18 -9
- package/dist/core/template-loader.js.map +1 -1
- package/dist/core/update.d.ts +2 -0
- package/dist/core/update.d.ts.map +1 -1
- package/dist/core/update.js +25 -7
- package/dist/core/update.js.map +1 -1
- package/dsh/ACTION-GUIDE.md +366 -0
- package/dsh/ADAPTER.md +65 -0
- package/dsh/README.md +264 -0
- package/dsh/cordis.patch.yml +26 -0
- package/dsh/docs/compat/commands.md +873 -0
- package/dsh/docs/compat/kb-skills.md +883 -0
- package/dsh/docs/compat/persistence.md +1013 -0
- package/dsh/docs/compat/pipeline.md +1565 -0
- package/dsh/docs/compat/req.md +1243 -0
- package/dsh/docs/compat/stage.md +1437 -0
- package/dsh/lib/adapters/cli.js +624 -0
- package/dsh/lib/adapters/dashboard.js +393 -0
- package/dsh/lib/adapters/workspace.js +150 -0
- package/dsh/lib/index.js +189 -0
- package/dsh/lib/surface/command.js +186 -0
- package/dsh/lib/surface/prompt.js +131 -0
- package/dsh/lib/tools/index.js +1245 -0
- package/dsh/lib/tools/shared.js +320 -0
- package/dsh/package.json +45 -0
- package/package.json +9 -2
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenFeel × DSH 适配器 — 工具定义原语
|
|
3
|
+
*
|
|
4
|
+
* DSH 的工具注册表只要求 `{ name, description, parameters, output, execute }`,
|
|
5
|
+
* 其中 `parameters` 是**受限 JSON Schema 子集**(type/properties/required/
|
|
6
|
+
* additionalProperties/items/enum/const + 注释键)。这里用纯函数生成这些描述,
|
|
7
|
+
* 使插件保持零依赖,同时保证参数在模型侧是强类型枚举。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 组装一个字符串枚举参数。
|
|
12
|
+
* @param {readonly string[]} values
|
|
13
|
+
* @param {string} description
|
|
14
|
+
* @param {object} [options]
|
|
15
|
+
* @param {boolean} [options.required]
|
|
16
|
+
* @returns {Record<string, unknown>}
|
|
17
|
+
*/
|
|
18
|
+
export function stringEnum(values, description, options = {}) {
|
|
19
|
+
return {
|
|
20
|
+
type: 'string',
|
|
21
|
+
enum: [...values],
|
|
22
|
+
description,
|
|
23
|
+
...(options.required === true ? { required: true } : {}),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 组装一个固定值为常量的字符串参数(用于单动作工具的显式契约)。
|
|
29
|
+
* @param {string} value 唯一合法值。
|
|
30
|
+
* @param {string} description
|
|
31
|
+
* @returns {Record<string, unknown>}
|
|
32
|
+
*/
|
|
33
|
+
export function stringConst(value, description) {
|
|
34
|
+
return { type: 'string', const: value, description, required: true };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 组装一个字符串参数。
|
|
39
|
+
* @param {string} description
|
|
40
|
+
* @param {object} [options]
|
|
41
|
+
* @param {boolean} [options.required]
|
|
42
|
+
* @returns {Record<string, unknown>}
|
|
43
|
+
*/
|
|
44
|
+
export function string(description, options = {}) {
|
|
45
|
+
return { type: 'string', description, ...(options.required === true ? { required: true } : {}) };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 组装一个字符串数组参数。
|
|
50
|
+
* @param {string} description
|
|
51
|
+
* @param {object} [options]
|
|
52
|
+
* @param {boolean} [options.required]
|
|
53
|
+
* @returns {Record<string, unknown>}
|
|
54
|
+
*/
|
|
55
|
+
export function stringArray(description, options = {}) {
|
|
56
|
+
return {
|
|
57
|
+
type: 'array',
|
|
58
|
+
items: { type: 'string' },
|
|
59
|
+
description,
|
|
60
|
+
...(options.required === true ? { required: true } : {}),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 组装一个布尔参数。
|
|
66
|
+
* @param {string} description
|
|
67
|
+
* @returns {Record<string, unknown>}
|
|
68
|
+
*/
|
|
69
|
+
export function boolean(description) {
|
|
70
|
+
return { type: 'boolean', description };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 组装一个整数参数。
|
|
75
|
+
* @param {string} description
|
|
76
|
+
* @param {object} [options]
|
|
77
|
+
* @param {number} [options.minimum] 仅用于文档;DSH 子集不强制 minimum。
|
|
78
|
+
* @returns {Record<string, unknown>}
|
|
79
|
+
*/
|
|
80
|
+
export function integer(description, options = {}) {
|
|
81
|
+
const text = options.minimum === undefined ? description : `${description}(≥ ${options.minimum})`;
|
|
82
|
+
return { type: 'integer', description: text };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 把一个「属性表」编译成 DSH 接受的**裸 JSON Schema** 参数对象。
|
|
87
|
+
*
|
|
88
|
+
* 为什么需要它:本插件直接调用 `ctx.tools.register()`(不经 `defineTool`),
|
|
89
|
+
* 注册表对 `parameters` **不做编译也不做校验**,会把定义原样投影给模型 provider。
|
|
90
|
+
* 因此工具作者侧沿用的 author DSL 写法(属性级 `required: true`、无根 `type`)
|
|
91
|
+
* 必须在交给注册表**之前**转换成本子集接受的形态:
|
|
92
|
+
* - 根节点 `type: 'object'`;
|
|
93
|
+
* - 属性级 `required: true` 收敛为对象级 `required: [...]` 数组;
|
|
94
|
+
* - 其余键(type/enum/const/items/description)原样保留。
|
|
95
|
+
*
|
|
96
|
+
* @param {Record<string, any>} properties 作者侧属性表。
|
|
97
|
+
* @returns {{ type: 'object', properties: Record<string, any>, required?: string[] }}
|
|
98
|
+
*/
|
|
99
|
+
export function compileParameters(properties) {
|
|
100
|
+
/** @type {Record<string, any>} */
|
|
101
|
+
const compiled = {};
|
|
102
|
+
/** @type {string[]} */
|
|
103
|
+
const required = [];
|
|
104
|
+
for (const [key, spec] of Object.entries(properties)) {
|
|
105
|
+
if (typeof spec !== 'object' || spec === null) {
|
|
106
|
+
throw new Error(`feel: 参数 "${key}" 必须是 schema 对象`);
|
|
107
|
+
}
|
|
108
|
+
const { required: isRequired, ...rest } = spec;
|
|
109
|
+
compiled[key] = rest;
|
|
110
|
+
if (isRequired === true) required.push(key);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
type: 'object',
|
|
114
|
+
properties: compiled,
|
|
115
|
+
...(required.length === 0 ? {} : { required }),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 统一的工具输出 schema。
|
|
121
|
+
*
|
|
122
|
+
* 三条硬约束,全部由 DSH 的真实校验(线上启动报错)推导而来:
|
|
123
|
+
*
|
|
124
|
+
* 1. **本插件直接调用 `ctx.tools.register()`,不经 `defineTool`**,因此
|
|
125
|
+
* `output.schema` 被当作**裸 JSON Schema** 校验(`assertSupportedJsonSchema`),
|
|
126
|
+
* 而不是 author DSL——属性级 `required: true` 在这里是非法关键字。
|
|
127
|
+
* 2. 该强制子集不接受 `required`(对象级或属性级);本适配器的输出恒由
|
|
128
|
+
* 适配器自己构造,契约靠类型与描述表达,不声明 required。
|
|
129
|
+
* 3. **不放无类型节点**:`type: 'json'` 或纯注释节点会被 provider 桥接层
|
|
130
|
+
* 转成 `type: null`,API 随即以
|
|
131
|
+
* `schema must be a JSON Schema of 'type: "object"', got 'type: null'` 拒绝。
|
|
132
|
+
*
|
|
133
|
+
* 因此结构化载荷统一走 `details`(JSON **字符串**):类型具体、跨 provider 稳定。
|
|
134
|
+
*
|
|
135
|
+
* @returns {Record<string, unknown>}
|
|
136
|
+
*/
|
|
137
|
+
export function envelopeSchema() {
|
|
138
|
+
return {
|
|
139
|
+
type: 'object',
|
|
140
|
+
additionalProperties: false,
|
|
141
|
+
properties: {
|
|
142
|
+
ok: { type: 'boolean', description: '命令是否成功。' },
|
|
143
|
+
action: { type: 'string', description: '实际执行的动作。' },
|
|
144
|
+
summary: { type: 'string', description: '一句话结果摘要(人类可读)。' },
|
|
145
|
+
details: {
|
|
146
|
+
type: 'string',
|
|
147
|
+
description: 'OpenFeel CLI 返回的结构化数据的 JSON 文本(无数据时省略)。需要精确字段时解析它。',
|
|
148
|
+
},
|
|
149
|
+
next: {
|
|
150
|
+
type: 'object',
|
|
151
|
+
additionalProperties: false,
|
|
152
|
+
description: '建议的下一步工具调用(若适用)。',
|
|
153
|
+
properties: {
|
|
154
|
+
tool: { type: 'string', description: '工具名。' },
|
|
155
|
+
arguments: { type: 'string', description: '建议参数的 JSON 文本。' },
|
|
156
|
+
why: { type: 'string', description: '为什么建议这样做。' },
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* 把实现侧载荷转成 wire 输出。
|
|
165
|
+
*
|
|
166
|
+
* 实现侧继续用 `{ ok, action, summary, data, next }`(`data` 任意值,便于内部
|
|
167
|
+
* 断言与组合);这里把 `data` 序列化为 `details` 字符串、把 `next.arguments`
|
|
168
|
+
* 序列化为字符串,使 wire 输出只含**具体类型**。
|
|
169
|
+
*
|
|
170
|
+
* @param {string} action
|
|
171
|
+
* @param {{ summary: string, data?: unknown, next?: { tool: string, arguments: unknown, why: string } | null }} payload
|
|
172
|
+
* @returns {{ ok: true, action: string, summary: string, details?: string, next?: { tool: string, arguments: string, why: string } }}
|
|
173
|
+
*/
|
|
174
|
+
export function toWireEnvelope(action, payload) {
|
|
175
|
+
/** @type {{ ok: true, action: string, summary: string, details?: string, next?: { tool: string, arguments: string, why: string } }} */
|
|
176
|
+
const wire = { ok: true, action, summary: payload.summary };
|
|
177
|
+
if (payload.data !== undefined && payload.data !== null) {
|
|
178
|
+
wire.details = stringifySafe(payload.data);
|
|
179
|
+
}
|
|
180
|
+
if (payload.next !== undefined && payload.next !== null) {
|
|
181
|
+
wire.next = {
|
|
182
|
+
tool: payload.next.tool,
|
|
183
|
+
arguments: stringifySafe(payload.next.arguments ?? {}),
|
|
184
|
+
why: payload.next.why,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
return wire;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* 安全序列化:处理循环引用与不可序列化值,永不抛错。
|
|
192
|
+
* @param {unknown} value
|
|
193
|
+
* @returns {string}
|
|
194
|
+
*/
|
|
195
|
+
export function stringifySafe(value) {
|
|
196
|
+
try {
|
|
197
|
+
/** @type {WeakSet<object>} */
|
|
198
|
+
const seen = new WeakSet();
|
|
199
|
+
return JSON.stringify(
|
|
200
|
+
value,
|
|
201
|
+
(_key, item) => {
|
|
202
|
+
if (typeof item === 'object' && item !== null) {
|
|
203
|
+
if (seen.has(item)) return '[循环引用]';
|
|
204
|
+
seen.add(item);
|
|
205
|
+
}
|
|
206
|
+
if (typeof item === 'bigint') return item.toString();
|
|
207
|
+
return item;
|
|
208
|
+
},
|
|
209
|
+
2,
|
|
210
|
+
);
|
|
211
|
+
} catch {
|
|
212
|
+
return String(value);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* 把工具结果渲染成模型可读文本。
|
|
218
|
+
*
|
|
219
|
+
* 模型看到的是 `summary` + `details` 里的结构化 JSON;
|
|
220
|
+
* 结构化部分保留原始字段,不做二次裁剪,以免丢失 agent 需要的细节。
|
|
221
|
+
* @param {unknown} args
|
|
222
|
+
* @param {{ ok?: boolean, action?: string, summary?: string, details?: string, next?: { tool?: string, arguments?: string, why?: string } }} value
|
|
223
|
+
* @returns {Array<{ type: 'text', text: string }>}
|
|
224
|
+
*/
|
|
225
|
+
export function renderEnvelope(_args, value) {
|
|
226
|
+
const parts = [`[${value.ok === false ? 'FAIL' : 'OK'}] ${value.action ?? ''}: ${value.summary ?? ''}`.trim()];
|
|
227
|
+
if (value.next !== undefined && value.next !== null) {
|
|
228
|
+
parts.push(`下一步建议:调用 ${value.next.tool ?? '?'}(${value.next.arguments ?? '{}'}) — ${value.next.why ?? ''}`);
|
|
229
|
+
}
|
|
230
|
+
if (typeof value.details === 'string' && value.details !== '') {
|
|
231
|
+
const text = value.details;
|
|
232
|
+
parts.push(text.length > 12_000 ? `${text.slice(0, 12_000)}\n…(已截断 ${text.length - 12_000} 字符)` : text);
|
|
233
|
+
}
|
|
234
|
+
return [{ type: 'text', text: parts.join('\n\n') }];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* 生成工具的 presentCall(对话流卡片)。
|
|
239
|
+
* @param {string} title
|
|
240
|
+
* @param {string} action
|
|
241
|
+
* @param {unknown} [rawInput]
|
|
242
|
+
*/
|
|
243
|
+
export function presentCall(title, action, rawInput) {
|
|
244
|
+
return {
|
|
245
|
+
card: 'generic',
|
|
246
|
+
title,
|
|
247
|
+
kind: action === 'get' || action === 'list' || action === 'show' ? 'read' : 'other',
|
|
248
|
+
...(rawInput === undefined ? {} : { rawInput }),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* 按字典序稳定排序对象键(用于确定性输出)。
|
|
254
|
+
* @param {Record<string, unknown>} record
|
|
255
|
+
* @returns {Record<string, unknown>}
|
|
256
|
+
*/
|
|
257
|
+
export function sortedKeys(record) {
|
|
258
|
+
/** @type {Record<string, unknown>} */
|
|
259
|
+
const out = {};
|
|
260
|
+
for (const key of Object.keys(record).sort()) out[key] = record[key];
|
|
261
|
+
return out;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* 断言某个必填参数存在。
|
|
266
|
+
* @param {Record<string, unknown>} args
|
|
267
|
+
* @param {string} name
|
|
268
|
+
* @param {string} action
|
|
269
|
+
* @returns {string}
|
|
270
|
+
*/
|
|
271
|
+
export function requireArg(args, name, action) {
|
|
272
|
+
const value = args[name];
|
|
273
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
274
|
+
throw new Error(`feel: action "${action}" 需要参数 "${name}"(非空字符串)`);
|
|
275
|
+
}
|
|
276
|
+
return value.trim();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* 追加一个可选参数到 argv(存在时才追加)。
|
|
281
|
+
* @param {string[]} argv
|
|
282
|
+
* @param {string} flag
|
|
283
|
+
* @param {unknown} value
|
|
284
|
+
* @param {(value: unknown) => string} [format]
|
|
285
|
+
* @returns {string[]}
|
|
286
|
+
*/
|
|
287
|
+
export function pushOption(argv, flag, value, format) {
|
|
288
|
+
if (value === undefined || value === null || value === '') return argv;
|
|
289
|
+
argv.push(flag);
|
|
290
|
+
argv.push(format === undefined ? String(value) : format(value));
|
|
291
|
+
return argv;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* 追加一个位置参数(不是 flag;空值跳过)。
|
|
296
|
+
*
|
|
297
|
+
* 与 `pushOption(argv, '', value)` 的区别:后者会把空字符串当作 flag 推入 argv,
|
|
298
|
+
* 使 Commander 多收到一个空位置参数(实测 `feel_op create` 报
|
|
299
|
+
* `too many arguments ... Expected 2 arguments but got 3`)。
|
|
300
|
+
* @param {string[]} argv
|
|
301
|
+
* @param {unknown} value
|
|
302
|
+
* @returns {string[]}
|
|
303
|
+
*/
|
|
304
|
+
export function pushPositional(argv, value) {
|
|
305
|
+
if (value === undefined || value === null || value === '') return argv;
|
|
306
|
+
argv.push(String(value));
|
|
307
|
+
return argv;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* 追加一个布尔开关参数。
|
|
312
|
+
* @param {string[]} argv
|
|
313
|
+
* @param {string} flag
|
|
314
|
+
* @param {unknown} value
|
|
315
|
+
* @returns {string[]}
|
|
316
|
+
*/
|
|
317
|
+
export function pushFlag(argv, flag, value) {
|
|
318
|
+
if (value === true) argv.push(flag);
|
|
319
|
+
return argv;
|
|
320
|
+
}
|
package/dsh/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "openfeel-dsh",
|
|
3
|
+
"version": "1.3.1",
|
|
4
|
+
"description": "OpenFeel × DSH — Feel 模式适配器:把 OpenFeel 的流水线、需求分发、版本归档与知识库接入 DeepSeek Harness",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": true,
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"main": "lib/index.js",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./lib/index.js",
|
|
11
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
12
|
+
"./package.json": "./package.json"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"lib",
|
|
16
|
+
"cordis.patch.yml",
|
|
17
|
+
"README.md",
|
|
18
|
+
"docs"
|
|
19
|
+
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=20.17.0"
|
|
22
|
+
},
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/Liuary/OpenFeel.git",
|
|
26
|
+
"directory": "dsh"
|
|
27
|
+
},
|
|
28
|
+
"dsh": {
|
|
29
|
+
"bundle": {
|
|
30
|
+
"patch": "./cordis.patch.yml"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"openfeel": {
|
|
34
|
+
"adapter": "dsh",
|
|
35
|
+
"requiresCli": ">=1.3.1"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"openfeel",
|
|
39
|
+
"dsh",
|
|
40
|
+
"deepseek-harness",
|
|
41
|
+
"adapter",
|
|
42
|
+
"workflow",
|
|
43
|
+
"governance"
|
|
44
|
+
]
|
|
45
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openfeel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "AI Agent 开发流程治理 CLI 工具",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -56,6 +56,13 @@
|
|
|
56
56
|
"files": [
|
|
57
57
|
"dist",
|
|
58
58
|
"bin",
|
|
59
|
-
"schemas"
|
|
59
|
+
"schemas",
|
|
60
|
+
"dsh/lib",
|
|
61
|
+
"dsh/cordis.patch.yml",
|
|
62
|
+
"dsh/package.json",
|
|
63
|
+
"dsh/README.md",
|
|
64
|
+
"dsh/ADAPTER.md",
|
|
65
|
+
"dsh/ACTION-GUIDE.md",
|
|
66
|
+
"dsh/docs"
|
|
60
67
|
]
|
|
61
68
|
}
|