@siming-org/core 0.4.0 → 0.5.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/dist/index.d.ts +1086 -9
- package/dist/index.js +1556 -354
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { readFileSync } from "fs";
|
|
3
|
-
import { z as
|
|
3
|
+
import { z as z20 } from "zod";
|
|
4
4
|
|
|
5
5
|
// src/store/client.ts
|
|
6
6
|
import { MongoClient } from "mongodb";
|
|
@@ -104,6 +104,9 @@ var BIZ_CODE_MESSAGES = {
|
|
|
104
104
|
TASK_PROJECT_IMMUTABLE: "\u4EFB\u52A1\u5F52\u5C5E\u9879\u76EE\u521B\u5EFA\u540E\u4E0D\u53EF\u53D8\u66F4",
|
|
105
105
|
DEFAULT_PROJECT_IMMUTABLE: "\u9ED8\u8BA4\u9879\u76EE\u4E0D\u53EF\u505C\u7528",
|
|
106
106
|
TEMPLATE_NAME_CONFLICT: "\u76EE\u6807\u9879\u76EE\u5185\u5DF2\u5B58\u5728\u540C\u540D\u6A21\u677F",
|
|
107
|
+
// T202609020002 模板业务 code 域(创建冲突/不可变;E11000 兜底由 repo 层按 keyValue 二次映射为 TEMPLATE_CODE_CONFLICT)
|
|
108
|
+
TEMPLATE_CODE_CONFLICT: "\u76EE\u6807\u9879\u76EE\u5185\u5DF2\u5B58\u5728\u76F8\u540C code \u7684\u6A21\u677F\uFF08code \u9879\u76EE\u5185\u552F\u4E00\uFF09",
|
|
109
|
+
TEMPLATE_CODE_IMMUTABLE: "\u6A21\u677F code \u521B\u5EFA\u540E\u4E0D\u53EF\u53D8\uFF08\u5982\u9700\u65B0\u6807\u8BC6\u8BF7\u590D\u5236\u6A21\u677F\u6307\u5B9A\u65B0 code\uFF09",
|
|
107
110
|
// 仅覆盖 404 分支(不存在);archived 走独立的 409 PROJECT_ARCHIVED,勿在此合并语义
|
|
108
111
|
PROJECT_NOT_FOUND: "\u9879\u76EE\u4E0D\u5B58\u5728",
|
|
109
112
|
AGENT_SKILL_SCOPE_CONFLICT: "Agent \u7ED1\u5B9A\u7684 Skill \u4F5C\u7528\u57DF\u4E0D\u517C\u5BB9\uFF08\u5168\u5C40 Agent \u4EC5\u53EF\u7ED1\u5B9A\u5168\u5C40 Skill\uFF09",
|
|
@@ -131,7 +134,26 @@ var BIZ_CODE_MESSAGES = {
|
|
|
131
134
|
TASK_SKIP_NODE_NOT_FOUND: "skip \u6E05\u5355\u542B\u6A21\u677F\u4E0D\u5B58\u5728\u7684\u8282\u70B9 id\uFF08\u89C1\u9519\u8BEF\u8BE6\u60C5\u7684\u5408\u6CD5\u8282\u70B9\u6E05\u5355\uFF09",
|
|
132
135
|
TASK_TYPE_TRACK_MISMATCH: "\u4EFB\u52A1\u7C7B\u578B\u4E0E\u8F68\u9053\u7EC4\u5408\u975E\u6CD5\uFF08research \u8F68\u9053\u4E3A\u8C03\u7814\u7C7B\u578B\u4E13\u7528\u503C\uFF1BUI \u5FAE\u8C03\u4EC5\u9002\u7528 ui \u8F68\u9053\uFF09",
|
|
133
136
|
// T202608290001:停用模板创建任务守卫(资产启停——停用对创建通道同样不可见)
|
|
134
|
-
TEMPLATE_DISABLED: "\u6A21\u677F\u5DF2\u505C\u7528\uFF0C\u7981\u6B62\u7528\u4E8E\u521B\u5EFA\u4EFB\u52A1\uFF08Web \u7BA1\u7406\u53F0\u53EF\u91CD\u65B0\u542F\u7528\uFF09"
|
|
137
|
+
TEMPLATE_DISABLED: "\u6A21\u677F\u5DF2\u505C\u7528\uFF0C\u7981\u6B62\u7528\u4E8E\u521B\u5EFA\u4EFB\u52A1\uFF08Web \u7BA1\u7406\u53F0\u53EF\u91CD\u65B0\u542F\u7528\uFF09",
|
|
138
|
+
// T202608310001:鉴权域(错误响应以 error: bizCode 为机器可判键)
|
|
139
|
+
AUTH_REQUIRED: "\u9700\u8981\u767B\u5F55\u6216\u914D\u7F6E Token\uFF08Web \u767B\u5F55\uFF1BCLI \u8FD0\u884C siming auth token set \u914D\u7F6E\uFF09",
|
|
140
|
+
AUTH_TOKEN_INVALID: "Token \u65E0\u6548\u6216\u5DF2\u88AB\u91CD\u7F6E\uFF08Web \u7BA1\u7406\u53F0\u91CD\u65B0\u751F\u6210\u540E\u8FD0\u884C siming auth token set \u66F4\u65B0\uFF09",
|
|
141
|
+
AUTH_INVALID_CREDENTIALS: "\u8D26\u53F7\u6216\u5BC6\u7801\u9519\u8BEF",
|
|
142
|
+
AUTH_FORBIDDEN: "\u5F53\u524D\u51ED\u8BC1\u65E0\u6743\u6267\u884C\u6B64\u64CD\u4F5C\uFF08\u9879\u76EE Token \u4EC5\u9650\u672C\u9879\u76EE\u8BFB\u5199\u4E0E\u5168\u5C40\u8D44\u4EA7\u53EA\u8BFB\uFF09",
|
|
143
|
+
AUTH_ACCOUNT_EXISTS: "\u7BA1\u7406\u5458\u8D26\u53F7\u5DF2\u521D\u59CB\u5316\uFF08\u65E0\u9700\u91CD\u590D\u521B\u5EFA\uFF09",
|
|
144
|
+
// T202609010002:批量写入域(信封级——空批/超限整批拒绝并提示拆分)
|
|
145
|
+
BATCH_EMPTY: "\u6279\u91CF\u6761\u76EE\u4E3A\u7A7A\uFF08items \u81F3\u5C11 1 \u6761\uFF09",
|
|
146
|
+
BATCH_LIMIT_EXCEEDED: "\u5355\u6B21\u6279\u91CF\u4E0A\u9650 50 \u6761\uFF0C\u8BF7\u62C6\u5206\u4E3A\u591A\u6B21\u8C03\u7528",
|
|
147
|
+
BATCH_PAYLOAD_TOO_LARGE: "\u6279\u91CF\u5168\u6587\u5FEB\u7167\u603B\u91CF\u8D85\u9650\uFF082M \u5B57\u7B26/\u6279\uFF09\uFF0C\u8BF7\u62C6\u5206\u4E3A\u591A\u6B21\u8C03\u7528\u6216\u6539\u7528 path \u5F15\u7528",
|
|
148
|
+
// T202609020001:通用 DAG 节点资产域
|
|
149
|
+
NODE_PRESET_CODE_EXISTS: "\u8282\u70B9 code \u5DF2\u5B58\u5728\uFF08code \u5168\u5C40\u552F\u4E00\uFF0C\u8DE8\u4F5C\u7528\u57DF\u4E0D\u53EF\u91CD\u590D\uFF09",
|
|
150
|
+
NODE_PRESET_CODE_IMMUTABLE: "\u8282\u70B9 code \u521B\u5EFA\u540E\u4E0D\u53EF\u53D8",
|
|
151
|
+
NODE_PRESET_VERSION_REQUIRED: "\u8282\u70B9\u5185\u5BB9\u53D8\u66F4\u5FC5\u987B\u540C\u6B65\u63D0\u5347\u7248\u672C\u53F7\uFF08version \u5B57\u6BB5\uFF09",
|
|
152
|
+
TEMPLATE_NODE_ID_DUPLICATE: "\u6A21\u677F\u5185\u8282\u70B9 id \u91CD\u590D\uFF08\u5148\u5728\u8282\u70B9\u5E93\u4E2D\u590D\u5236\u6539\u540D\u6216\u8C03\u6574\u6A21\u677F\u8282\u70B9\u6807\u8BC6\uFF09",
|
|
153
|
+
NODE_LIBRARY_COMPOSITION_NOT_FOUND: "\u7EC4\u5408\u5B9A\u4E49\u4E0D\u5B58\u5728\uFF08\u591A\u5E93\u540C\u540D\u7EC4\u5408\u9700\u663E\u5F0F\u6307\u5B9A\u6765\u6E90\u5E93\uFF09",
|
|
154
|
+
UPGRADE_DECISION_REQUIRED: "\u672C\u5730\u5DF2\u4FEE\u6539\u7684\u8282\u70B9\u5FC5\u987B\u663E\u5F0F\u51B3\u7B56\uFF08upgrade/keep/skip\uFF09",
|
|
155
|
+
UPGRADE_STALE: "\u6A21\u677F\u72B6\u6001\u4E0E\u5347\u7EA7\u8BA1\u5212\u4E0D\u7B26\uFF08\u8BA1\u5212\u540E\u6A21\u677F\u5DF2\u88AB\u7F16\u8F91\uFF09\uFF0C\u8BF7\u91CD\u65B0\u83B7\u53D6\u5347\u7EA7\u8BA1\u5212",
|
|
156
|
+
FLOW_MANIFEST_INVALID: "\u8282\u70B9\u5E93 manifest \u683C\u5F0F\u6216\u7248\u672C\u4E0D\u652F\u6301"
|
|
135
157
|
};
|
|
136
158
|
var AppError = class extends Error {
|
|
137
159
|
};
|
|
@@ -150,8 +172,25 @@ var NotFoundError = class extends AppError {
|
|
|
150
172
|
statusCode = 404;
|
|
151
173
|
code = "not_found";
|
|
152
174
|
bizCode;
|
|
175
|
+
/**
|
|
176
|
+
* T202609020002:not-found 相近候选清单(模板寻址防转录错误——格式合法但不存在的
|
|
177
|
+
* code/数据库 id,错误信息携带项目内相近模板,转录错误可当场发现)。附加到响应体
|
|
178
|
+
* 的 candidates 字段;仅模板寻址路由经 withTemplateCandidates 注入(构造后写入故非
|
|
179
|
+
* readonly——外部读、内部一次写),其余资源 not-found 形态不变。
|
|
180
|
+
*/
|
|
181
|
+
templateCandidates;
|
|
182
|
+
withTemplateCandidates(candidates) {
|
|
183
|
+
this.templateCandidates = candidates;
|
|
184
|
+
return this;
|
|
185
|
+
}
|
|
153
186
|
toResponse() {
|
|
154
|
-
return {
|
|
187
|
+
return {
|
|
188
|
+
error: this.bizCode ?? this.code,
|
|
189
|
+
message: this.message,
|
|
190
|
+
resource: this.resource,
|
|
191
|
+
id: this.id,
|
|
192
|
+
...this.templateCandidates !== void 0 ? { candidates: this.templateCandidates } : {}
|
|
193
|
+
};
|
|
155
194
|
}
|
|
156
195
|
};
|
|
157
196
|
var BadRequestError = class extends AppError {
|
|
@@ -207,6 +246,31 @@ var ValidationError = class extends AppError {
|
|
|
207
246
|
return { error: this.bizCode ?? this.code, message: this.message, issues: this.issues };
|
|
208
247
|
}
|
|
209
248
|
};
|
|
249
|
+
var UnauthorizedError = class extends AppError {
|
|
250
|
+
constructor(bizCode) {
|
|
251
|
+
super(BIZ_CODE_MESSAGES[bizCode]);
|
|
252
|
+
this.bizCode = bizCode;
|
|
253
|
+
this.name = "UnauthorizedError";
|
|
254
|
+
}
|
|
255
|
+
bizCode;
|
|
256
|
+
statusCode = 401;
|
|
257
|
+
code = "unauthorized";
|
|
258
|
+
toResponse() {
|
|
259
|
+
return { error: this.bizCode, message: this.message };
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
var ForbiddenError = class extends AppError {
|
|
263
|
+
statusCode = 403;
|
|
264
|
+
code = "forbidden";
|
|
265
|
+
bizCode = "AUTH_FORBIDDEN";
|
|
266
|
+
constructor() {
|
|
267
|
+
super(BIZ_CODE_MESSAGES.AUTH_FORBIDDEN);
|
|
268
|
+
this.name = "ForbiddenError";
|
|
269
|
+
}
|
|
270
|
+
toResponse() {
|
|
271
|
+
return { error: this.bizCode, message: this.message };
|
|
272
|
+
}
|
|
273
|
+
};
|
|
210
274
|
|
|
211
275
|
// src/store/repos/skill.repo.ts
|
|
212
276
|
function createSkillRepo(db) {
|
|
@@ -268,6 +332,12 @@ function createSkillRepo(db) {
|
|
|
268
332
|
const skills = await base.list(filter);
|
|
269
333
|
return new Map(skills.map((s) => [s.name, s]));
|
|
270
334
|
},
|
|
335
|
+
async hasNameScopeConflict(name, scope) {
|
|
336
|
+
return await base._collection.countDocuments(
|
|
337
|
+
scope === "global" ? { name, scope: "project" } : { name, scope: "global" },
|
|
338
|
+
{ limit: 1 }
|
|
339
|
+
) > 0;
|
|
340
|
+
},
|
|
271
341
|
async copySkill(sourceName, source, target, newName) {
|
|
272
342
|
const src = await base._collection.findOne(
|
|
273
343
|
source.scope === "project" ? { name: sourceName, scope: "project", projectId: source.projectId } : { name: sourceName, scope: "global" }
|
|
@@ -379,6 +449,12 @@ function createAgentRepo(db) {
|
|
|
379
449
|
async listAgents(opts) {
|
|
380
450
|
return base.list(listQuery({}, opts));
|
|
381
451
|
},
|
|
452
|
+
async hasNameScopeConflict(name, scope) {
|
|
453
|
+
return await base._collection.countDocuments(
|
|
454
|
+
scope === "global" ? { name, scope: "project" } : { name, scope: "global" },
|
|
455
|
+
{ limit: 1 }
|
|
456
|
+
) > 0;
|
|
457
|
+
},
|
|
382
458
|
async copyAgent(sourceName, source, target, newName, skillRepo) {
|
|
383
459
|
const src = await base._collection.findOne(
|
|
384
460
|
source.scope === "project" ? { name: sourceName, scope: "project", projectId: source.projectId } : { name: sourceName, scope: "global" }
|
|
@@ -435,48 +511,8 @@ async function assertBoundSkillsCompatible(skillRepo, agentScope, agentProjectId
|
|
|
435
511
|
}
|
|
436
512
|
}
|
|
437
513
|
|
|
438
|
-
// src/
|
|
439
|
-
|
|
440
|
-
const base = createRepo(db.collection("dag_templates"));
|
|
441
|
-
return {
|
|
442
|
-
...base,
|
|
443
|
-
createDagTemplate(data) {
|
|
444
|
-
return base.create(data);
|
|
445
|
-
},
|
|
446
|
-
async listDagTemplates(filter) {
|
|
447
|
-
const query = {};
|
|
448
|
-
if (filter?.projectId) query.projectId = filter.projectId;
|
|
449
|
-
if (filter?.name) query.name = filter.name;
|
|
450
|
-
if (!filter?.includeDisabled) Object.assign(query, enabledNeFalse());
|
|
451
|
-
return base.list(query);
|
|
452
|
-
},
|
|
453
|
-
async copyDagTemplate(sourceId, targetProjectId, newName) {
|
|
454
|
-
const source = await base.getById(sourceId);
|
|
455
|
-
if (!source) {
|
|
456
|
-
throw new ConflictError("dagTemplate", `copy source not found: ${sourceId}`);
|
|
457
|
-
}
|
|
458
|
-
const dup = await base._collection.findOne(
|
|
459
|
-
{ projectId: targetProjectId, name: newName },
|
|
460
|
-
{ projection: { _id: 1 } }
|
|
461
|
-
);
|
|
462
|
-
if (dup) {
|
|
463
|
-
throw new ConflictError("dagTemplate", `name conflict in target project: ${newName}`);
|
|
464
|
-
}
|
|
465
|
-
const { id: _sourceId, createdAt: _ca, updatedAt: _ua, enabled: _sourceEnabled, ...rest } = source;
|
|
466
|
-
const copy = {
|
|
467
|
-
...rest,
|
|
468
|
-
name: newName,
|
|
469
|
-
projectId: targetProjectId,
|
|
470
|
-
isDefault: false,
|
|
471
|
-
version: "1.0.0"
|
|
472
|
-
};
|
|
473
|
-
return base.create(copy);
|
|
474
|
-
}
|
|
475
|
-
};
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
// src/schemas/task.ts
|
|
479
|
-
import { z as z4 } from "zod";
|
|
514
|
+
// src/schemas/dag-template.ts
|
|
515
|
+
import { z as z3 } from "zod";
|
|
480
516
|
|
|
481
517
|
// src/schemas/skill.ts
|
|
482
518
|
import { z as z2 } from "zod";
|
|
@@ -666,19 +702,37 @@ var SkillCopySchema = z2.object({
|
|
|
666
702
|
});
|
|
667
703
|
|
|
668
704
|
// src/schemas/dag-template.ts
|
|
669
|
-
import { z as z3 } from "zod";
|
|
670
705
|
var DagNodePhaseSchema = z3.string();
|
|
671
706
|
var DagNodeTrackSchema = z3.string();
|
|
672
707
|
var DAG_PHASES = ["entry", "track", "test", "exit"];
|
|
673
708
|
var DAG_TRACKS = ["backend", "ui", "all"];
|
|
674
709
|
var NODE_ID_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
710
|
+
var capabilityNamesSchema = z3.array(z3.string().min(1)).superRefine((names, ctx) => {
|
|
711
|
+
const seen = /* @__PURE__ */ new Set();
|
|
712
|
+
names.forEach((name, index) => {
|
|
713
|
+
if (seen.has(name)) {
|
|
714
|
+
ctx.addIssue({ code: "custom", path: [index], message: `\u540D\u79F0\u91CD\u590D\uFF1A${name}` });
|
|
715
|
+
}
|
|
716
|
+
seen.add(name);
|
|
717
|
+
});
|
|
718
|
+
});
|
|
719
|
+
var SourcePresetSchema = z3.object({
|
|
720
|
+
code: z3.string().min(1),
|
|
721
|
+
version: z3.string(),
|
|
722
|
+
contentHash: z3.string(),
|
|
723
|
+
/** Agent 名称独立基线;不改变既有 contentHash 算法,存量缺键按空清单比较 */
|
|
724
|
+
agents: capabilityNamesSchema.optional()
|
|
725
|
+
});
|
|
675
726
|
var DagNodeSchema = z3.object({
|
|
676
727
|
id: z3.string().regex(NODE_ID_PATTERN, "node id \u53EA\u5141\u8BB8\u5B57\u6BCD/\u6570\u5B57/\u4E0B\u5212\u7EBF/\u8FDE\u5B57\u7B26\uFF08\u70B9\u8DEF\u5F84\u62FC\u63A5\u5B89\u5168\u524D\u63D0\uFF09"),
|
|
677
728
|
label: z3.string(),
|
|
678
729
|
phase: DagNodePhaseSchema,
|
|
679
730
|
track: DagNodeTrackSchema,
|
|
680
731
|
prompt: z3.string(),
|
|
681
|
-
skills:
|
|
732
|
+
skills: capabilityNamesSchema.default([]),
|
|
733
|
+
/** 存量模板允许缺键;新组装和任务实例化路径写入完整数组 */
|
|
734
|
+
agents: capabilityNamesSchema.optional(),
|
|
735
|
+
sourcePreset: SourcePresetSchema.optional()
|
|
682
736
|
});
|
|
683
737
|
var PausePointTypeSchema = z3.enum(["human_approval", "checkpoint"]);
|
|
684
738
|
var PAUSE_POINT_TYPES = ["human_approval", "checkpoint"];
|
|
@@ -701,6 +755,14 @@ var EdgePausePointSchema = EdgePausePointBaseSchema.superRefine((pp, ctx) => {
|
|
|
701
755
|
});
|
|
702
756
|
}
|
|
703
757
|
});
|
|
758
|
+
var TEMPLATE_CODE_REGEX = /^[a-z0-9]+(-[a-z0-9]+)+$/;
|
|
759
|
+
var TEMPLATE_CODE_MAX_LENGTH = 31;
|
|
760
|
+
var TemplateCodeFieldSchema = z3.string().max(TEMPLATE_CODE_MAX_LENGTH).regex(TEMPLATE_CODE_REGEX, "code \u5FC5\u987B\u4E3A kebab-case \u4E14\u81F3\u5C11\u542B\u4E00\u4E2A\u8FDE\u5B57\u7B26\u6BB5\uFF08\u5982 full-workflow\uFF09\uFF0C\u4E0D\u53EF\u4E0E\u6570\u636E\u5E93 id \u5F62\u6001\u6DF7\u6DC6");
|
|
761
|
+
var TemplateCandidateSchema = z3.object({
|
|
762
|
+
id: z3.string(),
|
|
763
|
+
code: z3.string(),
|
|
764
|
+
name: z3.string()
|
|
765
|
+
});
|
|
704
766
|
var DagEdgeSchema = z3.object({
|
|
705
767
|
from: z3.string(),
|
|
706
768
|
to: z3.string(),
|
|
@@ -714,6 +776,12 @@ var DagTemplateSchema = z3.object({
|
|
|
714
776
|
/** 归属项目 id(N012:实体层去哨兵 default,由路由层解析写入——校验项目存在且 active 后落库) */
|
|
715
777
|
projectId: z3.string(),
|
|
716
778
|
description: z3.string(),
|
|
779
|
+
/**
|
|
780
|
+
* T202609020002 业务 code(可选——存量迁移前的模板无此字段,由启动迁移补齐)。
|
|
781
|
+
* 项目内唯一({projectId, code} 复合唯一索引,迁移后建)、创建后不可变;
|
|
782
|
+
* name 仍承担展示职责(可改、可中文)——职责分离,改名不影响 code 引用。
|
|
783
|
+
*/
|
|
784
|
+
code: TemplateCodeFieldSchema.optional(),
|
|
717
785
|
nodes: z3.array(DagNodeSchema),
|
|
718
786
|
edges: z3.array(DagEdgeSchema),
|
|
719
787
|
/** UI 布局坐标(nodeId → {x,y})。UI 关注点,advance 引擎零感知;旧模板无此字段 → 前端 dagre 兜底布局(N008 D2) */
|
|
@@ -742,10 +810,228 @@ var DagTemplateUpdateSchema = DagTemplateCreateSchema.extend({
|
|
|
742
810
|
}).partial();
|
|
743
811
|
var DagTemplateCopySchema = z3.object({
|
|
744
812
|
newName: z3.string().min(1),
|
|
745
|
-
targetProjectId: z3.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex")
|
|
813
|
+
targetProjectId: z3.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex"),
|
|
814
|
+
newCode: TemplateCodeFieldSchema.optional()
|
|
746
815
|
});
|
|
816
|
+
function findDuplicateNodeIds(nodes) {
|
|
817
|
+
const seen = /* @__PURE__ */ new Set();
|
|
818
|
+
const duplicated = /* @__PURE__ */ new Set();
|
|
819
|
+
for (const node of nodes) {
|
|
820
|
+
if (seen.has(node.id)) duplicated.add(node.id);
|
|
821
|
+
seen.add(node.id);
|
|
822
|
+
}
|
|
823
|
+
return [...duplicated];
|
|
824
|
+
}
|
|
825
|
+
function assertUniqueNodeIds(nodes) {
|
|
826
|
+
const duplicated = findDuplicateNodeIds(nodes);
|
|
827
|
+
if (duplicated.length > 0) {
|
|
828
|
+
throw new ValidationError(
|
|
829
|
+
`\u6A21\u677F\u8282\u70B9 id \u91CD\u590D\uFF1A${duplicated.join(", ")}`,
|
|
830
|
+
duplicated.map((id) => ({ path: ["nodes"], message: `\u8282\u70B9 id "${id}" \u5728\u6A21\u677F\u5185\u91CD\u590D` })),
|
|
831
|
+
{
|
|
832
|
+
bizCode: "TEMPLATE_NODE_ID_DUPLICATE",
|
|
833
|
+
message: `${BIZ_CODE_MESSAGES.TEMPLATE_NODE_ID_DUPLICATE}\uFF1A${duplicated.join(", ")}`
|
|
834
|
+
}
|
|
835
|
+
);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// src/store/repos/dag-template.repo.ts
|
|
840
|
+
function normalizeNameToTemplateCode(name) {
|
|
841
|
+
const folded = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
842
|
+
return folded.slice(0, TEMPLATE_CODE_MAX_LENGTH);
|
|
843
|
+
}
|
|
844
|
+
function isLegalTemplateCode(code) {
|
|
845
|
+
return code.length <= TEMPLATE_CODE_MAX_LENGTH && TEMPLATE_CODE_REGEX.test(code);
|
|
846
|
+
}
|
|
847
|
+
function buildTemplateCode(seedName, takenCodes) {
|
|
848
|
+
const taken = new Set(takenCodes);
|
|
849
|
+
const base = normalizeNameToTemplateCode(seedName);
|
|
850
|
+
let primary;
|
|
851
|
+
if (base === "") {
|
|
852
|
+
primary = "tpl-1";
|
|
853
|
+
} else if (isLegalTemplateCode(base)) {
|
|
854
|
+
primary = base;
|
|
855
|
+
} else {
|
|
856
|
+
primary = `tpl-${base}`.slice(0, TEMPLATE_CODE_MAX_LENGTH);
|
|
857
|
+
}
|
|
858
|
+
let candidate = primary;
|
|
859
|
+
let n = 1;
|
|
860
|
+
while (taken.has(candidate) || !isLegalTemplateCode(candidate)) {
|
|
861
|
+
n += 1;
|
|
862
|
+
const suffix = `-${n}`;
|
|
863
|
+
candidate = `${primary.slice(0, TEMPLATE_CODE_MAX_LENGTH - suffix.length)}${suffix}`;
|
|
864
|
+
}
|
|
865
|
+
return candidate;
|
|
866
|
+
}
|
|
867
|
+
function levenshtein(a, b) {
|
|
868
|
+
const m = a.length;
|
|
869
|
+
const n = b.length;
|
|
870
|
+
if (m === 0) return n;
|
|
871
|
+
if (n === 0) return m;
|
|
872
|
+
let prev = Array.from({ length: n + 1 }, (_, j) => j);
|
|
873
|
+
for (let i = 1; i <= m; i++) {
|
|
874
|
+
const curr = [i];
|
|
875
|
+
for (let j = 1; j <= n; j++) {
|
|
876
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
877
|
+
curr[j] = Math.min(prev[j - 1] + cost, curr[j - 1] + 1, prev[j] + 1);
|
|
878
|
+
}
|
|
879
|
+
prev = curr;
|
|
880
|
+
}
|
|
881
|
+
return prev[n];
|
|
882
|
+
}
|
|
883
|
+
function scoreTemplateCandidate(ref, tpl) {
|
|
884
|
+
const refLower = ref.toLowerCase();
|
|
885
|
+
if (tpl.name === ref) return 100;
|
|
886
|
+
const code = tpl.code?.toLowerCase();
|
|
887
|
+
if (code === refLower) return 100;
|
|
888
|
+
if (code !== void 0) {
|
|
889
|
+
if (code.startsWith(refLower) || refLower.startsWith(code)) return 80;
|
|
890
|
+
if (levenshtein(code, refLower) <= 2) return 60;
|
|
891
|
+
}
|
|
892
|
+
const nameLower = tpl.name.toLowerCase();
|
|
893
|
+
if (nameLower.startsWith(refLower) || refLower.startsWith(nameLower)) return 70;
|
|
894
|
+
if (nameLower.includes(refLower) || refLower.includes(nameLower)) return 65;
|
|
895
|
+
if (tpl.id !== void 0 && ref.length === 24 && levenshtein(tpl.id, ref) <= 2) return 50;
|
|
896
|
+
if (levenshtein(nameLower, refLower) <= 2) return 40;
|
|
897
|
+
return null;
|
|
898
|
+
}
|
|
899
|
+
var OBJECT_ID_HEX_RE = /^[a-f0-9]{24}$/;
|
|
900
|
+
function dupKeyViolation(e) {
|
|
901
|
+
if (typeof e !== "object" || e === null) return null;
|
|
902
|
+
if (!("code" in e && e.code === 11e3)) return null;
|
|
903
|
+
const kv = e.keyValue;
|
|
904
|
+
return typeof kv === "object" && kv !== null ? kv : null;
|
|
905
|
+
}
|
|
906
|
+
function createDagTemplateRepo(db) {
|
|
907
|
+
const base = createRepo(db.collection("dag_templates"));
|
|
908
|
+
const listProjectCodes = async (projectId) => {
|
|
909
|
+
const docs = await base._collection.find({ projectId }, { projection: { code: 1 } }).toArray();
|
|
910
|
+
return docs.map((d) => typeof d.code === "string" ? d.code : "").filter((c) => c !== "");
|
|
911
|
+
};
|
|
912
|
+
const getByCode = async (projectId, code) => {
|
|
913
|
+
const doc = await base._collection.findOne({ projectId, code });
|
|
914
|
+
return doc ? toEntity(doc) : null;
|
|
915
|
+
};
|
|
916
|
+
return {
|
|
917
|
+
...base,
|
|
918
|
+
async createDagTemplate(data) {
|
|
919
|
+
const payload = { ...data };
|
|
920
|
+
if (payload.code === void 0) {
|
|
921
|
+
payload.code = buildTemplateCode(data.name, await listProjectCodes(data.projectId));
|
|
922
|
+
}
|
|
923
|
+
try {
|
|
924
|
+
return await base.create(payload);
|
|
925
|
+
} catch (e) {
|
|
926
|
+
const kv = dupKeyViolation(e);
|
|
927
|
+
if (kv !== null && "code" in kv) {
|
|
928
|
+
throw new ConflictError("dag-template", `code conflict: ${String(kv.code)}`, {
|
|
929
|
+
bizCode: "TEMPLATE_CODE_CONFLICT",
|
|
930
|
+
message: BIZ_CODE_MESSAGES.TEMPLATE_CODE_CONFLICT
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
if (kv !== null && "name" in kv) {
|
|
934
|
+
throw new ConflictError("dag-template", `name conflict: ${String(kv.name)}`, {
|
|
935
|
+
bizCode: "TEMPLATE_NAME_CONFLICT",
|
|
936
|
+
message: BIZ_CODE_MESSAGES.TEMPLATE_NAME_CONFLICT
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
throw e;
|
|
940
|
+
}
|
|
941
|
+
},
|
|
942
|
+
async listDagTemplates(filter) {
|
|
943
|
+
const query = {};
|
|
944
|
+
if (filter?.projectId) query.projectId = filter.projectId;
|
|
945
|
+
if (filter?.name) query.name = filter.name;
|
|
946
|
+
if (filter?.code) query.code = filter.code;
|
|
947
|
+
if (!filter?.includeDisabled) Object.assign(query, enabledNeFalse());
|
|
948
|
+
return base.list(query);
|
|
949
|
+
},
|
|
950
|
+
async copyDagTemplate(sourceId, targetProjectId, newName, newCode) {
|
|
951
|
+
const source = await base.getById(sourceId);
|
|
952
|
+
if (!source) {
|
|
953
|
+
throw new ConflictError("dagTemplate", `copy source not found: ${sourceId}`);
|
|
954
|
+
}
|
|
955
|
+
const dup = await base._collection.findOne(
|
|
956
|
+
{ projectId: targetProjectId, name: newName },
|
|
957
|
+
{ projection: { _id: 1 } }
|
|
958
|
+
);
|
|
959
|
+
if (dup) {
|
|
960
|
+
throw new ConflictError("dagTemplate", `name conflict in target project: ${newName}`, {
|
|
961
|
+
bizCode: "TEMPLATE_NAME_CONFLICT",
|
|
962
|
+
message: BIZ_CODE_MESSAGES.TEMPLATE_NAME_CONFLICT
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
if (newCode !== void 0) {
|
|
966
|
+
const dupCode = await base._collection.findOne(
|
|
967
|
+
{ projectId: targetProjectId, code: newCode },
|
|
968
|
+
{ projection: { _id: 1, name: 1 } }
|
|
969
|
+
);
|
|
970
|
+
if (dupCode) {
|
|
971
|
+
throw new ConflictError("dagTemplate", `code conflict in target project: ${newCode}`, {
|
|
972
|
+
bizCode: "TEMPLATE_CODE_CONFLICT",
|
|
973
|
+
message: `${BIZ_CODE_MESSAGES.TEMPLATE_CODE_CONFLICT}\uFF08\u51B2\u7A81\u6A21\u677F\uFF1A${String(dupCode.name)}\uFF09`
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
const code = newCode ?? buildTemplateCode(newName, await listProjectCodes(targetProjectId));
|
|
978
|
+
const { id: _sourceId, createdAt: _ca, updatedAt: _ua, enabled: _sourceEnabled, code: _sourceCode, ...rest } = source;
|
|
979
|
+
const copy = {
|
|
980
|
+
...rest,
|
|
981
|
+
name: newName,
|
|
982
|
+
code,
|
|
983
|
+
projectId: targetProjectId,
|
|
984
|
+
isDefault: false,
|
|
985
|
+
version: "1.0.0"
|
|
986
|
+
};
|
|
987
|
+
try {
|
|
988
|
+
return await base.create(copy);
|
|
989
|
+
} catch (e) {
|
|
990
|
+
const kv = dupKeyViolation(e);
|
|
991
|
+
if (kv !== null && "code" in kv) {
|
|
992
|
+
throw new ConflictError("dag-template", `code conflict: ${String(kv.code)}`, {
|
|
993
|
+
bizCode: "TEMPLATE_CODE_CONFLICT",
|
|
994
|
+
message: BIZ_CODE_MESSAGES.TEMPLATE_CODE_CONFLICT
|
|
995
|
+
});
|
|
996
|
+
}
|
|
997
|
+
if (kv !== null && "name" in kv) {
|
|
998
|
+
throw new ConflictError("dag-template", `name conflict: ${String(kv.name)}`, {
|
|
999
|
+
bizCode: "TEMPLATE_NAME_CONFLICT",
|
|
1000
|
+
message: BIZ_CODE_MESSAGES.TEMPLATE_NAME_CONFLICT
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
throw e;
|
|
1004
|
+
}
|
|
1005
|
+
},
|
|
1006
|
+
async getByCode(projectId, code) {
|
|
1007
|
+
return getByCode(projectId, code);
|
|
1008
|
+
},
|
|
1009
|
+
async resolveTemplateByRef(ref, projectCtx) {
|
|
1010
|
+
if (OBJECT_ID_HEX_RE.test(ref)) {
|
|
1011
|
+
const byId = await base.getById(ref);
|
|
1012
|
+
if (byId) return byId;
|
|
1013
|
+
}
|
|
1014
|
+
if (projectCtx !== void 0 && !OBJECT_ID_HEX_RE.test(ref)) {
|
|
1015
|
+
return getByCode(projectCtx, ref);
|
|
1016
|
+
}
|
|
1017
|
+
return null;
|
|
1018
|
+
},
|
|
1019
|
+
async findSimilarTemplates(projectId, ref, limit = 5) {
|
|
1020
|
+
const docs = await base._collection.find({ projectId }, { projection: { _id: 1, code: 1, name: 1 } }).toArray();
|
|
1021
|
+
const scored = docs.map((d) => {
|
|
1022
|
+
const id = d._id.toString();
|
|
1023
|
+
const code = typeof d.code === "string" ? d.code : "";
|
|
1024
|
+
const name = typeof d.name === "string" ? d.name : "";
|
|
1025
|
+
const score = scoreTemplateCandidate(ref, { id, ...code !== "" ? { code } : {}, name });
|
|
1026
|
+
return score === null ? null : { score, candidate: { id, code, name } };
|
|
1027
|
+
}).filter((x) => x !== null).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
1028
|
+
return scored.map((x) => x.candidate);
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
747
1032
|
|
|
748
1033
|
// src/schemas/task.ts
|
|
1034
|
+
import { z as z4 } from "zod";
|
|
749
1035
|
var TASK_TYPES = ["feature", "bugfix", "ui-tweak", "research"];
|
|
750
1036
|
var ENTRY_ID_REGEX = /^[a-z0-9]{6}$/;
|
|
751
1037
|
var CheckItemSchema = z4.object({
|
|
@@ -803,18 +1089,20 @@ var NodeStateSchema = z4.object({
|
|
|
803
1089
|
enteredAt: z4.date().nullable().default(null),
|
|
804
1090
|
completedAt: z4.date().nullable().default(null)
|
|
805
1091
|
});
|
|
1092
|
+
var DagInstanceNodeSchema = z4.object({
|
|
1093
|
+
id: z4.string(),
|
|
1094
|
+
label: z4.string(),
|
|
1095
|
+
// N016 F9:枚举放宽(注册表权威)
|
|
1096
|
+
phase: z4.string(),
|
|
1097
|
+
track: z4.string(),
|
|
1098
|
+
prompt: z4.string(),
|
|
1099
|
+
skills: z4.array(z4.string()).default([]),
|
|
1100
|
+
agents: z4.array(z4.string()).default([])
|
|
1101
|
+
});
|
|
806
1102
|
var DagInstanceSchema = z4.object({
|
|
807
1103
|
templateId: z4.string(),
|
|
808
1104
|
templateVersion: z4.string(),
|
|
809
|
-
nodes: z4.array(
|
|
810
|
-
id: z4.string(),
|
|
811
|
-
label: z4.string(),
|
|
812
|
-
// N016 F9:枚举放宽(注册表权威)
|
|
813
|
-
phase: z4.string(),
|
|
814
|
-
track: z4.string(),
|
|
815
|
-
prompt: z4.string(),
|
|
816
|
-
skills: z4.array(z4.string()).default([])
|
|
817
|
-
})),
|
|
1105
|
+
nodes: z4.array(DagInstanceNodeSchema),
|
|
818
1106
|
// N017 D1:暂停点内联到边(human_approval=审批门 / checkpoint=检查点),顶层数组删除
|
|
819
1107
|
edges: z4.array(z4.object({
|
|
820
1108
|
from: z4.string(),
|
|
@@ -875,6 +1163,12 @@ var TaskSchema = z4.object({
|
|
|
875
1163
|
var TaskCreateInputSchema = z4.object({
|
|
876
1164
|
title: z4.string(),
|
|
877
1165
|
track: z4.string(),
|
|
1166
|
+
/**
|
|
1167
|
+
* T202609020002:模板引用支持 code|id 双形态——24-hex 直查数据库 id 命中即用
|
|
1168
|
+
* (hex 未命中不转 code 检索:code 必含连字符,与 hex 形态天然互斥);非 hex 在
|
|
1169
|
+
* 项目上下文内按 code 检索(项目域经鉴权过滤:project Token 强制本项目,
|
|
1170
|
+
* open/admin 显式 projectId 优先)。任务实体落库的 dagTemplateId 恒为解析后的数据库 id。
|
|
1171
|
+
*/
|
|
878
1172
|
dagTemplateId: z4.string(),
|
|
879
1173
|
/**
|
|
880
1174
|
* T202608240003 创建时剪枝:显式剔除的节点 id(在轨道自动剔除之外)。
|
|
@@ -1236,6 +1530,7 @@ function toNodeInfo(node, task) {
|
|
|
1236
1530
|
track: node.track,
|
|
1237
1531
|
prompt: renderPrompt(node.prompt, task),
|
|
1238
1532
|
skills: node.skills,
|
|
1533
|
+
agents: node.agents ?? [],
|
|
1239
1534
|
upcomingPause: nextEdge?.pausePoint?.description ?? null
|
|
1240
1535
|
};
|
|
1241
1536
|
}
|
|
@@ -1619,7 +1914,8 @@ function deepCopyNode(node) {
|
|
|
1619
1914
|
phase: node.phase,
|
|
1620
1915
|
track: node.track,
|
|
1621
1916
|
prompt: node.prompt,
|
|
1622
|
-
skills: [...node.skills]
|
|
1917
|
+
skills: [...node.skills],
|
|
1918
|
+
agents: [...node.agents ?? []]
|
|
1623
1919
|
};
|
|
1624
1920
|
}
|
|
1625
1921
|
function deepCopyEdge(edge) {
|
|
@@ -1746,17 +2042,32 @@ async function assertModelAliasExists(repo, code) {
|
|
|
1746
2042
|
|
|
1747
2043
|
// src/store/repos/enum-registry.repo.ts
|
|
1748
2044
|
var REFERENCE_CHECKS = {
|
|
1749
|
-
dag_phase:
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
2045
|
+
dag_phase: [
|
|
2046
|
+
{ collection: "dag_templates", query: (v) => ({ "nodes.phase": v }) },
|
|
2047
|
+
{ collection: "node_presets", query: (v) => ({ phase: v }) }
|
|
2048
|
+
],
|
|
2049
|
+
dag_track: [
|
|
2050
|
+
{ collection: "dag_templates", query: (v) => ({ "nodes.track": v }) },
|
|
2051
|
+
{ collection: "node_presets", query: (v) => ({ track: v }) }
|
|
2052
|
+
],
|
|
2053
|
+
pause_type: [{ collection: "dag_templates", query: (v) => ({ "edges.pausePoint.type": v }) }],
|
|
2054
|
+
task_status: [{ collection: "tasks", query: (v) => ({ status: v }) }],
|
|
2055
|
+
node_status: [{ collection: "tasks", query: (v) => ({ "dagInstance.nodeStates.status": v }) }],
|
|
2056
|
+
skill_category: [{ collection: "skills", query: (v) => ({ category: v }) }],
|
|
1755
2057
|
scope: void 0,
|
|
1756
2058
|
// T202608260002:agent 功能类型被 agents.function 引用(reviewer/executor 为 builtin 禁删;
|
|
1757
2059
|
// 检测映射为未来非 builtin 扩值留防线)
|
|
1758
|
-
agent_function: { collection: "agents", query: (v) => ({ "function": v }) }
|
|
2060
|
+
agent_function: [{ collection: "agents", query: (v) => ({ "function": v }) }]
|
|
1759
2061
|
};
|
|
2062
|
+
async function countReferences(db, category, value) {
|
|
2063
|
+
const referrers = REFERENCE_CHECKS[category];
|
|
2064
|
+
if (!referrers) return 0;
|
|
2065
|
+
let total = 0;
|
|
2066
|
+
for (const referrer of referrers) {
|
|
2067
|
+
total += await db.collection(referrer.collection).countDocuments(referrer.query(value));
|
|
2068
|
+
}
|
|
2069
|
+
return total;
|
|
2070
|
+
}
|
|
1760
2071
|
function createEnumRegistryRepo(db) {
|
|
1761
2072
|
const base = createRepo(db.collection("enum_registry"), { nameField: "category" });
|
|
1762
2073
|
return {
|
|
@@ -1805,15 +2116,12 @@ function createEnumRegistryRepo(db) {
|
|
|
1805
2116
|
});
|
|
1806
2117
|
}
|
|
1807
2118
|
if (prev?.active === true && incoming.active === false) {
|
|
1808
|
-
const
|
|
1809
|
-
if (
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
message: `${BIZ_CODE_MESSAGES.ENUM_ENTRY_IN_USE}\uFF08${count} \u4E2A\u8D44\u6E90\u5F15\u7528\uFF09`
|
|
1815
|
-
});
|
|
1816
|
-
}
|
|
2119
|
+
const count = await countReferences(db, category, incoming.value);
|
|
2120
|
+
if (count > 0) {
|
|
2121
|
+
throw new ConflictError("enum-registry", `${category}/${incoming.value}`, {
|
|
2122
|
+
bizCode: "ENUM_ENTRY_IN_USE",
|
|
2123
|
+
message: `${BIZ_CODE_MESSAGES.ENUM_ENTRY_IN_USE}\uFF08${count} \u4E2A\u8D44\u6E90\u5F15\u7528\uFF09`
|
|
2124
|
+
});
|
|
1817
2125
|
}
|
|
1818
2126
|
}
|
|
1819
2127
|
}
|
|
@@ -1840,15 +2148,12 @@ function createEnumRegistryRepo(db) {
|
|
|
1840
2148
|
message: BIZ_CODE_MESSAGES.ENUM_ENTRY_BUILTIN
|
|
1841
2149
|
});
|
|
1842
2150
|
}
|
|
1843
|
-
const
|
|
1844
|
-
if (
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
message: `${BIZ_CODE_MESSAGES.ENUM_ENTRY_IN_USE}\uFF08${count} \u4E2A\u8D44\u6E90\u5F15\u7528\uFF09`
|
|
1850
|
-
});
|
|
1851
|
-
}
|
|
2151
|
+
const count = await countReferences(db, category, value);
|
|
2152
|
+
if (count > 0) {
|
|
2153
|
+
throw new ConflictError("enum-registry", `${category}/${value}`, {
|
|
2154
|
+
bizCode: "ENUM_ENTRY_IN_USE",
|
|
2155
|
+
message: `${BIZ_CODE_MESSAGES.ENUM_ENTRY_IN_USE}\uFF08${count} \u4E2A\u8D44\u6E90\u5F15\u7528\uFF09`
|
|
2156
|
+
});
|
|
1852
2157
|
}
|
|
1853
2158
|
const remaining = existing.entries.filter((e) => e.value !== value);
|
|
1854
2159
|
return await base.update(existing.id, { entries: remaining }) !== null;
|
|
@@ -1856,22 +2161,316 @@ function createEnumRegistryRepo(db) {
|
|
|
1856
2161
|
};
|
|
1857
2162
|
}
|
|
1858
2163
|
|
|
2164
|
+
// src/store/repos/node-preset.repo.ts
|
|
2165
|
+
function createNodePresetRepo(db) {
|
|
2166
|
+
const base = createRepo(db.collection("node_presets"), { nameField: "code" });
|
|
2167
|
+
const assertCodeAvailable = async (code) => {
|
|
2168
|
+
const clash = await base._collection.findOne({ code }, { projection: { _id: 1 } });
|
|
2169
|
+
if (clash) {
|
|
2170
|
+
throw new ConflictError("node-preset", code, {
|
|
2171
|
+
bizCode: "NODE_PRESET_CODE_EXISTS",
|
|
2172
|
+
message: BIZ_CODE_MESSAGES.NODE_PRESET_CODE_EXISTS
|
|
2173
|
+
});
|
|
2174
|
+
}
|
|
2175
|
+
};
|
|
2176
|
+
return {
|
|
2177
|
+
...base,
|
|
2178
|
+
async createNodePreset(data) {
|
|
2179
|
+
await assertCodeAvailable(data.code);
|
|
2180
|
+
return base.create(data);
|
|
2181
|
+
},
|
|
2182
|
+
async getByCode(code, opts) {
|
|
2183
|
+
const doc = await base._collection.findOne({
|
|
2184
|
+
code,
|
|
2185
|
+
...opts?.includeDisabled ? {} : enabledNeFalse()
|
|
2186
|
+
});
|
|
2187
|
+
return doc ? toEntity(doc) : null;
|
|
2188
|
+
},
|
|
2189
|
+
async updateByCode(code, patch) {
|
|
2190
|
+
const existing = await this.getByCode(code, { includeDisabled: true });
|
|
2191
|
+
if (!existing || !existing.id) return null;
|
|
2192
|
+
return base.update(existing.id, patch);
|
|
2193
|
+
},
|
|
2194
|
+
async deleteByCode(code) {
|
|
2195
|
+
const existing = await this.getByCode(code, { includeDisabled: true });
|
|
2196
|
+
if (!existing || !existing.id) return false;
|
|
2197
|
+
return base.delete(existing.id);
|
|
2198
|
+
},
|
|
2199
|
+
async listNodePresets(filter) {
|
|
2200
|
+
const query = {
|
|
2201
|
+
...filter?.includeDisabled ? {} : enabledNeFalse(),
|
|
2202
|
+
...filter?.scope ? filter.scope === "project" ? { scope: "project", ...filter.projectId ? { projectId: filter.projectId } : {} } : { scope: "global" } : filter?.projectId ? { $or: [{ scope: "global" }, { scope: "project", projectId: filter.projectId }] } : {},
|
|
2203
|
+
...filter?.q ? { $or: [
|
|
2204
|
+
{ code: { $regex: escapeRegExpLiteral(filter.q), $options: "i" } },
|
|
2205
|
+
{ label: { $regex: escapeRegExpLiteral(filter.q), $options: "i" } }
|
|
2206
|
+
] } : {}
|
|
2207
|
+
};
|
|
2208
|
+
return base.list(query);
|
|
2209
|
+
},
|
|
2210
|
+
async listByCodes(codes) {
|
|
2211
|
+
if (codes.length === 0) return /* @__PURE__ */ new Map();
|
|
2212
|
+
const presets = await base.list({ code: { $in: codes } });
|
|
2213
|
+
return new Map(presets.map((p) => [p.code, p]));
|
|
2214
|
+
},
|
|
2215
|
+
async isCodeOccupied(code) {
|
|
2216
|
+
return await base._collection.countDocuments({ code }, { limit: 1 }) > 0;
|
|
2217
|
+
},
|
|
2218
|
+
async copyNodePreset(sourceCode, target, input) {
|
|
2219
|
+
const source = await this.getByCode(sourceCode, { includeDisabled: true });
|
|
2220
|
+
if (!source) {
|
|
2221
|
+
throw new ConflictError("node-preset", `copy source not found: ${sourceCode}`);
|
|
2222
|
+
}
|
|
2223
|
+
await assertCodeAvailable(input.newCode);
|
|
2224
|
+
const { id: _sourceId, createdAt: _ca, updatedAt: _ua, enabled: _sourceEnabled, source: _sourceOrigin, projectId: _sourceProjectId, ...rest } = source;
|
|
2225
|
+
const copy = {
|
|
2226
|
+
...JSON.parse(JSON.stringify(rest)),
|
|
2227
|
+
agents: [...source.agents ?? []],
|
|
2228
|
+
code: input.newCode,
|
|
2229
|
+
scope: target.scope,
|
|
2230
|
+
version: "1.0.0",
|
|
2231
|
+
...target.scope === "project" && target.projectId ? { projectId: target.projectId } : {}
|
|
2232
|
+
};
|
|
2233
|
+
return base.create(copy);
|
|
2234
|
+
}
|
|
2235
|
+
};
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
// src/store/repos/node-library.repo.ts
|
|
2239
|
+
function createNodeLibraryRepo(db) {
|
|
2240
|
+
const base = createRepo(db.collection("node_libraries"), { nameField: "name" });
|
|
2241
|
+
return {
|
|
2242
|
+
...base,
|
|
2243
|
+
async upsertNodeLibrary(data) {
|
|
2244
|
+
const now = /* @__PURE__ */ new Date();
|
|
2245
|
+
const existing = await base.getByName(data.name);
|
|
2246
|
+
if (existing && existing.id) {
|
|
2247
|
+
const updated = await base.update(existing.id, {
|
|
2248
|
+
version: data.version,
|
|
2249
|
+
scope: data.scope,
|
|
2250
|
+
...data.scope === "project" && data.projectId ? { projectId: data.projectId } : {},
|
|
2251
|
+
nodes: data.nodes,
|
|
2252
|
+
compositions: data.compositions,
|
|
2253
|
+
installedAt: now
|
|
2254
|
+
});
|
|
2255
|
+
if (!updated) throw new Error(`node_library upsert \u672A\u547D\u4E2D\u5DF2\u5B9A\u4F4D\u767B\u8BB0\uFF1A${data.name}\uFF08\u542F\u52A8\u671F\u4E0D\u5E94\u53D1\u751F\uFF09`);
|
|
2256
|
+
return updated;
|
|
2257
|
+
}
|
|
2258
|
+
const created = await base.create({
|
|
2259
|
+
name: data.name,
|
|
2260
|
+
version: data.version,
|
|
2261
|
+
scope: data.scope,
|
|
2262
|
+
...data.scope === "project" && data.projectId ? { projectId: data.projectId } : {},
|
|
2263
|
+
nodes: data.nodes,
|
|
2264
|
+
compositions: data.compositions,
|
|
2265
|
+
installedAt: now
|
|
2266
|
+
});
|
|
2267
|
+
return created;
|
|
2268
|
+
},
|
|
2269
|
+
async listNodeLibraries(filter) {
|
|
2270
|
+
const query = filter?.scope ? filter.scope === "project" ? { scope: "project", ...filter.projectId ? { projectId: filter.projectId } : {} } : { scope: "global" } : filter?.projectId ? { $or: [{ scope: "global" }, { scope: "project", projectId: filter.projectId }] } : {};
|
|
2271
|
+
const docs = await base._collection.find(query).toArray();
|
|
2272
|
+
return docs.map((doc) => toEntity(doc));
|
|
2273
|
+
}
|
|
2274
|
+
};
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
// src/store/repos/auth.repo.ts
|
|
2278
|
+
import { ObjectId as ObjectId3 } from "mongodb";
|
|
2279
|
+
import { z as z5 } from "zod";
|
|
2280
|
+
var AUTH_COLLECTIONS = {
|
|
2281
|
+
accounts: "auth_accounts",
|
|
2282
|
+
tokens: "auth_tokens",
|
|
2283
|
+
sessions: "auth_sessions"
|
|
2284
|
+
};
|
|
2285
|
+
var SINGLETON_ACCOUNT_ID = new ObjectId3("000000000000000000000001");
|
|
2286
|
+
function createAuthAccountRepo(db) {
|
|
2287
|
+
const base = createRepo(db.collection(AUTH_COLLECTIONS.accounts));
|
|
2288
|
+
return {
|
|
2289
|
+
...base,
|
|
2290
|
+
async getAccount() {
|
|
2291
|
+
const doc = await base._collection.findOne({});
|
|
2292
|
+
return doc ? toEntity(doc) : null;
|
|
2293
|
+
},
|
|
2294
|
+
/** 创建唯一管理员账号:已有账号 → 409(并发初始化仅首个成功,PRD §3.2)。
|
|
2295
|
+
* 单例机制 = 固定 _id 插入(E11000 兜底任意并发形态——异名并发同样仅首个成功) */
|
|
2296
|
+
async createAccount(username, passwordHash) {
|
|
2297
|
+
const existing = await base._collection.findOne({}, { projection: { _id: 1 } });
|
|
2298
|
+
if (existing) {
|
|
2299
|
+
throw new ConflictError("auth-account", "singleton", { message: "admin account already exists" });
|
|
2300
|
+
}
|
|
2301
|
+
const now = /* @__PURE__ */ new Date();
|
|
2302
|
+
const result = await base._collection.insertOne({
|
|
2303
|
+
_id: SINGLETON_ACCOUNT_ID,
|
|
2304
|
+
username,
|
|
2305
|
+
passwordHash,
|
|
2306
|
+
createdAt: now,
|
|
2307
|
+
updatedAt: now
|
|
2308
|
+
});
|
|
2309
|
+
return { username, passwordHash, createdAt: now, updatedAt: now, id: result.insertedId.toString() };
|
|
2310
|
+
},
|
|
2311
|
+
async updatePassword(accountId, newPasswordHash) {
|
|
2312
|
+
return base.update(accountId, { passwordHash: newPasswordHash });
|
|
2313
|
+
}
|
|
2314
|
+
};
|
|
2315
|
+
}
|
|
2316
|
+
function createAuthTokenRepo(db) {
|
|
2317
|
+
const base = createRepo(db.collection(AUTH_COLLECTIONS.tokens));
|
|
2318
|
+
return {
|
|
2319
|
+
...base,
|
|
2320
|
+
/** sha256(token 全串含前缀) 等值查询(唯一索引命中) */
|
|
2321
|
+
async findByHash(tokenHash) {
|
|
2322
|
+
const doc = await base._collection.findOne({ tokenHash });
|
|
2323
|
+
return doc ? toEntity(doc) : null;
|
|
2324
|
+
},
|
|
2325
|
+
async getTokenStatus(type, projectId) {
|
|
2326
|
+
const query = { type };
|
|
2327
|
+
if (type === "project") query.projectId = projectId;
|
|
2328
|
+
const doc = await base._collection.findOne(query, { projection: { createdAt: 1 } });
|
|
2329
|
+
return doc ? { hasToken: true, createdAt: doc.createdAt ?? null } : { hasToken: false, createdAt: null };
|
|
2330
|
+
},
|
|
2331
|
+
/** 重置语义:旧 Token 删除 + 新 Token 落库(重置后旧值立即失效,PRD §3.4)。
|
|
2332
|
+
* 事务包删+插(rs0 支持——失败窗口不留零 Token 状态,失败不变性);admin 单例 /
|
|
2333
|
+
* project 每项目一枚由 partial unique 索引兜底并发交错 */
|
|
2334
|
+
async upsertToken(type, tokenHash, projectId) {
|
|
2335
|
+
const query = { type };
|
|
2336
|
+
if (type === "project") {
|
|
2337
|
+
if (!projectId) throw new ConflictError("auth-token", "missing-projectId", { message: "project token requires projectId" });
|
|
2338
|
+
query.projectId = projectId;
|
|
2339
|
+
}
|
|
2340
|
+
const db2 = base._collection.db;
|
|
2341
|
+
const session = db2.client.startSession();
|
|
2342
|
+
try {
|
|
2343
|
+
const holder = { token: null };
|
|
2344
|
+
await session.withTransaction(async () => {
|
|
2345
|
+
await base._collection.deleteMany(query, { session });
|
|
2346
|
+
const now = /* @__PURE__ */ new Date();
|
|
2347
|
+
const doc = { tokenHash, type, createdAt: now, updatedAt: now };
|
|
2348
|
+
if (type === "project" && projectId !== void 0) doc.projectId = projectId;
|
|
2349
|
+
const result = await base._collection.insertOne(doc, { session });
|
|
2350
|
+
holder.token = { ...doc, id: result.insertedId.toString() };
|
|
2351
|
+
});
|
|
2352
|
+
if (holder.token === null) {
|
|
2353
|
+
throw new ConflictError("auth-token", "transaction-empty", { message: "Token upsert \u672A\u4EA7\u751F\u7ED3\u679C" });
|
|
2354
|
+
}
|
|
2355
|
+
return holder.token;
|
|
2356
|
+
} finally {
|
|
2357
|
+
await session.endSession();
|
|
2358
|
+
}
|
|
2359
|
+
},
|
|
2360
|
+
async listProjectTokenStatuses(projectIds) {
|
|
2361
|
+
const docs = await base._collection.find({ type: "project", projectId: { $in: projectIds } }, { projection: { projectId: 1, createdAt: 1 } }).toArray();
|
|
2362
|
+
const map = /* @__PURE__ */ new Map();
|
|
2363
|
+
for (const id of projectIds) map.set(id, { hasToken: false, createdAt: null });
|
|
2364
|
+
for (const doc of docs) {
|
|
2365
|
+
if (typeof doc.projectId === "string") {
|
|
2366
|
+
map.set(doc.projectId, { hasToken: true, createdAt: doc.createdAt ?? null });
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
return map;
|
|
2370
|
+
}
|
|
2371
|
+
};
|
|
2372
|
+
}
|
|
2373
|
+
var AuthSessionSchema = z5.object({
|
|
2374
|
+
id: z5.string().optional(),
|
|
2375
|
+
sessionId: z5.string().min(1),
|
|
2376
|
+
/** 管理员账号 id(会话 = 管理员身份) */
|
|
2377
|
+
accountId: z5.string().min(1),
|
|
2378
|
+
expiresAt: z5.date(),
|
|
2379
|
+
createdAt: z5.date().optional(),
|
|
2380
|
+
updatedAt: z5.date().optional()
|
|
2381
|
+
});
|
|
2382
|
+
function createAuthSessionRepo(db) {
|
|
2383
|
+
const base = createRepo(db.collection(AUTH_COLLECTIONS.sessions));
|
|
2384
|
+
return {
|
|
2385
|
+
...base,
|
|
2386
|
+
async createSession(sessionId, accountId, expiresAt) {
|
|
2387
|
+
return base.create({ sessionId, accountId, expiresAt });
|
|
2388
|
+
},
|
|
2389
|
+
/** 有效期判定内联:过期即删返回 null(不等 TTL 清理) */
|
|
2390
|
+
async findValidSession(sessionId) {
|
|
2391
|
+
const doc = await base._collection.findOne({ sessionId });
|
|
2392
|
+
if (!doc) return null;
|
|
2393
|
+
if (!doc.expiresAt || doc.expiresAt.getTime() <= Date.now()) {
|
|
2394
|
+
await base.delete(doc._id.toString());
|
|
2395
|
+
return null;
|
|
2396
|
+
}
|
|
2397
|
+
return toEntity(doc);
|
|
2398
|
+
},
|
|
2399
|
+
async deleteBySessionId(sessionId) {
|
|
2400
|
+
const result = await base._collection.deleteOne({ sessionId });
|
|
2401
|
+
return result.deletedCount > 0;
|
|
2402
|
+
},
|
|
2403
|
+
/** 启动时清扫过期会话(幂等,配合 TTL 索引) */
|
|
2404
|
+
async deleteExpiredSessions() {
|
|
2405
|
+
const result = await base._collection.deleteMany({ expiresAt: { $lte: /* @__PURE__ */ new Date() } });
|
|
2406
|
+
return result.deletedCount;
|
|
2407
|
+
}
|
|
2408
|
+
};
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
// src/store/auth-crypto.ts
|
|
2412
|
+
import { createHash, randomBytes, scrypt as scryptCb, timingSafeEqual } from "crypto";
|
|
2413
|
+
import { promisify } from "util";
|
|
2414
|
+
var scrypt = promisify(scryptCb);
|
|
2415
|
+
var SCRYPT_N = 16384;
|
|
2416
|
+
var SCRYPT_R = 8;
|
|
2417
|
+
var SCRYPT_P = 1;
|
|
2418
|
+
var SCRYPT_KEYLEN = 64;
|
|
2419
|
+
var SALT_LEN = 16;
|
|
2420
|
+
async function hashPassword(password) {
|
|
2421
|
+
const salt = randomBytes(SALT_LEN);
|
|
2422
|
+
const derived = await scrypt(password, salt, SCRYPT_KEYLEN, { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P });
|
|
2423
|
+
return `scrypt$${SCRYPT_N}$${SCRYPT_R}$${SCRYPT_P}$${salt.toString("base64")}$${derived.toString("base64")}`;
|
|
2424
|
+
}
|
|
2425
|
+
async function verifyPassword(password, stored) {
|
|
2426
|
+
const parts = stored.split("$");
|
|
2427
|
+
if (parts.length !== 6 || parts[0] !== "scrypt") return false;
|
|
2428
|
+
const [, nStr, rStr, pStr, saltB64, hashB64] = parts;
|
|
2429
|
+
const N = Number(nStr);
|
|
2430
|
+
const r = Number(rStr);
|
|
2431
|
+
const p = Number(pStr);
|
|
2432
|
+
if (!Number.isInteger(N) || !Number.isInteger(r) || !Number.isInteger(p)) return false;
|
|
2433
|
+
try {
|
|
2434
|
+
const salt = Buffer.from(saltB64, "base64");
|
|
2435
|
+
const expected = Buffer.from(hashB64, "base64");
|
|
2436
|
+
const derived = await scrypt(password, salt, expected.length, { N, r, p });
|
|
2437
|
+
return derived.length === expected.length && timingSafeEqual(derived, expected);
|
|
2438
|
+
} catch {
|
|
2439
|
+
return false;
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
function generateToken(type) {
|
|
2443
|
+
const prefix = type === "admin" ? "admin-" : "project-";
|
|
2444
|
+
return `${prefix}${randomBytes(32).toString("base64url")}`;
|
|
2445
|
+
}
|
|
2446
|
+
function tokenTypeFromValue(token) {
|
|
2447
|
+
if (token.startsWith("admin-")) return "admin";
|
|
2448
|
+
if (token.startsWith("project-")) return "project";
|
|
2449
|
+
return null;
|
|
2450
|
+
}
|
|
2451
|
+
function hashToken(token) {
|
|
2452
|
+
return createHash("sha256").update(token).digest("hex");
|
|
2453
|
+
}
|
|
2454
|
+
function generateSessionId() {
|
|
2455
|
+
return randomBytes(32).toString("hex");
|
|
2456
|
+
}
|
|
2457
|
+
|
|
1859
2458
|
// src/schemas/agent.ts
|
|
1860
|
-
import { z as
|
|
2459
|
+
import { z as z7 } from "zod";
|
|
1861
2460
|
|
|
1862
2461
|
// src/schemas/model-alias.ts
|
|
1863
|
-
import { z as
|
|
2462
|
+
import { z as z6 } from "zod";
|
|
1864
2463
|
var SIMING_CODE_REGEX = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
1865
|
-
var ModelAliasShapeSchema =
|
|
1866
|
-
id:
|
|
2464
|
+
var ModelAliasShapeSchema = z6.object({
|
|
2465
|
+
id: z6.string().optional(),
|
|
1867
2466
|
/** siming code:创建后不可变(PUT 显式拒绝 MODEL_CODE_IMMUTABLE,非静默剥离) */
|
|
1868
|
-
code:
|
|
2467
|
+
code: z6.string().regex(SIMING_CODE_REGEX, "code \u5FC5\u987B\u4E3A kebab-case\uFF08\u5982 primary\u3001fast-model\uFF09"),
|
|
1869
2468
|
/** 展示名(如「主力模型」) */
|
|
1870
|
-
name:
|
|
2469
|
+
name: z6.string().min(1, "\u5C55\u793A\u540D\u4E0D\u80FD\u4E3A\u7A7A"),
|
|
1871
2470
|
/** 真实模型标识(如 zhipuai/glm-5.3;改此处 = 引用方全局替换生效) */
|
|
1872
|
-
realModel:
|
|
1873
|
-
createdAt:
|
|
1874
|
-
updatedAt:
|
|
2471
|
+
realModel: z6.string().min(1, "\u771F\u5B9E\u6A21\u578B\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A"),
|
|
2472
|
+
createdAt: z6.date().optional(),
|
|
2473
|
+
updatedAt: z6.date().optional()
|
|
1875
2474
|
});
|
|
1876
2475
|
var ModelAliasSchema = ModelAliasShapeSchema;
|
|
1877
2476
|
var ModelAliasCreateSchema = ModelAliasShapeSchema.omit({
|
|
@@ -1879,18 +2478,18 @@ var ModelAliasCreateSchema = ModelAliasShapeSchema.omit({
|
|
|
1879
2478
|
createdAt: true,
|
|
1880
2479
|
updatedAt: true
|
|
1881
2480
|
});
|
|
1882
|
-
var ModelAliasUpdateSchema =
|
|
1883
|
-
code:
|
|
1884
|
-
name:
|
|
1885
|
-
realModel:
|
|
2481
|
+
var ModelAliasUpdateSchema = z6.object({
|
|
2482
|
+
code: z6.string().regex(SIMING_CODE_REGEX).optional(),
|
|
2483
|
+
name: z6.string().min(1).optional(),
|
|
2484
|
+
realModel: z6.string().min(1).optional()
|
|
1886
2485
|
});
|
|
1887
2486
|
var ModelAliasWithRefCountSchema = ModelAliasShapeSchema.extend({
|
|
1888
|
-
refCount:
|
|
2487
|
+
refCount: z6.number().int().min(0)
|
|
1889
2488
|
});
|
|
1890
2489
|
|
|
1891
2490
|
// src/schemas/agent.ts
|
|
1892
|
-
var AgentScopeSchema =
|
|
1893
|
-
var AgentFunctionSchema =
|
|
2491
|
+
var AgentScopeSchema = z7.enum(["global", "project"]);
|
|
2492
|
+
var AgentFunctionSchema = z7.enum(["reviewer", "executor"]);
|
|
1894
2493
|
var AgentScopeRefine = (val, ctx) => {
|
|
1895
2494
|
if (val.scope === "project" && !val.projectId) {
|
|
1896
2495
|
ctx.addIssue({
|
|
@@ -1900,17 +2499,17 @@ var AgentScopeRefine = (val, ctx) => {
|
|
|
1900
2499
|
});
|
|
1901
2500
|
}
|
|
1902
2501
|
};
|
|
1903
|
-
var AgentShapeSchema =
|
|
1904
|
-
id:
|
|
1905
|
-
name:
|
|
1906
|
-
description:
|
|
1907
|
-
systemPrompt:
|
|
1908
|
-
boundSkills:
|
|
1909
|
-
model:
|
|
2502
|
+
var AgentShapeSchema = z7.object({
|
|
2503
|
+
id: z7.string().optional(),
|
|
2504
|
+
name: z7.string(),
|
|
2505
|
+
description: z7.string(),
|
|
2506
|
+
systemPrompt: z7.string(),
|
|
2507
|
+
boundSkills: z7.array(z7.string()).default([]),
|
|
2508
|
+
model: z7.string(),
|
|
1910
2509
|
/** 资产版本(N015 F13):install/export 版本对比依据;default 仅读侧兜底(createRepo 不做读侧解析,存量回填走 M6 迁移) */
|
|
1911
|
-
version:
|
|
1912
|
-
tools:
|
|
1913
|
-
permissions:
|
|
2510
|
+
version: z7.string().default("1.0.0"),
|
|
2511
|
+
tools: z7.array(z7.string()).default([]),
|
|
2512
|
+
permissions: z7.array(z7.string()).default([]),
|
|
1914
2513
|
/** 引用文档集合(可选——存量单文件资产无此字段,行为与升级前一致) */
|
|
1915
2514
|
references: createReferencesSchema(AGENT_MAIN_DOC).optional(),
|
|
1916
2515
|
/** 作用域:global=跨项目复用(默认,仅作存量迁移读侧兜底)/ project=项目专用(N012 D3) */
|
|
@@ -1918,15 +2517,15 @@ var AgentShapeSchema = z6.object({
|
|
|
1918
2517
|
/** 功能类型(T202608260002):default 仅创建侧归一——toEntity 直转不经 Zod parse,读侧归一在 repo 层 + 回填走 M12 */
|
|
1919
2518
|
"function": AgentFunctionSchema.default("executor"),
|
|
1920
2519
|
/** 仅 scope=project 时必填且为合法 ObjectId hex(superRefine 保证) */
|
|
1921
|
-
projectId:
|
|
2520
|
+
projectId: z7.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex").optional(),
|
|
1922
2521
|
/**
|
|
1923
2522
|
* T202608290001 资产启停(可选——存量无此字段 = 启用,零迁移)。
|
|
1924
2523
|
* 全局判断规则:enabled === false 即停用,true/缺失均启用。
|
|
1925
2524
|
* 唯一写入口是专用启停端点(POST /:name/enabled)——Create/Update/Copy 均不含此字段。
|
|
1926
2525
|
*/
|
|
1927
|
-
enabled:
|
|
1928
|
-
createdAt:
|
|
1929
|
-
updatedAt:
|
|
2526
|
+
enabled: z7.boolean().optional(),
|
|
2527
|
+
createdAt: z7.date().optional(),
|
|
2528
|
+
updatedAt: z7.date().optional()
|
|
1930
2529
|
});
|
|
1931
2530
|
var agentAggregateLimitRefine = (val, ctx) => {
|
|
1932
2531
|
const totalChars = (val.systemPrompt?.length ?? 0) + sumReferenceContentChars(val.references);
|
|
@@ -1944,28 +2543,28 @@ var AgentCreateSchema = AgentShapeSchema.omit({
|
|
|
1944
2543
|
createdAt: true,
|
|
1945
2544
|
updatedAt: true,
|
|
1946
2545
|
enabled: true
|
|
1947
|
-
}).extend({ scope: AgentScopeSchema }).extend({ model:
|
|
2546
|
+
}).extend({ scope: AgentScopeSchema }).extend({ model: z7.string().regex(SIMING_CODE_REGEX, "model \u5FC5\u987B\u662F siming code\uFF08kebab-case\uFF09") }).extend({ "function": AgentFunctionSchema.default("executor") }).superRefine(AgentScopeRefine).superRefine(agentAggregateLimitRefine);
|
|
1948
2547
|
var AgentUpdateSchema = AgentShapeSchema.omit({
|
|
1949
2548
|
id: true,
|
|
1950
2549
|
createdAt: true,
|
|
1951
2550
|
updatedAt: true,
|
|
1952
2551
|
enabled: true
|
|
1953
2552
|
}).extend({
|
|
1954
|
-
boundSkills:
|
|
2553
|
+
boundSkills: z7.array(z7.string()).optional(),
|
|
1955
2554
|
// N015:version 放行(export apply 版本递增随 PUT 携带);optional 剥离实体层 default 避免 partial 空 body 注入
|
|
1956
|
-
version:
|
|
1957
|
-
tools:
|
|
1958
|
-
permissions:
|
|
2555
|
+
version: z7.string().optional(),
|
|
2556
|
+
tools: z7.array(z7.string()).optional(),
|
|
2557
|
+
permissions: z7.array(z7.string()).optional(),
|
|
1959
2558
|
scope: AgentScopeSchema.optional(),
|
|
1960
2559
|
// N013:model 收紧为 siming code 格式(存在性校验在 handler 层;undefined = 不更新该字段)
|
|
1961
|
-
model:
|
|
2560
|
+
model: z7.string().regex(SIMING_CODE_REGEX, "model \u5FC5\u987B\u662F siming code\uFF08kebab-case\uFF09").optional(),
|
|
1962
2561
|
// T202608260002:optional 剥离实体层 default——否则 PUT 空 body 会注入 executor 覆盖 reviewer
|
|
1963
2562
|
"function": AgentFunctionSchema.optional()
|
|
1964
2563
|
}).partial().superRefine(AgentScopeRefine).superRefine(agentAggregateLimitRefine);
|
|
1965
|
-
var AgentCopySchema =
|
|
1966
|
-
newName:
|
|
2564
|
+
var AgentCopySchema = z7.object({
|
|
2565
|
+
newName: z7.string(),
|
|
1967
2566
|
newScope: AgentScopeSchema,
|
|
1968
|
-
targetProjectId:
|
|
2567
|
+
targetProjectId: z7.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex").optional()
|
|
1969
2568
|
}).superRefine((val, ctx) => {
|
|
1970
2569
|
if (val.newScope === "project" && !val.targetProjectId) {
|
|
1971
2570
|
ctx.addIssue({
|
|
@@ -1977,17 +2576,18 @@ var AgentCopySchema = z6.object({
|
|
|
1977
2576
|
});
|
|
1978
2577
|
|
|
1979
2578
|
// src/schemas/export-bundle.ts
|
|
1980
|
-
import { z as
|
|
2579
|
+
import { z as z8 } from "zod";
|
|
1981
2580
|
var EXPORT_FORMAT_VERSION = "1.0";
|
|
1982
2581
|
var TemplatePayloadSchema = DagTemplateSchema.omit({
|
|
1983
2582
|
id: true,
|
|
1984
2583
|
projectId: true,
|
|
1985
2584
|
enabled: true,
|
|
1986
2585
|
createdAt: true,
|
|
1987
|
-
updatedAt: true
|
|
2586
|
+
updatedAt: true,
|
|
2587
|
+
code: true
|
|
1988
2588
|
}).extend({
|
|
1989
|
-
version:
|
|
1990
|
-
isDefault:
|
|
2589
|
+
version: z8.string(),
|
|
2590
|
+
isDefault: z8.boolean()
|
|
1991
2591
|
});
|
|
1992
2592
|
var SkillPayloadSchema = SkillShapeSchema.omit({
|
|
1993
2593
|
id: true,
|
|
@@ -1996,7 +2596,7 @@ var SkillPayloadSchema = SkillShapeSchema.omit({
|
|
|
1996
2596
|
createdAt: true,
|
|
1997
2597
|
updatedAt: true
|
|
1998
2598
|
}).extend({
|
|
1999
|
-
version:
|
|
2599
|
+
version: z8.string(),
|
|
2000
2600
|
scope: SkillScopeSchema,
|
|
2001
2601
|
references: createReferencesSchema(SKILL_MAIN_DOC)
|
|
2002
2602
|
});
|
|
@@ -2007,12 +2607,12 @@ var AgentPayloadSchema = AgentShapeSchema.omit({
|
|
|
2007
2607
|
createdAt: true,
|
|
2008
2608
|
updatedAt: true
|
|
2009
2609
|
}).extend({
|
|
2010
|
-
version:
|
|
2610
|
+
version: z8.string(),
|
|
2011
2611
|
scope: AgentScopeSchema,
|
|
2012
2612
|
references: createReferencesSchema(AGENT_MAIN_DOC),
|
|
2013
|
-
boundSkills:
|
|
2014
|
-
tools:
|
|
2015
|
-
permissions:
|
|
2613
|
+
boundSkills: z8.array(z8.string()),
|
|
2614
|
+
tools: z8.array(z8.string()),
|
|
2615
|
+
permissions: z8.array(z8.string()),
|
|
2016
2616
|
"function": AgentFunctionSchema
|
|
2017
2617
|
});
|
|
2018
2618
|
var ModelAliasPayloadSchema = ModelAliasSchema.omit({
|
|
@@ -2020,271 +2620,307 @@ var ModelAliasPayloadSchema = ModelAliasSchema.omit({
|
|
|
2020
2620
|
createdAt: true,
|
|
2021
2621
|
updatedAt: true
|
|
2022
2622
|
});
|
|
2023
|
-
var ExportBundleSchema =
|
|
2024
|
-
exportFormatVersion:
|
|
2623
|
+
var ExportBundleSchema = z8.object({
|
|
2624
|
+
exportFormatVersion: z8.literal(EXPORT_FORMAT_VERSION, {
|
|
2025
2625
|
error: `\u5BFC\u51FA\u683C\u5F0F\u7248\u672C\u4E0D\u652F\u6301\uFF0C\u5F53\u524D\u652F\u6301 ${EXPORT_FORMAT_VERSION}`
|
|
2026
2626
|
}),
|
|
2027
|
-
exportedAt:
|
|
2627
|
+
exportedAt: z8.string(),
|
|
2028
2628
|
/** 源环境悬空提示(§2.2:悬空 skill/agent.model 不阻断导出)。导出响应恒携带;导入入参可缺省——导入侧提示由 plan 生成,不消费本字段 */
|
|
2029
|
-
warnings:
|
|
2629
|
+
warnings: z8.array(z8.string()).optional(),
|
|
2030
2630
|
template: TemplatePayloadSchema,
|
|
2031
|
-
dependencies:
|
|
2032
|
-
skills:
|
|
2033
|
-
agents:
|
|
2034
|
-
modelAliases:
|
|
2631
|
+
dependencies: z8.object({
|
|
2632
|
+
skills: z8.array(SkillPayloadSchema),
|
|
2633
|
+
agents: z8.array(AgentPayloadSchema),
|
|
2634
|
+
modelAliases: z8.array(ModelAliasPayloadSchema)
|
|
2035
2635
|
})
|
|
2036
2636
|
});
|
|
2037
|
-
var ImportPlanRequestSchema =
|
|
2038
|
-
targetProjectId:
|
|
2637
|
+
var ImportPlanRequestSchema = z8.object({
|
|
2638
|
+
targetProjectId: z8.string().regex(OBJECT_ID_HEX, "targetProjectId must be a valid ObjectId hex"),
|
|
2039
2639
|
bundle: ExportBundleSchema
|
|
2040
2640
|
});
|
|
2041
|
-
var ImportDecisionSchema =
|
|
2042
|
-
|
|
2043
|
-
kind:
|
|
2044
|
-
name:
|
|
2641
|
+
var ImportDecisionSchema = z8.discriminatedUnion("kind", [
|
|
2642
|
+
z8.object({
|
|
2643
|
+
kind: z8.literal("skill"),
|
|
2644
|
+
name: z8.string(),
|
|
2045
2645
|
scope: SkillScopeSchema,
|
|
2046
|
-
action:
|
|
2646
|
+
action: z8.enum(["overwrite", "skip"])
|
|
2047
2647
|
}),
|
|
2048
|
-
|
|
2049
|
-
kind:
|
|
2050
|
-
name:
|
|
2648
|
+
z8.object({
|
|
2649
|
+
kind: z8.literal("agent"),
|
|
2650
|
+
name: z8.string(),
|
|
2051
2651
|
scope: AgentScopeSchema,
|
|
2052
|
-
action:
|
|
2652
|
+
action: z8.enum(["overwrite", "skip"])
|
|
2053
2653
|
}),
|
|
2054
|
-
|
|
2055
|
-
kind:
|
|
2056
|
-
name:
|
|
2057
|
-
action:
|
|
2654
|
+
z8.object({
|
|
2655
|
+
kind: z8.literal("modelAlias"),
|
|
2656
|
+
name: z8.string(),
|
|
2657
|
+
action: z8.enum(["overwrite", "skip"])
|
|
2058
2658
|
})
|
|
2059
2659
|
]);
|
|
2060
2660
|
var ImportApplyRequestSchema = ImportPlanRequestSchema.extend({
|
|
2061
2661
|
/** 未列出的冲突项缺省 skip(保守默认:未决策不覆盖) */
|
|
2062
|
-
decisions:
|
|
2662
|
+
decisions: z8.array(ImportDecisionSchema).default([])
|
|
2063
2663
|
});
|
|
2064
|
-
var DependencyStatusSchema =
|
|
2065
|
-
var DependencyCheckSchema =
|
|
2066
|
-
kind:
|
|
2067
|
-
name:
|
|
2664
|
+
var DependencyStatusSchema = z8.enum(["create", "identical", "conflict"]);
|
|
2665
|
+
var DependencyCheckSchema = z8.object({
|
|
2666
|
+
kind: z8.enum(["skill", "agent", "modelAlias"]),
|
|
2667
|
+
name: z8.string(),
|
|
2068
2668
|
/** skill/agent 携带;modelAlias 无 */
|
|
2069
|
-
scope:
|
|
2669
|
+
scope: z8.enum(["global", "project"]).optional(),
|
|
2070
2670
|
status: DependencyStatusSchema,
|
|
2071
2671
|
/** status ≠ create 时目标环境现值版本(modelAlias 无 version 概念不填) */
|
|
2072
|
-
currentVersion:
|
|
2073
|
-
importVersion:
|
|
2672
|
+
currentVersion: z8.string().optional(),
|
|
2673
|
+
importVersion: z8.string().optional(),
|
|
2074
2674
|
/** status = conflict 时差异字段名清单(如 "content, references") */
|
|
2075
|
-
changeSummary:
|
|
2675
|
+
changeSummary: z8.string().optional(),
|
|
2076
2676
|
/** 影响范围(PRD F13):global 资产影响所有项目 / project 资产仅影响所属项目 */
|
|
2077
|
-
impact:
|
|
2078
|
-
});
|
|
2079
|
-
var ImportPlanResponseSchema =
|
|
2080
|
-
template:
|
|
2081
|
-
name:
|
|
2082
|
-
willOverwrite:
|
|
2083
|
-
currentVersion:
|
|
2084
|
-
importVersion:
|
|
2085
|
-
isDefaultRequested:
|
|
2677
|
+
impact: z8.enum(["global", "project"])
|
|
2678
|
+
});
|
|
2679
|
+
var ImportPlanResponseSchema = z8.object({
|
|
2680
|
+
template: z8.object({
|
|
2681
|
+
name: z8.string(),
|
|
2682
|
+
willOverwrite: z8.boolean(),
|
|
2683
|
+
currentVersion: z8.string().optional(),
|
|
2684
|
+
importVersion: z8.string(),
|
|
2685
|
+
isDefaultRequested: z8.boolean(),
|
|
2086
2686
|
/** isDefault 降级后的最终生效值(F17:项目已有默认 → false) */
|
|
2087
|
-
isDefaultEffect:
|
|
2088
|
-
downgradeReason:
|
|
2687
|
+
isDefaultEffect: z8.boolean(),
|
|
2688
|
+
downgradeReason: z8.string().optional()
|
|
2089
2689
|
}),
|
|
2090
|
-
dependencies:
|
|
2091
|
-
/**
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2690
|
+
dependencies: z8.array(DependencyCheckSchema),
|
|
2691
|
+
/**
|
|
2692
|
+
* K10①:模板节点直接技能引用完整性逐项核对(node × skill 粒度)——
|
|
2693
|
+
* ok = 在 bundle 依赖 ∪ 目标环境可见集;missing = 双侧皆无(绑定悬空,non-blocking warning)
|
|
2694
|
+
*/
|
|
2695
|
+
directSkillChecks: z8.array(
|
|
2696
|
+
z8.object({
|
|
2697
|
+
node: z8.string(),
|
|
2698
|
+
skill: z8.string(),
|
|
2699
|
+
status: z8.enum(["ok", "missing"])
|
|
2700
|
+
})
|
|
2701
|
+
),
|
|
2702
|
+
/** 非阻断提示(源环境悬空绑定、agent.model 引用缺失、sourcePreset 来源缺失等) */
|
|
2703
|
+
warnings: z8.array(z8.string())
|
|
2704
|
+
});
|
|
2705
|
+
var DependencyActionSchema = z8.enum(["created", "updated", "skipped", "identical", "failed"]);
|
|
2706
|
+
var ImportApplyResponseSchema = z8.object({
|
|
2707
|
+
template: z8.object({
|
|
2708
|
+
id: z8.string(),
|
|
2709
|
+
name: z8.string(),
|
|
2710
|
+
action: z8.enum(["created", "updated"]),
|
|
2711
|
+
isDefaultEffect: z8.boolean()
|
|
2101
2712
|
}),
|
|
2102
|
-
dependencies:
|
|
2103
|
-
|
|
2104
|
-
kind:
|
|
2105
|
-
name:
|
|
2106
|
-
scope:
|
|
2713
|
+
dependencies: z8.array(
|
|
2714
|
+
z8.object({
|
|
2715
|
+
kind: z8.enum(["skill", "agent", "modelAlias"]),
|
|
2716
|
+
name: z8.string(),
|
|
2717
|
+
scope: z8.enum(["global", "project"]).optional(),
|
|
2107
2718
|
action: DependencyActionSchema,
|
|
2108
2719
|
/** action = failed 时的错误说明 */
|
|
2109
|
-
message:
|
|
2720
|
+
message: z8.string().optional()
|
|
2110
2721
|
})
|
|
2111
|
-
)
|
|
2722
|
+
),
|
|
2723
|
+
/** K10②:apply 时刻目标环境缺失来源而被剥除 sourcePreset 的节点 id 清单(降级自建导入) */
|
|
2724
|
+
sourceStripped: z8.array(z8.string())
|
|
2112
2725
|
});
|
|
2113
2726
|
function decisionKey(kind, scope, name) {
|
|
2114
2727
|
return `${kind}:${scope ?? ""}:${name}`;
|
|
2115
2728
|
}
|
|
2116
2729
|
|
|
2117
2730
|
// src/schemas/advance.ts
|
|
2118
|
-
import { z as
|
|
2119
|
-
var AdvanceRequestSchema =
|
|
2120
|
-
note:
|
|
2731
|
+
import { z as z9 } from "zod";
|
|
2732
|
+
var AdvanceRequestSchema = z9.object({
|
|
2733
|
+
note: z9.string().optional(),
|
|
2121
2734
|
/** min(1).max(2000) 与 record summary 端点对齐(同一 $set 目标,两入口校验强度一致) */
|
|
2122
|
-
summary:
|
|
2735
|
+
summary: z9.string().min(1).max(2e3).optional()
|
|
2123
2736
|
});
|
|
2124
|
-
var ApproveRequestSchema =
|
|
2125
|
-
decision:
|
|
2126
|
-
comment:
|
|
2737
|
+
var ApproveRequestSchema = z9.object({
|
|
2738
|
+
decision: z9.enum(["approved", "rejected"]),
|
|
2739
|
+
comment: z9.string().optional()
|
|
2127
2740
|
});
|
|
2128
|
-
var TaskPublicSchema =
|
|
2129
|
-
taskId:
|
|
2130
|
-
title:
|
|
2741
|
+
var TaskPublicSchema = z9.object({
|
|
2742
|
+
taskId: z9.string(),
|
|
2743
|
+
title: z9.string(),
|
|
2131
2744
|
// T202608240003:任务类型标签(缺省 = 裸任务/旧任务;模板 PRD 调研变体判定消费)
|
|
2132
|
-
type:
|
|
2133
|
-
currentNode:
|
|
2134
|
-
currentPhase:
|
|
2135
|
-
status:
|
|
2136
|
-
pausedAt:
|
|
2137
|
-
track:
|
|
2138
|
-
});
|
|
2139
|
-
var NodeInfoSchema =
|
|
2140
|
-
nodeId:
|
|
2141
|
-
label:
|
|
2142
|
-
track:
|
|
2143
|
-
prompt:
|
|
2144
|
-
skills:
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2745
|
+
type: z9.enum(TASK_TYPES).optional(),
|
|
2746
|
+
currentNode: z9.string(),
|
|
2747
|
+
currentPhase: z9.string(),
|
|
2748
|
+
status: z9.string(),
|
|
2749
|
+
pausedAt: z9.string().nullable(),
|
|
2750
|
+
track: z9.string()
|
|
2751
|
+
});
|
|
2752
|
+
var NodeInfoSchema = z9.object({
|
|
2753
|
+
nodeId: z9.string(),
|
|
2754
|
+
label: z9.string(),
|
|
2755
|
+
track: z9.string(),
|
|
2756
|
+
prompt: z9.string(),
|
|
2757
|
+
skills: z9.array(z9.string()),
|
|
2758
|
+
agents: z9.array(z9.string()),
|
|
2759
|
+
upcomingPause: z9.string().nullable()
|
|
2760
|
+
});
|
|
2761
|
+
var AdvanceResponseSchema = z9.discriminatedUnion("status", [
|
|
2762
|
+
z9.object({
|
|
2763
|
+
status: z9.literal("advanced"),
|
|
2150
2764
|
task: TaskPublicSchema,
|
|
2151
2765
|
nextNode: NodeInfoSchema
|
|
2152
2766
|
}),
|
|
2153
|
-
|
|
2154
|
-
status:
|
|
2767
|
+
z9.object({
|
|
2768
|
+
status: z9.literal("paused"),
|
|
2155
2769
|
task: TaskPublicSchema,
|
|
2156
2770
|
pausePoint: EdgePausePointSchema,
|
|
2157
|
-
guidance:
|
|
2771
|
+
guidance: z9.string()
|
|
2158
2772
|
}),
|
|
2159
|
-
|
|
2160
|
-
status:
|
|
2773
|
+
z9.object({
|
|
2774
|
+
status: z9.literal("completed"),
|
|
2161
2775
|
task: TaskPublicSchema
|
|
2162
2776
|
})
|
|
2163
2777
|
]);
|
|
2164
|
-
var ApproveResponseSchema =
|
|
2165
|
-
|
|
2166
|
-
status:
|
|
2778
|
+
var ApproveResponseSchema = z9.discriminatedUnion("status", [
|
|
2779
|
+
z9.object({
|
|
2780
|
+
status: z9.literal("advanced"),
|
|
2167
2781
|
task: TaskPublicSchema,
|
|
2168
2782
|
nextNode: NodeInfoSchema
|
|
2169
2783
|
}),
|
|
2170
|
-
|
|
2171
|
-
status:
|
|
2784
|
+
z9.object({
|
|
2785
|
+
status: z9.literal("rejected"),
|
|
2172
2786
|
task: TaskPublicSchema,
|
|
2173
|
-
guidance:
|
|
2787
|
+
guidance: z9.string()
|
|
2174
2788
|
}),
|
|
2175
2789
|
// 「completed」分支当前引擎不可达(approve 从 paused 流转必有下一节点,技术方案 §2.2
|
|
2176
2790
|
// 已删 paused→completed 直达路径)——按方案 §2.1 契约保留作前向兼容预留
|
|
2177
|
-
|
|
2178
|
-
status:
|
|
2791
|
+
z9.object({
|
|
2792
|
+
status: z9.literal("completed"),
|
|
2179
2793
|
task: TaskPublicSchema
|
|
2180
2794
|
})
|
|
2181
2795
|
]);
|
|
2182
|
-
var PauseRequestSchema =
|
|
2183
|
-
reason:
|
|
2796
|
+
var PauseRequestSchema = z9.object({
|
|
2797
|
+
reason: z9.string().optional()
|
|
2184
2798
|
});
|
|
2185
|
-
var ResumeRequestSchema =
|
|
2186
|
-
decision:
|
|
2799
|
+
var ResumeRequestSchema = z9.object({
|
|
2800
|
+
decision: z9.string().optional()
|
|
2187
2801
|
});
|
|
2188
|
-
var CancelRequestSchema =
|
|
2189
|
-
reason:
|
|
2802
|
+
var CancelRequestSchema = z9.object({
|
|
2803
|
+
reason: z9.string().min(1).max(500).optional()
|
|
2190
2804
|
});
|
|
2191
2805
|
|
|
2192
2806
|
// src/schemas/task-doc.ts
|
|
2193
|
-
import { z as
|
|
2194
|
-
var TaskContextSchema =
|
|
2195
|
-
task:
|
|
2196
|
-
taskId:
|
|
2197
|
-
title:
|
|
2807
|
+
import { z as z10 } from "zod";
|
|
2808
|
+
var TaskContextSchema = z10.object({
|
|
2809
|
+
task: z10.object({
|
|
2810
|
+
taskId: z10.string(),
|
|
2811
|
+
title: z10.string(),
|
|
2198
2812
|
// T202608240003:与 TaskPublicSchema 同步(toTaskPublic 运行时已产出,schema 声明漂移会导致 typed 消费方 strip 掉 type)
|
|
2199
|
-
type:
|
|
2200
|
-
currentNode:
|
|
2201
|
-
currentPhase:
|
|
2202
|
-
status:
|
|
2203
|
-
pausedAt:
|
|
2204
|
-
track:
|
|
2813
|
+
type: z10.enum(TASK_TYPES).optional(),
|
|
2814
|
+
currentNode: z10.string(),
|
|
2815
|
+
currentPhase: z10.string(),
|
|
2816
|
+
status: z10.string(),
|
|
2817
|
+
pausedAt: z10.string().nullable(),
|
|
2818
|
+
track: z10.string()
|
|
2205
2819
|
}),
|
|
2206
|
-
currentNode:
|
|
2207
|
-
nodeId:
|
|
2208
|
-
label:
|
|
2209
|
-
phase:
|
|
2210
|
-
track:
|
|
2820
|
+
currentNode: z10.object({
|
|
2821
|
+
nodeId: z10.string(),
|
|
2822
|
+
label: z10.string(),
|
|
2823
|
+
phase: z10.string(),
|
|
2824
|
+
track: z10.string(),
|
|
2211
2825
|
/** renderPrompt 渲染后(AI 推进任务的直接输入) */
|
|
2212
|
-
prompt:
|
|
2213
|
-
skills:
|
|
2826
|
+
prompt: z10.string(),
|
|
2827
|
+
skills: z10.array(z10.string()),
|
|
2828
|
+
agents: z10.array(z10.string()),
|
|
2214
2829
|
/** 完成当前节点后出边上的暂停点描述(经 findNextEdge 按轨选边) */
|
|
2215
|
-
upcomingPause:
|
|
2830
|
+
upcomingPause: z10.string().nullable()
|
|
2216
2831
|
}).nullable(),
|
|
2217
|
-
pausedAtEdge:
|
|
2218
|
-
from:
|
|
2219
|
-
to:
|
|
2832
|
+
pausedAtEdge: z10.object({
|
|
2833
|
+
from: z10.string(),
|
|
2834
|
+
to: z10.string(),
|
|
2220
2835
|
pausePoint: EdgePausePointBaseSchema
|
|
2221
2836
|
}).nullable(),
|
|
2222
2837
|
/** 全节点实时状态清单 */
|
|
2223
|
-
nodes:
|
|
2224
|
-
nodeId:
|
|
2225
|
-
label:
|
|
2226
|
-
status:
|
|
2227
|
-
enteredAt:
|
|
2228
|
-
completedAt:
|
|
2838
|
+
nodes: z10.array(z10.object({
|
|
2839
|
+
nodeId: z10.string(),
|
|
2840
|
+
label: z10.string(),
|
|
2841
|
+
status: z10.string(),
|
|
2842
|
+
enteredAt: z10.date().nullable(),
|
|
2843
|
+
completedAt: z10.date().nullable()
|
|
2229
2844
|
})),
|
|
2230
2845
|
/** N020 D1:任务级结构化文档(未写时 null) */
|
|
2231
2846
|
taskDoc: TaskDocContentSchema.nullable(),
|
|
2232
2847
|
/** N020 D1:节点执行记录(仅含已写记录的 nodeId) */
|
|
2233
|
-
nodeRecords:
|
|
2848
|
+
nodeRecords: z10.record(z10.string(), NodeRecordSchema),
|
|
2234
2849
|
/** N020 D1:跨节点累积素材(架构信息收集条目) */
|
|
2235
|
-
archNotes:
|
|
2850
|
+
archNotes: z10.array(ArchNoteSchema)
|
|
2236
2851
|
});
|
|
2237
|
-
var TaskSummarySchema =
|
|
2238
|
-
taskId:
|
|
2239
|
-
title:
|
|
2852
|
+
var TaskSummarySchema = z10.object({
|
|
2853
|
+
taskId: z10.string(),
|
|
2854
|
+
title: z10.string(),
|
|
2240
2855
|
// T202608240003:任务类型标签(缺省 = 裸任务/旧任务)
|
|
2241
|
-
type:
|
|
2242
|
-
projectId:
|
|
2243
|
-
dagTemplateId:
|
|
2244
|
-
currentNode:
|
|
2245
|
-
currentPhase:
|
|
2246
|
-
status:
|
|
2247
|
-
pausedAt:
|
|
2248
|
-
track:
|
|
2249
|
-
progress:
|
|
2250
|
-
completed:
|
|
2251
|
-
total:
|
|
2856
|
+
type: z10.enum(TASK_TYPES).optional(),
|
|
2857
|
+
projectId: z10.string(),
|
|
2858
|
+
dagTemplateId: z10.string(),
|
|
2859
|
+
currentNode: z10.string(),
|
|
2860
|
+
currentPhase: z10.string(),
|
|
2861
|
+
status: z10.string(),
|
|
2862
|
+
pausedAt: z10.string().nullable(),
|
|
2863
|
+
track: z10.string(),
|
|
2864
|
+
progress: z10.object({
|
|
2865
|
+
completed: z10.number(),
|
|
2866
|
+
total: z10.number()
|
|
2252
2867
|
}),
|
|
2253
|
-
createdAt:
|
|
2254
|
-
updatedAt:
|
|
2868
|
+
createdAt: z10.date().optional(),
|
|
2869
|
+
updatedAt: z10.date().optional()
|
|
2255
2870
|
});
|
|
2256
2871
|
|
|
2257
2872
|
// src/schemas/write-ack.ts
|
|
2258
|
-
import { z as
|
|
2873
|
+
import { z as z11 } from "zod";
|
|
2259
2874
|
var WRITE_ACK_ENTRY_KINDS = ["check", "artifact", "decision", "confirmation", "archnote"];
|
|
2260
|
-
var WriteAckEntryKindSchema =
|
|
2261
|
-
var EchoItemSchema =
|
|
2262
|
-
path:
|
|
2263
|
-
excerpt:
|
|
2875
|
+
var WriteAckEntryKindSchema = z11.enum(WRITE_ACK_ENTRY_KINDS);
|
|
2876
|
+
var EchoItemSchema = z11.object({
|
|
2877
|
+
path: z11.string(),
|
|
2878
|
+
excerpt: z11.string()
|
|
2264
2879
|
});
|
|
2265
|
-
var TaskProgressSchema =
|
|
2266
|
-
completed:
|
|
2267
|
-
total:
|
|
2880
|
+
var TaskProgressSchema = z11.object({
|
|
2881
|
+
completed: z11.number().int(),
|
|
2882
|
+
total: z11.number().int()
|
|
2268
2883
|
});
|
|
2269
2884
|
var TaskProgressPublicSchema = TaskPublicSchema.extend({
|
|
2270
|
-
projectId:
|
|
2885
|
+
projectId: z11.string(),
|
|
2271
2886
|
progress: TaskProgressSchema
|
|
2272
2887
|
});
|
|
2273
|
-
var WriteAckSchema =
|
|
2274
|
-
taskId:
|
|
2888
|
+
var WriteAckSchema = z11.object({
|
|
2889
|
+
taskId: z11.string(),
|
|
2275
2890
|
/** 本次写入触及的字段点路径(如 doc.what / nodeRecords.PRD.checks) */
|
|
2276
|
-
updated:
|
|
2891
|
+
updated: z11.array(z11.string()),
|
|
2277
2892
|
/** 新建条目标识($push 五类对象条目;无新条目的端点缺省) */
|
|
2278
|
-
entry:
|
|
2279
|
-
id:
|
|
2893
|
+
entry: z11.object({
|
|
2894
|
+
id: z11.string(),
|
|
2280
2895
|
kind: WriteAckEntryKindSchema
|
|
2281
2896
|
}).optional(),
|
|
2282
2897
|
/** 写入内容回显(触及字段逐条;无值可回显的端点可缺省) */
|
|
2283
|
-
echo:
|
|
2284
|
-
updatedAt:
|
|
2898
|
+
echo: z11.array(EchoItemSchema).optional(),
|
|
2899
|
+
updatedAt: z11.date(),
|
|
2285
2900
|
task: TaskProgressPublicSchema
|
|
2286
2901
|
});
|
|
2287
|
-
var
|
|
2902
|
+
var TaskBatchEntrySchema = z11.object({
|
|
2903
|
+
index: z11.number().int().min(0),
|
|
2904
|
+
id: z11.string().optional(),
|
|
2905
|
+
kind: WriteAckEntryKindSchema.optional()
|
|
2906
|
+
});
|
|
2907
|
+
var TaskBatchFailureSchema = z11.object({
|
|
2908
|
+
index: z11.number().int().min(0),
|
|
2909
|
+
message: z11.string(),
|
|
2910
|
+
hint: z11.string().optional()
|
|
2911
|
+
});
|
|
2912
|
+
var TaskBatchAckSchema = z11.object({
|
|
2913
|
+
taskId: z11.string(),
|
|
2914
|
+
/** 成功落库条目数(部分失败语义:成功条目保留不回滚) */
|
|
2915
|
+
applied: z11.number().int().min(0),
|
|
2916
|
+
/** 本次写入触及的字段点路径 */
|
|
2917
|
+
updated: z11.array(z11.string()),
|
|
2918
|
+
entries: z11.array(TaskBatchEntrySchema),
|
|
2919
|
+
failures: z11.array(TaskBatchFailureSchema),
|
|
2920
|
+
updatedAt: z11.date(),
|
|
2921
|
+
task: TaskProgressPublicSchema
|
|
2922
|
+
});
|
|
2923
|
+
var CreateTaskResponseSchema = z11.object({
|
|
2288
2924
|
task: TaskProgressPublicSchema,
|
|
2289
2925
|
firstNode: NodeInfoSchema
|
|
2290
2926
|
});
|
|
@@ -2300,34 +2936,34 @@ function excerpt(value, max = 120) {
|
|
|
2300
2936
|
return `${chars.slice(0, max - 1).join("")}\u2026`;
|
|
2301
2937
|
}
|
|
2302
2938
|
var CONTEXT_VIEWS = ["basic", "full"];
|
|
2303
|
-
var ContextViewSchema =
|
|
2939
|
+
var ContextViewSchema = z11.enum(CONTEXT_VIEWS);
|
|
2304
2940
|
|
|
2305
2941
|
// src/schemas/project.ts
|
|
2306
|
-
import { z as
|
|
2307
|
-
var ProjectStatusSchema =
|
|
2308
|
-
var ProjectSchema =
|
|
2309
|
-
id:
|
|
2310
|
-
key:
|
|
2311
|
-
name:
|
|
2312
|
-
description:
|
|
2942
|
+
import { z as z12 } from "zod";
|
|
2943
|
+
var ProjectStatusSchema = z12.enum(["active", "archived"]);
|
|
2944
|
+
var ProjectSchema = z12.object({
|
|
2945
|
+
id: z12.string().optional(),
|
|
2946
|
+
key: z12.string().regex(/^[a-z0-9][a-z0-9-]{1,30}$/, "key \u5FC5\u987B\u4E3A kebab-case\uFF082-31 \u5B57\u7B26\uFF09"),
|
|
2947
|
+
name: z12.string().min(1).max(50),
|
|
2948
|
+
description: z12.string().max(500).optional(),
|
|
2313
2949
|
status: ProjectStatusSchema.default("active"),
|
|
2314
|
-
createdAt:
|
|
2315
|
-
updatedAt:
|
|
2950
|
+
createdAt: z12.date().optional(),
|
|
2951
|
+
updatedAt: z12.date().optional()
|
|
2316
2952
|
});
|
|
2317
|
-
var ProjectCreateSchema =
|
|
2318
|
-
key:
|
|
2319
|
-
name:
|
|
2320
|
-
description:
|
|
2953
|
+
var ProjectCreateSchema = z12.object({
|
|
2954
|
+
key: z12.string().regex(/^[a-z0-9][a-z0-9-]{1,30}$/, "key \u5FC5\u987B\u4E3A kebab-case\uFF082-31 \u5B57\u7B26\uFF09").optional(),
|
|
2955
|
+
name: z12.string().min(1).max(50),
|
|
2956
|
+
description: z12.string().max(500).optional()
|
|
2321
2957
|
});
|
|
2322
|
-
var ProjectUpdateSchema =
|
|
2323
|
-
name:
|
|
2324
|
-
description:
|
|
2958
|
+
var ProjectUpdateSchema = z12.object({
|
|
2959
|
+
name: z12.string().min(1).max(50).optional(),
|
|
2960
|
+
description: z12.string().max(500).optional(),
|
|
2325
2961
|
status: ProjectStatusSchema.optional()
|
|
2326
2962
|
});
|
|
2327
2963
|
|
|
2328
2964
|
// src/schemas/enum-registry.ts
|
|
2329
|
-
import { z as
|
|
2330
|
-
var EnumRegistryCategorySchema =
|
|
2965
|
+
import { z as z13 } from "zod";
|
|
2966
|
+
var EnumRegistryCategorySchema = z13.enum([
|
|
2331
2967
|
"dag_phase",
|
|
2332
2968
|
"dag_track",
|
|
2333
2969
|
"pause_type",
|
|
@@ -2337,62 +2973,277 @@ var EnumRegistryCategorySchema = z12.enum([
|
|
|
2337
2973
|
"scope",
|
|
2338
2974
|
"agent_function"
|
|
2339
2975
|
]);
|
|
2340
|
-
var EnumEntrySchema =
|
|
2976
|
+
var EnumEntrySchema = z13.object({
|
|
2341
2977
|
/** 领域层存库的原始枚举值(不可臆造;builtin 值 engine/DB 强依赖;kebab 约束与 skill name 对齐,保证 DELETE /entries/:value 可寻址) */
|
|
2342
|
-
value:
|
|
2978
|
+
value: z13.string().regex(/^[a-z0-9][a-z0-9_-]*$/, "value \u4EC5\u652F\u6301\u5C0F\u5199\u5B57\u6BCD/\u6570\u5B57/\u4E0B\u5212\u7EBF/\u8FDE\u5B57\u7B26\uFF08kebab-case\uFF09"),
|
|
2343
2979
|
/** 展示文案(消费端下拉直接取用) */
|
|
2344
|
-
label:
|
|
2980
|
+
label: z13.string().min(1, "label \u4E0D\u80FD\u4E3A\u7A7A"),
|
|
2345
2981
|
/** 可选语义色 token(如 --success / --warning),消费端映射到 UI */
|
|
2346
|
-
color:
|
|
2982
|
+
color: z13.string().optional(),
|
|
2347
2983
|
/** 下拉排序(升序) */
|
|
2348
|
-
order:
|
|
2984
|
+
order: z13.number().int().default(0),
|
|
2349
2985
|
/** 内置值:禁删 + label/color 可改但 value 不可改(D8);seed 时标记 */
|
|
2350
|
-
builtin:
|
|
2986
|
+
builtin: z13.boolean().default(false),
|
|
2351
2987
|
/** 启停:false = 下拉禁用展示(历史数据仍可读,新选择不可用) */
|
|
2352
|
-
active:
|
|
2988
|
+
active: z13.boolean().default(true)
|
|
2353
2989
|
});
|
|
2354
|
-
var EnumRegistrySchema =
|
|
2355
|
-
id:
|
|
2990
|
+
var EnumRegistrySchema = z13.object({
|
|
2991
|
+
id: z13.string().optional(),
|
|
2356
2992
|
category: EnumRegistryCategorySchema,
|
|
2357
|
-
entries:
|
|
2358
|
-
updatedAt:
|
|
2993
|
+
entries: z13.array(EnumEntrySchema).default([]),
|
|
2994
|
+
updatedAt: z13.date().optional()
|
|
2359
2995
|
});
|
|
2360
|
-
var EnumRegistryUpdateSchema =
|
|
2361
|
-
entries:
|
|
2996
|
+
var EnumRegistryUpdateSchema = z13.object({
|
|
2997
|
+
entries: z13.array(EnumEntrySchema)
|
|
2362
2998
|
});
|
|
2363
2999
|
|
|
2364
3000
|
// src/schemas/asset-enabled.ts
|
|
2365
|
-
import { z as
|
|
2366
|
-
var AssetSetEnabledSchema =
|
|
2367
|
-
enabled:
|
|
3001
|
+
import { z as z14 } from "zod";
|
|
3002
|
+
var AssetSetEnabledSchema = z14.object({
|
|
3003
|
+
enabled: z14.boolean()
|
|
3004
|
+
});
|
|
3005
|
+
|
|
3006
|
+
// src/schemas/auth.ts
|
|
3007
|
+
import { z as z15 } from "zod";
|
|
3008
|
+
var AuthTokenTypeSchema = z15.enum(["admin", "project"]);
|
|
3009
|
+
var AuthAccountSchema = z15.object({
|
|
3010
|
+
id: z15.string().optional(),
|
|
3011
|
+
username: z15.string().min(3, "\u8D26\u53F7\u81F3\u5C11 3 \u4E2A\u5B57\u7B26").max(50).regex(/^[a-zA-Z0-9_-]+$/, "\u8D26\u53F7\u4EC5\u5141\u8BB8\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\u4E0E\u8FDE\u5B57\u7B26"),
|
|
3012
|
+
/** 存储形态:scrypt$N$r$p$salt$hash(base64 段),明文仅存在于请求体 */
|
|
3013
|
+
passwordHash: z15.string().min(1),
|
|
3014
|
+
createdAt: z15.date().optional(),
|
|
3015
|
+
updatedAt: z15.date().optional()
|
|
3016
|
+
});
|
|
3017
|
+
var AuthAccountCreateSchema = z15.object({
|
|
3018
|
+
username: AuthAccountSchema.shape.username,
|
|
3019
|
+
password: z15.string().min(8, "\u5BC6\u7801\u81F3\u5C11 8 \u4E2A\u5B57\u7B26").max(128)
|
|
3020
|
+
});
|
|
3021
|
+
var PasswordChangeSchema = z15.object({
|
|
3022
|
+
currentPassword: z15.string().min(1),
|
|
3023
|
+
newPassword: z15.string().min(8, "\u5BC6\u7801\u81F3\u5C11 8 \u4E2A\u5B57\u7B26").max(128)
|
|
3024
|
+
});
|
|
3025
|
+
var LoginSchema = z15.object({
|
|
3026
|
+
username: z15.string().min(1),
|
|
3027
|
+
password: z15.string().min(1)
|
|
3028
|
+
});
|
|
3029
|
+
var AuthTokenSchema = z15.object({
|
|
3030
|
+
id: z15.string().optional(),
|
|
3031
|
+
/** sha256(token 全串含前缀) hex——明文 Token 不入库 */
|
|
3032
|
+
tokenHash: z15.string().regex(/^[a-f0-9]{64}$/),
|
|
3033
|
+
type: AuthTokenTypeSchema,
|
|
3034
|
+
/** type=project 时必填且不可缺失(superRefine 保证);指向 projects.id(24-hex) */
|
|
3035
|
+
projectId: z15.string().regex(/^[a-f0-9]{24}$/, "projectId \u5FC5\u987B\u4E3A 24-hex ObjectId").optional(),
|
|
3036
|
+
createdAt: z15.date().optional(),
|
|
3037
|
+
updatedAt: z15.date().optional()
|
|
3038
|
+
}).superRefine((data, ctx) => {
|
|
3039
|
+
if (data.type === "project" && data.projectId === void 0) {
|
|
3040
|
+
ctx.addIssue({ code: "custom", path: ["projectId"], message: "\u9879\u76EE Token \u5FC5\u987B\u7ED1\u5B9A projectId" });
|
|
3041
|
+
}
|
|
3042
|
+
});
|
|
3043
|
+
var AuthTokenGenResponseSchema = z15.object({
|
|
3044
|
+
token: z15.string().min(1),
|
|
3045
|
+
type: AuthTokenTypeSchema,
|
|
3046
|
+
projectId: z15.string().optional()
|
|
3047
|
+
});
|
|
3048
|
+
var AuthTokenStatusSchema = z15.object({
|
|
3049
|
+
type: AuthTokenTypeSchema,
|
|
3050
|
+
projectId: z15.string().optional(),
|
|
3051
|
+
hasToken: z15.boolean(),
|
|
3052
|
+
createdAt: z15.date().nullable().optional()
|
|
3053
|
+
});
|
|
3054
|
+
var AuthWhoamiSchema = z15.object({
|
|
3055
|
+
identity: z15.enum(["admin", "project", "none"]),
|
|
3056
|
+
project: z15.object({
|
|
3057
|
+
id: z15.string(),
|
|
3058
|
+
key: z15.string(),
|
|
3059
|
+
name: z15.string()
|
|
3060
|
+
}).nullable().optional(),
|
|
3061
|
+
authEnabled: z15.boolean(),
|
|
3062
|
+
server: z15.object({
|
|
3063
|
+
url: z15.string()
|
|
3064
|
+
})
|
|
3065
|
+
});
|
|
3066
|
+
var AuthStatusSchema = z15.object({
|
|
3067
|
+
authEnabled: z15.boolean(),
|
|
3068
|
+
/** 鉴权开启且无管理员账号 = 初始化引导窗口(PRD §3.2) */
|
|
3069
|
+
needsSetup: z15.boolean()
|
|
3070
|
+
});
|
|
3071
|
+
|
|
3072
|
+
// src/schemas/node-preset.ts
|
|
3073
|
+
import { z as z16 } from "zod";
|
|
3074
|
+
var NODE_PRESET_CODE_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
3075
|
+
var NodePresetScopeRefine = (val, ctx) => {
|
|
3076
|
+
if (val.scope === "project" && !val.projectId) {
|
|
3077
|
+
ctx.addIssue({
|
|
3078
|
+
code: "custom",
|
|
3079
|
+
path: ["projectId"],
|
|
3080
|
+
message: "scope=project \u65F6 projectId \u5FC5\u586B"
|
|
3081
|
+
});
|
|
3082
|
+
}
|
|
3083
|
+
};
|
|
3084
|
+
var uniqueNamesSchema = z16.array(z16.string().min(1)).superRefine((names, ctx) => {
|
|
3085
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3086
|
+
names.forEach((name, index) => {
|
|
3087
|
+
if (seen.has(name)) {
|
|
3088
|
+
ctx.addIssue({ code: "custom", path: [index], message: `\u540D\u79F0\u91CD\u590D\uFF1A${name}` });
|
|
3089
|
+
}
|
|
3090
|
+
seen.add(name);
|
|
3091
|
+
});
|
|
3092
|
+
});
|
|
3093
|
+
var NodePresetShapeSchema = z16.object({
|
|
3094
|
+
id: z16.string().optional(),
|
|
3095
|
+
code: z16.string().regex(NODE_PRESET_CODE_PATTERN, "kebab-case only"),
|
|
3096
|
+
label: z16.string().min(1),
|
|
3097
|
+
/** 组装默认节点标识(模板图结构域;字符集同 NODE_ID_PATTERN——节点记录点路径拼接安全前提) */
|
|
3098
|
+
nodeId: z16.string().regex(/^[A-Za-z0-9_-]+$/, "nodeId \u53EA\u5141\u8BB8\u5B57\u6BCD/\u6570\u5B57/\u4E0B\u5212\u7EBF/\u8FDE\u5B57\u7B26"),
|
|
3099
|
+
/** 运行时注册表判定(对齐 dag_phase/dag_track 放宽策略——Zod 只做类型级) */
|
|
3100
|
+
phase: z16.string(),
|
|
3101
|
+
track: z16.string(),
|
|
3102
|
+
prompt: z16.string().min(1),
|
|
3103
|
+
/** 绑定技能名单(by-name 引用;版本要求只存在于 npm 分发层 manifest,不侵入运行链路) */
|
|
3104
|
+
skills: uniqueNamesSchema,
|
|
3105
|
+
/** 节点执行时可调用的 Agent 名单(by-name 引用;正文仍由独立 Agent 资产承载) */
|
|
3106
|
+
agents: uniqueNamesSchema,
|
|
3107
|
+
scope: z16.enum(["global", "project"]),
|
|
3108
|
+
projectId: z16.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex").optional(),
|
|
3109
|
+
description: z16.string().optional(),
|
|
3110
|
+
version: z16.string(),
|
|
3111
|
+
/** 分发来源标注(flow install 落库时填充包名;手工创建无此字段——「来源可见可核查」信任模型) */
|
|
3112
|
+
source: z16.object({ package: z16.string().min(1) }).optional(),
|
|
3113
|
+
/** 资产启停(可选——存量无此字段 = 启用;唯一写入口 POST /:code/enabled,不入 Create/Update/Copy) */
|
|
3114
|
+
enabled: z16.boolean().optional(),
|
|
3115
|
+
createdAt: z16.date().optional(),
|
|
3116
|
+
updatedAt: z16.date().optional()
|
|
3117
|
+
});
|
|
3118
|
+
var NodePresetSchema = NodePresetShapeSchema.superRefine(NodePresetScopeRefine);
|
|
3119
|
+
var NodePresetCreateSchema = NodePresetShapeSchema.omit({
|
|
3120
|
+
id: true,
|
|
3121
|
+
createdAt: true,
|
|
3122
|
+
updatedAt: true,
|
|
3123
|
+
enabled: true
|
|
3124
|
+
}).extend({
|
|
3125
|
+
skills: uniqueNamesSchema.default([]),
|
|
3126
|
+
agents: uniqueNamesSchema.default([]),
|
|
3127
|
+
version: z16.string().default("1.0.0")
|
|
3128
|
+
}).superRefine(NodePresetScopeRefine);
|
|
3129
|
+
var NodePresetUpdateSchema = NodePresetShapeSchema.omit({
|
|
3130
|
+
id: true,
|
|
3131
|
+
createdAt: true,
|
|
3132
|
+
updatedAt: true,
|
|
3133
|
+
enabled: true
|
|
3134
|
+
}).extend({
|
|
3135
|
+
version: z16.string().optional()
|
|
3136
|
+
}).partial().superRefine(NodePresetScopeRefine);
|
|
3137
|
+
var NodePresetCopySchema = z16.object({
|
|
3138
|
+
newCode: z16.string().regex(NODE_PRESET_CODE_PATTERN, "kebab-case only"),
|
|
3139
|
+
newScope: z16.enum(["global", "project"]),
|
|
3140
|
+
targetProjectId: z16.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex").optional()
|
|
3141
|
+
}).superRefine((val, ctx) => {
|
|
3142
|
+
if (val.newScope === "project" && !val.targetProjectId) {
|
|
3143
|
+
ctx.addIssue({
|
|
3144
|
+
code: "custom",
|
|
3145
|
+
path: ["targetProjectId"],
|
|
3146
|
+
message: "newScope=project \u65F6 targetProjectId \u5FC5\u586B"
|
|
3147
|
+
});
|
|
3148
|
+
}
|
|
3149
|
+
});
|
|
3150
|
+
|
|
3151
|
+
// src/schemas/node-library.ts
|
|
3152
|
+
import { z as z17 } from "zod";
|
|
3153
|
+
var CompositionEdgeSchema = z17.object({
|
|
3154
|
+
from: z17.string().min(1),
|
|
3155
|
+
to: z17.string().min(1),
|
|
3156
|
+
condition: z17.string().optional(),
|
|
3157
|
+
pausePoint: EdgePausePointSchema.optional()
|
|
3158
|
+
});
|
|
3159
|
+
var CompositionSchema = z17.object({
|
|
3160
|
+
name: z17.string().min(1),
|
|
3161
|
+
description: z17.string().optional(),
|
|
3162
|
+
nodes: z17.array(z17.string().min(1)).min(1),
|
|
3163
|
+
edges: z17.array(CompositionEdgeSchema)
|
|
3164
|
+
});
|
|
3165
|
+
var NodeLibrarySchema = z17.object({
|
|
3166
|
+
id: z17.string().optional(),
|
|
3167
|
+
/** 库名 = npm 包名(同库重装按名 upsert 覆盖登记) */
|
|
3168
|
+
name: z17.string().min(1),
|
|
3169
|
+
version: z17.string(),
|
|
3170
|
+
scope: z17.enum(["global", "project"]),
|
|
3171
|
+
projectId: z17.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex").optional(),
|
|
3172
|
+
/** 已装节点 code 清单(flow compose 逐 code 解析 node_presets) */
|
|
3173
|
+
nodes: z17.array(z17.string()),
|
|
3174
|
+
compositions: z17.array(CompositionSchema),
|
|
3175
|
+
installedAt: z17.date().optional()
|
|
3176
|
+
});
|
|
3177
|
+
var NodeLibraryUpsertSchema = z17.object({
|
|
3178
|
+
name: z17.string().min(1),
|
|
3179
|
+
version: z17.string(),
|
|
3180
|
+
scope: z17.enum(["global", "project"]),
|
|
3181
|
+
projectId: z17.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex").optional(),
|
|
3182
|
+
nodes: z17.array(z17.string()),
|
|
3183
|
+
compositions: z17.array(CompositionSchema)
|
|
3184
|
+
}).superRefine((val, ctx) => {
|
|
3185
|
+
if (val.scope === "project" && !val.projectId) {
|
|
3186
|
+
ctx.addIssue({
|
|
3187
|
+
code: "custom",
|
|
3188
|
+
path: ["projectId"],
|
|
3189
|
+
message: "scope=project \u65F6 projectId \u5FC5\u586B"
|
|
3190
|
+
});
|
|
3191
|
+
}
|
|
3192
|
+
});
|
|
3193
|
+
|
|
3194
|
+
// src/schemas/template-upgrade.ts
|
|
3195
|
+
import { z as z18 } from "zod";
|
|
3196
|
+
var UpgradeNodeStatusSchema = z18.enum([
|
|
3197
|
+
"updatable",
|
|
3198
|
+
"local-modified",
|
|
3199
|
+
"up-to-date",
|
|
3200
|
+
"source-missing",
|
|
3201
|
+
"source-disabled"
|
|
3202
|
+
]);
|
|
3203
|
+
var UpgradeActionSchema = z18.enum(["upgrade", "keep", "skip"]);
|
|
3204
|
+
var UpgradeDecisionSchema = z18.object({
|
|
3205
|
+
nodeId: z18.string().min(1),
|
|
3206
|
+
action: UpgradeActionSchema,
|
|
3207
|
+
expected: z18.object({ status: UpgradeNodeStatusSchema }).optional()
|
|
3208
|
+
});
|
|
3209
|
+
var UpgradeApplyRequestSchema = z18.object({
|
|
3210
|
+
decisions: z18.array(UpgradeDecisionSchema).min(1)
|
|
2368
3211
|
});
|
|
2369
3212
|
|
|
2370
3213
|
// src/config/server-config.ts
|
|
2371
|
-
import { z as
|
|
2372
|
-
var SimingLogLevelSchema =
|
|
2373
|
-
var SimingConfigSchema =
|
|
2374
|
-
mongoUri:
|
|
2375
|
-
port:
|
|
3214
|
+
import { z as z19 } from "zod";
|
|
3215
|
+
var SimingLogLevelSchema = z19.enum(["debug", "info", "warn", "error"]);
|
|
3216
|
+
var SimingConfigSchema = z19.object({
|
|
3217
|
+
mongoUri: z19.string().min(1).default("mongodb://localhost:27017"),
|
|
3218
|
+
port: z19.coerce.number().int().min(1).max(65535).default(7777),
|
|
2376
3219
|
/**
|
|
2377
3220
|
* N018 D8:默认仅绑定本机回环(安全默认);远程/局域接入(F7)显式配置 host。
|
|
2378
3221
|
* 超出 PRD V1 配置清单的新增项,依据见技术方案 §3.4「超范围扩展标注」。
|
|
2379
3222
|
*/
|
|
2380
|
-
host:
|
|
3223
|
+
host: z19.string().min(1).default("127.0.0.1"),
|
|
2381
3224
|
logLevel: SimingLogLevelSchema.default("info"),
|
|
2382
3225
|
/**
|
|
2383
|
-
* F6
|
|
2384
|
-
*
|
|
3226
|
+
* T202608310001:鉴权开关(F1)——从 F6 预留宽松形态收紧为类型化 schema。
|
|
3227
|
+
* 默认关闭 = 本地开发/存量行为零破坏;云主机部署显式开启(SIMING_AUTH_ENABLED=true)。
|
|
2385
3228
|
*/
|
|
2386
|
-
auth:
|
|
2387
|
-
|
|
3229
|
+
auth: z19.object({
|
|
3230
|
+
enabled: z19.boolean().default(false)
|
|
3231
|
+
}).prefault({}),
|
|
3232
|
+
cors: z19.unknown().optional()
|
|
2388
3233
|
});
|
|
2389
|
-
var SIMING_CONFIG_KEYS = ["mongoUri", "port", "host", "logLevel"];
|
|
3234
|
+
var SIMING_CONFIG_KEYS = ["mongoUri", "port", "host", "logLevel", "auth"];
|
|
2390
3235
|
var SIMING_CONFIG_ENV_KEYS = {
|
|
2391
3236
|
mongoUri: "SIMING_MONGO_URI",
|
|
2392
3237
|
port: "PORT",
|
|
2393
3238
|
host: "SIMING_HOST",
|
|
2394
|
-
logLevel: "SIMING_LOG_LEVEL"
|
|
3239
|
+
logLevel: "SIMING_LOG_LEVEL",
|
|
3240
|
+
auth: "SIMING_AUTH_ENABLED"
|
|
2395
3241
|
};
|
|
3242
|
+
function parseAuthEnabledEnvValue(raw) {
|
|
3243
|
+
if (raw === "true") return true;
|
|
3244
|
+
if (raw === "false") return false;
|
|
3245
|
+
throw new Error(`SIMING_AUTH_ENABLED \u4EC5\u63A5\u53D7 true/false\uFF0C\u6536\u5230: ${raw}`);
|
|
3246
|
+
}
|
|
2396
3247
|
|
|
2397
3248
|
// src/version-bump.ts
|
|
2398
3249
|
function tryBumpPatch(version) {
|
|
@@ -2407,6 +3258,266 @@ function bumpPatchOrPassthrough(version) {
|
|
|
2407
3258
|
return tryBumpPatch(version) ?? version;
|
|
2408
3259
|
}
|
|
2409
3260
|
|
|
3261
|
+
// src/content-hash.ts
|
|
3262
|
+
import { createHash as createHash2 } from "crypto";
|
|
3263
|
+
function computeNodeContentHash(node) {
|
|
3264
|
+
const canonical = JSON.stringify({
|
|
3265
|
+
label: node.label,
|
|
3266
|
+
phase: node.phase,
|
|
3267
|
+
track: node.track,
|
|
3268
|
+
prompt: node.prompt,
|
|
3269
|
+
skills: [...node.skills].sort()
|
|
3270
|
+
});
|
|
3271
|
+
return createHash2("sha256").update(canonical).digest("hex").slice(0, 16);
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3274
|
+
// src/template-content-diff.ts
|
|
3275
|
+
function stableStringify(value) {
|
|
3276
|
+
if (value === null || typeof value !== "object") {
|
|
3277
|
+
return JSON.stringify(value) ?? "null";
|
|
3278
|
+
}
|
|
3279
|
+
if (Array.isArray(value)) {
|
|
3280
|
+
return `[${value.map((v) => v === void 0 ? "null" : stableStringify(v)).join(",")}]`;
|
|
3281
|
+
}
|
|
3282
|
+
const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
3283
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(",")}}`;
|
|
3284
|
+
}
|
|
3285
|
+
function sameAgentNames(a, b) {
|
|
3286
|
+
return stableStringify([...a ?? []].sort()) === stableStringify([...b ?? []].sort());
|
|
3287
|
+
}
|
|
3288
|
+
function edgeKey(e) {
|
|
3289
|
+
return JSON.stringify([e.from, e.to, e.condition ?? null, e.pausePoint ?? null]);
|
|
3290
|
+
}
|
|
3291
|
+
function diffTemplateContent(imported, existing) {
|
|
3292
|
+
const importedById = new Map(imported.nodes.map((n) => [n.id, n]));
|
|
3293
|
+
const existingById = new Map(existing.nodes.map((n) => [n.id, n]));
|
|
3294
|
+
const nodesAdded = [...importedById.keys()].filter((id) => !existingById.has(id)).sort();
|
|
3295
|
+
const nodesRemoved = [...existingById.keys()].filter((id) => !importedById.has(id)).sort();
|
|
3296
|
+
const nodesChanged = [...importedById.keys()].filter((id) => {
|
|
3297
|
+
const a = importedById.get(id);
|
|
3298
|
+
const b = existingById.get(id);
|
|
3299
|
+
if (a === void 0 || b === void 0) return false;
|
|
3300
|
+
const normA = { ...a, skills: a.skills ?? [] };
|
|
3301
|
+
const normB = { ...b, skills: b.skills ?? [] };
|
|
3302
|
+
return computeNodeContentHash(normA) !== computeNodeContentHash(normB) || !sameAgentNames(a.agents, b.agents) || !sameAgentNames(a.sourcePreset?.agents, b.sourcePreset?.agents);
|
|
3303
|
+
}).sort();
|
|
3304
|
+
const edgesChanged = stableStringify([...imported.edges].map(edgeKey).sort()) !== stableStringify([...existing.edges].map(edgeKey).sort());
|
|
3305
|
+
const descriptionChanged = imported.description !== existing.description;
|
|
3306
|
+
const layoutChanged = stableStringify(imported.layout ?? {}) !== stableStringify(existing.layout ?? {});
|
|
3307
|
+
const identical = imported.name === existing.name && !descriptionChanged && nodesAdded.length === 0 && nodesRemoved.length === 0 && nodesChanged.length === 0 && !edgesChanged && !layoutChanged;
|
|
3308
|
+
return {
|
|
3309
|
+
identical,
|
|
3310
|
+
changes: { nodesAdded, nodesRemoved, nodesChanged, edgesChanged, descriptionChanged, layoutChanged }
|
|
3311
|
+
};
|
|
3312
|
+
}
|
|
3313
|
+
|
|
3314
|
+
// src/template-upgrade.ts
|
|
3315
|
+
function presetContent(preset) {
|
|
3316
|
+
return {
|
|
3317
|
+
label: preset.label,
|
|
3318
|
+
phase: preset.phase,
|
|
3319
|
+
track: preset.track,
|
|
3320
|
+
prompt: preset.prompt,
|
|
3321
|
+
skills: preset.skills
|
|
3322
|
+
};
|
|
3323
|
+
}
|
|
3324
|
+
var EMPTY_DIFF = {
|
|
3325
|
+
prompt: null,
|
|
3326
|
+
label: null,
|
|
3327
|
+
phase: null,
|
|
3328
|
+
track: null,
|
|
3329
|
+
skills: null,
|
|
3330
|
+
agents: null
|
|
3331
|
+
};
|
|
3332
|
+
function diffNames(beforeNames, afterNames) {
|
|
3333
|
+
const before = new Set(beforeNames);
|
|
3334
|
+
const after = new Set(afterNames);
|
|
3335
|
+
const added = [...after].filter((name) => !before.has(name)).sort();
|
|
3336
|
+
const removed = [...before].filter((name) => !after.has(name)).sort();
|
|
3337
|
+
return added.length === 0 && removed.length === 0 ? null : { added, removed };
|
|
3338
|
+
}
|
|
3339
|
+
function sameNames(left, right) {
|
|
3340
|
+
return JSON.stringify([...left].sort()) === JSON.stringify([...right].sort());
|
|
3341
|
+
}
|
|
3342
|
+
function diffNodeAgainstPreset(node, preset) {
|
|
3343
|
+
return {
|
|
3344
|
+
prompt: node.prompt === preset.prompt ? null : "changed",
|
|
3345
|
+
label: node.label === preset.label ? null : "changed",
|
|
3346
|
+
phase: node.phase === preset.phase ? null : "changed",
|
|
3347
|
+
track: node.track === preset.track ? null : "changed",
|
|
3348
|
+
skills: diffNames(node.skills, preset.skills),
|
|
3349
|
+
agents: diffNames(node.agents ?? [], preset.agents ?? [])
|
|
3350
|
+
};
|
|
3351
|
+
}
|
|
3352
|
+
function computeUpgradeStatus(node, latest) {
|
|
3353
|
+
const source = node.sourcePreset;
|
|
3354
|
+
if (!source) return { status: "up-to-date", localModified: false };
|
|
3355
|
+
const agentsModified = !sameNames(node.agents ?? [], source.agents ?? []);
|
|
3356
|
+
const localModified = computeNodeContentHash(node) !== source.contentHash || agentsModified;
|
|
3357
|
+
if (!latest) return { status: "source-missing", localModified };
|
|
3358
|
+
if (latest.enabled === false) return { status: "source-disabled", localModified };
|
|
3359
|
+
if (localModified) return { status: "local-modified", localModified };
|
|
3360
|
+
return latest.version !== source.version ? { status: "updatable", localModified: false } : { status: "up-to-date", localModified: false };
|
|
3361
|
+
}
|
|
3362
|
+
function buildUpgradePlan(template, presetsByCode) {
|
|
3363
|
+
const items = [];
|
|
3364
|
+
for (const node of template.nodes) {
|
|
3365
|
+
const source = node.sourcePreset;
|
|
3366
|
+
if (!source) continue;
|
|
3367
|
+
const latest = presetsByCode.get(source.code) ?? null;
|
|
3368
|
+
const { status, localModified } = computeUpgradeStatus(node, latest);
|
|
3369
|
+
items.push({
|
|
3370
|
+
nodeId: node.id,
|
|
3371
|
+
source: { code: source.code, version: source.version, contentHash: source.contentHash },
|
|
3372
|
+
latest: latest ? {
|
|
3373
|
+
version: latest.version,
|
|
3374
|
+
contentHash: computeNodeContentHash(presetContent(latest)),
|
|
3375
|
+
enabled: latest.enabled ?? true
|
|
3376
|
+
} : null,
|
|
3377
|
+
status,
|
|
3378
|
+
localModified,
|
|
3379
|
+
diff: latest ? diffNodeAgainstPreset(node, latest) : EMPTY_DIFF
|
|
3380
|
+
});
|
|
3381
|
+
}
|
|
3382
|
+
return {
|
|
3383
|
+
templateVersion: template.version,
|
|
3384
|
+
items,
|
|
3385
|
+
upgradableCount: items.filter((item) => item.status === "updatable").length
|
|
3386
|
+
};
|
|
3387
|
+
}
|
|
3388
|
+
function applyUpgradeDecisions(template, presetsByCode, decisions) {
|
|
3389
|
+
const sourceNodeIds = new Set(template.nodes.filter((n) => n.sourcePreset).map((n) => n.id));
|
|
3390
|
+
const decisionMap = /* @__PURE__ */ new Map();
|
|
3391
|
+
const unknown = [];
|
|
3392
|
+
const duplicated = [];
|
|
3393
|
+
for (const decision of decisions) {
|
|
3394
|
+
if (decisionMap.has(decision.nodeId)) duplicated.push(decision.nodeId);
|
|
3395
|
+
if (!sourceNodeIds.has(decision.nodeId)) unknown.push(decision.nodeId);
|
|
3396
|
+
decisionMap.set(decision.nodeId, decision);
|
|
3397
|
+
}
|
|
3398
|
+
if (duplicated.length > 0) {
|
|
3399
|
+
throw new ValidationError(
|
|
3400
|
+
`\u51B3\u7B56\u542B\u91CD\u590D\u8282\u70B9 id\uFF1A${duplicated.join(", ")}`,
|
|
3401
|
+
duplicated.map((id) => ({ path: ["decisions"], message: `\u8282\u70B9 ${id} \u5B58\u5728\u591A\u6761\u51B3\u7B56` }))
|
|
3402
|
+
);
|
|
3403
|
+
}
|
|
3404
|
+
if (unknown.length > 0) {
|
|
3405
|
+
throw new ValidationError(
|
|
3406
|
+
`\u51B3\u7B56\u542B\u6A21\u677F\u5916\u6216\u65E0\u6765\u6E90\u6807\u8BB0\u7684\u8282\u70B9 id\uFF1A${unknown.join(", ")}`,
|
|
3407
|
+
unknown.map((id) => ({ path: ["decisions"], message: `\u8282\u70B9 ${id} \u4E0D\u5728\u53EF\u5347\u7EA7\u8282\u70B9\u6E05\u5355\u4E2D` }))
|
|
3408
|
+
);
|
|
3409
|
+
}
|
|
3410
|
+
const nodes = template.nodes.map((node) => ({
|
|
3411
|
+
...node,
|
|
3412
|
+
skills: [...node.skills],
|
|
3413
|
+
agents: [...node.agents ?? []],
|
|
3414
|
+
...node.sourcePreset ? {
|
|
3415
|
+
sourcePreset: {
|
|
3416
|
+
...node.sourcePreset,
|
|
3417
|
+
...node.sourcePreset.agents ? { agents: [...node.sourcePreset.agents] } : {}
|
|
3418
|
+
}
|
|
3419
|
+
} : {}
|
|
3420
|
+
}));
|
|
3421
|
+
const results = [];
|
|
3422
|
+
const stale = [];
|
|
3423
|
+
let changed = false;
|
|
3424
|
+
for (let i = 0; i < template.nodes.length; i++) {
|
|
3425
|
+
const original = template.nodes[i];
|
|
3426
|
+
const source = original.sourcePreset;
|
|
3427
|
+
if (!source) continue;
|
|
3428
|
+
const working = nodes[i];
|
|
3429
|
+
const latest = presetsByCode.get(source.code) ?? null;
|
|
3430
|
+
const { status, localModified } = computeUpgradeStatus(original, latest);
|
|
3431
|
+
const decision = decisionMap.get(original.id);
|
|
3432
|
+
if (localModified && !decision) {
|
|
3433
|
+
throw new ValidationError(
|
|
3434
|
+
`\u8282\u70B9 ${original.id} \u672C\u5730\u5DF2\u4FEE\u6539\uFF08\u72B6\u6001 ${status}\uFF09\uFF0C\u5FC5\u987B\u663E\u5F0F\u51B3\u7B56\uFF08upgrade/keep/skip\uFF09`,
|
|
3435
|
+
[{ path: ["decisions"], message: `\u8282\u70B9 ${original.id} \u7F3A\u5C11\u51B3\u7B56` }],
|
|
3436
|
+
{
|
|
3437
|
+
bizCode: "UPGRADE_DECISION_REQUIRED",
|
|
3438
|
+
message: `${BIZ_CODE_MESSAGES.UPGRADE_DECISION_REQUIRED}\uFF1A${original.id}`
|
|
3439
|
+
}
|
|
3440
|
+
);
|
|
3441
|
+
}
|
|
3442
|
+
if (localModified && decision?.action === "upgrade" && decision.expected === void 0) {
|
|
3443
|
+
throw new ValidationError(
|
|
3444
|
+
`\u8282\u70B9 ${original.id} \u672C\u5730\u5DF2\u4FEE\u6539\uFF0C\u8986\u76D6\u6027 upgrade \u51B3\u7B56\u5FC5\u987B\u643A\u5E26 expected={status:'local-modified'}\uFF08\u91CD\u65B0 plan \u540E\u5E26 expected \u91CD\u8BD5\uFF09`,
|
|
3445
|
+
[{ path: ["decisions"], message: `\u8282\u70B9 ${original.id} \u51B3\u7B56\u7F3A expected \u57FA\u7EBF` }],
|
|
3446
|
+
{
|
|
3447
|
+
bizCode: "UPGRADE_STALE",
|
|
3448
|
+
message: `${BIZ_CODE_MESSAGES.UPGRADE_STALE}\uFF08${original.id} \u51B3\u7B56\u7F3A expected\uFF09`
|
|
3449
|
+
}
|
|
3450
|
+
);
|
|
3451
|
+
}
|
|
3452
|
+
if (!decision) {
|
|
3453
|
+
results.push({ nodeId: original.id, action: "skip", applied: false, reason: "\u672A\u51B3\u7B56\uFF08\u4FDD\u5B88\u8DF3\u8FC7\uFF09" });
|
|
3454
|
+
continue;
|
|
3455
|
+
}
|
|
3456
|
+
if (decision.expected && decision.expected.status !== status) {
|
|
3457
|
+
stale.push(`${original.id}: ${decision.expected.status} \u2192 ${status}`);
|
|
3458
|
+
}
|
|
3459
|
+
if (status === "source-missing" || status === "source-disabled") {
|
|
3460
|
+
results.push({
|
|
3461
|
+
nodeId: original.id,
|
|
3462
|
+
action: "skip",
|
|
3463
|
+
applied: false,
|
|
3464
|
+
reason: status === "source-missing" ? "\u6765\u6E90\u8282\u70B9\u7F3A\u5931\uFF0C\u6309\u7F3A\u7701 skip \u5904\u7406" : "\u6765\u6E90\u8282\u70B9\u5DF2\u505C\u7528\uFF0C\u6309\u7F3A\u7701 skip \u5904\u7406"
|
|
3465
|
+
});
|
|
3466
|
+
continue;
|
|
3467
|
+
}
|
|
3468
|
+
if (!latest) continue;
|
|
3469
|
+
if (status === "up-to-date") {
|
|
3470
|
+
results.push({
|
|
3471
|
+
nodeId: original.id,
|
|
3472
|
+
action: decision.action,
|
|
3473
|
+
applied: false,
|
|
3474
|
+
reason: "\u5DF2\u662F\u6700\u65B0\uFF08\u65E0\u53D8\u66F4\uFF09"
|
|
3475
|
+
});
|
|
3476
|
+
continue;
|
|
3477
|
+
}
|
|
3478
|
+
if (decision.action === "skip") {
|
|
3479
|
+
results.push({ nodeId: original.id, action: "skip", applied: false });
|
|
3480
|
+
continue;
|
|
3481
|
+
}
|
|
3482
|
+
if (decision.action === "upgrade") {
|
|
3483
|
+
working.label = latest.label;
|
|
3484
|
+
working.phase = latest.phase;
|
|
3485
|
+
working.track = latest.track;
|
|
3486
|
+
working.prompt = latest.prompt;
|
|
3487
|
+
working.skills = [...latest.skills];
|
|
3488
|
+
working.agents = [...latest.agents ?? []];
|
|
3489
|
+
working.sourcePreset = {
|
|
3490
|
+
code: source.code,
|
|
3491
|
+
version: latest.version,
|
|
3492
|
+
contentHash: computeNodeContentHash(presetContent(latest)),
|
|
3493
|
+
agents: [...latest.agents ?? []]
|
|
3494
|
+
};
|
|
3495
|
+
changed = true;
|
|
3496
|
+
results.push({ nodeId: original.id, action: "upgrade", applied: true });
|
|
3497
|
+
} else {
|
|
3498
|
+
working.sourcePreset = {
|
|
3499
|
+
code: source.code,
|
|
3500
|
+
version: latest.version,
|
|
3501
|
+
contentHash: computeNodeContentHash(original),
|
|
3502
|
+
agents: [...original.agents ?? []]
|
|
3503
|
+
};
|
|
3504
|
+
changed = true;
|
|
3505
|
+
results.push({ nodeId: original.id, action: "keep", applied: true });
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3508
|
+
if (stale.length > 0) {
|
|
3509
|
+
throw new ValidationError(
|
|
3510
|
+
`\u6A21\u677F\u72B6\u6001\u4E0E\u5347\u7EA7\u8BA1\u5212\u4E0D\u7B26\uFF08plan \u540E\u5DF2\u88AB\u4FEE\u6539\uFF09\uFF0C\u8BF7\u91CD\u65B0 plan\u3002\u53D8\u5316\uFF1A${stale.join("; ")}`,
|
|
3511
|
+
stale.map((s) => ({ path: ["decisions"], message: s })),
|
|
3512
|
+
{
|
|
3513
|
+
bizCode: "UPGRADE_STALE",
|
|
3514
|
+
message: `${BIZ_CODE_MESSAGES.UPGRADE_STALE}\uFF08${stale.join("; ")}\uFF09`
|
|
3515
|
+
}
|
|
3516
|
+
);
|
|
3517
|
+
}
|
|
3518
|
+
return { nodes, nextVersion: changed ? bumpPatch(template.version) : null, results };
|
|
3519
|
+
}
|
|
3520
|
+
|
|
2410
3521
|
// src/task/entry-id.ts
|
|
2411
3522
|
var ENTRY_ID_LENGTH = 6;
|
|
2412
3523
|
var BASE36_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
@@ -2424,7 +3535,7 @@ function generateEntryId(existing) {
|
|
|
2424
3535
|
|
|
2425
3536
|
// src/template-transfer/payload.ts
|
|
2426
3537
|
function toTemplatePayload(tpl) {
|
|
2427
|
-
const { id: _id, projectId: _projectId, enabled: _enabled, createdAt: _ca, updatedAt: _ua, ...rest } = tpl;
|
|
3538
|
+
const { id: _id, projectId: _projectId, enabled: _enabled, createdAt: _ca, updatedAt: _ua, code: _code, ...rest } = tpl;
|
|
2428
3539
|
return { ...rest };
|
|
2429
3540
|
}
|
|
2430
3541
|
function toSkillPayload(skill) {
|
|
@@ -2615,6 +3726,26 @@ async function buildImportPlan(deps, targetProjectId, bundle) {
|
|
|
2615
3726
|
}
|
|
2616
3727
|
}
|
|
2617
3728
|
}
|
|
3729
|
+
const directSkillChecks = [];
|
|
3730
|
+
const unbundledNames = [...new Set(bundle.template.nodes.flatMap((n) => n.skills).filter((s) => !bundledSkillNames.has(s)))];
|
|
3731
|
+
const envVisibleSkills = unbundledNames.length > 0 ? await deps.skillRepo.listSkillsByNames(unbundledNames, "project", targetProjectId) : /* @__PURE__ */ new Map();
|
|
3732
|
+
for (const node of bundle.template.nodes) {
|
|
3733
|
+
for (const skillName of node.skills) {
|
|
3734
|
+
if (bundledSkillNames.has(skillName) || envVisibleSkills.has(skillName)) {
|
|
3735
|
+
directSkillChecks.push({ node: node.id, skill: skillName, status: "ok" });
|
|
3736
|
+
} else {
|
|
3737
|
+
directSkillChecks.push({ node: node.id, skill: skillName, status: "missing" });
|
|
3738
|
+
warnings.push(`\u6A21\u677F\u8282\u70B9 ${node.id} \u76F4\u63A5\u5F15\u7528\u6280\u80FD "${skillName}" \u4E0D\u5728\u5BFC\u51FA\u5305\u4E2D\u4E14\u76EE\u6807\u73AF\u5883\u4E0D\u5B58\u5728\uFF08\u7ED1\u5B9A\u60AC\u7A7A\uFF09`);
|
|
3739
|
+
}
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
const presetCodes = [...new Set(bundle.template.nodes.flatMap((n) => n.sourcePreset ? [n.sourcePreset.code] : []))];
|
|
3743
|
+
const presetsByCode = presetCodes.length > 0 ? await deps.nodePresetRepo.listByCodes(presetCodes) : /* @__PURE__ */ new Map();
|
|
3744
|
+
for (const node of bundle.template.nodes) {
|
|
3745
|
+
if (node.sourcePreset && !presetsByCode.has(node.sourcePreset.code)) {
|
|
3746
|
+
warnings.push(`\u6A21\u677F\u8282\u70B9 ${node.id} \u7684\u6765\u6E90\u8282\u70B9 "${node.sourcePreset.code}" \u5728\u76EE\u6807\u73AF\u5883\u4E0D\u5B58\u5728\uFF0C\u5BFC\u5165\u5C06\u5265\u9664\u6765\u6E90\u6807\u8BB0\uFF08\u964D\u7EA7\u81EA\u5EFA\u8282\u70B9\uFF09`);
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
2618
3749
|
return {
|
|
2619
3750
|
template: {
|
|
2620
3751
|
name: bundle.template.name,
|
|
@@ -2626,6 +3757,7 @@ async function buildImportPlan(deps, targetProjectId, bundle) {
|
|
|
2626
3757
|
...bundle.template.isDefault && !isDefaultEffect ? { downgradeReason: "project_has_default" } : {}
|
|
2627
3758
|
},
|
|
2628
3759
|
dependencies: [...skillChecks, ...agentChecks, ...aliasChecks],
|
|
3760
|
+
directSkillChecks,
|
|
2629
3761
|
warnings
|
|
2630
3762
|
};
|
|
2631
3763
|
}
|
|
@@ -2637,15 +3769,27 @@ async function applyImport(deps, targetProjectId, bundle, decisions) {
|
|
|
2637
3769
|
const projectHasDefault = await hasProjectDefault(deps, targetProjectId);
|
|
2638
3770
|
const isDefaultEffect = bundle.template.isDefault && !projectHasDefault;
|
|
2639
3771
|
const tplPayload = bundle.template;
|
|
2640
|
-
const
|
|
3772
|
+
const sourceCodes = [...new Set(tplPayload.nodes.flatMap((n) => n.sourcePreset ? [n.sourcePreset.code] : []))];
|
|
3773
|
+
const presetsByCode = sourceCodes.length > 0 ? await deps.nodePresetRepo.listByCodes(sourceCodes) : /* @__PURE__ */ new Map();
|
|
3774
|
+
const sourceStripped = [];
|
|
3775
|
+
const nodesForWrite = tplPayload.nodes.map((node) => {
|
|
3776
|
+
if (node.sourcePreset && !presetsByCode.has(node.sourcePreset.code)) {
|
|
3777
|
+
sourceStripped.push(node.id);
|
|
3778
|
+
const { sourcePreset: _stripped, ...rest } = node;
|
|
3779
|
+
return rest;
|
|
3780
|
+
}
|
|
3781
|
+
return node;
|
|
3782
|
+
});
|
|
3783
|
+
const createInput = { ...tplPayload, projectId: targetProjectId, isDefault: isDefaultEffect, nodes: nodesForWrite };
|
|
2641
3784
|
const parsed = DagTemplateCreateSchema.safeParse(createInput);
|
|
2642
3785
|
if (!parsed.success) {
|
|
2643
3786
|
throw new ValidationError("\u6A21\u677F\u5185\u5BB9\u6821\u9A8C\u5931\u8D25\uFF08\u5BFC\u5165\u6587\u4EF6\u5B57\u6BB5\u975E\u6CD5\uFF09", parsed.error.issues);
|
|
2644
3787
|
}
|
|
3788
|
+
assertUniqueNodeIds(nodesForWrite);
|
|
2645
3789
|
const contentFields = {
|
|
2646
3790
|
name: tplPayload.name,
|
|
2647
3791
|
description: tplPayload.description,
|
|
2648
|
-
nodes:
|
|
3792
|
+
nodes: nodesForWrite,
|
|
2649
3793
|
edges: tplPayload.edges,
|
|
2650
3794
|
version: tplPayload.version,
|
|
2651
3795
|
isDefault: isDefaultEffect,
|
|
@@ -2794,7 +3938,7 @@ async function applyImport(deps, targetProjectId, bundle, decisions) {
|
|
|
2794
3938
|
results.push({ kind: "modelAlias", name: payload.code, action: "failed", message: errorMessage(err) });
|
|
2795
3939
|
}
|
|
2796
3940
|
}
|
|
2797
|
-
return { template: templateResult, dependencies: results };
|
|
3941
|
+
return { template: templateResult, dependencies: results, sourceStripped };
|
|
2798
3942
|
}
|
|
2799
3943
|
function decide(status, decision) {
|
|
2800
3944
|
if (status === "identical") return "identical";
|
|
@@ -2812,7 +3956,7 @@ function errorMessage(err) {
|
|
|
2812
3956
|
}
|
|
2813
3957
|
|
|
2814
3958
|
// src/index.ts
|
|
2815
|
-
var PackageJsonSchema =
|
|
3959
|
+
var PackageJsonSchema = z20.object({ version: z20.string().min(1) });
|
|
2816
3960
|
var packageJsonParsed = PackageJsonSchema.safeParse(
|
|
2817
3961
|
JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"))
|
|
2818
3962
|
);
|
|
@@ -2826,6 +3970,7 @@ function ping() {
|
|
|
2826
3970
|
export {
|
|
2827
3971
|
AGENT_MAIN_DOC,
|
|
2828
3972
|
ARTIFACT_TYPES,
|
|
3973
|
+
AUTH_COLLECTIONS,
|
|
2829
3974
|
AdvanceRequestSchema,
|
|
2830
3975
|
AdvanceResponseSchema,
|
|
2831
3976
|
AgentCopySchema,
|
|
@@ -2842,11 +3987,21 @@ export {
|
|
|
2842
3987
|
ArchNoteSchema,
|
|
2843
3988
|
ArtifactSchema,
|
|
2844
3989
|
AssetSetEnabledSchema,
|
|
3990
|
+
AuthAccountCreateSchema,
|
|
3991
|
+
AuthAccountSchema,
|
|
3992
|
+
AuthStatusSchema,
|
|
3993
|
+
AuthTokenGenResponseSchema,
|
|
3994
|
+
AuthTokenSchema,
|
|
3995
|
+
AuthTokenStatusSchema,
|
|
3996
|
+
AuthTokenTypeSchema,
|
|
3997
|
+
AuthWhoamiSchema,
|
|
2845
3998
|
BIZ_CODE_MESSAGES,
|
|
2846
3999
|
BadRequestError,
|
|
2847
4000
|
CONTEXT_VIEWS,
|
|
2848
4001
|
CancelRequestSchema,
|
|
2849
4002
|
CheckItemSchema,
|
|
4003
|
+
CompositionEdgeSchema,
|
|
4004
|
+
CompositionSchema,
|
|
2850
4005
|
ConfirmationSchema,
|
|
2851
4006
|
ConflictError,
|
|
2852
4007
|
ContextViewSchema,
|
|
@@ -2855,6 +4010,7 @@ export {
|
|
|
2855
4010
|
DAG_TRACKS,
|
|
2856
4011
|
DEFAULT_PROJECT_KEY,
|
|
2857
4012
|
DagEdgeSchema,
|
|
4013
|
+
DagInstanceNodeSchema,
|
|
2858
4014
|
DagInstanceSchema,
|
|
2859
4015
|
DagNodePhaseSchema,
|
|
2860
4016
|
DagNodeSchema,
|
|
@@ -2877,6 +4033,7 @@ export {
|
|
|
2877
4033
|
EnumRegistrySchema,
|
|
2878
4034
|
EnumRegistryUpdateSchema,
|
|
2879
4035
|
ExportBundleSchema,
|
|
4036
|
+
ForbiddenError,
|
|
2880
4037
|
HistoryActionSchema,
|
|
2881
4038
|
HistoryEntrySchema,
|
|
2882
4039
|
ImportApplyRequestSchema,
|
|
@@ -2884,6 +4041,7 @@ export {
|
|
|
2884
4041
|
ImportDecisionSchema,
|
|
2885
4042
|
ImportPlanRequestSchema,
|
|
2886
4043
|
ImportPlanResponseSchema,
|
|
4044
|
+
LoginSchema,
|
|
2887
4045
|
ModelAliasCreateSchema,
|
|
2888
4046
|
ModelAliasPayloadSchema,
|
|
2889
4047
|
ModelAliasSchema,
|
|
@@ -2891,14 +4049,23 @@ export {
|
|
|
2891
4049
|
ModelAliasUpdateSchema,
|
|
2892
4050
|
ModelAliasWithRefCountSchema,
|
|
2893
4051
|
NODE_ID_PATTERN,
|
|
4052
|
+
NODE_PRESET_CODE_PATTERN,
|
|
2894
4053
|
NODE_STATUSES,
|
|
2895
4054
|
NodeInfoSchema,
|
|
4055
|
+
NodeLibrarySchema,
|
|
4056
|
+
NodeLibraryUpsertSchema,
|
|
4057
|
+
NodePresetCopySchema,
|
|
4058
|
+
NodePresetCreateSchema,
|
|
4059
|
+
NodePresetSchema,
|
|
4060
|
+
NodePresetShapeSchema,
|
|
4061
|
+
NodePresetUpdateSchema,
|
|
2896
4062
|
NodeRecordSchema,
|
|
2897
4063
|
NodeStateSchema,
|
|
2898
4064
|
NodeStatusSchema,
|
|
2899
4065
|
NotFoundError,
|
|
2900
4066
|
OBJECT_ID_HEX,
|
|
2901
4067
|
PAUSE_POINT_TYPES,
|
|
4068
|
+
PasswordChangeSchema,
|
|
2902
4069
|
PausePointTypeSchema,
|
|
2903
4070
|
PauseRequestSchema,
|
|
2904
4071
|
ProjectCreateSchema,
|
|
@@ -2921,9 +4088,15 @@ export {
|
|
|
2921
4088
|
SkillScopeSchema,
|
|
2922
4089
|
SkillShapeSchema,
|
|
2923
4090
|
SkillUpdateSchema,
|
|
4091
|
+
SourcePresetSchema,
|
|
2924
4092
|
TASK_PHASES,
|
|
2925
4093
|
TASK_STATUSES,
|
|
2926
4094
|
TASK_TYPES,
|
|
4095
|
+
TEMPLATE_CODE_MAX_LENGTH,
|
|
4096
|
+
TEMPLATE_CODE_REGEX,
|
|
4097
|
+
TaskBatchAckSchema,
|
|
4098
|
+
TaskBatchEntrySchema,
|
|
4099
|
+
TaskBatchFailureSchema,
|
|
2927
4100
|
TaskContextSchema,
|
|
2928
4101
|
TaskCreateInputSchema,
|
|
2929
4102
|
TaskDocContentSchema,
|
|
@@ -2934,7 +4107,14 @@ export {
|
|
|
2934
4107
|
TaskSchema,
|
|
2935
4108
|
TaskStatusSchema,
|
|
2936
4109
|
TaskSummarySchema,
|
|
4110
|
+
TemplateCandidateSchema,
|
|
4111
|
+
TemplateCodeFieldSchema,
|
|
2937
4112
|
TemplatePayloadSchema,
|
|
4113
|
+
UnauthorizedError,
|
|
4114
|
+
UpgradeActionSchema,
|
|
4115
|
+
UpgradeApplyRequestSchema,
|
|
4116
|
+
UpgradeDecisionSchema,
|
|
4117
|
+
UpgradeNodeStatusSchema,
|
|
2938
4118
|
VERSION,
|
|
2939
4119
|
ValidationError,
|
|
2940
4120
|
WRITE_ACK_ENTRY_KINDS,
|
|
@@ -2942,38 +4122,58 @@ export {
|
|
|
2942
4122
|
WriteAckSchema,
|
|
2943
4123
|
advanceTask,
|
|
2944
4124
|
applyImport,
|
|
4125
|
+
applyUpgradeDecisions,
|
|
2945
4126
|
approveTask,
|
|
2946
4127
|
assertBoundSkillsCompatible,
|
|
2947
4128
|
assertModelAliasExists,
|
|
2948
4129
|
assertReferenceAggregateLimit,
|
|
2949
4130
|
assertReferencesDeletable,
|
|
4131
|
+
assertUniqueNodeIds,
|
|
2950
4132
|
buildImportPlan,
|
|
4133
|
+
buildTemplateCode,
|
|
4134
|
+
buildUpgradePlan,
|
|
2951
4135
|
bumpPatch,
|
|
2952
4136
|
bumpPatchOrPassthrough,
|
|
2953
4137
|
cancelTask,
|
|
2954
4138
|
canonicalizeReferencePath,
|
|
2955
4139
|
checkTermination,
|
|
2956
4140
|
composeExportBundle,
|
|
4141
|
+
computeNodeContentHash,
|
|
4142
|
+
computeUpgradeStatus,
|
|
2957
4143
|
createAgentRepo,
|
|
4144
|
+
createAuthAccountRepo,
|
|
4145
|
+
createAuthSessionRepo,
|
|
4146
|
+
createAuthTokenRepo,
|
|
2958
4147
|
createDagTemplateRepo,
|
|
2959
4148
|
createEnumRegistryRepo,
|
|
2960
4149
|
createModelAliasRepo,
|
|
2961
4150
|
createMongoClient,
|
|
4151
|
+
createNodeLibraryRepo,
|
|
4152
|
+
createNodePresetRepo,
|
|
2962
4153
|
createProjectRepo,
|
|
2963
4154
|
createReferencesSchema,
|
|
2964
4155
|
createRepo,
|
|
2965
4156
|
createSkillRepo,
|
|
2966
4157
|
createTaskRepo,
|
|
2967
4158
|
decisionKey,
|
|
4159
|
+
diffTemplateContent,
|
|
2968
4160
|
enabledNeFalse,
|
|
2969
4161
|
escapeRegExpLiteral,
|
|
2970
4162
|
excerpt,
|
|
4163
|
+
findDuplicateNodeIds,
|
|
2971
4164
|
findNextEdge,
|
|
2972
4165
|
findTemplateByName,
|
|
2973
4166
|
generateEntryId,
|
|
2974
4167
|
generateProjectKey,
|
|
4168
|
+
generateSessionId,
|
|
4169
|
+
generateToken,
|
|
2975
4170
|
hasProjectDefault,
|
|
4171
|
+
hashPassword,
|
|
4172
|
+
hashToken,
|
|
4173
|
+
isLegalTemplateCode,
|
|
2976
4174
|
mkHistory,
|
|
4175
|
+
normalizeNameToTemplateCode,
|
|
4176
|
+
parseAuthEnabledEnvValue,
|
|
2977
4177
|
pauseTask,
|
|
2978
4178
|
ping,
|
|
2979
4179
|
pruneInstance,
|
|
@@ -2983,6 +4183,8 @@ export {
|
|
|
2983
4183
|
taskProgress,
|
|
2984
4184
|
toNodeInfo,
|
|
2985
4185
|
toTaskPublic,
|
|
4186
|
+
tokenTypeFromValue,
|
|
2986
4187
|
trackMatchSet,
|
|
4188
|
+
verifyPassword,
|
|
2987
4189
|
withTransaction
|
|
2988
4190
|
};
|