oasis_test 0.1.11 → 0.1.13
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.js +1169 -553
- package/package.json +10 -4
package/dist/index.js
CHANGED
|
@@ -73,6 +73,8 @@ function fold(model, op) {
|
|
|
73
73
|
...p2.description !== void 0 ? { description: p2.description } : {},
|
|
74
74
|
...p2.docType !== void 0 ? { docType: p2.docType } : {},
|
|
75
75
|
...p2.parts !== void 0 ? { parts: p2.parts } : {},
|
|
76
|
+
// 浅拷贝 fields:edit 会 {...a.fields,...} 重建,但 spawn 这份不能别名日志 payload
|
|
77
|
+
...p2.fields !== void 0 ? { fields: { ...p2.fields } } : {},
|
|
76
78
|
// 深拷贝:投影状态绝不别名日志数据(边后续会被 link/bump 改写,日志必须不可变)
|
|
77
79
|
inputs: (p2.inputs ?? []).map((e) => ({ ...e }))
|
|
78
80
|
});
|
|
@@ -90,6 +92,7 @@ function fold(model, op) {
|
|
|
90
92
|
if (p2.title !== void 0) a.title = p2.title;
|
|
91
93
|
if (p2.description !== void 0) a.description = p2.description;
|
|
92
94
|
if (p2.docType !== void 0) a.docType = p2.docType;
|
|
95
|
+
if (p2.fields !== void 0) a.fields = { ...a.fields, ...p2.fields };
|
|
93
96
|
break;
|
|
94
97
|
}
|
|
95
98
|
case "propose_revision": {
|
|
@@ -601,6 +604,40 @@ var init_read_model = __esm({
|
|
|
601
604
|
}
|
|
602
605
|
});
|
|
603
606
|
|
|
607
|
+
// ../core/src/node-identity.ts
|
|
608
|
+
function slugify(s2, max) {
|
|
609
|
+
return s2.trim().toLowerCase().replace(/^wo-/, "").replace(/[^a-z0-9一-龥]+/g, "-").replace(/^-|-$/g, "").slice(0, max);
|
|
610
|
+
}
|
|
611
|
+
function deriveArtifactId(type, workspace, title, usedIds) {
|
|
612
|
+
const wsSlug = slugify(workspace.split(":").pop() ?? workspace, 24) || "ws";
|
|
613
|
+
const titleSlug = slugify(title, 30) || "untitled";
|
|
614
|
+
const base = `artifact:${type}:${wsSlug}-${titleSlug}`;
|
|
615
|
+
let id = base;
|
|
616
|
+
let n = 2;
|
|
617
|
+
while (usedIds.has(id)) id = `${base}-${n++}`;
|
|
618
|
+
usedIds.add(id);
|
|
619
|
+
return id;
|
|
620
|
+
}
|
|
621
|
+
function resolveNodeRef(ref, candidates) {
|
|
622
|
+
const r = ref.trim();
|
|
623
|
+
if (r.startsWith("artifact:")) {
|
|
624
|
+
const hit = candidates.find((n) => n.id === r);
|
|
625
|
+
return hit ? { ok: true, id: hit.id } : { ok: false, reason: "not_found" };
|
|
626
|
+
}
|
|
627
|
+
const sep = r.indexOf(":");
|
|
628
|
+
const type = (sep >= 0 ? r.slice(0, sep) : r).trim().toLowerCase();
|
|
629
|
+
const title = sep >= 0 ? r.slice(sep + 1).trim().toLowerCase() : null;
|
|
630
|
+
const matches = candidates.filter((n) => n.type.toLowerCase() === type && (title === null || (n.title ?? "").toLowerCase() === title));
|
|
631
|
+
if (matches.length === 0) return { ok: false, reason: "not_found" };
|
|
632
|
+
if (matches.length === 1) return { ok: true, id: matches[0].id };
|
|
633
|
+
return { ok: false, reason: "ambiguous", candidates: matches };
|
|
634
|
+
}
|
|
635
|
+
var init_node_identity = __esm({
|
|
636
|
+
"../core/src/node-identity.ts"() {
|
|
637
|
+
"use strict";
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
|
|
604
641
|
// ../core/src/content-handler.ts
|
|
605
642
|
function defaultHandlersByContentType() {
|
|
606
643
|
return /* @__PURE__ */ new Map([
|
|
@@ -678,7 +715,7 @@ var init_content_type_handler = __esm({
|
|
|
678
715
|
const parts = ctx.queuedPartLabels.map((p2) => `\`${p2}\``).join("\u3001") || "\uFF08\u961F\u5217\u4E3A\u7A7A\uFF09";
|
|
679
716
|
return [
|
|
680
717
|
`\u5404\u90E8\u4EF6\u4ECE**\u5171\u540C\u7956\u5148 H** \u5206\u5934\u4EA7\u51FA\u3001\u5404\u6539\u5404\u5757\uFF1A\u6BCF\u6761\u5185\u5BB9\u5728 \`parts/<\u540D>/CONTENT.md\`\uFF08\u6216\u5176\u5C55\u5F00\u6587\u4EF6\uFF09\u91CC\uFF08${parts}\uFF09\uFF0CH \u7684\u5185\u5BB9\u5728 \`deliverable/\`\uFF08\u4ECE\u96F6\u5206\u89E3\u5219\u4E3A\u7A7A\uFF09\u3002`,
|
|
681
|
-
`\u628A\u5B83\u4EEC\u5408\u6210
|
|
718
|
+
`\u628A\u5B83\u4EEC\u5408\u6210\u4E00\u4E2A\u5B8C\u6574\u7684\u6574\u5408\u7248**\u5C31\u5730\u5199\u8FDB \`deliverable/\`**\u2014\u2014**\u4EE5 H \u4E3A\u5E95**\u3001\u628A\u5404\u90E8\u4EF6\u6539\u52A8**\u5168\u5E76\u8FDB\u6765**\uFF08\u5355\u6587\u4EF6 \`deliverable/CONTENT.md\`\uFF0C\u591A\u6587\u4EF6\u94FA\u6210\u591A\u4E2A\u6587\u4EF6\uFF1B\u5927\u6587\u4EF6\u589E\u91CF\u5199\uFF1AWrite \u9AA8\u67B6 \u2192 Edit \u8FFD\u52A0\uFF09\uFF0C\u7136\u540E\uFF1A`,
|
|
682
719
|
`\`oasis integrate ${ctx.artifactId} --from-dir deliverable/ --reason "<\u96C6\u6210\u8BF4\u660E>"\` \u2192 \`oasis conclude ${ctx.artifactId}\`\u3002`,
|
|
683
720
|
``,
|
|
684
721
|
`**\u5FE0\u5B9E\u5408\u5E76\u3001\u522B\u4E22\u4EFB\u4F55\u90E8\u4EF6**\uFF1B\u63A5\u53E3/\u547D\u540D\u5BF9\u4E0D\u9F50\u5C31\u6309\u4E0A\u6E38\u89C4\u683C\u6821\u6B63\uFF0C\u8BA9 N \u4E2A\u90E8\u4EF6\u62FC\u6210\u4E00\u4E2A\u81EA\u6D3D\u6574\u4F53\u3002`
|
|
@@ -729,7 +766,7 @@ var init_content_type_handler = __esm({
|
|
|
729
766
|
`\u8FD9\u662F\u4EE3\u7801\u2014\u2014\u5404\u90E8\u4EF6**\u5DF2\u81EA\u884C\u5E76\u5165\u5DE5\u5355\u5206\u652F \`${feat}\`**\uFF08\u6BCF\u4E2A part \u5E72\u5B8C\u81EA\u5DF1 merge \u8FDB\u6765\u3001\u89E3\u4E86\u5404\u81EA\u7684\u51B2\u7A81\uFF09\u3002\u4F60 owner **\u4E0D\u7528\u518D\u9010\u6761\u5408\u5E76**\uFF0C\u53EA\u9A8C\u6574\u4F53 + \u6536\u53E3\uFF1A`,
|
|
730
767
|
`1. \`git clone ${r0?.remote ?? "<remote>"} repos/${r0?.id ?? "<id>"}\` \u2192 \`git checkout ${feat}\`\uFF0C**\u9A8C\u6574\u4F53\u80FD\u6784\u5EFA/\u80FD\u8DD1**\uFF08\u8FD9\u624D\u662F\u4F60 owner \u7684\u5224\u65AD\u6D3B\uFF09\u3002`,
|
|
731
768
|
`2. \u82E5\u6709\u6B8B\u7559\u95EE\u9898\uFF08\u63A5\u53E3\u6CA1\u5BF9\u9F50\u3001\u6784\u5EFA\u5931\u8D25\u3001\u90E8\u4EF6\u8854\u63A5\u5904\u8981\u6536\u5C3E\uFF09\u2192 \u5C31\u5730\u4FEE\u3001\`git commit\`\u3001\`git push origin ${feat}\`\u3002`,
|
|
732
|
-
`3. \`oasis integrate ${artifactId} --reason "<\u96C6\u6210\u8BF4\u660E>"\` \u2192 \`oasis conclude ${artifactId}\`\uFF08\u7CFB\u7EDF\u4ECE \`${feat}\` \u6536\u96C6
|
|
769
|
+
`3. \`oasis integrate ${artifactId} --reason "<\u96C6\u6210\u8BF4\u660E>"\` \u2192 \`oasis conclude ${artifactId}\`\uFF08\u7CFB\u7EDF\u4ECE \`${feat}\` \u6536\u96C6\u6574\u5408\u7248\uFF1Bconclude \u540E\u9010\u4ED3\u540C\u6B65\u8FDB develop\uFF09\u3002`,
|
|
733
770
|
``,
|
|
734
771
|
`**\u4F60\u8FD9\u7EA7\u662F\u628A\u5173\u6574\u4F53\u81EA\u6D3D\u3001\u4E0D\u662F\u91CD\u65B0\u5408\u5E76**\u2014\u2014\u5408\u5E76\u5DF2\u7ECF\u5728\u5404\u90E8\u4EF6\u6536\u5C3E\u65F6\u5404\u81EA\u505A\u5B8C\u4E86\u3002`
|
|
735
772
|
];
|
|
@@ -1248,6 +1285,48 @@ var init_grounding = __esm({
|
|
|
1248
1285
|
});
|
|
1249
1286
|
|
|
1250
1287
|
// ../core/src/kernel.ts
|
|
1288
|
+
function fieldValueMatches(val, type) {
|
|
1289
|
+
switch (type) {
|
|
1290
|
+
case "string":
|
|
1291
|
+
return typeof val === "string";
|
|
1292
|
+
case "number":
|
|
1293
|
+
return typeof val === "number";
|
|
1294
|
+
case "boolean":
|
|
1295
|
+
return typeof val === "boolean";
|
|
1296
|
+
case "string[]":
|
|
1297
|
+
return Array.isArray(val) && val.every((x2) => typeof x2 === "string");
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
function coerceFieldValue(key, val, type) {
|
|
1301
|
+
if (fieldValueMatches(val, type)) return val;
|
|
1302
|
+
if (typeof val === "string") {
|
|
1303
|
+
if (type === "number" && val.trim() !== "" && !Number.isNaN(Number(val))) return Number(val);
|
|
1304
|
+
if (type === "boolean" && (val === "true" || val === "false")) return val === "true";
|
|
1305
|
+
}
|
|
1306
|
+
throw new KernelError(`\u5B57\u6BB5 '${key}' \u7C7B\u578B\u5E94\u4E3A ${type}${type === "string[]" ? "\uFF08\u6570\u7EC4\u503C\u8BF7\u7528 --set-json\uFF09" : ""}`);
|
|
1307
|
+
}
|
|
1308
|
+
function validateArtifactFields(fields, specs, mode) {
|
|
1309
|
+
const declared = new Map((specs ?? []).map((s2) => [s2.name, s2]));
|
|
1310
|
+
let out;
|
|
1311
|
+
if (fields !== void 0) {
|
|
1312
|
+
out = {};
|
|
1313
|
+
for (const [key, val] of Object.entries(fields)) {
|
|
1314
|
+
const spec = declared.get(key);
|
|
1315
|
+
if (!spec) {
|
|
1316
|
+
throw new KernelError(`\u672A\u58F0\u660E\u5B57\u6BB5 '${key}'\uFF08${declared.size > 0 ? `\u8BE5\u7C7B\u578B\u58F0\u660E\uFF1A${[...declared.keys()].join(", ")}` : "\u8BE5\u7C7B\u578B\u672A\u58F0\u660E\u4EFB\u4F55\u81EA\u5B9A\u4E49\u5B57\u6BB5"}\uFF09`);
|
|
1317
|
+
}
|
|
1318
|
+
out[key] = val === null ? null : coerceFieldValue(key, val, spec.type);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
if (mode === "spawn") {
|
|
1322
|
+
for (const spec of specs ?? []) {
|
|
1323
|
+
if (spec.required && (fields?.[spec.name] === void 0 || fields[spec.name] === null)) {
|
|
1324
|
+
throw new KernelError(`\u7F3A\u5FC5\u586B\u5B57\u6BB5 '${spec.name}'\uFF08${spec.type}\uFF09`);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
return out;
|
|
1329
|
+
}
|
|
1251
1330
|
var import_node_crypto, SYSTEM_ACTOR, MAX_PREREQ_DEPTH, KernelError, FrozenError, Kernel;
|
|
1252
1331
|
var init_kernel = __esm({
|
|
1253
1332
|
"../core/src/kernel.ts"() {
|
|
@@ -1256,6 +1335,7 @@ var init_kernel = __esm({
|
|
|
1256
1335
|
init_src();
|
|
1257
1336
|
init_read_model();
|
|
1258
1337
|
init_content_handler();
|
|
1338
|
+
init_node_identity();
|
|
1259
1339
|
init_governance();
|
|
1260
1340
|
init_manifest_store();
|
|
1261
1341
|
init_grounding();
|
|
@@ -1350,6 +1430,7 @@ var init_kernel = __esm({
|
|
|
1350
1430
|
);
|
|
1351
1431
|
}
|
|
1352
1432
|
this.enforceAcl("spawn", typeDef, args.actor);
|
|
1433
|
+
const fields = this.schema.size > 0 ? validateArtifactFields(args.fields, typeDef?.fieldDefs, "spawn") : args.fields;
|
|
1353
1434
|
const inputs = [];
|
|
1354
1435
|
for (const edge of args.inputs ?? []) {
|
|
1355
1436
|
if (edge.to === args.id) throw new KernelError("\u4E0D\u80FD\u4F9D\u8D56\u81EA\u5DF1");
|
|
@@ -1378,7 +1459,8 @@ var init_kernel = __esm({
|
|
|
1378
1459
|
...args.title !== void 0 ? { title: args.title } : {},
|
|
1379
1460
|
...args.description !== void 0 ? { description: args.description } : {},
|
|
1380
1461
|
...args.docType !== void 0 ? { docType: args.docType } : {},
|
|
1381
|
-
...args.parts !== void 0 ? { parts: args.parts } : {}
|
|
1462
|
+
...args.parts !== void 0 ? { parts: args.parts } : {},
|
|
1463
|
+
...fields !== void 0 ? { fields } : {}
|
|
1382
1464
|
};
|
|
1383
1465
|
await this.commit(args.id, args.actor, "spawn_artifact", args.id, payload);
|
|
1384
1466
|
return this.model.artifacts.get(args.id);
|
|
@@ -1993,7 +2075,64 @@ var init_kernel = __esm({
|
|
|
1993
2075
|
* 预检(②终态校验 + ③advisory + ④爆炸半径)。preview 白送:
|
|
1994
2076
|
* structuredClone 读模型、灌假设 op、看终态——derived-not-stored 的红利,无须仿真引擎。
|
|
1995
2077
|
*/
|
|
2078
|
+
/**
|
|
2079
|
+
* 二期 title-identity:把 plan 里的【节点引用】(`type` | `type:title` | `id`)解析成最终 id。
|
|
2080
|
+
* - spawn 省略 id → 据 (type,title) 派生(usedIds 预种工单现有 id + 本批显式 id,不和存量撞)。
|
|
2081
|
+
* - 引用现有/本批节点 → 在 plan.workspace 范围内 {现有 ∪ 本批 spawn} 匹配;撞多个 throw 带候选、无则 throw。
|
|
2082
|
+
* - 批内**先派生所有 spawn 的 id,再解引用**(link 才引得到本批新建的)。
|
|
2083
|
+
* - workspace 缺省 = 只走 id(向后兼容 passthrough)。**幂等**:解析一份已解析的 plan = 原样。
|
|
2084
|
+
* 落 oplog 的是解析后的 id(重放确定)。
|
|
2085
|
+
*/
|
|
2086
|
+
resolveInterventionRefs(plan) {
|
|
2087
|
+
const ws = plan.workspace;
|
|
2088
|
+
const existing = ws ? [...this.model.artifacts.values()].filter((a) => a.workspace === ws).map((a) => ({ id: a.id, type: a.type, ...a.title ? { title: a.title } : {} })) : [];
|
|
2089
|
+
const usedIds = new Set(existing.map((n) => n.id));
|
|
2090
|
+
for (const op of plan.ops) if (op.action === "spawn" && op.id) usedIds.add(op.id);
|
|
2091
|
+
const batch = [];
|
|
2092
|
+
const spawnIds = plan.ops.map((op) => {
|
|
2093
|
+
if (op.action !== "spawn") return void 0;
|
|
2094
|
+
if (op.id) {
|
|
2095
|
+
batch.push({ id: op.id, type: op.type, ...op.title ? { title: op.title } : {} });
|
|
2096
|
+
return op.id;
|
|
2097
|
+
}
|
|
2098
|
+
if (!ws) throw new KernelError("spawn \u7701\u7565 id \u9700\u8981 InterventionPlan.workspace + title \u624D\u80FD\u6D3E\u751F");
|
|
2099
|
+
const title = op.title?.trim();
|
|
2100
|
+
if (!title) throw new KernelError("spawn \u7701\u7565 id \u65F6 title \u5FC5\u586B\uFF08\u636E (type,title) \u6D3E\u751F id\uFF09");
|
|
2101
|
+
if ([...existing, ...batch].some((n) => n.type === op.type && (n.title ?? "").toLowerCase() === title.toLowerCase()))
|
|
2102
|
+
throw new KernelError(`\u8282\u70B9 title \u649E\u540D\uFF1A${op.type}\u300C${title}\u300D\u5728\u672C\u5DE5\u5355\u5DF2\u5B58\u5728,\u8BF7\u6362\u4E2A\u533A\u5206\u7684\u540D\u5B57`);
|
|
2103
|
+
const id = deriveArtifactId(op.type, ws, title, usedIds);
|
|
2104
|
+
batch.push({ id, type: op.type, title });
|
|
2105
|
+
return id;
|
|
2106
|
+
});
|
|
2107
|
+
const candidates = [...existing, ...batch];
|
|
2108
|
+
const ref = (r, what) => {
|
|
2109
|
+
if (r === void 0) return void 0;
|
|
2110
|
+
if (!ws) return r;
|
|
2111
|
+
const res = resolveNodeRef(r, candidates);
|
|
2112
|
+
if (res.ok) return res.id;
|
|
2113
|
+
if (res.reason === "ambiguous")
|
|
2114
|
+
throw new KernelError(`${what}\u300C${r}\u300D\u5339\u914D\u5230\u591A\u4E2A\u8282\u70B9,\u8BF7\u6307\u660E\u54EA\u4E2A:
|
|
2115
|
+
${res.candidates.map((c) => ` - ${c.type}:${c.title ?? "(\u65E0title)"} \u2192 ${c.id}`).join("\n")}`);
|
|
2116
|
+
throw new KernelError(`${what}\u300C${r}\u300D\u627E\u4E0D\u5230\u5BF9\u5E94\u8282\u70B9\uFF08\u53EF\u7528 type / type:title / id\uFF09`);
|
|
2117
|
+
};
|
|
2118
|
+
const ops = plan.ops.map((op, i) => {
|
|
2119
|
+
switch (op.action) {
|
|
2120
|
+
case "spawn":
|
|
2121
|
+
return { ...op, id: spawnIds[i], ...op.workspace === void 0 && ws ? { workspace: ws } : {}, ...op.inputs ? { inputs: op.inputs.map((e) => ({ to: ref(e.to, "spawn \u4F9D\u8D56"), ...e.required !== void 0 ? { required: e.required } : {} })) } : {} };
|
|
2122
|
+
case "link":
|
|
2123
|
+
case "unlink":
|
|
2124
|
+
case "pin":
|
|
2125
|
+
return { ...op, artifactId: ref(op.artifactId, op.action), to: ref(op.to, `${op.action} \u4E0A\u6E38`) };
|
|
2126
|
+
case "annotate":
|
|
2127
|
+
return { ...op, artifactId: ref(op.artifactId, "annotate"), ...op.raisedFrom ? { raisedFrom: ref(op.raisedFrom, "annotate raisedFrom") } : {} };
|
|
2128
|
+
default:
|
|
2129
|
+
return { ...op, artifactId: ref(op.artifactId, op.action) };
|
|
2130
|
+
}
|
|
2131
|
+
});
|
|
2132
|
+
return { ...plan, ops };
|
|
2133
|
+
}
|
|
1996
2134
|
async previewIntervention(plan) {
|
|
2135
|
+
plan = this.resolveInterventionRefs(plan);
|
|
1997
2136
|
const constituents = this.buildConstituents(plan);
|
|
1998
2137
|
const touched = [...new Set(constituents.map((c) => c.artifactId))];
|
|
1999
2138
|
const baseSeqs = Object.fromEntries(touched.map((id) => [id, this.model.lastSeq.get(id) ?? 0]));
|
|
@@ -2122,6 +2261,7 @@ var init_kernel = __esm({
|
|
|
2122
2261
|
* v1 注:inverse 批次未物化(oplog 信息足以推导),可逆性=再发一个 intervention。
|
|
2123
2262
|
*/
|
|
2124
2263
|
async applyIntervention(plan, baseSeqs, actor) {
|
|
2264
|
+
plan = this.resolveInterventionRefs(plan);
|
|
2125
2265
|
const { changeClass, nonMonotonicOps } = classifyIntervention(plan);
|
|
2126
2266
|
if (changeClass === "C" && !isHumanActor(actor)) {
|
|
2127
2267
|
throw new KernelError(
|
|
@@ -2199,27 +2339,31 @@ var init_kernel = __esm({
|
|
|
2199
2339
|
buildConstituentsRaw(plan) {
|
|
2200
2340
|
const order = { spawn: 0, edit: 1, reopen: 2, assign: 3, link: 4, pin: 5, unlink: 6, cancelPart: 7, seal: 8, annotate: 9 };
|
|
2201
2341
|
const sorted = [...plan.ops].sort((a, b2) => order[a.action] - order[b2.action]);
|
|
2202
|
-
const spawnedHere = new Set(plan.ops.flatMap((op) => op.action === "spawn" ? [op.id] : []));
|
|
2342
|
+
const spawnedHere = new Set(plan.ops.flatMap((op) => op.action === "spawn" && op.id ? [op.id] : []));
|
|
2203
2343
|
const isPrereqFill = (id) => plan.ops.some((op) => op.action === "link" && op.to === id && !spawnedHere.has(op.artifactId));
|
|
2204
2344
|
return sorted.map((op) => {
|
|
2205
2345
|
switch (op.action) {
|
|
2206
2346
|
case "spawn": {
|
|
2347
|
+
const spawnId = op.id;
|
|
2348
|
+
if (!spawnId) throw new KernelError("internal: spawn id \u672A\u89E3\u6790\uFF08\u5E94\u5148\u8FC7 resolveInterventionRefs\uFF09");
|
|
2207
2349
|
const typeDef = this.schema.get(op.type);
|
|
2208
2350
|
if (this.schema.size > 0 && !typeDef) throw new KernelError(`unknown ArtifactType: ${op.type}`);
|
|
2351
|
+
const spawnFields = this.schema.size > 0 ? validateArtifactFields(op.fields, typeDef?.fieldDefs, "spawn") : op.fields;
|
|
2209
2352
|
const owner = op.owner ?? this.resolveOwner(op.type, typeDef);
|
|
2210
2353
|
return {
|
|
2211
|
-
artifactId:
|
|
2354
|
+
artifactId: spawnId,
|
|
2212
2355
|
kind: "spawn_artifact",
|
|
2213
|
-
target:
|
|
2356
|
+
target: spawnId,
|
|
2214
2357
|
payload: {
|
|
2215
2358
|
type: op.type,
|
|
2216
2359
|
owner,
|
|
2217
2360
|
workspace: op.workspace ?? "ws:default",
|
|
2218
2361
|
persistence: "persistent",
|
|
2219
|
-
createdVia: isPrereqFill(
|
|
2362
|
+
createdVia: isPrereqFill(spawnId) ? "prereq" : "manual-spawn",
|
|
2220
2363
|
...op.title !== void 0 ? { title: op.title } : {},
|
|
2221
2364
|
...op.description !== void 0 ? { description: op.description } : {},
|
|
2222
2365
|
...op.docType !== void 0 ? { docType: op.docType } : {},
|
|
2366
|
+
...spawnFields !== void 0 ? { fields: spawnFields } : {},
|
|
2223
2367
|
inputs: (op.inputs ?? []).map((e) => ({
|
|
2224
2368
|
to: e.to,
|
|
2225
2369
|
pinned: concludedHead(this.model, e.to),
|
|
@@ -2254,7 +2398,12 @@ var init_kernel = __esm({
|
|
|
2254
2398
|
...op.blocks !== void 0 ? { blocks: op.blocks } : {}
|
|
2255
2399
|
}
|
|
2256
2400
|
};
|
|
2257
|
-
case "edit":
|
|
2401
|
+
case "edit": {
|
|
2402
|
+
let editFields = op.fields;
|
|
2403
|
+
if (this.schema.size > 0 && op.fields !== void 0) {
|
|
2404
|
+
const a = this.model.artifacts.get(op.artifactId);
|
|
2405
|
+
editFields = validateArtifactFields(op.fields, a ? this.schema.get(a.type)?.fieldDefs : void 0, "edit");
|
|
2406
|
+
}
|
|
2258
2407
|
return {
|
|
2259
2408
|
artifactId: op.artifactId,
|
|
2260
2409
|
kind: "edit_artifact",
|
|
@@ -2262,9 +2411,11 @@ var init_kernel = __esm({
|
|
|
2262
2411
|
payload: {
|
|
2263
2412
|
...op.title !== void 0 ? { title: op.title } : {},
|
|
2264
2413
|
...op.description !== void 0 ? { description: op.description } : {},
|
|
2265
|
-
...op.docType !== void 0 ? { docType: op.docType } : {}
|
|
2414
|
+
...op.docType !== void 0 ? { docType: op.docType } : {},
|
|
2415
|
+
...editFields !== void 0 ? { fields: editFields } : {}
|
|
2266
2416
|
}
|
|
2267
2417
|
};
|
|
2418
|
+
}
|
|
2268
2419
|
}
|
|
2269
2420
|
});
|
|
2270
2421
|
}
|
|
@@ -2500,7 +2651,7 @@ function commandReference(opts) {
|
|
|
2500
2651
|
const write = [`**\u5199**`];
|
|
2501
2652
|
if (opts.action === "integrate") {
|
|
2502
2653
|
write.push(
|
|
2503
|
-
opts.isCode ? `- \`oasis integrate ${id} --reason "<\u96C6\u6210\u8BF4\u660E>"\` \u2014\u2014 **\u6536\u53E3\u4E13\u7528**\uFF1A\u628A\u5DE5\u5355\u5206\u652F\u7684\u6574\u5408\u7248\u843D\u4E3A R\\*\uFF08\u7CFB\u7EDF\u4ECE git \u6536\u96C6\uFF1B\u89C1\u300C\u600E\u4E48\u63D0\u4EA4\u300D\uFF09\u3002**\u7EDD\u4E0D\u8981\u7528 propose/advance \u4EA4\u96C6\u6210**\u3002` : `- \`oasis integrate ${id} --from-dir deliverable/ --reason "<\u96C6\u6210\u8BF4\u660E>"\` \u2014\u2014 **\u6536\u53E3\u4E13\u7528**\uFF1A\u628A\u4F60\u5728 deliverable/ \u5408\u6210\u7684
|
|
2654
|
+
opts.isCode ? `- \`oasis integrate ${id} --reason "<\u96C6\u6210\u8BF4\u660E>"\` \u2014\u2014 **\u6536\u53E3\u4E13\u7528**\uFF1A\u628A\u5DE5\u5355\u5206\u652F\u7684\u6574\u5408\u7248\u843D\u4E3A R\\*\uFF08\u7CFB\u7EDF\u4ECE git \u6536\u96C6\uFF1B\u89C1\u300C\u600E\u4E48\u63D0\u4EA4\u300D\uFF09\u3002**\u7EDD\u4E0D\u8981\u7528 propose/advance \u4EA4\u96C6\u6210**\u3002` : `- \`oasis integrate ${id} --from-dir deliverable/ --reason "<\u96C6\u6210\u8BF4\u660E>"\` \u2014\u2014 **\u6536\u53E3\u4E13\u7528**\uFF1A\u628A\u4F60\u5728 deliverable/ \u5408\u6210\u7684\u6574\u5408\u7248\u843D\u4E3A\u65B0 head\uFF08\u7CFB\u7EDF\u5185\u90E8\u4F1A\u628A\u961F\u5217\u91CC\u5176\u4F59\u5F85\u843D\u7248\u672C\u4E00\u5E76\u5E76\u8FDB\u6765\uFF09\u3002**\u7EDD\u4E0D\u8981\u7528 propose / advance \u4EA4\u96C6\u6210**\uFF08\u90A3\u4F1A\u628A\u96C6\u6210\u5F53\u6210\u53C8\u4E00\u4E2A\u5E76\u5217\u7248\u672C\u3001\u843D\u9519\u7248\u672C\uFF09\u3002`
|
|
2504
2655
|
);
|
|
2505
2656
|
} else {
|
|
2506
2657
|
const reviewSuffix = opts.hasOpenAnnotations ? "\u5F53\u524D\u6709\u672A\u51B3\u4EBA\u5DE5/\u6279\u6CE8\u8981\u6C42\uFF0C\u63D0\u4EA4\u540E\u4F1A\u8FDB\u5165\u4EBA\u5DE5\u5BA1\u6838/\u961F\u5217\uFF0C\u4E0D\u4F1A\u81EA\u52A8\u843D head\uFF1B\u4E0D\u8981\u628A\u4EBA\u5DE5\u53D8\u66F4\u8BF7\u6C42\u81EA\u884C\u6807\u4E3A resolved\uFF0C\u7B49\u4EBA\u5BA1\u6838\u901A\u8FC7\u6216\u9000\u56DE\u3002" : "\u4F60\u662F\u5355 producer\u3001\u961F\u5217\u7A7A\u65F6\u5B83**\u81EA\u52A8\u843D head**\uFF0C\u4E0D\u7528 advance\u3002";
|
|
@@ -2510,15 +2661,15 @@ function commandReference(opts) {
|
|
|
2510
2661
|
}
|
|
2511
2662
|
write.push(
|
|
2512
2663
|
`- \`oasis conclude ${id}\` \u2014\u2014 \u5BA3\u5E03\u4EA7\u7269**\u5B8C\u6574\u3001\u53EF\u88AB\u4E0B\u6E38\u6D88\u8D39**\uFF0C\u76D6\u4E00\u4E2A\u91CC\u7A0B\u7891\u3002\u524D\u63D0\uFF1A\u961F\u5217\u5DF2\u6E05\u7A7A\u3001\u4E14\u65E0\u672A\u89E3\u6790 gap / \u672A\u7B54\u590D\u56DE\u538B\uFF08\u5426\u5219\u88AB\u62D2\uFF09\u3002\u76D6\u5B8C\u4E0B\u6E38\u6536\u5230\u901A\u77E5\u3002`,
|
|
2513
|
-
`- \`oasis add-revision ${id} --
|
|
2664
|
+
`- \`oasis add-revision ${id} --desc "<\u8FD9\u90E8\u4EF6\u505A\u4EC0\u4E48>" [--name <\u90E8\u4EF6\u540D>] [--role <\u89D2\u8272>]\` \u2014\u2014 \u628A\u672C\u4EA7\u7269\u62C6\u51FA\u4E00\u4E2A**\u53EF\u5E76\u884C\u4EA7\u51FA\u7684\u5B50\u90E8\u4EF6**\u6D3E\u51FA\u53BB\u3002\`--desc\`\uFF08**\u5FC5\u586B**\uFF09= \u8FD9\u4E2A\u5B50\u90E8\u4EF6\u8981\u5B8C\u6210\u4EC0\u4E48\uFF1B\`--name\` = \u7ED9\u5B83\u8D77\u7684\u90E8\u4EF6\u540D\uFF08\u53EF\u9009\uFF0C\u7F3A\u7701\u81EA\u52A8\u751F\u6210\uFF09\uFF1B\`--role\` = \u8C01\u6765\u505A\uFF08\u53EF\u9009\uFF0C\u7F3A\u7701=\u4F60\u81EA\u5DF1\uFF09\u3002\u5404\u90E8\u4EF6\u4EA7\u5B8C\u7531\u4F60 \`oasis integrate\` \u6536\u53E3\u3002`,
|
|
2514
2665
|
`- \`oasis gap ${id} --desc "<\u7F3A\u4EC0\u4E48>"\` \u2014\u2014 \u7F3A\u4E00\u4E2A\u8FD8\u4E0D\u5B58\u5728\u7684\u524D\u7F6E\u3001\u505A\u4E0D\u4E0B\u53BB\u65F6\u7528\u3002\`--desc\` \u63CF\u8FF0\u7F3A\u4EC0\u4E48\uFF08\u534F\u8C03\u8005\u636E\u5B83\u5224\u8FDE\u65E2\u6709/\u65B0\u5EFA\uFF09\u3002\u62A5\u5B8C**\u6536\u5DE5**\uFF1A\u8FD9\u6761\u4F1A\u6682\u505C\u3001\u524D\u7F6E\u5C31\u7EEA\u540E\u91CD\u65B0\u5524\u8D77\u4F60\uFF1B\u671F\u95F4\u522B propose\u3002`,
|
|
2515
2666
|
`- \`oasis annotate <\u4E0A\u6E38id> --from ${id} --body "<\u7591\u95EE>"\` \u2014\u2014 \u5BF9\u67D0\u4E0A\u6E38\u63D0\u7591\u95EE/\u5F02\u8BAE\uFF08\u56DE\u538B\uFF09\u3002\`<\u4E0A\u6E38id>\`=\u88AB\u95EE\u7684\u4E0A\u6E38\uFF1B\`--from\`=\u672C\u4EA7\u7269\uFF08\u7CFB\u7EDF\u636E\u6B64\u5728\u4E0A\u6E38\u7B54\u590D\u540E\u91CD\u6D3E\u4F60\uFF09\uFF1B\`--body\`=\u5177\u4F53\u95EE\u9898\u3002\u63D0\u5B8C**\u6536\u5DE5**\u7B49\u7B54\u590D\uFF0C\u522B propose\u3002`
|
|
2516
2667
|
);
|
|
2517
2668
|
if (opts.hasOpenAnnotations) {
|
|
2518
|
-
write.push(`- \`oasis resolve <annId> --
|
|
2669
|
+
write.push(`- \`oasis resolve <annId> --as resolved|wontfix|acknowledged [--note "<\u8BF4\u660E>"]\` \u2014\u2014 \u7B54\u590D / \u4E86\u7ED3**\u6302\u5728\u4F60\u4EA7\u7269\u4E0A\u7684** annotation\uFF08\u542B\u4E0B\u6E38\u56DE\u538B\uFF09\u3002\`<annId>\`\uFF08\u4F4D\u7F6E\u53C2\u6570\uFF09= \u8981\u4E86\u7ED3\u7684\u6279\u6CE8 id\uFF1B\`--as\` = \u7ED3\u8BBA\uFF08resolved=\u5DF2\u89E3\u51B3 / wontfix=\u4E0D\u6539 / acknowledged=\u5DF2\u77E5\u6089\uFF09\uFF1B\`--note\` = \u8865\u5145\u8BF4\u660E\uFF08\u53EF\u9009\uFF09\u3002`);
|
|
2519
2670
|
}
|
|
2520
2671
|
if (opts.action === "integrate") {
|
|
2521
|
-
write.push(`- \`oasis reject <revId> --reason "<\u4E3A\u4EC0\u4E48>"\` \u2014\u2014 \u5F03\u7528\u961F\u5217\u91CC\u67D0\u4E2A\u574F\u7684\u90E8\u4EF6-revision\uFF08\u4E0D\u5E76\u8FDB\u96C6\u6210\uFF09\u3002`);
|
|
2672
|
+
write.push(`- \`oasis reject <artifactId> <revId> --reason "<\u4E3A\u4EC0\u4E48>"\` \u2014\u2014 \u5F03\u7528\u961F\u5217\u91CC\u67D0\u4E2A\u574F\u7684\u90E8\u4EF6-revision\uFF08\u4E0D\u5E76\u8FDB\u96C6\u6210\uFF09\u3002\`<artifactId>\`\uFF08**\u7B2C\u4E00\u4E2A**\u4F4D\u7F6E\u53C2\u6570\uFF09= \u4EA7\u7269 id\uFF1B\`<revId>\`\uFF08\u7B2C\u4E8C\u4E2A\uFF09= \u8981\u5F03\u7528\u7684 revision id\uFF1B\`--reason\` = \u4E3A\u4EC0\u4E48\u5F03\u3002`);
|
|
2522
2673
|
}
|
|
2523
2674
|
return [`## \u4F60\u80FD\u7528\u7684 oasis \u547D\u4EE4\uFF08\u672C\u6B21\uFF09`, ...write, ``, ...readFull];
|
|
2524
2675
|
}
|
|
@@ -2672,6 +2823,10 @@ async function assembleContext(args) {
|
|
|
2672
2823
|
}
|
|
2673
2824
|
}
|
|
2674
2825
|
}
|
|
2826
|
+
if (args.materializeSkills) {
|
|
2827
|
+
const skillFiles = await args.materializeSkills().catch(() => ({}));
|
|
2828
|
+
for (const [p2, c] of Object.entries(skillFiles)) files[p2] = c;
|
|
2829
|
+
}
|
|
2675
2830
|
const wsCodeRepo = args.codeRepo ?? null;
|
|
2676
2831
|
const isCodeArtifact = typeDef?.contentType === "code" && !!wsCodeRepo?.repos?.some((r) => r.remote);
|
|
2677
2832
|
files["TASK.md"] = [
|
|
@@ -2935,6 +3090,8 @@ var init_dispatcher = __esm({
|
|
|
2935
3090
|
inFlight = /* @__PURE__ */ new Map();
|
|
2936
3091
|
/** v1 调度准入:在途会话的 actor(jobKey → actorId),用于每 agent 并发计数。与 inFlight 同生同灭。 */
|
|
2937
3092
|
inFlightActor = /* @__PURE__ */ new Map();
|
|
3093
|
+
/** 在途会话的派发时刻(jobKey → ms),算"已跑多久"用(协调者病历的会话情况)。与 inFlight 同生同灭。 */
|
|
3094
|
+
inFlightStartedAt = /* @__PURE__ */ new Map();
|
|
2938
3095
|
strikes = /* @__PURE__ */ new Map();
|
|
2939
3096
|
/** v1 调度准入:上一轮 tick 中"够格但被并发上限挡下"的 produce(隐式队列;下个 tick 重评)。
|
|
2940
3097
|
* jobKey → 撞的是哪道闸(global 全局总量 / per-actor owner 自己到上限)+ 该 actor——
|
|
@@ -2979,8 +3136,9 @@ var init_dispatcher = __esm({
|
|
|
2979
3136
|
artifactId: this.artifactIdFromJobKey(jobKey),
|
|
2980
3137
|
sessionId: handle.id,
|
|
2981
3138
|
actor: this.inFlightActor.get(jobKey) ?? "",
|
|
2982
|
-
action: jobKey.slice(0, jobKey.indexOf("::"))
|
|
3139
|
+
action: jobKey.slice(0, jobKey.indexOf("::")),
|
|
2983
3140
|
// produce / review / integrate
|
|
3141
|
+
startedAt: new Date(this.inFlightStartedAt.get(jobKey) ?? Date.now()).toISOString()
|
|
2984
3142
|
});
|
|
2985
3143
|
}
|
|
2986
3144
|
return out;
|
|
@@ -3129,6 +3287,8 @@ var init_dispatcher = __esm({
|
|
|
3129
3287
|
const lastFailure = retry && retry.count > 0 && retry.lastReason ? { reason: retry.lastReason } : void 0;
|
|
3130
3288
|
const ws = kernel.model.artifacts.get(spec.artifactId)?.workspace;
|
|
3131
3289
|
const codeRepo = ws ? await this.opts.resolveCodeRepo?.(ws) ?? null : null;
|
|
3290
|
+
const binding = await this.opts.resolveBinding?.(spec.actor) ?? void 0;
|
|
3291
|
+
const materializeSkills = this.opts.materializeSkills ? () => this.opts.materializeSkills(spec.actor, binding?.runtimeKind ?? "claude") : void 0;
|
|
3132
3292
|
const bundle = await assembleContext({
|
|
3133
3293
|
model: kernel.model,
|
|
3134
3294
|
blobs: this.opts.blobs,
|
|
@@ -3140,9 +3300,9 @@ var init_dispatcher = __esm({
|
|
|
3140
3300
|
...lastFailure ? { lastFailure } : {},
|
|
3141
3301
|
...this.opts.schema !== void 0 ? { schema: this.opts.schema } : {},
|
|
3142
3302
|
...codeRepo ? { codeRepo } : {},
|
|
3143
|
-
...this.opts.resolveActorContext !== void 0 ? { resolveActorContext: this.opts.resolveActorContext } : {}
|
|
3303
|
+
...this.opts.resolveActorContext !== void 0 ? { resolveActorContext: this.opts.resolveActorContext } : {},
|
|
3304
|
+
...materializeSkills ? { materializeSkills } : {}
|
|
3144
3305
|
});
|
|
3145
|
-
const binding = await this.opts.resolveBinding?.(spec.actor) ?? void 0;
|
|
3146
3306
|
const provisioned = this.opts.provision ? await this.opts.provision(spec.actor) : void 0;
|
|
3147
3307
|
const job = {
|
|
3148
3308
|
actor: spec.actor,
|
|
@@ -3159,6 +3319,7 @@ var init_dispatcher = __esm({
|
|
|
3159
3319
|
this.inFlight.set(jobKey, session);
|
|
3160
3320
|
this.inFlightActor.set(jobKey, spec.actor);
|
|
3161
3321
|
const dispatchedAtMs = Date.now();
|
|
3322
|
+
this.inFlightStartedAt.set(jobKey, dispatchedAtMs);
|
|
3162
3323
|
const seqAtDispatch = seqNow;
|
|
3163
3324
|
const sink = this.opts.trajectory;
|
|
3164
3325
|
if (sink) {
|
|
@@ -3224,6 +3385,7 @@ var init_dispatcher = __esm({
|
|
|
3224
3385
|
}
|
|
3225
3386
|
this.inFlight.delete(jobKey);
|
|
3226
3387
|
this.inFlightActor.delete(jobKey);
|
|
3388
|
+
this.inFlightStartedAt.delete(jobKey);
|
|
3227
3389
|
const seqAtExit = kernel.model.lastSeq.get(spec.artifactId) ?? 0;
|
|
3228
3390
|
if (sink) {
|
|
3229
3391
|
void (async () => {
|
|
@@ -3363,6 +3525,7 @@ var init_src2 = __esm({
|
|
|
3363
3525
|
"../core/src/index.ts"() {
|
|
3364
3526
|
"use strict";
|
|
3365
3527
|
init_read_model();
|
|
3528
|
+
init_node_identity();
|
|
3366
3529
|
init_content_handler();
|
|
3367
3530
|
init_content_type_handler();
|
|
3368
3531
|
init_diff();
|
|
@@ -20363,6 +20526,36 @@ var init_drafts = __esm({
|
|
|
20363
20526
|
}
|
|
20364
20527
|
});
|
|
20365
20528
|
|
|
20529
|
+
// ../server/src/governance/workorder-drafts.ts
|
|
20530
|
+
var WorkorderDraftStore;
|
|
20531
|
+
var init_workorder_drafts = __esm({
|
|
20532
|
+
"../server/src/governance/workorder-drafts.ts"() {
|
|
20533
|
+
"use strict";
|
|
20534
|
+
WorkorderDraftStore = class {
|
|
20535
|
+
drafts = /* @__PURE__ */ new Map();
|
|
20536
|
+
seq = 0;
|
|
20537
|
+
submit(d) {
|
|
20538
|
+
const draft = { ...d, id: `wodraft:${++this.seq}`, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
20539
|
+
this.drafts.set(draft.id, draft);
|
|
20540
|
+
return draft;
|
|
20541
|
+
}
|
|
20542
|
+
list() {
|
|
20543
|
+
return [...this.drafts.values()];
|
|
20544
|
+
}
|
|
20545
|
+
/** 按 workspace 取最新一份:friday 用它发起建单时生成的 workspace id 取回草案,无需解析 chat 文本。 */
|
|
20546
|
+
forWorkspace(workspace) {
|
|
20547
|
+
return [...this.drafts.values()].reverse().find((d) => d.workspace === workspace);
|
|
20548
|
+
}
|
|
20549
|
+
get(id) {
|
|
20550
|
+
return this.drafts.get(id);
|
|
20551
|
+
}
|
|
20552
|
+
remove(id) {
|
|
20553
|
+
return this.drafts.delete(id);
|
|
20554
|
+
}
|
|
20555
|
+
};
|
|
20556
|
+
}
|
|
20557
|
+
});
|
|
20558
|
+
|
|
20366
20559
|
// ../server/src/domains/collab/planner.ts
|
|
20367
20560
|
function ensureDefaultWorkorderTypes(schema) {
|
|
20368
20561
|
const out = [...schema];
|
|
@@ -20371,18 +20564,17 @@ function ensureDefaultWorkorderTypes(schema) {
|
|
|
20371
20564
|
}
|
|
20372
20565
|
return out;
|
|
20373
20566
|
}
|
|
20374
|
-
function
|
|
20375
|
-
const
|
|
20376
|
-
|
|
20377
|
-
|
|
20378
|
-
const
|
|
20379
|
-
|
|
20380
|
-
|
|
20381
|
-
|
|
20567
|
+
function upgradeDefaultFieldDefs(schema) {
|
|
20568
|
+
const changed = [];
|
|
20569
|
+
for (const def of DEFAULT_TYPES) {
|
|
20570
|
+
if (!def.fieldDefs) continue;
|
|
20571
|
+
const cur = schema.find((item) => item.name === def.name);
|
|
20572
|
+
if (cur && !cur.fieldDefs) {
|
|
20573
|
+
cur.fieldDefs = def.fieldDefs;
|
|
20574
|
+
changed.push(def.name);
|
|
20575
|
+
}
|
|
20382
20576
|
}
|
|
20383
|
-
|
|
20384
|
-
function workorderSpecBody(spec) {
|
|
20385
|
-
return WORKORDER_SPEC_PREFIX + JSON.stringify(spec);
|
|
20577
|
+
return changed;
|
|
20386
20578
|
}
|
|
20387
20579
|
function parseWorkorderSpec(body) {
|
|
20388
20580
|
const first = body.split("\n", 1)[0] ?? "";
|
|
@@ -20420,6 +20612,12 @@ async function buildDynamicWorkorderPlan(input) {
|
|
|
20420
20612
|
return match;
|
|
20421
20613
|
};
|
|
20422
20614
|
const briefOwner = input.actor;
|
|
20615
|
+
const specFields = {};
|
|
20616
|
+
if (input.goal?.trim()) specFields["goal"] = input.goal.trim();
|
|
20617
|
+
if (input.acceptanceCriteria?.length) {
|
|
20618
|
+
const items = input.acceptanceCriteria.map((x2) => x2.trim()).filter(Boolean);
|
|
20619
|
+
if (items.length > 0) specFields["acceptanceCriteria"] = items;
|
|
20620
|
+
}
|
|
20423
20621
|
const ops = [
|
|
20424
20622
|
{
|
|
20425
20623
|
action: "spawn",
|
|
@@ -20429,23 +20627,12 @@ async function buildDynamicWorkorderPlan(input) {
|
|
|
20429
20627
|
workspace: input.workspace,
|
|
20430
20628
|
// 工单名 → brief.title;工单目标/brief → brief.description(别再把 name 塞进 description、丢掉 goal)
|
|
20431
20629
|
...input.title?.trim() ? { title: input.title.trim() } : {},
|
|
20432
|
-
description: input.description
|
|
20630
|
+
description: input.description,
|
|
20631
|
+
...Object.keys(specFields).length > 0 ? { fields: specFields } : {}
|
|
20433
20632
|
}
|
|
20434
20633
|
];
|
|
20435
|
-
const spec = {};
|
|
20436
|
-
if (input.goal?.trim()) spec.goal = input.goal.trim();
|
|
20437
|
-
if (input.acceptanceCriteria?.length) {
|
|
20438
|
-
const items = input.acceptanceCriteria.map((x2) => x2.trim()).filter(Boolean);
|
|
20439
|
-
if (items.length > 0) spec.acceptanceCriteria = items;
|
|
20440
|
-
}
|
|
20441
|
-
if (spec.goal !== void 0 || spec.acceptanceCriteria !== void 0) {
|
|
20442
|
-
ops.push({ action: "annotate", artifactId: briefId, body: workorderSpecBody(spec), blocks: false });
|
|
20443
|
-
}
|
|
20444
20634
|
const spawned = [{ id: briefId, type: "brief", role: "pm", owner: briefOwner }];
|
|
20445
20635
|
const agentGraph = input.agentPlan ? normalizeAgentPlan(input.agentPlan, schema.map((d) => d.name)) : null;
|
|
20446
|
-
const briefByType = new Map(
|
|
20447
|
-
(input.agentPlan?.nodes ?? []).filter((n) => typeof n.brief === "string" && n.brief.trim().length > 0).map((n) => [n.type.trim().toLowerCase(), n.brief.trim()])
|
|
20448
|
-
);
|
|
20449
20636
|
if (input.agentPlan && !agentGraph) {
|
|
20450
20637
|
issues.push({
|
|
20451
20638
|
code: "planner_invalid",
|
|
@@ -20453,51 +20640,63 @@ async function buildDynamicWorkorderPlan(input) {
|
|
|
20453
20640
|
message: "pm_agent \u7684\u89C4\u5212\u7ED3\u679C\u4E3A\u7A7A\u6216\u683C\u5F0F\u65E0\u6548\uFF0C\u5DF2\u56DE\u9000\u5230\u89C4\u5219 planner\u3002"
|
|
20454
20641
|
});
|
|
20455
20642
|
}
|
|
20456
|
-
const
|
|
20643
|
+
for (const dup of agentGraph?.dupTitles ?? []) {
|
|
20644
|
+
issues.push({ code: "title_collision", severity: "warning", message: `\u8282\u70B9 title \u649E\u540D\u300C${dup}\u300D\u2014\u2014\u540C\u4E00\u5DE5\u5355\u540C\u7C7B\u578B title \u987B\u552F\u4E00\uFF0C\u5DF2\u5FFD\u7565\u91CD\u590D\u9879\uFF1B\u8BF7\u6539\u4E2A\u533A\u5206\u7684\u540D\u5B57\u3002` });
|
|
20645
|
+
}
|
|
20646
|
+
const usedIds = /* @__PURE__ */ new Set([briefId]);
|
|
20457
20647
|
const availableNodes = [];
|
|
20458
|
-
|
|
20459
|
-
const
|
|
20460
|
-
|
|
20461
|
-
|
|
20462
|
-
code: "missing_artifact_type",
|
|
20463
|
-
|
|
20464
|
-
|
|
20465
|
-
|
|
20648
|
+
if (agentGraph) {
|
|
20649
|
+
for (const node of agentGraph.nodes) {
|
|
20650
|
+
const def = schemaByName.get(node.type);
|
|
20651
|
+
if (!def) {
|
|
20652
|
+
issues.push({ code: "missing_artifact_type", severity: "warning", artifactType: node.type, message: `\u7F3A\u5C11 ArtifactType ${node.type}\uFF0C\u672C\u6B21\u4E0D\u4F1A\u521B\u5EFA\u8BE5\u8282\u70B9\u3002` });
|
|
20653
|
+
continue;
|
|
20654
|
+
}
|
|
20655
|
+
const owner = await resolveRole(def.ownerRole, node.type);
|
|
20656
|
+
const ownerActor = owner.actor ?? `pending:role:${def.ownerRole}`;
|
|
20657
|
+
availableNodes.push({
|
|
20658
|
+
type: node.type,
|
|
20659
|
+
title: node.title,
|
|
20660
|
+
id: deriveArtifactId(node.type, input.workspace, node.title, usedIds),
|
|
20661
|
+
role: def.ownerRole,
|
|
20662
|
+
owner: ownerActor,
|
|
20663
|
+
dependsOn: node.dependsOn,
|
|
20664
|
+
...node.brief ? { brief: node.brief } : {}
|
|
20466
20665
|
});
|
|
20467
|
-
continue;
|
|
20468
20666
|
}
|
|
20469
|
-
|
|
20470
|
-
const
|
|
20471
|
-
|
|
20472
|
-
|
|
20473
|
-
|
|
20667
|
+
} else {
|
|
20668
|
+
const desired = desiredArtifacts(primaryType, input.description);
|
|
20669
|
+
for (const type of desired) {
|
|
20670
|
+
const def = schemaByName.get(type);
|
|
20671
|
+
if (!def) {
|
|
20672
|
+
issues.push({ code: "missing_artifact_type", severity: "warning", artifactType: type, message: `\u7F3A\u5C11 ArtifactType ${type}\uFF0C\u672C\u6B21\u4E0D\u4F1A\u521B\u5EFA\u8BE5\u8282\u70B9\u3002` });
|
|
20673
|
+
continue;
|
|
20674
|
+
}
|
|
20675
|
+
const owner = await resolveRole(def.ownerRole, type);
|
|
20676
|
+
const ownerActor = owner.actor ?? (type === primaryType ? input.actor : `pending:role:${def.ownerRole}`);
|
|
20677
|
+
const id = type === primaryType ? primaryId : `artifact:${type}:${slug4}`;
|
|
20678
|
+
availableNodes.push({ type, id, role: def.ownerRole, owner: ownerActor });
|
|
20679
|
+
}
|
|
20474
20680
|
}
|
|
20475
20681
|
for (const node of availableNodes) {
|
|
20476
|
-
const inputs =
|
|
20682
|
+
const inputs = agentGraph ? agentEdgesFor(node.dependsOn ?? [], availableNodes, briefId, issues) : ruleEdgesFor(node.type, availableNodes, briefId);
|
|
20477
20683
|
ops.push({
|
|
20478
20684
|
action: "spawn",
|
|
20479
20685
|
id: node.id,
|
|
20480
20686
|
type: node.type,
|
|
20481
20687
|
owner: node.owner,
|
|
20482
20688
|
workspace: input.workspace,
|
|
20689
|
+
...node.title ? { title: node.title } : {},
|
|
20483
20690
|
inputs,
|
|
20484
|
-
description: node.type === "prd" && issues.length > 0 ? `\u964D\u7EA7\u65B9\u6848\u6587\u6863\uFF1A${displayTitle}` : `${labelForType(node.type)}\uFF1A${displayTitle}`
|
|
20691
|
+
description: !agentGraph && node.type === "prd" && issues.length > 0 ? `\u964D\u7EA7\u65B9\u6848\u6587\u6863\uFF1A${displayTitle}` : `${labelForType(node.type)}\uFF1A${node.title ?? displayTitle}`
|
|
20485
20692
|
});
|
|
20486
20693
|
ops.push({
|
|
20487
20694
|
action: "annotate",
|
|
20488
20695
|
artifactId: node.id,
|
|
20489
|
-
body:
|
|
20490
|
-
blocks: false
|
|
20491
|
-
});
|
|
20492
|
-
spawned.push(node);
|
|
20493
|
-
}
|
|
20494
|
-
if (issues.length > 0) {
|
|
20495
|
-
ops.push({
|
|
20496
|
-
action: "annotate",
|
|
20497
|
-
artifactId: briefId,
|
|
20498
|
-
body: planningIssueBody(issues),
|
|
20696
|
+
body: node.brief ?? taskBookFor(node.type, input.description, node.role, issues, agentGraph?.notes),
|
|
20499
20697
|
blocks: false
|
|
20500
20698
|
});
|
|
20699
|
+
spawned.push({ id: node.id, type: node.type, role: node.role, owner: node.owner });
|
|
20501
20700
|
}
|
|
20502
20701
|
return {
|
|
20503
20702
|
plan: {
|
|
@@ -20543,18 +20742,21 @@ function buildPmAgentPlannerPrompt(input) {
|
|
|
20543
20742
|
"",
|
|
20544
20743
|
JSON.stringify({
|
|
20545
20744
|
nodes: [
|
|
20546
|
-
{ type: "prd",
|
|
20547
|
-
{ type: "
|
|
20745
|
+
{ type: "prd", title: "\u9700\u6C42\u4E0E\u8303\u56F4\u8BF4\u660E", dependsOn: ["brief"], brief: "\u8FD9\u4E2A\u8282\u70B9\u5177\u4F53\u8981\u505A\u4EC0\u4E48\u7684\u89C4\u683C\uFF1A\u4EA7\u51FA\u4EC0\u4E48\u3001\u8981\u70B9\u3001\u7EA6\u675F" },
|
|
20746
|
+
{ type: "research", title: "\u7ADE\u54C1\u8C03\u7814", dependsOn: ["brief"], brief: "\u2026" },
|
|
20747
|
+
{ type: "dev", title: "\u540E\u7AEF\u5B9E\u73B0", dependsOn: ["prd:\u9700\u6C42\u4E0E\u8303\u56F4\u8BF4\u660E"], brief: "\u2026" }
|
|
20548
20748
|
],
|
|
20549
20749
|
notes: ["\u53EF\u9009\uFF1A\u89C4\u5212\u7406\u7531\u6216\u98CE\u9669\u63D0\u793A"]
|
|
20550
20750
|
}, null, 2),
|
|
20551
20751
|
"",
|
|
20552
20752
|
"\u89C4\u5219\uFF1A",
|
|
20553
20753
|
"- brief \u6839\u8282\u70B9\u7531\u7CFB\u7EDF\u56FA\u5B9A\u521B\u5EFA\uFF0C\u4F60\u4E0D\u8981\u628A brief \u5199\u8FDB nodes\u3002",
|
|
20554
|
-
"- type \u53EA\u80FD\u4ECE\u4E0A\u9762\u3010\u53EF\u7528 ArtifactType\u3011\u76EE\u5F55\u91CC\u9009\uFF08\u4EE5\u6CE8\u5165\u76EE\u5F55\u4E3A\u51C6\
|
|
20555
|
-
"- \u6BCF\u4E2A\u8282\u70B9\
|
|
20556
|
-
|
|
20557
|
-
"-
|
|
20754
|
+
"- type \u53EA\u80FD\u4ECE\u4E0A\u9762\u3010\u53EF\u7528 ArtifactType\u3011\u76EE\u5F55\u91CC\u9009\uFF08\u4EE5\u6CE8\u5165\u76EE\u5F55\u4E3A\u51C6\uFF09\u3002",
|
|
20755
|
+
"- \u6BCF\u4E2A\u8282\u70B9\u5FC5\u7ED9 title\uFF08\u226420\u5B57\u3001\u7528\u6765\u533A\u5206\u8282\u70B9\uFF09+ brief\uFF08\u8FD9\u4E2A\u8282\u70B9\u8981\u4EA7\u51FA\u4EC0\u4E48\u7684\u89C4\u683C\uFF0C\u522B\u5957\u6A21\u677F\u3001\u522B\u53EA\u5199\u7C7B\u578B\u540D\uFF09\u3002",
|
|
20756
|
+
'- \u3010\u53EF\u6709\u591A\u4E2A\u540C type \u8282\u70B9\u3011\uFF0C\u7528\u4E0D\u540C title \u533A\u5206\uFF08\u5982\u4E24\u4E2A prd\uFF1A"\u524D\u7AEF\u9700\u6C42"\u3001"\u540E\u7AEF\u9700\u6C42"\uFF1B\u591A\u4EFD\u8C03\u7814\u62A5\u544A\u5404\u53D6\u5176\u540D\uFF09\u3002',
|
|
20757
|
+
"- \u540C\u4E00\u5DE5\u5355\u5185\u3001\u540C type \u4E0B title \u5FC5\u987B\u552F\u4E00\uFF1Btitle \u4E0D\u542B\u534A\u89D2\u5192\u53F7 `:`\uFF08\u4E2D\u6587\u5168\u89D2 `\uFF1A` \u53EF\u4EE5\uFF09\u3002",
|
|
20758
|
+
'- dependsOn \u5F15\u7528\u4E0A\u6E38\u8282\u70B9\u7684 "type:title"\uFF08\u5982 "prd:\u9700\u6C42\u4E0E\u8303\u56F4\u8BF4\u660E"\uFF09\uFF0C\u6216\u7528 "brief" \u5F15\u7528\u5DE5\u5355\u6839\uFF1B\u65E0\u4E0A\u6E38\u7ED9 []\u3002',
|
|
20759
|
+
"- \u7B80\u5355\u9700\u6C42\u53EF\u4EE5\u53EA\u8981 prd\uFF1B\u6709\u8C03\u7814/\u8BBE\u8BA1/\u5F00\u53D1/\u6D4B\u8BD5/\u67B6\u6784\u9700\u8981\u65F6\u518D\u52A0\u5BF9\u5E94\u8282\u70B9\u3002"
|
|
20558
20760
|
].join("\n");
|
|
20559
20761
|
}
|
|
20560
20762
|
function parseAgentWorkorderPlan(text) {
|
|
@@ -20652,16 +20854,28 @@ function desiredArtifacts(primaryType, description) {
|
|
|
20652
20854
|
}
|
|
20653
20855
|
function normalizeAgentPlan(plan, validTypes) {
|
|
20654
20856
|
const allowed = validTypes.map((t) => t.trim().toLowerCase()).filter((t) => t !== "brief");
|
|
20655
|
-
const
|
|
20656
|
-
const
|
|
20857
|
+
const nodes = [];
|
|
20858
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20859
|
+
const dupTitles = [];
|
|
20657
20860
|
for (const node of plan.nodes) {
|
|
20658
20861
|
const type = node.type.trim().toLowerCase();
|
|
20659
20862
|
if (type === "brief") continue;
|
|
20660
|
-
if (!
|
|
20661
|
-
|
|
20863
|
+
if (!allowed.includes(type)) continue;
|
|
20864
|
+
const title = (node.title?.trim() || labelForType(type)).slice(0, 40);
|
|
20865
|
+
const key = `${type}:${title.toLowerCase()}`;
|
|
20866
|
+
if (seen.has(key)) {
|
|
20867
|
+
dupTitles.push(`${type}:${title}`);
|
|
20868
|
+
continue;
|
|
20869
|
+
}
|
|
20870
|
+
seen.add(key);
|
|
20871
|
+
nodes.push({
|
|
20872
|
+
type,
|
|
20873
|
+
title,
|
|
20874
|
+
dependsOn: node.dependsOn?.map((d) => d.trim()).filter(Boolean) ?? [],
|
|
20875
|
+
...node.brief ? { brief: node.brief } : {}
|
|
20876
|
+
});
|
|
20662
20877
|
}
|
|
20663
|
-
|
|
20664
|
-
return filtered.length > 0 ? { types: filtered, dependsOn, ...plan.notes?.length ? { notes: plan.notes } : {} } : null;
|
|
20878
|
+
return nodes.length > 0 ? { nodes, dupTitles, ...plan.notes?.length ? { notes: plan.notes } : {} } : null;
|
|
20665
20879
|
}
|
|
20666
20880
|
function extractJson(text) {
|
|
20667
20881
|
const fence = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
@@ -20674,23 +20888,30 @@ function extractJson(text) {
|
|
|
20674
20888
|
if (arrStart >= 0 && arrEnd > arrStart) return text.slice(arrStart, arrEnd + 1);
|
|
20675
20889
|
return null;
|
|
20676
20890
|
}
|
|
20677
|
-
function
|
|
20678
|
-
const idOf = (t) => nodes.find((node) => node.type === t)?.id;
|
|
20891
|
+
function agentEdgesFor(dependsOn, nodes, briefId, issues) {
|
|
20679
20892
|
const edges = /* @__PURE__ */ new Set();
|
|
20680
|
-
const
|
|
20681
|
-
|
|
20682
|
-
|
|
20683
|
-
|
|
20684
|
-
|
|
20685
|
-
|
|
20686
|
-
if (id) edges.add(id);
|
|
20687
|
-
}
|
|
20893
|
+
for (const dep of dependsOn) {
|
|
20894
|
+
const d = dep.trim();
|
|
20895
|
+
if (!d) continue;
|
|
20896
|
+
if (d.toLowerCase() === "brief") {
|
|
20897
|
+
edges.add(briefId);
|
|
20898
|
+
continue;
|
|
20688
20899
|
}
|
|
20900
|
+
const sep = d.indexOf(":");
|
|
20901
|
+
const depType = (sep >= 0 ? d.slice(0, sep) : d).trim().toLowerCase();
|
|
20902
|
+
const depTitle = sep >= 0 ? d.slice(sep + 1).trim().toLowerCase() : null;
|
|
20903
|
+
const match = nodes.find((n) => n.type === depType && (depTitle === null || (n.title ?? "").toLowerCase() === depTitle));
|
|
20904
|
+
if (match) edges.add(match.id);
|
|
20905
|
+
else issues.push({ code: "planner_invalid", severity: "warning", message: `\u8282\u70B9\u4F9D\u8D56\u300C${dep}\u300D\u627E\u4E0D\u5230\u5BF9\u5E94\u4E0A\u6E38\uFF08\u6309 type:title \u5339\u914D\uFF09\uFF0C\u5DF2\u5FFD\u7565\u8FD9\u6761\u8FB9\u3002` });
|
|
20689
20906
|
}
|
|
20690
|
-
if (edges.size === 0)
|
|
20691
|
-
|
|
20692
|
-
|
|
20693
|
-
|
|
20907
|
+
if (edges.size === 0) edges.add(briefId);
|
|
20908
|
+
return [...edges].map((to) => ({ to, required: true }));
|
|
20909
|
+
}
|
|
20910
|
+
function ruleEdgesFor(type, nodes, briefId) {
|
|
20911
|
+
const idOf = (t) => nodes.find((node) => node.type === t)?.id;
|
|
20912
|
+
const edges = /* @__PURE__ */ new Set();
|
|
20913
|
+
if (type !== "prd") edges.add(idOf("prd") ?? briefId);
|
|
20914
|
+
else edges.add(briefId);
|
|
20694
20915
|
if (type === "adr") {
|
|
20695
20916
|
const research = idOf("research");
|
|
20696
20917
|
if (research) edges.add(research);
|
|
@@ -20727,17 +20948,6 @@ ${plannerNotes.map((note) => `- ${note}`).join("\n")}` : "";
|
|
|
20727
20948
|
notes
|
|
20728
20949
|
].join("\n");
|
|
20729
20950
|
}
|
|
20730
|
-
function planningIssueBody(issues) {
|
|
20731
|
-
return [
|
|
20732
|
-
`${PLANNING_ISSUES_PREFIX}${JSON.stringify(dedupeIssues(issues))}`,
|
|
20733
|
-
"",
|
|
20734
|
-
"# \u5DE5\u5355\u89C4\u5212\u63D0\u793A",
|
|
20735
|
-
"",
|
|
20736
|
-
...dedupeIssues(issues).map((issue2) => `- ${issue2.message}`),
|
|
20737
|
-
"",
|
|
20738
|
-
"\u7CFB\u7EDF\u5DF2\u5148\u521B\u5EFA\u53EF\u63A5\u5355\u8282\u70B9\uFF1B\u7F3A\u5931\u80FD\u529B\u6682\u65F6\u964D\u7EA7\u4E3A PM \u65B9\u6848\u6587\u6863\uFF0C\u8865\u9F50\u5458\u5DE5\u6216\u8FD0\u884C\u65F6\u7ED1\u5B9A\u540E\u53EF\u518D\u6269\u56FE\u3002"
|
|
20739
|
-
].join("\n");
|
|
20740
|
-
}
|
|
20741
20951
|
function dedupeIssues(issues) {
|
|
20742
20952
|
const seen = /* @__PURE__ */ new Set();
|
|
20743
20953
|
const out = [];
|
|
@@ -20763,16 +20973,19 @@ function labelForType(type) {
|
|
|
20763
20973
|
}
|
|
20764
20974
|
function artifactSlug(idOrWorkspace, description) {
|
|
20765
20975
|
const source = idOrWorkspace.split(":").pop() ?? description;
|
|
20766
|
-
|
|
20767
|
-
return slug4 || `wo-${Date.now()}`;
|
|
20976
|
+
return slugify(source, 40) || `wo-${Date.now()}`;
|
|
20768
20977
|
}
|
|
20769
|
-
var
|
|
20978
|
+
var DEFAULT_TYPES, WORKORDER_SPEC_PREFIX;
|
|
20770
20979
|
var init_planner = __esm({
|
|
20771
20980
|
"../server/src/domains/collab/planner.ts"() {
|
|
20772
20981
|
"use strict";
|
|
20773
|
-
|
|
20982
|
+
init_src2();
|
|
20774
20983
|
DEFAULT_TYPES = [
|
|
20775
|
-
|
|
20984
|
+
// brief 的 goal/acceptanceCriteria 现在是【自定义字段】(Phase 3,替代旧 OASIS_WORKORDER_SPEC 批注,铲 #1 死锁根)
|
|
20985
|
+
{ name: "brief", contentType: "text", ownerRole: "pm", fieldDefs: [
|
|
20986
|
+
{ name: "goal", type: "string", description: "\u4E00\u53E5\u8BDD\u76EE\u6807\uFF1A\u8FD9\u4E2A\u5DE5\u5355\u8981\u8FBE\u6210\u4EC0\u4E48" },
|
|
20987
|
+
{ name: "acceptanceCriteria", type: "string[]", description: "\u9A8C\u6536\u6761\u76EE\uFF08\u6EE1\u8DB3\u5373\u7B97\u5B8C\u6210\uFF09" }
|
|
20988
|
+
] },
|
|
20776
20989
|
{ name: "prd", contentType: "text", ownerRole: "pm" },
|
|
20777
20990
|
{ name: "research", contentType: "text", ownerRole: "research" },
|
|
20778
20991
|
{ name: "design", contentType: "text", ownerRole: "designer" },
|
|
@@ -20784,145 +20997,6 @@ var init_planner = __esm({
|
|
|
20784
20997
|
}
|
|
20785
20998
|
});
|
|
20786
20999
|
|
|
20787
|
-
// ../server/src/domains/collab/node-state.ts
|
|
20788
|
-
function nodeLabel(a) {
|
|
20789
|
-
if (a.title) return a.title;
|
|
20790
|
-
if (a.description) return a.description;
|
|
20791
|
-
const tail = a.id.split(":").slice(2).join(":");
|
|
20792
|
-
return tail ? `\u672A\u547D\u540D ${a.type}\uFF08${tail}\uFF09` : `\u672A\u547D\u540D ${a.type}`;
|
|
20793
|
-
}
|
|
20794
|
-
function nodeInfo(model, a) {
|
|
20795
|
-
const lifecycle = lifecycleOf(model, a.id);
|
|
20796
|
-
const sealed = lifecycle !== "active";
|
|
20797
|
-
const concluded = isConcluded(model, a.id);
|
|
20798
|
-
const mark = sealed ? lifecycle : pendingConcludeAttempt(model, a.id) ? "gate-pending" : blockedOf(model, a.id).blocked ? "blocked" : concluded ? "concluded" : a.currentRev ? "active" : "embryo";
|
|
20799
|
-
const hasWorking = revisionsOf(model, a.id).some((r) => r.state === "working");
|
|
20800
|
-
return { mark, queueLen: queueOf(model, a.id).length, concluded, sealed, hasWorking };
|
|
20801
|
-
}
|
|
20802
|
-
function deriveStage(members) {
|
|
20803
|
-
if (members.length > 0 && members.every((m2) => m2.sealed)) return "sealed";
|
|
20804
|
-
const live = members.filter((m2) => !m2.sealed);
|
|
20805
|
-
if (live.length > 0 && live.every((m2) => m2.concluded)) return "concluded";
|
|
20806
|
-
const reviewing = live.some((m2) => m2.mark === "gate-pending" || m2.mark === "active" && m2.queueLen > 0);
|
|
20807
|
-
if (reviewing) return "review";
|
|
20808
|
-
const progressing = live.some((m2) => m2.mark === "active" || m2.mark === "embryo");
|
|
20809
|
-
const blocked = live.some((m2) => m2.mark === "blocked");
|
|
20810
|
-
if (blocked && !progressing) return "blocked";
|
|
20811
|
-
return "executing";
|
|
20812
|
-
}
|
|
20813
|
-
function deriveNodeStage(info) {
|
|
20814
|
-
if (info.sealed) return "sealed";
|
|
20815
|
-
switch (info.mark) {
|
|
20816
|
-
case "concluded":
|
|
20817
|
-
return "concluded";
|
|
20818
|
-
case "gate-pending":
|
|
20819
|
-
return "gate";
|
|
20820
|
-
case "blocked":
|
|
20821
|
-
return "blocked";
|
|
20822
|
-
case "embryo":
|
|
20823
|
-
return "embryo";
|
|
20824
|
-
default:
|
|
20825
|
-
if (info.hasWorking) return "editing";
|
|
20826
|
-
if (info.queueLen > 0) return "review";
|
|
20827
|
-
return "merged";
|
|
20828
|
-
}
|
|
20829
|
-
}
|
|
20830
|
-
var init_node_state = __esm({
|
|
20831
|
-
"../server/src/domains/collab/node-state.ts"() {
|
|
20832
|
-
"use strict";
|
|
20833
|
-
init_src2();
|
|
20834
|
-
}
|
|
20835
|
-
});
|
|
20836
|
-
|
|
20837
|
-
// ../server/src/domains/collab/workorders.ts
|
|
20838
|
-
function buildWorkorderSummaries(model, resolveActor, resolveProject) {
|
|
20839
|
-
const byWorkspace = /* @__PURE__ */ new Map();
|
|
20840
|
-
for (const a of model.artifacts.values()) {
|
|
20841
|
-
const arr = byWorkspace.get(a.workspace) ?? [];
|
|
20842
|
-
arr.push(a);
|
|
20843
|
-
byWorkspace.set(a.workspace, arr);
|
|
20844
|
-
}
|
|
20845
|
-
const ref = (id) => {
|
|
20846
|
-
const extra = resolveActor?.(id);
|
|
20847
|
-
return {
|
|
20848
|
-
id,
|
|
20849
|
-
...extra?.name ? { name: extra.name } : {},
|
|
20850
|
-
...extra?.role ? { role: extra.role } : {},
|
|
20851
|
-
...extra?.avatar ? { avatar: extra.avatar } : {}
|
|
20852
|
-
};
|
|
20853
|
-
};
|
|
20854
|
-
const out = [];
|
|
20855
|
-
for (const [workspace, arts] of byWorkspace) {
|
|
20856
|
-
const members = arts.map((a) => nodeInfo(model, a));
|
|
20857
|
-
const seed = arts.find((a) => a.inputs.length === 0) ?? arts[0];
|
|
20858
|
-
const spec = workorderSpecOf(model, arts);
|
|
20859
|
-
const concluded = members.filter((m2) => m2.concluded).length;
|
|
20860
|
-
const lastOps = arts.map((a) => model.lastOpAt.get(a.id)).filter((t) => Boolean(t)).sort();
|
|
20861
|
-
const participantIds = [...new Set(arts.map((a) => a.owner))];
|
|
20862
|
-
const woTitle = seed.title ?? seed.description ?? workspace;
|
|
20863
|
-
out.push({
|
|
20864
|
-
id: workspace,
|
|
20865
|
-
title: woTitle,
|
|
20866
|
-
// 工单完整内容 = 根产物 description;仅当与标题不同(根产物有独立 title)时给出,避免与 title 重复
|
|
20867
|
-
...seed.description && seed.description !== woTitle ? { description: seed.description } : {},
|
|
20868
|
-
// 一句话目标:优先取结构化 spec.goal;缺省时前端回退 description 首段(投影不替前端做回退,只给真值)
|
|
20869
|
-
...spec?.goal ? { goal: spec.goal } : {},
|
|
20870
|
-
stage: deriveStage(members),
|
|
20871
|
-
owner: ref(seed.owner),
|
|
20872
|
-
participants: participantIds.map(ref),
|
|
20873
|
-
artifactCount: arts.length,
|
|
20874
|
-
progress: { concluded, total: arts.length },
|
|
20875
|
-
updatedAt: lastOps[lastOps.length - 1] ?? "",
|
|
20876
|
-
projectId: resolveProject?.(workspace) ?? null,
|
|
20877
|
-
planning: planningStatusOf(model, arts),
|
|
20878
|
-
...workspaceDispatchPaused(model, arts) ? { dispatchPaused: true } : {}
|
|
20879
|
-
});
|
|
20880
|
-
}
|
|
20881
|
-
return out.sort((a, b2) => a.updatedAt < b2.updatedAt ? 1 : a.updatedAt > b2.updatedAt ? -1 : 0);
|
|
20882
|
-
}
|
|
20883
|
-
function planningStatusOf(model, arts) {
|
|
20884
|
-
const ids = new Set(arts.map((a) => a.id));
|
|
20885
|
-
const issues = [...model.annotations.values()].filter((annotation) => ids.has(annotation.anchor.target)).flatMap((annotation) => parsePlanningIssues(annotation.body));
|
|
20886
|
-
return issues.length > 0 ? { status: "needs_staffing", issues } : { status: "ready", issues: [] };
|
|
20887
|
-
}
|
|
20888
|
-
function workorderSpecOf(model, arts) {
|
|
20889
|
-
const seed = arts.find((a) => a.type === "brief") ?? arts.find((a) => a.inputs.length === 0);
|
|
20890
|
-
if (!seed) return null;
|
|
20891
|
-
for (const annotation of model.annotations.values()) {
|
|
20892
|
-
if (annotation.anchor.target !== seed.id) continue;
|
|
20893
|
-
const spec = parseWorkorderSpec(annotation.body);
|
|
20894
|
-
if (spec) return spec;
|
|
20895
|
-
}
|
|
20896
|
-
return null;
|
|
20897
|
-
}
|
|
20898
|
-
function workspaceDispatchPaused(model, arts) {
|
|
20899
|
-
const seed = arts.find((a) => a.type === "brief") ?? arts.find((a) => a.inputs.length === 0);
|
|
20900
|
-
if (!seed) return false;
|
|
20901
|
-
for (const annotation of model.annotations.values()) {
|
|
20902
|
-
if (annotation.anchor.target !== seed.id) continue;
|
|
20903
|
-
if (annotation.state === "open" && annotation.body.startsWith(DISPATCH_HOLD_PREFIX)) return true;
|
|
20904
|
-
}
|
|
20905
|
-
return false;
|
|
20906
|
-
}
|
|
20907
|
-
function openDispatchHoldOf(model, arts) {
|
|
20908
|
-
const seed = arts.find((a) => a.type === "brief") ?? arts.find((a) => a.inputs.length === 0);
|
|
20909
|
-
if (!seed) return null;
|
|
20910
|
-
for (const annotation of model.annotations.values()) {
|
|
20911
|
-
if (annotation.anchor.target !== seed.id) continue;
|
|
20912
|
-
if (annotation.state === "open" && annotation.body.startsWith(DISPATCH_HOLD_PREFIX)) return annotation.id;
|
|
20913
|
-
}
|
|
20914
|
-
return null;
|
|
20915
|
-
}
|
|
20916
|
-
var DISPATCH_HOLD_PREFIX;
|
|
20917
|
-
var init_workorders = __esm({
|
|
20918
|
-
"../server/src/domains/collab/workorders.ts"() {
|
|
20919
|
-
"use strict";
|
|
20920
|
-
init_node_state();
|
|
20921
|
-
init_planner();
|
|
20922
|
-
DISPATCH_HOLD_PREFIX = "OASIS_DISPATCH_HOLD";
|
|
20923
|
-
}
|
|
20924
|
-
});
|
|
20925
|
-
|
|
20926
21000
|
// ../server/src/server.ts
|
|
20927
21001
|
function wantsChatParts(req) {
|
|
20928
21002
|
const accept = req.headers.accept;
|
|
@@ -21183,6 +21257,10 @@ async function runCommand(kernel, blobs, oplog, actor, command, args, ctx = {})
|
|
|
21183
21257
|
...p2.spec !== void 0 ? { spec: String(p2.spec) } : {},
|
|
21184
21258
|
...Array.isArray(p2.deps) ? { deps: p2.deps.map(String) } : {}
|
|
21185
21259
|
}));
|
|
21260
|
+
const rawFields = args["fields"];
|
|
21261
|
+
if (rawFields !== void 0 && (typeof rawFields !== "object" || rawFields === null || Array.isArray(rawFields))) {
|
|
21262
|
+
throw new Error("\u53C2\u6570 fields \u5FC5\u987B\u662F\u5BF9\u8C61 {key: value}");
|
|
21263
|
+
}
|
|
21186
21264
|
const createdViaArg = optStr(args, "createdVia");
|
|
21187
21265
|
if (createdViaArg !== void 0 && createdViaArg !== "seed" && createdViaArg !== "manual-spawn") {
|
|
21188
21266
|
throw new Error(`createdVia \u4EC5\u63A5\u53D7 seed\uFF08\u53D1\u8D77\u5DE5\u5355\uFF09\u6216 manual-spawn\uFF1Bauto-trigger/prereq \u7531\u5185\u6838\u5185\u90E8\u8BBE\u7F6E`);
|
|
@@ -21274,6 +21352,33 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
|
|
|
21274
21352
|
if (!preview2.endState.ok) {
|
|
21275
21353
|
throw new Error(`\u52A8\u6001\u89C4\u5212\u7EC8\u6001\u6821\u9A8C\u4E0D\u8FC7\uFF1A${preview2.endState.violations.join("\uFF1B")}`);
|
|
21276
21354
|
}
|
|
21355
|
+
if (args["stage"] === true && ctx.workorderDrafts) {
|
|
21356
|
+
const draft = ctx.workorderDrafts.submit({
|
|
21357
|
+
workspace,
|
|
21358
|
+
plan: planned.plan,
|
|
21359
|
+
rootArtifactId: planned.rootArtifactId,
|
|
21360
|
+
seed: {
|
|
21361
|
+
brief,
|
|
21362
|
+
...goal !== void 0 ? { goal } : {},
|
|
21363
|
+
...acceptanceCriteria && acceptanceCriteria.length > 0 ? { acceptanceCriteria } : {},
|
|
21364
|
+
...args["dispatch"] === false ? { dispatch: false } : {}
|
|
21365
|
+
},
|
|
21366
|
+
draftedBy: actor
|
|
21367
|
+
});
|
|
21368
|
+
return {
|
|
21369
|
+
message: planned.issues.length > 0 ? `staged workorder draft ${draft.id}\uFF1A${planned.spawned.length} \u4E2A\u8282\u70B9\u5F85\u4EBA\u786E\u8BA4\uFF0C${planned.issues.length} \u4E2A staffing \u63D0\u793A` : `staged workorder draft ${draft.id}\uFF1A${planned.spawned.length} \u4E2A\u8282\u70B9\u5F85\u4EBA\u786E\u8BA4`,
|
|
21370
|
+
data: {
|
|
21371
|
+
draftId: draft.id,
|
|
21372
|
+
workspace,
|
|
21373
|
+
rootArtifactId: planned.rootArtifactId,
|
|
21374
|
+
spawned: planned.spawned,
|
|
21375
|
+
planning: {
|
|
21376
|
+
status: planned.issues.length > 0 ? "needs_staffing" : "ready",
|
|
21377
|
+
issues: planned.issues
|
|
21378
|
+
}
|
|
21379
|
+
}
|
|
21380
|
+
};
|
|
21381
|
+
}
|
|
21277
21382
|
const applied = await kernel.applyIntervention(planned.plan, preview2.baseSeqs, actor);
|
|
21278
21383
|
const briefArtifactId = planned.rootArtifactId;
|
|
21279
21384
|
if (brief && brief.trim()) {
|
|
@@ -21283,7 +21388,7 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
|
|
|
21283
21388
|
await kernel.conclude({ artifactId: briefArtifactId, actor, note: "\u5EFA\u5355\u65F6\u7531\u53D1\u8D77\u4EBA\u62CD\u677F\u5B9A\u7A3F" });
|
|
21284
21389
|
}
|
|
21285
21390
|
if (args["dispatch"] === false) {
|
|
21286
|
-
await
|
|
21391
|
+
await ctx.artifactState?.setWorkspaceDispatchHold(workspace, true);
|
|
21287
21392
|
}
|
|
21288
21393
|
return {
|
|
21289
21394
|
message: planned.issues.length > 0 ? `planned ${workspace}: created ${planned.spawned.length} nodes\uFF0C${planned.issues.length} \u4E2A staffing \u63D0\u793A\u5DF2\u5199\u5165\u5DE5\u5355` : `planned ${workspace}: created ${planned.spawned.length} nodes`,
|
|
@@ -21312,7 +21417,8 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
|
|
|
21312
21417
|
...optStr(args, "description") !== void 0 ? { description: optStr(args, "description") } : {},
|
|
21313
21418
|
...optStr(args, "docType") !== void 0 ? { docType: optStr(args, "docType") } : {},
|
|
21314
21419
|
...inputs !== void 0 ? { inputs } : {},
|
|
21315
|
-
...parts !== void 0 ? { parts } : {}
|
|
21420
|
+
...parts !== void 0 ? { parts } : {},
|
|
21421
|
+
...rawFields !== void 0 ? { fields: rawFields } : {}
|
|
21316
21422
|
});
|
|
21317
21423
|
return { message: `spawned ${artifact.id}\uFF08owner=${artifact.owner}${artifact.parts?.length ? `\uFF0Cparts=${artifact.parts.map((p2) => p2.name).join("/")}` : ""}\uFF09`, data: artifact };
|
|
21318
21424
|
}
|
|
@@ -21836,17 +21942,9 @@ async function startOasisServer(opts) {
|
|
|
21836
21942
|
...opts.planWorkorderWithAgent ? { planWorkorderWithAgent: opts.planWorkorderWithAgent } : {},
|
|
21837
21943
|
...opts.resolveCodeRepo ? { resolveCodeRepo: opts.resolveCodeRepo } : {},
|
|
21838
21944
|
...opts.dispatchProduce ? { dispatchProduce: opts.dispatchProduce } : {},
|
|
21945
|
+
...opts.workorderDrafts ? { workorderDrafts: opts.workorderDrafts } : {},
|
|
21839
21946
|
...opts.gitEnv ? { gitEnv: opts.gitEnv } : {}
|
|
21840
21947
|
});
|
|
21841
|
-
if (body.command === "spawn" && opts.chatSession) {
|
|
21842
|
-
const workspace = body.args?.["workspace"];
|
|
21843
|
-
if (workspace && workspace !== "ws:default") {
|
|
21844
|
-
const chatSid = req.headers["x-chat-session-id"];
|
|
21845
|
-
if (typeof chatSid === "string") {
|
|
21846
|
-
await opts.chatSession.linkWorkOrder(chatSid, workspace).catch(() => void 0);
|
|
21847
|
-
}
|
|
21848
|
-
}
|
|
21849
|
-
}
|
|
21850
21948
|
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify(result));
|
|
21851
21949
|
} catch (err) {
|
|
21852
21950
|
const status = err instanceof FrozenError ? 409 : 400;
|
|
@@ -21883,6 +21981,40 @@ async function startOasisServer(opts) {
|
|
|
21883
21981
|
}
|
|
21884
21982
|
return;
|
|
21885
21983
|
}
|
|
21984
|
+
if (url.pathname === "/api/workorder/draft" && req.method === "GET" && opts.workorderDrafts) {
|
|
21985
|
+
const workspace = url.searchParams.get("workspace");
|
|
21986
|
+
const draft = workspace ? opts.workorderDrafts.forWorkspace(workspace) : void 0;
|
|
21987
|
+
if (!draft) {
|
|
21988
|
+
res.writeHead(404, { "content-type": "application/json" }).end(JSON.stringify({ error: "no staged workorder draft" }));
|
|
21989
|
+
return;
|
|
21990
|
+
}
|
|
21991
|
+
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ data: draft }));
|
|
21992
|
+
return;
|
|
21993
|
+
}
|
|
21994
|
+
if (url.pathname === "/api/workorder/apply" && req.method === "POST") {
|
|
21995
|
+
try {
|
|
21996
|
+
const body = JSON.parse(rawBody);
|
|
21997
|
+
const result = await engine.kernel.applyIntervention(body.plan, body.baseSeqs, actor);
|
|
21998
|
+
const brief = engine.kernel.model.artifacts.get(body.briefArtifactId);
|
|
21999
|
+
if (brief && brief.description && brief.description.trim()) {
|
|
22000
|
+
const briefRef = await engine.blobs.put(new TextEncoder().encode(brief.description));
|
|
22001
|
+
await engine.kernel.proposeRevision({ artifactId: body.briefArtifactId, actor, contentRef: briefRef, reason: "\u5EFA\u5355\uFF1A\u843D\u5B9A\u53D1\u8D77\u4EBA\u6572\u5B9A\u7684 brief \u9996\u7248" });
|
|
22002
|
+
await engine.kernel.advanceQueue(body.briefArtifactId, actor);
|
|
22003
|
+
await engine.kernel.conclude({ artifactId: body.briefArtifactId, actor, note: "\u5EFA\u5355\u65F6\u7531\u53D1\u8D77\u4EBA\u62CD\u677F\u5B9A\u7A3F" });
|
|
22004
|
+
}
|
|
22005
|
+
if (body.dispatch === false && brief?.workspace) {
|
|
22006
|
+
await opts.artifactState?.setWorkspaceDispatchHold(brief.workspace, true);
|
|
22007
|
+
}
|
|
22008
|
+
res.writeHead(200, { "content-type": "application/json" }).end(
|
|
22009
|
+
JSON.stringify({ ...result, briefArtifactId: body.briefArtifactId, workspace: brief?.workspace ?? null })
|
|
22010
|
+
);
|
|
22011
|
+
} catch (err) {
|
|
22012
|
+
res.writeHead(409, { "content-type": "application/json" }).end(
|
|
22013
|
+
JSON.stringify({ error: err instanceof Error ? err.message : String(err) })
|
|
22014
|
+
);
|
|
22015
|
+
}
|
|
22016
|
+
return;
|
|
22017
|
+
}
|
|
21886
22018
|
if (url.pathname === "/api/intervention/draft" && req.method === "POST" && opts.drafts) {
|
|
21887
22019
|
try {
|
|
21888
22020
|
const plan = JSON.parse(rawBody);
|
|
@@ -22129,8 +22261,7 @@ async function startOasisServer(opts) {
|
|
|
22129
22261
|
const session = await opts.dispatchChat({
|
|
22130
22262
|
actorId: body.actorId,
|
|
22131
22263
|
message: body.message,
|
|
22132
|
-
...body.sessionId ? { sessionId: body.sessionId } : {}
|
|
22133
|
-
...body.chatSessionId ? { chatSessionId: body.chatSessionId } : {}
|
|
22264
|
+
...body.sessionId ? { sessionId: body.sessionId } : {}
|
|
22134
22265
|
});
|
|
22135
22266
|
let finished = false;
|
|
22136
22267
|
res.on("close", () => {
|
|
@@ -22216,6 +22347,14 @@ async function startOasisServer(opts) {
|
|
|
22216
22347
|
});
|
|
22217
22348
|
writeFileSync14(join16(dir, "SKILLS.md"), ["# \u53EF\u7528\u6280\u80FD", "", "\u4EE5\u4E0B\u6280\u80FD\u5DF2\u4E3A\u4F60\u542F\u7528\uFF0C\u53EF\u5728\u672C\u6B21\u4F1A\u8BDD\u4E2D\u76F4\u63A5\u4F7F\u7528\uFF1A", "", ...lines].join("\n"));
|
|
22218
22349
|
}
|
|
22350
|
+
if (opts.materializeSkills) {
|
|
22351
|
+
const skillFiles = await opts.materializeSkills(body.actorId, "claude").catch(() => ({}));
|
|
22352
|
+
for (const [rel, content] of Object.entries(skillFiles)) {
|
|
22353
|
+
const file = join16(dir, rel);
|
|
22354
|
+
mkdirSync13(dirname13(file), { recursive: true });
|
|
22355
|
+
writeFileSync14(file, content);
|
|
22356
|
+
}
|
|
22357
|
+
}
|
|
22219
22358
|
const activeConnectors = actorCtx.connectors.filter(
|
|
22220
22359
|
(c) => c.status === "connected" || c.status === "verifying"
|
|
22221
22360
|
);
|
|
@@ -22231,8 +22370,8 @@ async function startOasisServer(opts) {
|
|
|
22231
22370
|
}
|
|
22232
22371
|
}
|
|
22233
22372
|
const { spawn: spawn6 } = await import("node:child_process");
|
|
22234
|
-
const { randomUUID:
|
|
22235
|
-
const sessionId = body.sessionId ??
|
|
22373
|
+
const { randomUUID: randomUUID14 } = await import("node:crypto");
|
|
22374
|
+
const sessionId = body.sessionId ?? randomUUID14();
|
|
22236
22375
|
const args = [
|
|
22237
22376
|
"-p",
|
|
22238
22377
|
body.message,
|
|
@@ -22243,7 +22382,7 @@ async function startOasisServer(opts) {
|
|
|
22243
22382
|
];
|
|
22244
22383
|
const child = spawn6(opts.claudeBin ?? "claude", args, {
|
|
22245
22384
|
...spawnCwd ? { cwd: spawnCwd } : {},
|
|
22246
|
-
env: { ...process.env, ...env
|
|
22385
|
+
env: { ...process.env, ...env },
|
|
22247
22386
|
stdio: ["ignore", "pipe", "pipe"]
|
|
22248
22387
|
});
|
|
22249
22388
|
if (typedParts) {
|
|
@@ -22535,8 +22674,8 @@ var init_server3 = __esm({
|
|
|
22535
22674
|
init_mcp2();
|
|
22536
22675
|
init_router();
|
|
22537
22676
|
init_drafts();
|
|
22677
|
+
init_workorder_drafts();
|
|
22538
22678
|
init_planner();
|
|
22539
|
-
init_workorders();
|
|
22540
22679
|
defaultResolveActor = (token) => token.startsWith("token:") ? token.slice("token:".length) : null;
|
|
22541
22680
|
enc = (s2) => new TextEncoder().encode(s2);
|
|
22542
22681
|
CHAT_OUTPUT_LIMIT = 16e3;
|
|
@@ -22614,11 +22753,11 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
|
|
|
22614
22753
|
return m2;
|
|
22615
22754
|
};
|
|
22616
22755
|
return {
|
|
22617
|
-
issue(
|
|
22756
|
+
issue(name, ttlMs = defaultTtlMs) {
|
|
22618
22757
|
const m2 = prune();
|
|
22619
22758
|
const token = `ent_${(0, import_node_crypto3.randomBytes)(24).toString("base64url")}`;
|
|
22620
22759
|
const expiresAt = Date.now() + ttlMs;
|
|
22621
|
-
m2.set(token, {
|
|
22760
|
+
m2.set(token, { name, expiresAt });
|
|
22622
22761
|
write(m2);
|
|
22623
22762
|
return { token, expiresAt: new Date(expiresAt).toISOString() };
|
|
22624
22763
|
},
|
|
@@ -22626,7 +22765,7 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
|
|
|
22626
22765
|
const m2 = prune();
|
|
22627
22766
|
const entry = m2.get(token);
|
|
22628
22767
|
if (!entry) return null;
|
|
22629
|
-
return {
|
|
22768
|
+
return { name: entry.name, ...entry.issuedNodeId ? { issuedNodeId: entry.issuedNodeId } : {} };
|
|
22630
22769
|
},
|
|
22631
22770
|
consume(token) {
|
|
22632
22771
|
const m2 = prune();
|
|
@@ -22634,7 +22773,15 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
|
|
|
22634
22773
|
if (!entry) return null;
|
|
22635
22774
|
m2.delete(token);
|
|
22636
22775
|
write(m2);
|
|
22637
|
-
return {
|
|
22776
|
+
return { name: entry.name, ...entry.issuedNodeId ? { issuedNodeId: entry.issuedNodeId } : {} };
|
|
22777
|
+
},
|
|
22778
|
+
setIssuedNodeId(token, nodeId) {
|
|
22779
|
+
const m2 = read();
|
|
22780
|
+
const entry = m2.get(token);
|
|
22781
|
+
if (entry) {
|
|
22782
|
+
entry.issuedNodeId = nodeId;
|
|
22783
|
+
write(m2);
|
|
22784
|
+
}
|
|
22638
22785
|
}
|
|
22639
22786
|
};
|
|
22640
22787
|
}
|
|
@@ -23746,6 +23893,147 @@ var init_projection = __esm({
|
|
|
23746
23893
|
}
|
|
23747
23894
|
});
|
|
23748
23895
|
|
|
23896
|
+
// ../server/src/domains/collab/node-state.ts
|
|
23897
|
+
function nodeLabel(a) {
|
|
23898
|
+
if (a.title) return a.title;
|
|
23899
|
+
if (a.description) return a.description;
|
|
23900
|
+
const tail = a.id.split(":").slice(2).join(":");
|
|
23901
|
+
return tail ? `\u672A\u547D\u540D ${a.type}\uFF08${tail}\uFF09` : `\u672A\u547D\u540D ${a.type}`;
|
|
23902
|
+
}
|
|
23903
|
+
function nodeInfo(model, a) {
|
|
23904
|
+
const lifecycle = lifecycleOf(model, a.id);
|
|
23905
|
+
const sealed = lifecycle !== "active";
|
|
23906
|
+
const concluded = isConcluded(model, a.id);
|
|
23907
|
+
const mark = sealed ? lifecycle : pendingConcludeAttempt(model, a.id) ? "gate-pending" : blockedOf(model, a.id).blocked ? "blocked" : concluded ? "concluded" : a.currentRev ? "active" : "embryo";
|
|
23908
|
+
const hasWorking = revisionsOf(model, a.id).some((r) => r.state === "working");
|
|
23909
|
+
return { mark, queueLen: queueOf(model, a.id).length, concluded, sealed, hasWorking };
|
|
23910
|
+
}
|
|
23911
|
+
function deriveStage(members) {
|
|
23912
|
+
if (members.length > 0 && members.every((m2) => m2.sealed)) return "sealed";
|
|
23913
|
+
const live = members.filter((m2) => !m2.sealed);
|
|
23914
|
+
if (live.length > 0 && live.every((m2) => m2.concluded)) return "concluded";
|
|
23915
|
+
const reviewing = live.some((m2) => m2.mark === "gate-pending" || m2.mark === "active" && m2.queueLen > 0);
|
|
23916
|
+
if (reviewing) return "review";
|
|
23917
|
+
const progressing = live.some((m2) => m2.mark === "active" || m2.mark === "embryo");
|
|
23918
|
+
const blocked = live.some((m2) => m2.mark === "blocked");
|
|
23919
|
+
if (blocked && !progressing) return "blocked";
|
|
23920
|
+
return "executing";
|
|
23921
|
+
}
|
|
23922
|
+
function deriveNodeStage(info) {
|
|
23923
|
+
if (info.sealed) return "sealed";
|
|
23924
|
+
switch (info.mark) {
|
|
23925
|
+
case "concluded":
|
|
23926
|
+
return "concluded";
|
|
23927
|
+
case "gate-pending":
|
|
23928
|
+
return "gate";
|
|
23929
|
+
case "blocked":
|
|
23930
|
+
return "blocked";
|
|
23931
|
+
case "embryo":
|
|
23932
|
+
return "embryo";
|
|
23933
|
+
default:
|
|
23934
|
+
if (info.hasWorking) return "editing";
|
|
23935
|
+
if (info.queueLen > 0) return "review";
|
|
23936
|
+
return "merged";
|
|
23937
|
+
}
|
|
23938
|
+
}
|
|
23939
|
+
var init_node_state = __esm({
|
|
23940
|
+
"../server/src/domains/collab/node-state.ts"() {
|
|
23941
|
+
"use strict";
|
|
23942
|
+
init_src2();
|
|
23943
|
+
}
|
|
23944
|
+
});
|
|
23945
|
+
|
|
23946
|
+
// ../server/src/domains/collab/workorders.ts
|
|
23947
|
+
function buildWorkorderSummaries(model, resolveActor, resolveProject, dispatchPausedOf) {
|
|
23948
|
+
const byWorkspace = /* @__PURE__ */ new Map();
|
|
23949
|
+
for (const a of model.artifacts.values()) {
|
|
23950
|
+
const arr = byWorkspace.get(a.workspace) ?? [];
|
|
23951
|
+
arr.push(a);
|
|
23952
|
+
byWorkspace.set(a.workspace, arr);
|
|
23953
|
+
}
|
|
23954
|
+
const ref = (id) => {
|
|
23955
|
+
const extra = resolveActor?.(id);
|
|
23956
|
+
return {
|
|
23957
|
+
id,
|
|
23958
|
+
...extra?.name ? { name: extra.name } : {},
|
|
23959
|
+
...extra?.role ? { role: extra.role } : {},
|
|
23960
|
+
...extra?.avatar ? { avatar: extra.avatar } : {}
|
|
23961
|
+
};
|
|
23962
|
+
};
|
|
23963
|
+
const out = [];
|
|
23964
|
+
for (const [workspace, arts] of byWorkspace) {
|
|
23965
|
+
const members = arts.map((a) => nodeInfo(model, a));
|
|
23966
|
+
const seed = arts.find((a) => a.inputs.length === 0) ?? arts[0];
|
|
23967
|
+
const spec = workorderSpecOf(model, arts);
|
|
23968
|
+
const concluded = members.filter((m2) => m2.concluded).length;
|
|
23969
|
+
const lastOps = arts.map((a) => model.lastOpAt.get(a.id)).filter((t) => Boolean(t)).sort();
|
|
23970
|
+
const participantIds = [...new Set(arts.map((a) => a.owner))];
|
|
23971
|
+
const woTitle = seed.title ?? seed.description ?? workspace;
|
|
23972
|
+
out.push({
|
|
23973
|
+
id: workspace,
|
|
23974
|
+
title: woTitle,
|
|
23975
|
+
// 工单完整内容 = 根产物 description;仅当与标题不同(根产物有独立 title)时给出,避免与 title 重复
|
|
23976
|
+
...seed.description && seed.description !== woTitle ? { description: seed.description } : {},
|
|
23977
|
+
// 一句话目标:优先取结构化 spec.goal;缺省时前端回退 description 首段(投影不替前端做回退,只给真值)
|
|
23978
|
+
...spec?.goal ? { goal: spec.goal } : {},
|
|
23979
|
+
stage: deriveStage(members),
|
|
23980
|
+
owner: ref(seed.owner),
|
|
23981
|
+
participants: participantIds.map(ref),
|
|
23982
|
+
artifactCount: arts.length,
|
|
23983
|
+
progress: { concluded, total: arts.length },
|
|
23984
|
+
updatedAt: lastOps[lastOps.length - 1] ?? "",
|
|
23985
|
+
projectId: resolveProject?.(workspace) ?? null,
|
|
23986
|
+
planning: planningStatusOf(model, arts),
|
|
23987
|
+
...dispatchPausedOf?.(workspace) ? { dispatchPaused: true } : {}
|
|
23988
|
+
});
|
|
23989
|
+
}
|
|
23990
|
+
return out.sort((a, b2) => a.updatedAt < b2.updatedAt ? 1 : a.updatedAt > b2.updatedAt ? -1 : 0);
|
|
23991
|
+
}
|
|
23992
|
+
function planningStatusOf(_model, arts) {
|
|
23993
|
+
const issues = [];
|
|
23994
|
+
const seenRoles = /* @__PURE__ */ new Set();
|
|
23995
|
+
for (const a of arts) {
|
|
23996
|
+
const m2 = /^pending:role:(.+)$/.exec(a.owner);
|
|
23997
|
+
if (!m2) continue;
|
|
23998
|
+
const role = m2[1];
|
|
23999
|
+
if (seenRoles.has(role)) continue;
|
|
24000
|
+
seenRoles.add(role);
|
|
24001
|
+
issues.push({ code: "missing_role", severity: "warning", role, message: `${role} \u89D2\u8272\u65E0 active \u5458\u5DE5\uFF0C\u76F8\u5173\u8282\u70B9\u5F85\u6D3E\u2014\u2014\u8BF7\u8865\u4EBA\u6216 assign\u3002` });
|
|
24002
|
+
}
|
|
24003
|
+
return issues.length > 0 ? { status: "needs_staffing", issues } : { status: "ready", issues: [] };
|
|
24004
|
+
}
|
|
24005
|
+
function specFromFields(fields) {
|
|
24006
|
+
if (!fields) return null;
|
|
24007
|
+
const out = {};
|
|
24008
|
+
const goal = fields["goal"];
|
|
24009
|
+
if (typeof goal === "string" && goal.trim()) out.goal = goal.trim();
|
|
24010
|
+
const ac = fields["acceptanceCriteria"];
|
|
24011
|
+
if (Array.isArray(ac)) {
|
|
24012
|
+
const items = ac.filter((x2) => typeof x2 === "string" && x2.trim().length > 0).map((x2) => x2.trim());
|
|
24013
|
+
if (items.length > 0) out.acceptanceCriteria = items;
|
|
24014
|
+
}
|
|
24015
|
+
return out.goal !== void 0 || out.acceptanceCriteria !== void 0 ? out : null;
|
|
24016
|
+
}
|
|
24017
|
+
function workorderSpecOf(model, arts) {
|
|
24018
|
+
const seed = arts.find((a) => a.type === "brief") ?? arts.find((a) => a.inputs.length === 0);
|
|
24019
|
+
if (!seed) return null;
|
|
24020
|
+
const fromFields = specFromFields(seed.fields);
|
|
24021
|
+
if (fromFields) return fromFields;
|
|
24022
|
+
for (const annotation of model.annotations.values()) {
|
|
24023
|
+
if (annotation.anchor.target !== seed.id) continue;
|
|
24024
|
+
const spec = parseWorkorderSpec(annotation.body);
|
|
24025
|
+
if (spec) return spec;
|
|
24026
|
+
}
|
|
24027
|
+
return null;
|
|
24028
|
+
}
|
|
24029
|
+
var init_workorders = __esm({
|
|
24030
|
+
"../server/src/domains/collab/workorders.ts"() {
|
|
24031
|
+
"use strict";
|
|
24032
|
+
init_node_state();
|
|
24033
|
+
init_planner();
|
|
24034
|
+
}
|
|
24035
|
+
});
|
|
24036
|
+
|
|
23749
24037
|
// ../server/src/domains/projects/service.ts
|
|
23750
24038
|
function namedError(name, message) {
|
|
23751
24039
|
const err = new Error(message);
|
|
@@ -24003,6 +24291,8 @@ var init_service = __esm({
|
|
|
24003
24291
|
nodeOutputs = /* @__PURE__ */ new Map();
|
|
24004
24292
|
// B5:工单→项目绑定(key=workOrderId)。与 node_outputs 同属合法薄旁存——reset 不清、重放时种回。
|
|
24005
24293
|
workspaceBindings = /* @__PURE__ */ new Map();
|
|
24294
|
+
// #2 派单门:处于"待派发"的工单 id(薄旁存,同 binding——dispatcher/工单视图读它,不进 oplog)。
|
|
24295
|
+
dispatchHeld = /* @__PURE__ */ new Set();
|
|
24006
24296
|
/**
|
|
24007
24297
|
* B2 清空式重建用:清掉 oplog 派生的事实投影(artifact/revision/event/annotation),
|
|
24008
24298
|
* 保留 node_output(合法薄投影,投影正依赖它补 source*;由 registerNodeOutput 独立维护)。
|
|
@@ -24070,6 +24360,13 @@ var init_service = __esm({
|
|
|
24070
24360
|
async listWorkspaceBindings() {
|
|
24071
24361
|
return [...this.workspaceBindings.values()].map((binding) => ({ ...binding }));
|
|
24072
24362
|
}
|
|
24363
|
+
async setWorkspaceDispatchHold(workOrderId, held) {
|
|
24364
|
+
if (held) this.dispatchHeld.add(workOrderId);
|
|
24365
|
+
else this.dispatchHeld.delete(workOrderId);
|
|
24366
|
+
}
|
|
24367
|
+
async listWorkspaceDispatchHeld() {
|
|
24368
|
+
return [...this.dispatchHeld];
|
|
24369
|
+
}
|
|
24073
24370
|
async listAll() {
|
|
24074
24371
|
return {
|
|
24075
24372
|
artifacts: [...this.artifacts.values()].map(cloneArtifact),
|
|
@@ -24077,7 +24374,8 @@ var init_service = __esm({
|
|
|
24077
24374
|
events: [...this.events.values()].map(cloneEvent),
|
|
24078
24375
|
annotations: [...this.annotations.values()].map(cloneAnnotation),
|
|
24079
24376
|
nodeOutputs: [...this.nodeOutputs.values()].map(cloneNodeOutput),
|
|
24080
|
-
workspaceBindings: [...this.workspaceBindings.values()].map((binding) => ({ ...binding }))
|
|
24377
|
+
workspaceBindings: [...this.workspaceBindings.values()].map((binding) => ({ ...binding })),
|
|
24378
|
+
workspaceDispatchHeld: [...this.dispatchHeld]
|
|
24081
24379
|
};
|
|
24082
24380
|
}
|
|
24083
24381
|
};
|
|
@@ -25205,9 +25503,10 @@ var init_dev_store = __esm({
|
|
|
25205
25503
|
for (const annotation of snap.annotations ?? []) await MemoryArtifactStateStore.prototype.upsertAnnotation.call(store, annotation);
|
|
25206
25504
|
for (const output of snap.nodeOutputs ?? []) await MemoryArtifactStateStore.prototype.upsertNodeOutput.call(store, output);
|
|
25207
25505
|
for (const binding of snap.workspaceBindings ?? []) await MemoryArtifactStateStore.prototype.upsertWorkspaceBinding.call(store, binding);
|
|
25506
|
+
for (const ws of snap.workspaceDispatchHeld ?? []) await MemoryArtifactStateStore.prototype.setWorkspaceDispatchHold.call(store, ws, true);
|
|
25208
25507
|
} else {
|
|
25209
25508
|
fs2.mkdirSync(path.dirname(file), { recursive: true });
|
|
25210
|
-
fs2.writeFileSync(file, JSON.stringify({ artifacts: [], revisions: [], events: [], annotations: [], nodeOutputs: [], workspaceBindings: [] }, null, 2));
|
|
25509
|
+
fs2.writeFileSync(file, JSON.stringify({ artifacts: [], revisions: [], events: [], annotations: [], nodeOutputs: [], workspaceBindings: [], workspaceDispatchHeld: [] }, null, 2));
|
|
25211
25510
|
}
|
|
25212
25511
|
return store;
|
|
25213
25512
|
}
|
|
@@ -25235,6 +25534,10 @@ var init_dev_store = __esm({
|
|
|
25235
25534
|
await super.upsertWorkspaceBinding(binding);
|
|
25236
25535
|
await this.save();
|
|
25237
25536
|
}
|
|
25537
|
+
async setWorkspaceDispatchHold(workOrderId, held) {
|
|
25538
|
+
await super.setWorkspaceDispatchHold(workOrderId, held);
|
|
25539
|
+
await this.save();
|
|
25540
|
+
}
|
|
25238
25541
|
async save() {
|
|
25239
25542
|
fs2.writeFileSync(this.file, JSON.stringify(await this.listAll(), null, 2));
|
|
25240
25543
|
}
|
|
@@ -25321,7 +25624,6 @@ var init_dev_store = __esm({
|
|
|
25321
25624
|
MemoryChatSessionStore = class {
|
|
25322
25625
|
sessions = /* @__PURE__ */ new Map();
|
|
25323
25626
|
messages = /* @__PURE__ */ new Map();
|
|
25324
|
-
sessionWorkOrders = /* @__PURE__ */ new Map();
|
|
25325
25627
|
async createSession(s2) {
|
|
25326
25628
|
this.sessions.set(s2.id, s2);
|
|
25327
25629
|
}
|
|
@@ -25338,7 +25640,6 @@ var init_dev_store = __esm({
|
|
|
25338
25640
|
async deleteSession(id) {
|
|
25339
25641
|
this.sessions.delete(id);
|
|
25340
25642
|
this.messages.delete(id);
|
|
25341
|
-
this.sessionWorkOrders.delete(id);
|
|
25342
25643
|
}
|
|
25343
25644
|
async appendMessage(m2) {
|
|
25344
25645
|
const list = this.messages.get(m2.sessionId) ?? [];
|
|
@@ -25349,14 +25650,6 @@ var init_dev_store = __esm({
|
|
|
25349
25650
|
const list = (this.messages.get(sessionId) ?? []).sort((a, b2) => a.seq - b2.seq);
|
|
25350
25651
|
return limit ? list.slice(-limit) : list;
|
|
25351
25652
|
}
|
|
25352
|
-
async linkWorkOrder(sessionId, workOrderId) {
|
|
25353
|
-
const set = this.sessionWorkOrders.get(sessionId) ?? /* @__PURE__ */ new Set();
|
|
25354
|
-
set.add(workOrderId);
|
|
25355
|
-
this.sessionWorkOrders.set(sessionId, set);
|
|
25356
|
-
}
|
|
25357
|
-
async listWorkOrdersForSession(sessionId) {
|
|
25358
|
-
return [...this.sessionWorkOrders.get(sessionId) ?? []];
|
|
25359
|
-
}
|
|
25360
25653
|
};
|
|
25361
25654
|
}
|
|
25362
25655
|
});
|
|
@@ -25474,12 +25767,36 @@ function typeDefError(def, mode) {
|
|
|
25474
25767
|
}
|
|
25475
25768
|
return null;
|
|
25476
25769
|
}
|
|
25477
|
-
var TYPE_DEF_SCHEMA, TypesAdminService;
|
|
25770
|
+
var RESERVED_FIELD_NAMES, TYPE_DEF_SCHEMA, TypesAdminService;
|
|
25478
25771
|
var init_types_admin = __esm({
|
|
25479
25772
|
"../server/src/types-admin.ts"() {
|
|
25480
25773
|
"use strict";
|
|
25481
25774
|
init_zod();
|
|
25482
25775
|
init_src2();
|
|
25776
|
+
RESERVED_FIELD_NAMES = /* @__PURE__ */ new Set([
|
|
25777
|
+
// Artifact 内置字段
|
|
25778
|
+
"id",
|
|
25779
|
+
"type",
|
|
25780
|
+
"title",
|
|
25781
|
+
"description",
|
|
25782
|
+
"docType",
|
|
25783
|
+
"currentRev",
|
|
25784
|
+
"owner",
|
|
25785
|
+
"workspace",
|
|
25786
|
+
"persistence",
|
|
25787
|
+
"createdVia",
|
|
25788
|
+
"inputs",
|
|
25789
|
+
"parts",
|
|
25790
|
+
"fields",
|
|
25791
|
+
// edit/spawn 自己的 flag + 全局 infra flag(--<字段> 转发要避开)
|
|
25792
|
+
"brief",
|
|
25793
|
+
"input",
|
|
25794
|
+
"set-json",
|
|
25795
|
+
"clear",
|
|
25796
|
+
"server",
|
|
25797
|
+
"token",
|
|
25798
|
+
"dir"
|
|
25799
|
+
]);
|
|
25483
25800
|
TYPE_DEF_SCHEMA = external_exports.object({
|
|
25484
25801
|
name: external_exports.string().regex(/^[a-z][a-z0-9-]*$/, "name \u987B\u5C0F\u5199\u5B57\u6BCD\u5F00\u5934\u3001\u9650\u5C0F\u5199\u5B57\u6BCD/\u6570\u5B57/\u8FDE\u5B57\u7B26"),
|
|
25485
25802
|
description: external_exports.string(),
|
|
@@ -25505,8 +25822,16 @@ var init_types_admin = __esm({
|
|
|
25505
25822
|
budget: external_exports.object({ wallClockMinutes: external_exports.number().positive().optional() }).strict(),
|
|
25506
25823
|
cascade: external_exports.enum(["auto", "manual"]),
|
|
25507
25824
|
acl: external_exports.object({ propose: external_exports.array(external_exports.string()).optional(), annotate: external_exports.array(external_exports.string()).optional(), spawn: external_exports.array(external_exports.string()).optional() }).strict(),
|
|
25508
|
-
mirror: external_exports.record(external_exports.string(), external_exports.unknown())
|
|
25825
|
+
mirror: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
25509
25826
|
// MirrorConfig 是连接器侧复杂类型——只要求是对象、内部松校验
|
|
25827
|
+
fieldDefs: external_exports.array(
|
|
25828
|
+
external_exports.object({
|
|
25829
|
+
name: external_exports.string().regex(/^[a-z][a-zA-Z0-9_]*$/, "\u5B57\u6BB5 name \u987B\u5C0F\u5199\u5B57\u6BCD\u5F00\u5934\u3001\u9650\u5B57\u6BCD/\u6570\u5B57/\u4E0B\u5212\u7EBF").refine((n) => !RESERVED_FIELD_NAMES.has(n), "\u5B57\u6BB5 name \u4E0D\u5F97\u649E Artifact \u5185\u7F6E\u5B57\u6BB5\u6216\u6838\u5FC3\u547D\u4EE4 flag\uFF08\u649E\u4E86\u6539\u7528 --set-json\uFF09"),
|
|
25830
|
+
type: external_exports.enum(["string", "string[]", "number", "boolean"]),
|
|
25831
|
+
required: external_exports.boolean().optional(),
|
|
25832
|
+
description: external_exports.string().optional()
|
|
25833
|
+
}).strict()
|
|
25834
|
+
)
|
|
25510
25835
|
}).partial().strict();
|
|
25511
25836
|
TypesAdminService = class {
|
|
25512
25837
|
constructor(store, kernels) {
|
|
@@ -25576,6 +25901,36 @@ var init_types_admin = __esm({
|
|
|
25576
25901
|
for (const k2 of this.kernels()) k2.removeType(name);
|
|
25577
25902
|
return { ok: true };
|
|
25578
25903
|
}
|
|
25904
|
+
// —— B 平面:自定义字段声明(fieldDefs)的增/改/删。都落到 edit(复用 zod 校验 + 传播 + usage 影响半径)。
|
|
25905
|
+
// 改名禁、删=deprecate(存量值留、新写入拒)都由"只有这三个动词、且 name 钉死"在结构上保证。
|
|
25906
|
+
/** 加一条字段声明。name 已声明 / 撞保留集(edit 的 zod 把住)则拒。 */
|
|
25907
|
+
async addField(typeName, spec) {
|
|
25908
|
+
const cur = (await this.store.list()).find((t) => t.name === typeName);
|
|
25909
|
+
if (!cur) return { ok: false, error: `\u7C7B\u578B\u4E0D\u5B58\u5728: ${typeName}` };
|
|
25910
|
+
if ((cur.fieldDefs ?? []).some((f2) => f2.name === spec.name)) {
|
|
25911
|
+
return { ok: false, error: `\u5B57\u6BB5\u5DF2\u58F0\u660E: ${spec.name}\uFF08\u6539\u5C5E\u6027\u7528 edit-field\u3001\u5220\u7528 drop-field\uFF09` };
|
|
25912
|
+
}
|
|
25913
|
+
return this.edit(typeName, { fieldDefs: [...cur.fieldDefs ?? [], spec] });
|
|
25914
|
+
}
|
|
25915
|
+
/** 改一条字段声明的属性(type/required/description);**name 钉死、不可改名**。 */
|
|
25916
|
+
async editField(typeName, fieldName, attrs) {
|
|
25917
|
+
const cur = (await this.store.list()).find((t) => t.name === typeName);
|
|
25918
|
+
if (!cur) return { ok: false, error: `\u7C7B\u578B\u4E0D\u5B58\u5728: ${typeName}` };
|
|
25919
|
+
const idx = (cur.fieldDefs ?? []).findIndex((f2) => f2.name === fieldName);
|
|
25920
|
+
if (idx < 0) return { ok: false, error: `\u5B57\u6BB5\u672A\u58F0\u660E: ${fieldName}\uFF08${typeName} \u58F0\u660E\uFF1A${(cur.fieldDefs ?? []).map((f2) => f2.name).join(", ") || "\u65E0"}\uFF09` };
|
|
25921
|
+
const nextDefs = [...cur.fieldDefs];
|
|
25922
|
+
nextDefs[idx] = { ...nextDefs[idx], ...attrs, name: fieldName };
|
|
25923
|
+
return this.edit(typeName, { fieldDefs: nextDefs });
|
|
25924
|
+
}
|
|
25925
|
+
/** 删一条字段声明(deprecate 语义:存量值留、新写入拒)。返回 usage 供影响半径预览。 */
|
|
25926
|
+
async dropField(typeName, fieldName) {
|
|
25927
|
+
const cur = (await this.store.list()).find((t) => t.name === typeName);
|
|
25928
|
+
if (!cur) return { ok: false, error: `\u7C7B\u578B\u4E0D\u5B58\u5728: ${typeName}` };
|
|
25929
|
+
if (!(cur.fieldDefs ?? []).some((f2) => f2.name === fieldName)) {
|
|
25930
|
+
return { ok: false, error: `\u5B57\u6BB5\u672A\u58F0\u660E: ${fieldName}` };
|
|
25931
|
+
}
|
|
25932
|
+
return this.edit(typeName, { fieldDefs: (cur.fieldDefs ?? []).filter((f2) => f2.name !== fieldName) });
|
|
25933
|
+
}
|
|
25579
25934
|
};
|
|
25580
25935
|
}
|
|
25581
25936
|
});
|
|
@@ -30047,6 +30402,10 @@ ${input.description}
|
|
|
30047
30402
|
if (enabledKeys.size === 0) return [];
|
|
30048
30403
|
return all.filter((s2) => enabledKeys.has(s2.id) || enabledKeys.has(s2.name));
|
|
30049
30404
|
}
|
|
30405
|
+
/** 全局已装技能平铺列表(「默认挂」materialize 用;installedSkillsWithAssignments 带分配关系太重)。 */
|
|
30406
|
+
async listInstalledSkills() {
|
|
30407
|
+
return this.opts.store.listInstalledSkills();
|
|
30408
|
+
}
|
|
30050
30409
|
/* ---------- 变量(2.8):AES-256-GCM,读侧只见掩码 ---------- */
|
|
30051
30410
|
/**
|
|
30052
30411
|
* 写入变量。
|
|
@@ -30565,6 +30924,61 @@ var init_routes = __esm({
|
|
|
30565
30924
|
}
|
|
30566
30925
|
});
|
|
30567
30926
|
|
|
30927
|
+
// ../server/src/domains/actors/skill-materializer.ts
|
|
30928
|
+
function skillsDirForRuntime(runtimeKind) {
|
|
30929
|
+
switch (runtimeKind) {
|
|
30930
|
+
case "claude":
|
|
30931
|
+
case "claude-code":
|
|
30932
|
+
return ".claude/skills";
|
|
30933
|
+
default:
|
|
30934
|
+
return ".agent_context/skills";
|
|
30935
|
+
}
|
|
30936
|
+
}
|
|
30937
|
+
function sanitizeSkillDir(name) {
|
|
30938
|
+
const s2 = name.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
30939
|
+
return s2 || "skill";
|
|
30940
|
+
}
|
|
30941
|
+
async function materializeSkillFiles(args) {
|
|
30942
|
+
const own = await args.service.listInstalledSkillsForActor(args.actorId);
|
|
30943
|
+
let extras = [];
|
|
30944
|
+
if (args.extraSkillIds && args.extraSkillIds.length > 0) {
|
|
30945
|
+
const ownIds = new Set(own.map((s2) => s2.id));
|
|
30946
|
+
const wanted = new Set(args.extraSkillIds);
|
|
30947
|
+
extras = (await args.service.listInstalledSkills()).filter((s2) => wanted.has(s2.id) && !ownIds.has(s2.id));
|
|
30948
|
+
}
|
|
30949
|
+
const skills = [...own, ...extras];
|
|
30950
|
+
if (skills.length === 0) return {};
|
|
30951
|
+
const prefix = skillsDirForRuntime(args.runtimeKind);
|
|
30952
|
+
const out = {};
|
|
30953
|
+
const dec2 = new TextDecoder();
|
|
30954
|
+
const usedDirs = /* @__PURE__ */ new Set();
|
|
30955
|
+
for (const skill of skills) {
|
|
30956
|
+
const files = await args.service.listSkillFiles(skill.id);
|
|
30957
|
+
if (files.length === 0) continue;
|
|
30958
|
+
let dir = sanitizeSkillDir(skill.name);
|
|
30959
|
+
if (usedDirs.has(dir)) {
|
|
30960
|
+
let n = 2;
|
|
30961
|
+
while (usedDirs.has(`${dir}-${n}`)) n++;
|
|
30962
|
+
dir = `${dir}-${n}`;
|
|
30963
|
+
}
|
|
30964
|
+
usedDirs.add(dir);
|
|
30965
|
+
for (const f2 of files) {
|
|
30966
|
+
try {
|
|
30967
|
+
const bytes = await args.blobs.get(f2.blobHash);
|
|
30968
|
+
out[`${prefix}/${dir}/${f2.path}`] = dec2.decode(bytes);
|
|
30969
|
+
} catch (err) {
|
|
30970
|
+
console.warn(`[skill-materializer] skip ${skill.id}/${f2.path} (blob ${f2.blobHash.slice(0, 12)}\u2026): ${String(err)}`);
|
|
30971
|
+
}
|
|
30972
|
+
}
|
|
30973
|
+
}
|
|
30974
|
+
return out;
|
|
30975
|
+
}
|
|
30976
|
+
var init_skill_materializer = __esm({
|
|
30977
|
+
"../server/src/domains/actors/skill-materializer.ts"() {
|
|
30978
|
+
"use strict";
|
|
30979
|
+
}
|
|
30980
|
+
});
|
|
30981
|
+
|
|
30568
30982
|
// ../server/src/domains/actors/index.ts
|
|
30569
30983
|
async function syncRegistryRolesToKernel(store, kernel) {
|
|
30570
30984
|
for (const actor of await store.listActors()) {
|
|
@@ -30622,6 +31036,7 @@ var init_actors = __esm({
|
|
|
30622
31036
|
init_service2();
|
|
30623
31037
|
init_routes();
|
|
30624
31038
|
init_stats();
|
|
31039
|
+
init_skill_materializer();
|
|
30625
31040
|
}
|
|
30626
31041
|
});
|
|
30627
31042
|
|
|
@@ -32111,14 +32526,20 @@ OASIS_DIR="$(pwd)"
|
|
|
32111
32526
|
NODE_DIR="$OASIS_DIR/${dir}"
|
|
32112
32527
|
NODE_CMD="$OASIS_DIR/node_modules/.bin/tsx $OASIS_DIR/packages/cli/src/main.ts"
|
|
32113
32528
|
|
|
32114
|
-
# \u5199\u5165\
|
|
32115
|
-
# CLI \u7684 oasis node \u4F1A\
|
|
32529
|
+
# \u5199\u5165\u4E00\u6B21\u6027\u63A5\u5165\u4EE4\u724C\uFF1A600 \u6743\u9650\u3001\u72EC\u7ACB\u6587\u4EF6\uFF0C\u4E0D\u653E\u8FDB\u547D\u4EE4\u884C/ExecStart\uFF08\u9632\u6CC4\u6F0F\u5230 ps/journald\uFF09\u3002
|
|
32530
|
+
# CLI \u7684 oasis node \u9996\u6B21\u542F\u52A8\u4F1A\u7528\u5B83\u5411\u670D\u52A1\u7AEF exchange \u53D6\u56DE nodeId + \u957F\u671F node-token\uFF0C
|
|
32531
|
+
# \u4E4B\u540E\u843D\u76D8\u5230 <dir>/node-id \u4E0E <dir>/node-token\uFF0C\u91CD\u542F\u4E0D\u518D exchange\u3002
|
|
32116
32532
|
mkdir -p "$NODE_DIR"
|
|
32117
|
-
( umask 077; cat > "$NODE_DIR/
|
|
32533
|
+
( umask 077; cat > "$NODE_DIR/enroll-token" <<'OASIS_ENROLL_TOKEN_EOF'
|
|
32118
32534
|
${p2.token}
|
|
32119
|
-
|
|
32535
|
+
OASIS_ENROLL_TOKEN_EOF
|
|
32120
32536
|
)
|
|
32121
32537
|
|
|
32538
|
+
# "Script was run" marker: tells the daemon to (re)report its available runtimes once on
|
|
32539
|
+
# next start. Adapters are picked up only on an explicit script run \u2014 never on plain
|
|
32540
|
+
# reconnects/restarts \u2014 so admin-side runtime deletions stay deleted until you re-run this.
|
|
32541
|
+
touch "$NODE_DIR/report-runtimes"
|
|
32542
|
+
|
|
32122
32543
|
# \u7528 systemd \u7528\u6237\u670D\u52A1\uFF08Linux\uFF09\u4FDD\u6301\u540E\u53F0\u5E38\u9A7B + \u5F00\u673A\u81EA\u542F
|
|
32123
32544
|
if command -v systemctl &>/dev/null && systemctl --user daemon-reload &>/dev/null 2>&1; then
|
|
32124
32545
|
SERVICE_FILE=~/.config/systemd/user/oasis-node.service
|
|
@@ -32133,11 +32554,12 @@ if command -v systemctl &>/dev/null && systemctl --user daemon-reload &>/dev/nul
|
|
|
32133
32554
|
echo '\u2192 \u5199\u5165 systemd \u7528\u6237\u670D\u52A1\u914D\u7F6E...'
|
|
32134
32555
|
cat > "$SERVICE_FILE" << EOF
|
|
32135
32556
|
[Unit]
|
|
32136
|
-
Description=Oasis Node Daemon
|
|
32557
|
+
Description=Oasis Node Daemon
|
|
32137
32558
|
After=network.target
|
|
32138
32559
|
|
|
32139
32560
|
[Service]
|
|
32140
|
-
ExecStart=$NODE_CMD node --server-ws ${p2.gatewayUrl} --
|
|
32561
|
+
ExecStart=$NODE_CMD node --server-ws ${p2.gatewayUrl} --dir $NODE_DIR
|
|
32562
|
+
Environment=PATH=$PATH:$HOME/.npm-global/bin:$HOME/.local/bin:/usr/local/bin
|
|
32141
32563
|
WorkingDirectory=$OASIS_DIR
|
|
32142
32564
|
Restart=on-failure
|
|
32143
32565
|
RestartSec=5
|
|
@@ -32176,7 +32598,6 @@ elif [[ "$(uname)" == "Darwin" ]]; then
|
|
|
32176
32598
|
<string>$MAIN</string>
|
|
32177
32599
|
<string>node</string>
|
|
32178
32600
|
<string>--server-ws</string><string>${p2.gatewayUrl}</string>
|
|
32179
|
-
<string>--id</string><string>${p2.nodeId}</string>
|
|
32180
32601
|
<string>--dir</string><string>$NODE_DIR</string>
|
|
32181
32602
|
</array>
|
|
32182
32603
|
<key>WorkingDirectory</key><string>$OASIS_DIR</string>
|
|
@@ -32204,7 +32625,7 @@ else
|
|
|
32204
32625
|
sleep 1
|
|
32205
32626
|
fi
|
|
32206
32627
|
echo '\u2192 \u542F\u52A8\u8282\u70B9\uFF08nohup \u540E\u53F0\uFF09...'
|
|
32207
|
-
nohup $NODE_CMD node --server-ws ${p2.gatewayUrl} --
|
|
32628
|
+
nohup $NODE_CMD node --server-ws ${p2.gatewayUrl} --dir $NODE_DIR > ~/.oasis-node.log 2>&1 &
|
|
32208
32629
|
echo "\u2713 \u8282\u70B9\u5DF2\u5728\u540E\u53F0\u542F\u52A8\uFF08PID $!\uFF09"
|
|
32209
32630
|
echo ' \u65E5\u5FD7\uFF1Atail -f ~/.oasis-node.log'
|
|
32210
32631
|
echo ' \u505C\u6B62\uFF1Apkill -f "oasis node"'
|
|
@@ -32239,9 +32660,10 @@ if (-not (Test-Path "node_modules")) {
|
|
|
32239
32660
|
Write-Host "\u2192 Dependencies already present, skipping."
|
|
32240
32661
|
}
|
|
32241
32662
|
|
|
32242
|
-
# \u2500\u2500 write token (ACL-restricted) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
32663
|
+
# \u2500\u2500 write enroll token (ACL-restricted) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
32664
|
+
# \u4E00\u6B21\u6027\u4EE4\u724C\uFF1Boasis node \u9996\u6B21\u542F\u52A8\u7528\u5B83 exchange \u53D6\u56DE nodeId + \u957F\u671F node-token\uFF0C\u4E4B\u540E\u843D\u76D8\u590D\u7528\u3002
|
|
32243
32665
|
New-Item -ItemType Directory -Force -Path $NODE_DIR | Out-Null
|
|
32244
|
-
$tokenPath = "$NODE_DIR\\
|
|
32666
|
+
$tokenPath = "$NODE_DIR\\enroll-token"
|
|
32245
32667
|
Set-Content -Path $tokenPath -Value "${p2.token}" -NoNewline -Encoding utf8
|
|
32246
32668
|
$acl = Get-Acl $tokenPath
|
|
32247
32669
|
$acl.SetAccessRuleProtection($true, $false)
|
|
@@ -32250,10 +32672,15 @@ $rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
|
|
|
32250
32672
|
$acl.AddAccessRule($rule)
|
|
32251
32673
|
Set-Acl $tokenPath $acl
|
|
32252
32674
|
|
|
32675
|
+
# "Script was run" marker: tells the daemon to (re)report its available runtimes once on
|
|
32676
|
+
# next start. Adapters are picked up only on an explicit script run \u2014 never on plain
|
|
32677
|
+
# reconnects/restarts \u2014 so admin-side runtime deletions stay deleted until you re-run this.
|
|
32678
|
+
Set-Content -Path "$NODE_DIR\\report-runtimes" -Value "" -NoNewline -Encoding utf8
|
|
32679
|
+
|
|
32253
32680
|
# \u2500\u2500 register Task Scheduler (run at logon, restart on failure) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
32254
32681
|
$tsx = "$INSTALL_DIR\\node_modules\\.bin\\tsx.cmd"
|
|
32255
32682
|
$main = "$INSTALL_DIR\\packages\\cli\\src\\main.ts"
|
|
32256
|
-
$args = "\`"$main\`" node --server-ws ${p2.gatewayUrl} --
|
|
32683
|
+
$args = "\`"$main\`" node --server-ws ${p2.gatewayUrl} --dir \`"$NODE_DIR\`""
|
|
32257
32684
|
|
|
32258
32685
|
$action = New-ScheduledTaskAction -Execute $tsx -Argument $args -WorkingDirectory $INSTALL_DIR
|
|
32259
32686
|
$trigger = New-ScheduledTaskTrigger -AtLogOn -User $env:USERNAME
|
|
@@ -32261,23 +32688,23 @@ $settings = New-ScheduledTaskSettingsSet -RestartCount 5 \`
|
|
|
32261
32688
|
-RestartInterval (New-TimeSpan -Minutes 1) \`
|
|
32262
32689
|
-ExecutionTimeLimit ([TimeSpan]::Zero) \`
|
|
32263
32690
|
-StartWhenAvailable $true
|
|
32264
|
-
Register-ScheduledTask -TaskName "OasisNode
|
|
32691
|
+
Register-ScheduledTask -TaskName "OasisNode" \`
|
|
32265
32692
|
-Action $action -Trigger $trigger -Settings $settings -Force | Out-Null
|
|
32266
32693
|
|
|
32267
32694
|
# \u2500\u2500 start now \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
32268
|
-
Start-ScheduledTask -TaskName "OasisNode
|
|
32695
|
+
Start-ScheduledTask -TaskName "OasisNode"
|
|
32269
32696
|
Pop-Location
|
|
32270
32697
|
|
|
32271
32698
|
Write-Host ""
|
|
32272
32699
|
Write-Host "\u2713 Node started (Task Scheduler, runs at every logon)"
|
|
32273
|
-
Write-Host " Status : Get-ScheduledTask -TaskName 'OasisNode
|
|
32274
|
-
Write-Host " Stop : Stop-ScheduledTask -TaskName 'OasisNode
|
|
32275
|
-
Write-Host " Remove : Unregister-ScheduledTask -TaskName 'OasisNode
|
|
32700
|
+
Write-Host " Status : Get-ScheduledTask -TaskName 'OasisNode'"
|
|
32701
|
+
Write-Host " Stop : Stop-ScheduledTask -TaskName 'OasisNode'"
|
|
32702
|
+
Write-Host " Remove : Unregister-ScheduledTask -TaskName 'OasisNode' -Confirm:$false"
|
|
32276
32703
|
`;
|
|
32277
32704
|
}
|
|
32278
32705
|
function buildNpxCmd(p2) {
|
|
32279
32706
|
const namePart = p2.nodeName ? ` --name ${p2.nodeName}` : "";
|
|
32280
|
-
return `npx -y oasis_test@latest start --server-ws ${p2.gatewayUrl}
|
|
32707
|
+
return `npx -y oasis_test@latest start --server-ws ${p2.gatewayUrl}${namePart} --enroll-token ${p2.token}`;
|
|
32281
32708
|
}
|
|
32282
32709
|
var init_connect_script = __esm({
|
|
32283
32710
|
"../server/src/domains/nodes/connect-script.ts"() {
|
|
@@ -32287,7 +32714,6 @@ var init_connect_script = __esm({
|
|
|
32287
32714
|
|
|
32288
32715
|
// ../server/src/domains/nodes/routes.ts
|
|
32289
32716
|
function nodesDomain(deps) {
|
|
32290
|
-
const nodeNames = /* @__PURE__ */ new Map();
|
|
32291
32717
|
let hubWired = false;
|
|
32292
32718
|
function wireHub(hub) {
|
|
32293
32719
|
if (hubWired) return;
|
|
@@ -32298,19 +32724,20 @@ function nodesDomain(deps) {
|
|
|
32298
32724
|
if (msg.type !== "hello") return;
|
|
32299
32725
|
const d = hub.connectedDaemons().find((c) => c.daemonId === daemonId);
|
|
32300
32726
|
if (!d) return;
|
|
32301
|
-
|
|
32302
|
-
|
|
32303
|
-
|
|
32304
|
-
|
|
32305
|
-
|
|
32306
|
-
|
|
32307
|
-
|
|
32308
|
-
|
|
32309
|
-
|
|
32310
|
-
|
|
32311
|
-
|
|
32312
|
-
|
|
32313
|
-
|
|
32727
|
+
if (msg.meta.runtimes) {
|
|
32728
|
+
const existing = await nodeStore.getNode(daemonId);
|
|
32729
|
+
await nodeStore.upsertNode({
|
|
32730
|
+
id: daemonId,
|
|
32731
|
+
hostname: d.meta.hostname,
|
|
32732
|
+
nodeVersion: d.meta.nodeVersion ?? "unknown",
|
|
32733
|
+
status: "online",
|
|
32734
|
+
// revives a previously soft-deleted node
|
|
32735
|
+
lastSeenAt: now(),
|
|
32736
|
+
enrolledAt: existing?.enrolledAt ?? now(),
|
|
32737
|
+
...d.meta.nodeName ? { name: d.meta.nodeName } : existing?.name ? { name: existing.name } : {}
|
|
32738
|
+
});
|
|
32739
|
+
for (const cap of msg.meta.runtimes) {
|
|
32740
|
+
await nodeStore.addRuntimeIfAbsent({
|
|
32314
32741
|
id: `runtime:${daemonId}:${cap.kind}`,
|
|
32315
32742
|
nodeId: daemonId,
|
|
32316
32743
|
kind: cap.kind,
|
|
@@ -32321,7 +32748,9 @@ function nodesDomain(deps) {
|
|
|
32321
32748
|
lastSeenAt: now()
|
|
32322
32749
|
});
|
|
32323
32750
|
}
|
|
32751
|
+
await nodeStore.setNodeRuntimesOnline(daemonId, true);
|
|
32324
32752
|
} else {
|
|
32753
|
+
await nodeStore.setNodeOnline(daemonId, true);
|
|
32325
32754
|
await nodeStore.setNodeRuntimesOnline(daemonId, true);
|
|
32326
32755
|
}
|
|
32327
32756
|
});
|
|
@@ -32331,35 +32760,24 @@ function nodesDomain(deps) {
|
|
|
32331
32760
|
};
|
|
32332
32761
|
}
|
|
32333
32762
|
return (router) => {
|
|
32763
|
+
let wirePoll = null;
|
|
32764
|
+
const tryWire = () => {
|
|
32765
|
+
const hub = deps.getHub();
|
|
32766
|
+
if (hub) {
|
|
32767
|
+
wireHub(hub);
|
|
32768
|
+
if (wirePoll) {
|
|
32769
|
+
clearInterval(wirePoll);
|
|
32770
|
+
wirePoll = null;
|
|
32771
|
+
}
|
|
32772
|
+
}
|
|
32773
|
+
};
|
|
32774
|
+
tryWire();
|
|
32775
|
+
if (!hubWired) wirePoll = setInterval(tryWire, 100);
|
|
32334
32776
|
async function syncConnectedDaemon(daemonId, hub) {
|
|
32335
32777
|
const d = hub.connectedDaemons().find((c) => c.daemonId === daemonId);
|
|
32336
32778
|
if (!d) return;
|
|
32337
|
-
|
|
32338
|
-
await deps.nodeStore.
|
|
32339
|
-
id: daemonId,
|
|
32340
|
-
hostname: d.meta.hostname,
|
|
32341
|
-
nodeVersion: d.meta.nodeVersion ?? "unknown",
|
|
32342
|
-
status: "online",
|
|
32343
|
-
lastSeenAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
32344
|
-
enrolledAt: existing?.enrolledAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
32345
|
-
...d.meta.nodeName ? { name: d.meta.nodeName } : existing?.name ? { name: existing.name } : {}
|
|
32346
|
-
});
|
|
32347
|
-
if (d.meta.runtimes) {
|
|
32348
|
-
for (const cap of d.meta.runtimes) {
|
|
32349
|
-
await deps.nodeStore.upsertRuntime({
|
|
32350
|
-
id: `runtime:${daemonId}:${cap.kind}`,
|
|
32351
|
-
nodeId: daemonId,
|
|
32352
|
-
kind: cap.kind,
|
|
32353
|
-
hostname: d.meta.hostname,
|
|
32354
|
-
...cap.binary ? { binary: cap.binary } : {},
|
|
32355
|
-
...cap.version ? { version: cap.version } : {},
|
|
32356
|
-
status: "online",
|
|
32357
|
-
lastSeenAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
32358
|
-
});
|
|
32359
|
-
}
|
|
32360
|
-
} else {
|
|
32361
|
-
await deps.nodeStore.setNodeRuntimesOnline(daemonId, true);
|
|
32362
|
-
}
|
|
32779
|
+
await deps.nodeStore.setNodeOnline(daemonId, true);
|
|
32780
|
+
await deps.nodeStore.setNodeRuntimesOnline(daemonId, true);
|
|
32363
32781
|
}
|
|
32364
32782
|
router.get("/api/nodes", async () => {
|
|
32365
32783
|
const hub = deps.getHub();
|
|
@@ -32414,16 +32832,11 @@ function nodesDomain(deps) {
|
|
|
32414
32832
|
router.post("/api/nodes/enroll", async (req) => {
|
|
32415
32833
|
const body = req.body ?? {};
|
|
32416
32834
|
const nodeName = body.nodeName?.trim() || void 0;
|
|
32417
|
-
const
|
|
32418
|
-
const
|
|
32419
|
-
const nodeId = existing?.id ?? requestedId ?? `node-${(0, import_node_crypto9.randomUUID)().slice(0, 8)}`;
|
|
32420
|
-
const enroll = deps.enrollTokens.issue(nodeId);
|
|
32421
|
-
if (nodeName) nodeNames.set(nodeId, nodeName);
|
|
32422
|
-
if (existing && nodeName) await deps.nodeStore.updateNodeName(nodeId, nodeName);
|
|
32423
|
-
const params = { gatewayUrl: deps.gatewayUrl, nodeId, token: enroll.token, repoRemote: deps.repoRemote };
|
|
32835
|
+
const enroll = deps.enrollTokens.issue(nodeName ?? "");
|
|
32836
|
+
const params = { gatewayUrl: deps.gatewayUrl, token: enroll.token, repoRemote: deps.repoRemote };
|
|
32424
32837
|
const script = body.platform === "windows" ? buildWindowsConnectScript(params) : buildConnectScript(params);
|
|
32425
|
-
const npxCmd = buildNpxCmd({ gatewayUrl: deps.gatewayUrl,
|
|
32426
|
-
return { status: 200, body: {
|
|
32838
|
+
const npxCmd = buildNpxCmd({ gatewayUrl: deps.gatewayUrl, token: enroll.token, nodeName });
|
|
32839
|
+
return { status: 200, body: { nodeName, token: enroll.token, script, gatewayUrl: deps.gatewayUrl, npxCmd } };
|
|
32427
32840
|
});
|
|
32428
32841
|
router.post("/api/nodes/exchange", async (req) => {
|
|
32429
32842
|
const body = req.body ?? {};
|
|
@@ -32431,15 +32844,22 @@ function nodesDomain(deps) {
|
|
|
32431
32844
|
if (!enrollToken) return { status: 400, body: { error: "missing enrollToken" } };
|
|
32432
32845
|
const entry = deps.enrollTokens.peek(enrollToken);
|
|
32433
32846
|
if (!entry) return { status: 401, body: { error: "invalid or expired enroll token" } };
|
|
32434
|
-
const
|
|
32435
|
-
const
|
|
32436
|
-
|
|
32437
|
-
|
|
32438
|
-
|
|
32439
|
-
|
|
32440
|
-
|
|
32441
|
-
|
|
32442
|
-
|
|
32847
|
+
const incomingNodeId = body.nodeId?.trim();
|
|
32848
|
+
const ownsIncoming = !!incomingNodeId && (deps.nodeTokens.list().some((e) => e.nodeId === incomingNodeId) || await deps.nodeStore.getNode(incomingNodeId) !== null);
|
|
32849
|
+
let nodeId;
|
|
32850
|
+
if (entry.issuedNodeId) {
|
|
32851
|
+
if (incomingNodeId && incomingNodeId !== entry.issuedNodeId && !ownsIncoming)
|
|
32852
|
+
return { status: 409, body: { error: "nodeId mismatch" } };
|
|
32853
|
+
nodeId = entry.issuedNodeId;
|
|
32854
|
+
} else if (ownsIncoming) {
|
|
32855
|
+
nodeId = incomingNodeId;
|
|
32856
|
+
deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
|
|
32857
|
+
} else {
|
|
32858
|
+
nodeId = `node-${(0, import_node_crypto9.randomUUID)().slice(0, 8)}`;
|
|
32859
|
+
deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
|
|
32860
|
+
}
|
|
32861
|
+
deps.enrollTokens.consume(enrollToken);
|
|
32862
|
+
if (entry.name) await deps.nodeStore.updateNodeName(nodeId, entry.name);
|
|
32443
32863
|
const existing = deps.nodeTokens.list().find((e) => e.nodeId === nodeId);
|
|
32444
32864
|
const token = existing?.token ?? deps.nodeTokens.issue(nodeId);
|
|
32445
32865
|
return { status: 200, body: { nodeId, token } };
|
|
@@ -32505,6 +32925,24 @@ function typesDomain(deps) {
|
|
|
32505
32925
|
const r = await deps.admin.delete(name);
|
|
32506
32926
|
return r.ok ? { status: 200, body: { ok: true } } : { status: 400, body: { error: r.error, ...r.usage ? { usage: r.usage } : {} } };
|
|
32507
32927
|
});
|
|
32928
|
+
router.post("/api/artifact-types/:name/fields", async (req) => {
|
|
32929
|
+
const name = req.params["name"];
|
|
32930
|
+
if (!name) return { status: 400, body: { error: "missing name" } };
|
|
32931
|
+
const r = await deps.admin.addField(name, req.body ?? {});
|
|
32932
|
+
return r.ok ? { status: 200, body: { ok: true, ...r.usage ? { usage: r.usage } : {} } } : { status: 400, body: { error: r.error } };
|
|
32933
|
+
});
|
|
32934
|
+
router.patch("/api/artifact-types/:name/fields/:field", async (req) => {
|
|
32935
|
+
const { name, field } = req.params;
|
|
32936
|
+
if (!name || !field) return { status: 400, body: { error: "missing name/field" } };
|
|
32937
|
+
const r = await deps.admin.editField(name, field, req.body ?? {});
|
|
32938
|
+
return r.ok ? { status: 200, body: { ok: true, ...r.usage ? { usage: r.usage } : {} } } : { status: 400, body: { error: r.error } };
|
|
32939
|
+
});
|
|
32940
|
+
router.delete("/api/artifact-types/:name/fields/:field", async (req) => {
|
|
32941
|
+
const { name, field } = req.params;
|
|
32942
|
+
if (!name || !field) return { status: 400, body: { error: "missing name/field" } };
|
|
32943
|
+
const r = await deps.admin.dropField(name, field);
|
|
32944
|
+
return r.ok ? { status: 200, body: { ok: true, ...r.usage ? { usage: r.usage } : {} } } : { status: 400, body: { error: r.error } };
|
|
32945
|
+
});
|
|
32508
32946
|
};
|
|
32509
32947
|
}
|
|
32510
32948
|
var init_types3 = __esm({
|
|
@@ -32522,17 +32960,20 @@ var init_node_store = __esm({
|
|
|
32522
32960
|
MemoryNodeStore = class {
|
|
32523
32961
|
nodes = /* @__PURE__ */ new Map();
|
|
32524
32962
|
runtimes = /* @__PURE__ */ new Map();
|
|
32525
|
-
deletedRuntimeIds = /* @__PURE__ */ new Set();
|
|
32526
32963
|
async upsertNode(node) {
|
|
32527
32964
|
this.nodes.set(node.id, { ...this.nodes.get(node.id), ...node });
|
|
32528
32965
|
}
|
|
32529
32966
|
async upsertRuntime(rt) {
|
|
32530
|
-
if (this.deletedRuntimeIds.has(rt.id)) return;
|
|
32531
32967
|
this.runtimes.set(rt.id, { ...this.runtimes.get(rt.id), ...rt });
|
|
32532
32968
|
}
|
|
32969
|
+
async addRuntimeIfAbsent(rt) {
|
|
32970
|
+
if (this.runtimes.has(rt.id)) return false;
|
|
32971
|
+
this.runtimes.set(rt.id, rt);
|
|
32972
|
+
return true;
|
|
32973
|
+
}
|
|
32533
32974
|
async setNodeOnline(id, online) {
|
|
32534
32975
|
const n = this.nodes.get(id);
|
|
32535
|
-
if (n) {
|
|
32976
|
+
if (n && n.status !== "deleted") {
|
|
32536
32977
|
n.status = online ? "online" : "offline";
|
|
32537
32978
|
n.lastSeenAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
32538
32979
|
}
|
|
@@ -32554,7 +32995,7 @@ var init_node_store = __esm({
|
|
|
32554
32995
|
return this.nodes.get(id) ?? null;
|
|
32555
32996
|
}
|
|
32556
32997
|
async listNodes() {
|
|
32557
|
-
return [...this.nodes.values()];
|
|
32998
|
+
return [...this.nodes.values()].filter((n) => n.status !== "deleted");
|
|
32558
32999
|
}
|
|
32559
33000
|
async listRuntimes(nodeId) {
|
|
32560
33001
|
const all = [...this.runtimes.values()];
|
|
@@ -32565,23 +33006,24 @@ var init_node_store = __esm({
|
|
|
32565
33006
|
for (const [k2, rt] of this.runtimes) {
|
|
32566
33007
|
if (rt.nodeId === id) this.runtimes.delete(k2);
|
|
32567
33008
|
}
|
|
32568
|
-
for (const k2 of [...this.deletedRuntimeIds]) {
|
|
32569
|
-
if (k2.startsWith(`runtime:${id}:`)) this.deletedRuntimeIds.delete(k2);
|
|
32570
|
-
}
|
|
32571
33009
|
}
|
|
32572
|
-
async
|
|
32573
|
-
|
|
32574
|
-
|
|
33010
|
+
async softDeleteNode(id) {
|
|
33011
|
+
const n = this.nodes.get(id);
|
|
33012
|
+
if (n) {
|
|
33013
|
+
n.status = "deleted";
|
|
33014
|
+
n.lastSeenAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
33015
|
+
}
|
|
33016
|
+
for (const [k2, rt] of this.runtimes) {
|
|
33017
|
+
if (rt.nodeId === id) this.runtimes.delete(k2);
|
|
32575
33018
|
}
|
|
32576
33019
|
}
|
|
32577
33020
|
async deleteRuntime(id) {
|
|
32578
|
-
this.deletedRuntimeIds.add(id);
|
|
32579
33021
|
const rt = this.runtimes.get(id);
|
|
32580
33022
|
this.runtimes.delete(id);
|
|
32581
33023
|
if (!rt) return { nodeDeleted: false };
|
|
32582
33024
|
const remaining = [...this.runtimes.values()].filter((r) => r.nodeId === rt.nodeId);
|
|
32583
33025
|
if (remaining.length === 0) {
|
|
32584
|
-
this.
|
|
33026
|
+
await this.softDeleteNode(rt.nodeId);
|
|
32585
33027
|
return { nodeDeleted: true };
|
|
32586
33028
|
}
|
|
32587
33029
|
return { nodeDeleted: false };
|
|
@@ -32598,7 +33040,6 @@ var init_node_store = __esm({
|
|
|
32598
33040
|
const snap = JSON.parse(fs5.readFileSync(file, "utf8"));
|
|
32599
33041
|
for (const n of snap.nodes ?? []) s2.nodes.set(n.id, n);
|
|
32600
33042
|
for (const r of snap.runtimes ?? []) s2.runtimes.set(r.id, r);
|
|
32601
|
-
for (const id of snap.deletedRuntimeIds ?? []) s2.deletedRuntimeIds.add(id);
|
|
32602
33043
|
} catch {
|
|
32603
33044
|
fs5.writeFileSync(file, JSON.stringify({ nodes: [], runtimes: [] }, null, 2));
|
|
32604
33045
|
}
|
|
@@ -32607,8 +33048,7 @@ var init_node_store = __esm({
|
|
|
32607
33048
|
flush() {
|
|
32608
33049
|
fs5.writeFileSync(this.file, JSON.stringify({
|
|
32609
33050
|
nodes: [...this.nodes.values()],
|
|
32610
|
-
runtimes: [...this.runtimes.values()]
|
|
32611
|
-
deletedRuntimeIds: [...this.deletedRuntimeIds]
|
|
33051
|
+
runtimes: [...this.runtimes.values()]
|
|
32612
33052
|
}, null, 2));
|
|
32613
33053
|
}
|
|
32614
33054
|
async upsertNode(node) {
|
|
@@ -32619,6 +33059,11 @@ var init_node_store = __esm({
|
|
|
32619
33059
|
await super.upsertRuntime(rt);
|
|
32620
33060
|
this.flush();
|
|
32621
33061
|
}
|
|
33062
|
+
async addRuntimeIfAbsent(rt) {
|
|
33063
|
+
const r = await super.addRuntimeIfAbsent(rt);
|
|
33064
|
+
if (r) this.flush();
|
|
33065
|
+
return r;
|
|
33066
|
+
}
|
|
32622
33067
|
async setNodeOnline(id, online) {
|
|
32623
33068
|
await super.setNodeOnline(id, online);
|
|
32624
33069
|
this.flush();
|
|
@@ -32635,8 +33080,8 @@ var init_node_store = __esm({
|
|
|
32635
33080
|
await super.deleteNode(id);
|
|
32636
33081
|
this.flush();
|
|
32637
33082
|
}
|
|
32638
|
-
async
|
|
32639
|
-
await super.
|
|
33083
|
+
async softDeleteNode(id) {
|
|
33084
|
+
await super.softDeleteNode(id);
|
|
32640
33085
|
this.flush();
|
|
32641
33086
|
}
|
|
32642
33087
|
async deleteRuntime(id) {
|
|
@@ -32649,10 +33094,10 @@ var init_node_store = __esm({
|
|
|
32649
33094
|
});
|
|
32650
33095
|
|
|
32651
33096
|
// ../server/src/domains/collab/workorder-detail.ts
|
|
32652
|
-
function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject, dispatchJournal = []) {
|
|
33097
|
+
function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject, dispatchJournal = [], dispatchPausedOf) {
|
|
32653
33098
|
const arts = [...model.artifacts.values()].filter((a) => a.workspace === workspaceId);
|
|
32654
33099
|
if (arts.length === 0) return null;
|
|
32655
|
-
const summary = buildWorkorderSummaries(model, resolveActor, resolveProject).find((w2) => w2.id === workspaceId);
|
|
33100
|
+
const summary = buildWorkorderSummaries(model, resolveActor, resolveProject, dispatchPausedOf).find((w2) => w2.id === workspaceId);
|
|
32656
33101
|
if (!summary) return null;
|
|
32657
33102
|
const ref = (id) => {
|
|
32658
33103
|
const extra = resolveActor?.(id);
|
|
@@ -32823,6 +33268,26 @@ var init_workorder_detail = __esm({
|
|
|
32823
33268
|
// ../server/src/domains/collab/inbox.ts
|
|
32824
33269
|
function buildInbox(model, me) {
|
|
32825
33270
|
const out = [];
|
|
33271
|
+
const creatorOf = /* @__PURE__ */ new Map();
|
|
33272
|
+
for (const a of model.artifacts.values()) {
|
|
33273
|
+
if (a.type === "brief") creatorOf.set(a.workspace, a.owner);
|
|
33274
|
+
}
|
|
33275
|
+
const staffingSeen = /* @__PURE__ */ new Set();
|
|
33276
|
+
for (const a of model.artifacts.values()) {
|
|
33277
|
+
const pendingRole = /^pending:role:(.+)$/.exec(a.owner);
|
|
33278
|
+
if (!pendingRole || creatorOf.get(a.workspace) !== me) continue;
|
|
33279
|
+
const key = `${a.workspace}:${pendingRole[1]}`;
|
|
33280
|
+
if (staffingSeen.has(key)) continue;
|
|
33281
|
+
staffingSeen.add(key);
|
|
33282
|
+
out.push({
|
|
33283
|
+
kind: "needs-staffing",
|
|
33284
|
+
artifactId: a.id,
|
|
33285
|
+
workspace: a.workspace,
|
|
33286
|
+
since: model.lastOpAt.get(a.id) ?? "",
|
|
33287
|
+
actionRequired: true,
|
|
33288
|
+
summary: `\u5DE5\u5355\u7F3A\u4EBA\u624B\uFF1A${pendingRole[1]} \u89D2\u8272\u65E0\u5458\u5DE5\uFF0C\u300C${nodeLabel(a)}\u300D\u5F85\u6D3E\u2014\u2014\u8BF7\u8865\u4EBA\u6216 assign`
|
|
33289
|
+
});
|
|
33290
|
+
}
|
|
32826
33291
|
for (const a of model.artifacts.values()) {
|
|
32827
33292
|
const ws = a.workspace;
|
|
32828
33293
|
const label = nodeLabel(a);
|
|
@@ -33018,6 +33483,11 @@ async function buildProjectResolver(artifacts) {
|
|
|
33018
33483
|
const byWorkOrder = new Map(bindings.map((binding) => [binding.workOrderId, binding.projectId]));
|
|
33019
33484
|
return (workOrderId) => byWorkOrder.get(workOrderId) ?? null;
|
|
33020
33485
|
}
|
|
33486
|
+
async function buildDispatchPausedResolver(artifacts) {
|
|
33487
|
+
if (!artifacts) return void 0;
|
|
33488
|
+
const held = new Set(await artifacts.listWorkspaceDispatchHeld());
|
|
33489
|
+
return (workspace) => held.has(workspace);
|
|
33490
|
+
}
|
|
33021
33491
|
function collabDomain(opts) {
|
|
33022
33492
|
const defaultCtx = {
|
|
33023
33493
|
kernel: opts.kernel,
|
|
@@ -33044,28 +33514,30 @@ function collabDomain(opts) {
|
|
|
33044
33514
|
const { kernel, registry: registry2, artifacts } = await resolveCtx(req.auth.companyId);
|
|
33045
33515
|
const resolve3 = await buildResolver(registry2);
|
|
33046
33516
|
const resolveProject = await buildProjectResolver(artifacts);
|
|
33047
|
-
|
|
33517
|
+
const dispatchPausedOf = await buildDispatchPausedResolver(artifacts);
|
|
33518
|
+
return { status: 200, body: { items: buildWorkorderSummaries(kernel.model, resolve3, resolveProject, dispatchPausedOf) } };
|
|
33048
33519
|
});
|
|
33049
33520
|
router.get("/api/workorders/:id", async (req) => {
|
|
33050
33521
|
const { kernel, registry: registry2, artifacts } = await resolveCtx(req.auth.companyId);
|
|
33051
33522
|
const resolve3 = await buildResolver(registry2);
|
|
33052
33523
|
const resolveProject = await buildProjectResolver(artifacts);
|
|
33053
|
-
const
|
|
33524
|
+
const dispatchPausedOf = await buildDispatchPausedResolver(artifacts);
|
|
33525
|
+
const detail = buildWorkorderDetail(kernel.model, req.params.id, resolve3, resolveProject, opts.dispatchJournal?.() ?? [], dispatchPausedOf);
|
|
33054
33526
|
if (!detail) {
|
|
33055
33527
|
return { status: 404, body: { error: { code: "not_found", message: `\u5DE5\u5355\u4E0D\u5B58\u5728: ${req.params.id}` } } };
|
|
33056
33528
|
}
|
|
33057
33529
|
return { status: 200, body: detail };
|
|
33058
33530
|
});
|
|
33059
33531
|
router.post("/api/workorders/:id/dispatch", async (req) => {
|
|
33060
|
-
const { kernel } = await resolveCtx(req.auth.companyId);
|
|
33532
|
+
const { kernel, artifacts } = await resolveCtx(req.auth.companyId);
|
|
33061
33533
|
const ws = req.params.id;
|
|
33062
33534
|
const arts = [...kernel.model.artifacts.values()].filter((a) => a.workspace === ws);
|
|
33063
33535
|
if (arts.length === 0) {
|
|
33064
33536
|
return { status: 404, body: { error: { code: "not_found", message: `\u5DE5\u5355\u4E0D\u5B58\u5728: ${ws}` } } };
|
|
33065
33537
|
}
|
|
33066
|
-
const
|
|
33067
|
-
if (!
|
|
33068
|
-
await
|
|
33538
|
+
const held = artifacts ? (await artifacts.listWorkspaceDispatchHeld()).includes(ws) : false;
|
|
33539
|
+
if (!held) return { status: 200, body: { dispatched: false } };
|
|
33540
|
+
await artifacts.setWorkspaceDispatchHold(ws, false);
|
|
33069
33541
|
return { status: 200, body: { dispatched: true } };
|
|
33070
33542
|
});
|
|
33071
33543
|
router.get("/api/inbox", async (req) => {
|
|
@@ -34016,23 +34488,6 @@ function createChatSessionsDomain(opts) {
|
|
|
34016
34488
|
await store.updateSession(req.params.id, { touchedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
34017
34489
|
return { status: 201, body: msg };
|
|
34018
34490
|
});
|
|
34019
|
-
router.get("/api/chat-sessions/:id/work-orders", async (req) => {
|
|
34020
|
-
const session = await store.getSession(req.params.id);
|
|
34021
|
-
if (!session || session.humanActorId !== req.auth.actor) throw new ApiError(404, "NOT_FOUND", "session not found");
|
|
34022
|
-
const workOrderIds = await store.listWorkOrdersForSession(req.params.id);
|
|
34023
|
-
if (!opts.kernel || workOrderIds.length === 0) return { status: 200, body: { items: [] } };
|
|
34024
|
-
const bindings = opts.artifactState ? await opts.artifactState.listWorkspaceBindings() : [];
|
|
34025
|
-
const byWo = new Map(bindings.map((b2) => [b2.workOrderId, b2.projectId]));
|
|
34026
|
-
const actors = opts.kernel ? await registry2.listActors().catch(() => []) : [];
|
|
34027
|
-
const byId = new Map(actors.map((a) => [a.id, a]));
|
|
34028
|
-
const resolver = (id) => {
|
|
34029
|
-
const a = byId.get(id);
|
|
34030
|
-
return a ? { name: a.name } : void 0;
|
|
34031
|
-
};
|
|
34032
|
-
const allSummaries = buildWorkorderSummaries(opts.kernel.model, resolver, (wo) => byWo.get(wo) ?? null);
|
|
34033
|
-
const items = allSummaries.filter((s2) => workOrderIds.includes(s2.id));
|
|
34034
|
-
return { status: 200, body: { items } };
|
|
34035
|
-
});
|
|
34036
34491
|
};
|
|
34037
34492
|
}
|
|
34038
34493
|
var import_node_crypto10;
|
|
@@ -34041,7 +34496,6 @@ var init_chat_sessions = __esm({
|
|
|
34041
34496
|
"use strict";
|
|
34042
34497
|
import_node_crypto10 = require("node:crypto");
|
|
34043
34498
|
init_router();
|
|
34044
|
-
init_workorders();
|
|
34045
34499
|
}
|
|
34046
34500
|
});
|
|
34047
34501
|
|
|
@@ -35582,16 +36036,16 @@ var init_worker = __esm({
|
|
|
35582
36036
|
\u2461 **\u786E\u5C5E\u56FE\u75C5\u624D\u4E0B\u5200**\uFF08link / spawn / cancel-part / \u6539\u8FDE\uFF09\uFF1B
|
|
35583
36037
|
\u2462 **\u62FF\u4E0D\u51C6\u3001\u6216\u6839\u56E0\u4E0D\u5728\u56FE\u4E0A**\uFF08\u50CF\u8FD0\u884C\u65F6\u5361\u6B7B\u3001\u5185\u5BB9\u5199\u4E0D\u51FA\u3001\u9700\u6C42\u672C\u8EAB\u77DB\u76FE\uFF09\u2192 **\u522B\u52A8\u56FE**\uFF0C\u4E00\u53E5\u8BDD\u8BF4\u6E05\u4F60\u5224\u65AD\u7684\u6839\u56E0 + \u5EFA\u8BAE\u8C01\u5904\u7F6E\uFF0C\u6536\u5DE5\u3002**\u8BEF\u5220\u4E00\u6761\u672C\u8BE5\u7B49\u7684\u6D41\u7A0B\uFF0C\u6BD4\u665A\u6551\u4E00\u4F1A\u513F\u66F4\u7CDF\u3002**
|
|
35584
36038
|
|
|
35585
|
-
\u3010\u6539\u56FE\uFF1A\u6682\u5B58 \u2192 \u63D0\u4EA4\u3011\u4F60\u80FD\u7528\u7684\u6539\u56FE\u547D\u4EE4\uFF08
|
|
35586
|
-
- \`oasis link <consumer> --to <up> [--required]\` \u2014\u2014 \u52A0\u4F9D\u8D56\u8FB9
|
|
35587
|
-
- \`oasis spawn <id> --type <t> --title "<\u77ED\u540D>" --brief "<\u89C4\u683C>" [--input <up>]\` \u2014\u2014 \u65B0\u5EFA\u8282\u70B9
|
|
35588
|
-
- \`oasis edit <id> [--title "\
|
|
35589
|
-
- \`oasis unlink <consumer> --to <up>\` \u2014\u2014 \
|
|
35590
|
-
- \`oasis cancel <id> [--note "\
|
|
35591
|
-
- \`oasis freeze <id> [--note "\
|
|
35592
|
-
- \`oasis cancel-part <id> --part <\u540D>\` \u2014\u2014 \u64A4\u90E8\u4EF6
|
|
35593
|
-
- \`oasis staged\` \u2014\u2014 \u770B\u5F53\u524D\u7F13\u51B2\u5168\u90E8\u5F85\u63D0\u4EA4\u9879
|
|
35594
|
-
- \`oasis apply --reason "
|
|
36039
|
+
\u3010\u6539\u56FE\uFF1A\u6682\u5B58 \u2192 \u63D0\u4EA4\u3011\u4F60\u80FD\u7528\u7684\u6539\u56FE\u547D\u4EE4\uFF08**\u90FD\u5148\u8FDB\u7F16\u8F91\u7F13\u51B2\u3001\u4E0D\u7ACB\u5373\u6539\u771F\u56FE**\uFF1B\u6539\u5B8C\u7528 \`oasis apply\` \u4E00\u6B21\u6027\u63D0\u4EA4\uFF09\uFF1A
|
|
36040
|
+
- \`oasis link <consumer> --to <up> [--required true]\` \u2014\u2014 \u7ED9 consumer \u52A0\u4E00\u6761\u6307\u5411\u4E0A\u6E38 up \u7684\u4F9D\u8D56\u8FB9\u3002\`<consumer>\`\uFF08\u4F4D\u7F6E\u53C2\u6570\uFF09= \u8981\u52A0\u4F9D\u8D56\u7684\u4E0B\u6E38\u8282\u70B9 id\uFF1B\`--to <up>\` = \u88AB\u4F9D\u8D56\u7684\u4E0A\u6E38 id\uFF1B\`--required true\` = \u8FD9\u6761\u662F**\u963B\u585E\u6027**\u4F9D\u8D56\uFF08up \u6CA1 conclude\uFF0Cconsumer \u5C31\u52A8\u4E0D\u4E86\uFF09\uFF0C**\u7701\u7565\u5219\u662F\u8F6F\u4F9D\u8D56**\uFF08\u53EA\u8BB0\u5173\u7CFB\u3001\u4E0D\u963B\u585E\uFF09\u3002\u53BB\u91CD\u65F6\u7528\u5B83\u628A"\u8BE5\u8FDE\u65E2\u6709"\u7684\u8FB9\u8865\u4E0A\u3002
|
|
36041
|
+
- \`oasis spawn <id> --type <t> [--title "<\u77ED\u540D>"] [--brief "<\u89C4\u683C>"] [--input <up>[,<up2>\u2026]]\` \u2014\u2014 \u65B0\u5EFA\u4E00\u4E2A\u8282\u70B9\u3002\`<id>\`\uFF08\u4F4D\u7F6E\u53C2\u6570\uFF09= \u65B0\u8282\u70B9 id\uFF08\u5F62\u5982 \`artifact:<type>:<\u540D>\`\uFF09\uFF1B\`--type <t>\` = \u7C7B\u578B\u540D\uFF08**\u5FC5\u586B**\uFF0C\u5148 \`oasis artifact-types\` \u770B\u53EF\u9009\u7C7B\u578B\u4E0E\u7528\u9014\uFF09\uFF1B\`--title\` = \u77ED\u540D\uFF1B\`--brief\` = \u7ED9\u5B83\u7684\u4EA7\u51FA\u89C4\u683C\uFF08\u5199\u6E05\u8981\u505A\u6210\u4EC0\u4E48\uFF0C\u4F1A\u6210\u4E3A\u8BE5\u8282\u70B9 owner \u7684\u4EFB\u52A1\u4E66\uFF09\uFF1B\`--input\` = \u5B83\u4F9D\u8D56\u54EA\u4E9B\u4E0A\u6E38\uFF08**\u9017\u53F7\u5206\u9694**\u591A\u4E2A\uFF09\u3002**\u786E\u65E0\u65E2\u6709\u53EF\u8FDE\u3001\u662F\u771F\u65B0\u9700\u6C42\u624D\u7528**\u3002
|
|
36042
|
+
- \`oasis edit <id> [--title "<\u65B0\u77ED\u540D>"] [--brief "<\u65B0\u89C4\u683C>"]\` \u2014\u2014 \u6539\u4E00\u4E2A**\u5DF2\u5B58\u5728**\u8282\u70B9\u7684\u6807\u9898 / brief\uFF08**\u53EA\u6539\u8FD9\u4E24\u6837\u5143\u6570\u636E\uFF0C\u7EDD\u4E0D\u78B0\u6B63\u6587**\uFF09\u3002\`<id>\` = \u88AB\u6539\u8282\u70B9\uFF1B\`--title\` / \`--brief\` \u7ED9\u54EA\u4E2A\u4F20\u54EA\u4E2A\u3002\u5408\u5E76\u53BB\u91CD\u65F6\u7528\u5B83\u628A\u4FDD\u7559\u4EF6\u7684 brief \u6269\u5199\u3002
|
|
36043
|
+
- \`oasis unlink <consumer> --to <up>\` \u2014\u2014 \u5220\u6389 consumer \u6307\u5411 up \u7684\u90A3\u6761\u4F9D\u8D56\u8FB9\u3002\`<consumer>\` = \u4E0B\u6E38\uFF1B\`--to <up>\` = \u4E0D\u518D\u4F9D\u8D56\u7684\u4E0A\u6E38\u3002\u6539\u8FDE\uFF08\u65AD\u9519\u8FB9 / \u6539\u8FDE\u5230\u6B63\u786E\u4E0A\u6E38\uFF09\u65F6\u7528\u3002
|
|
36044
|
+
- \`oasis cancel <id> [--note "<\u539F\u56E0>"]\` \u2014\u2014 **\u5F7B\u5E95\u5E9F\u5F03**\u4E00\u4E2A\u8282\u70B9\uFF1A\u5B83\u4E0D\u518D\u7B97\u6570\uFF0C\u7CFB\u7EDF**\u81EA\u52A8\u7EA7\u8054**\u65AD\u6389\u5B83\u6240\u6709\u4F9D\u8D56\u8FB9\u3001\u5E76\u91CD\u5524\u4E0B\u6E38\u8BA9\u5B83\u4EEC\u53BB\u6389\u5BF9\u5B83\u7684\u9002\u914D\u3002\`<id>\`\uFF08\u4F4D\u7F6E\u53C2\u6570\uFF09= \u88AB\u5E9F\u8282\u70B9\uFF1B\`--note\` = \u4E3A\u4EC0\u4E48\u5E9F\uFF08\u7559\u75D5\uFF09\u3002\u5EFA\u5728\u5DF2\u5E9F\u5730\u57FA\u4E0A\u3001\u6216\u8FD9\u8282\u70B9\u6839\u672C\u4E0D\u8BE5\u5B58\u5728\u65F6\u7528\u3002\u5E9F\u5F03\u540E\u8BE5\u8282\u70B9\u5728 \`oasis status\` \u91CC\u663E\u793A lifecycle=sealed:cancelled\u3002**\u9500\u6BC1\u6027**\u2014\u2014apply \u65F6\u6253\u5305\u6210\u63D0\u6848\u4EA4\u4EBA\u786E\u8BA4\u3002
|
|
36045
|
+
- \`oasis freeze <id> [--note "<\u539F\u56E0>"]\` \u2014\u2014 **\u51BB\u7ED3**\u4E00\u4E2A\u8282\u70B9\uFF1A\u4FDD\u7559\u5B83\u73B0\u6709\u4EA7\u51FA\u3001\u505C\u6B62\u518D\u6539\u52A8\uFF0C\u4F46**\u4F9D\u8D56\u8FB9\u90FD\u4FDD\u7559\u4E0D\u52A8**\uFF08\u4E0E cancel \u7684\u5173\u952E\u533A\u522B\u2014\u2014cancel \u662F\u65AD\u8FB9\u5E9F\u5F03\uFF0Cfreeze \u53EA\u662F\u6309\u4E0B\u6682\u505C\uFF09\u3002\`<id>\`\uFF08\u4F4D\u7F6E\u53C2\u6570\uFF09= \u88AB\u51BB\u8282\u70B9\uFF1B\`--note\` = \u4E3A\u4EC0\u4E48\u51BB\uFF08\u7559\u75D5\uFF09\u3002\u67D0\u8282\u70B9\u6682\u4E0D\u63A8\u8FDB\u3001\u4F46\u6210\u679C\u8981\u7559\u7740\u65F6\u7528\u3002\u51BB\u7ED3\u540E\u8BE5\u8282\u70B9\u5728 \`oasis status\` \u91CC\u663E\u793A lifecycle=sealed:frozen\u3002
|
|
36046
|
+
- \`oasis cancel-part <id> --part <\u90E8\u4EF6\u540D>\` \u2014\u2014 \u4ECE\u4E00\u4E2A\u5206\u89E3\u4E86\u90E8\u4EF6\u7684\u8282\u70B9\u4E0A**\u64A4\u6389\u4E00\u4E2A\u90E8\u4EF6**\u3002\`<id>\` = \u8282\u70B9\uFF1B\`--part\` = \u8981\u64A4\u7684\u90E8\u4EF6\u540D\uFF08**\u5FC5\u586B**\uFF09\u3002\u67D0 part \u6C38\u4E45\u4EA4\u4ED8\u4E0D\u4E86\u3001\u5361\u6B7B\u6574\u6761\u96C6\u6210\u65F6\u64A4\u6389\u5B83\u6551\u5176\u4F59\u3002**\u9500\u6BC1\u6027**\u2014\u2014\u8F6C\u4EBA\u5BA1\u3002
|
|
36047
|
+
- \`oasis staged\` \u2014\u2014 \u770B\u5F53\u524D\u7F16\u8F91\u7F13\u51B2\u91CC**\u5168\u90E8\u5F85\u63D0\u4EA4\u9879** + \u63D0\u4EA4\u540E\u5404\u53D7\u5F71\u54CD\u8282\u70B9\u4F1A\u53D8\u6210\u4EC0\u4E48\u6837\uFF08apply \u524D\u81EA\u68C0\uFF1B\u65E0\u53C2\u6570\uFF09\u3002
|
|
36048
|
+
- \`oasis apply --reason "<\u4E3A\u4EC0\u4E48\u8FD9\u4E48\u6539>"\` \u2014\u2014 **\u4E00\u6B21\u6027\u63D0\u4EA4**\u6574\u4E2A\u7F13\u51B2\u3002\`--reason\`\uFF08**\u5FC5\u586B**\uFF09\u5199\u6E05\u8FD9\u6279\u6539\u52A8\u7684\u610F\u56FE\u3002
|
|
35595
36049
|
\u6BCF\u4E2A\u6539\u56FE\u547D\u4EE4\u56DE\u663E"\u5DF2\u6682\u5B58\u4EC0\u4E48 + \u53D7\u5F71\u54CD\u8282\u70B9\u6682\u5B58\u540E\u7684\u6837\u5B50"\u3002\`apply\` \u65F6\u7CFB\u7EDF\u770B\u4F60\u8FD9\u6279 diff \u81EA\u52A8\u51B3\u5B9A\uFF1A\u80FD\u76F4\u63A5\u843D\u7684\u76F4\u63A5\u843D\uFF0C\u6D89\u53CA\u9500\u6BC1/\u6539\u6D3E/\u89E3\u5C01\u7684\u6253\u5305\u6210\u4E00\u4EFD\u63D0\u6848\u4EA4\u4EBA\u786E\u8BA4\u2014\u2014**\u4F60\u4E0D\u5FC5\u81EA\u5DF1\u5206\u7C7B\u3001\u4E0D\u5199 JSON**\u3002\u6388\u6743\u5355\u4F4D\u662F **diff \u4E0D\u662F\u80FD\u529B**\u3002
|
|
35596
36050
|
- \u6539\u5B8C**\u4E00\u5B9A apply**\uFF08\u5FD8\u4E86\u4E5F\u515C\u5E95\uFF1A\u6536\u5DE5\u65F6\u7F13\u51B2\u975E\u7A7A\uFF0C\u7CFB\u7EDF\u6309\u4E0A\u9762\u89C4\u5219\u66FF\u4F60\u63D0\u4EA4\uFF09\u3002
|
|
35597
36051
|
- \u4E00\u4EFD\u63D0\u6848\uFF1D**\u4E00\u4E2A\u8FDE\u8D2F\u4E8B\u52A1**\uFF1A\u5408\u5E76\u91CD\u590D\u5C31\u628A"\`edit\` \u6269\u4FDD\u7559\u4EF6 brief + \u6539\u8FDE + \`seal\` \u5E9F\u5F03\u4EF6"\u4E00\u8D77\u6682\u5B58\u518D apply\u3002
|
|
@@ -35599,10 +36053,10 @@ var init_worker = __esm({
|
|
|
35599
36053
|
- \u63D0\u4EA4\u540E**\u5C31\u6536\u5DE5**\uFF1B\u8F6C\u4EBA\u5BA1\u5C31\u5230\u6B64\u4E3A\u6B62\u2014\u2014\u4EBA\u786E\u8BA4\u540E\u56FE\u4F1A\u53D8\uFF0C\u9700\u8981\u65F6\u7CFB\u7EDF\u6309**\u65B0\u72B6\u6001**\u91CD\u65B0\u5524\u8D77\u4F60\uFF08\u4E0D\u5FC5\u7B49\u3001\u4E0D\u5FC5\u8BB0\u540E\u7EED\u3001\u4ECE\u56FE\u91CD\u5224\uFF09\u3002**\u8282\u70B9\u4E0A\u6709"\u88AB\u9A73\u56DE"\u7559\u75D5\u5C31\u522B\u91CD\u590D\u63D0\u3002**
|
|
35600
36054
|
|
|
35601
36055
|
\u3010\u8BFB\u56FE\u3011\uFF08\u90FD\u8BFB**\u771F\u56FE**\uFF0C\u4E0E\u522B\u7684 agent \u540C\u8BED\u4E49\uFF0C\u4E0D\u53D7\u4F60\u6682\u5B58\u5F71\u54CD\uFF09
|
|
35602
|
-
- \`oasis graph\`
|
|
35603
|
-
- \`oasis status <id>\` \u2014\u2014 \u5355\u4EA7\u7269\u72B6\u6001\
|
|
35604
|
-
- \`oasis content <id> [path]\` \u2014\u2014 \u5355\u4EA7\u7269\u6B63\u6587
|
|
35605
|
-
- \`oasis session-log <id|runId> [--n N]\` \u2014\u2014 \u770B\u67D0\u8282\u70B9\
|
|
36056
|
+
- \`oasis graph\` \u2014\u2014 \u5168\u56FE\u4F9D\u8D56\u8FB9\uFF08\u8C01\u8FDE\u8C01\uFF09\uFF1B\`oasis ls\` \u2014\u2014 \u5168\u4EA7\u7269\u5217\u8868\uFF08id / type / owner / head / \u961F\u5217 / \u72B6\u6001\u6807\uFF09\u3002\u5148\u7528\u5B83\u4EEC\u5EFA\u7ACB\u5168\u5C40\u56FE\u666F\u3002
|
|
36057
|
+
- \`oasis status <id>\` \u2014\u2014 \u5355\u4EA7\u7269\u7684\u72B6\u6001\u7D22\u5F15\uFF1Alifecycle\u3001\u5F53\u524D head\u3001\u4F9D\u8D56\u3001\u662F\u5426\u963B\u585E\u3001\u672A\u51B3\u6279\u6CE8\uFF08**\u4E0D\u542B\u6B63\u6587**\uFF0C\u5148\u770B\u8FD9\u4E2A\uFF09\u3002\`<id>\` = \u8981\u770B\u7684\u4EA7\u7269\u3002
|
|
36058
|
+
- \`oasis content <id> [path]\` \u2014\u2014 \u53D6\u5355\u4EA7\u7269**\u5B9E\u9645\u6B63\u6587**\uFF08\u5224\u65AD\u4E24\u8282\u70B9\u662F\u5426\u8BED\u4E49\u91CD\u590D\u3001\u8981\u4E0D\u8981\u5408\u5E76\u65F6\u8BFB\uFF09\u3002\`<id>\` = \u4EA7\u7269\uFF1B\u591A\u6587\u4EF6\u4EA7\u7269**\u7701\u7565 \`path\`** \u5148\u770B\u6587\u4EF6\u6E05\u5355\u3001\u518D\u7ED9 \`path\` \u53D6\u5355\u4E2A\u6587\u4EF6\u3002
|
|
36059
|
+
- \`oasis session-log <id|runId> [--n N]\` \u2014\u2014 \u770B\u67D0\u8282\u70B9 / \u67D0\u4F1A\u8BDD\u7684**\u8FD0\u884C\u65F6\u8F93\u51FA**\uFF08agent \u6B64\u523B\u5728\u8BF4 / \u5E72\u4EC0\u4E48\uFF0C\u975E\u5185\u6838\u4E8B\u5B9E\uFF09+ "\u8DDD\u4E0A\u6B21\u4E8B\u4EF6 X \u5206\u949F"\uFF0C\u5224\u5B83\u5728**\u60F3**\u8FD8\u662F**\u5361\u6B7B**\uFF08>10min\u2248\u5361\uFF09\u3002\u4F20 \`<artifactId>\` \u4F1A\u5217\u51FA\u8BE5\u8282\u70B9\u7684**\u5168\u90E8\u4F1A\u8BDD**\u3001\u518D\u5C55\u5F00\u6700\u8FD1\u5728\u8DD1\u7684\u90A3\u4E2A\uFF1B\u4F20\u5177\u4F53 \`<runId>\` \u76F4\u63A5\u770B\u90A3\u4E00\u4E2A\uFF1B\`--n N\` = \u770B\u6700\u65B0 N \u6761\u4E8B\u4EF6\uFF08\u9ED8\u8BA4 20\uFF09\u3002\u6B8B\u5DEE\u4E8C\u6B21\u5F52\u56E0\u3001\u6216\u60F3\u786E\u8BA4\u4E0A\u6E38\u662F\u5426\u771F\u5728\u88AB\u5E72\u65F6\u7528\u3002`;
|
|
35606
36060
|
CoordinatorWorker = class {
|
|
35607
36061
|
constructor(deps) {
|
|
35608
36062
|
this.deps = deps;
|
|
@@ -35751,15 +36205,6 @@ var init_worker = __esm({
|
|
|
35751
36205
|
gaps.length > 0 ? `\u672A\u89E3\u6790gap\xD7${gaps.length}` : null
|
|
35752
36206
|
].filter(Boolean).join("\u3001");
|
|
35753
36207
|
}
|
|
35754
|
-
graphDigest(exclude) {
|
|
35755
|
-
const lines = [];
|
|
35756
|
-
for (const a of this.deps.kernel.model.artifacts.values()) {
|
|
35757
|
-
if (a.id === exclude) continue;
|
|
35758
|
-
const deps = a.inputs.length > 0 ? `\uFF1B\u4F9D\u8D56 [${a.inputs.map((e) => e.to).join(", ")}]` : "";
|
|
35759
|
-
lines.push(`- ${a.id}\uFF08type=${a.type}\uFF5C${this.nodeState(a.id)}\uFF09${a.description ? `\uFF1A${a.description}` : ""}${deps}`);
|
|
35760
|
-
}
|
|
35761
|
-
return lines.length > 0 ? lines.join("\n") : "\uFF08\u56FE\u91CC\u6682\u65E0\u5176\u5B83\u4EA7\u7269\uFF09";
|
|
35762
|
-
}
|
|
35763
36208
|
/** 结构摘要(reconcile 去重用):id / type / 是否已交付 / title+brief / 依赖——不含进度态(去重只看语义)。 */
|
|
35764
36209
|
structDigest(exclude) {
|
|
35765
36210
|
const m2 = this.deps.kernel.model;
|
|
@@ -35804,22 +36249,51 @@ var init_worker = __esm({
|
|
|
35804
36249
|
* 真死锁且某 part 卡住整条集成 → 起草 cancelPart(C 类,人确认);能解就解;拿不准 / 非图病 → 别动、交人。
|
|
35805
36250
|
*/
|
|
35806
36251
|
async reconcileDeadlock(p2) {
|
|
35807
|
-
const
|
|
36252
|
+
const m2 = this.deps.kernel.model;
|
|
36253
|
+
const node = m2.artifacts.get(p2.id);
|
|
35808
36254
|
const mins = Math.round(p2.staleMs / 6e4);
|
|
35809
|
-
const parts = node?.parts?.map((x2) => x2.name).join("\u3001") || "\uFF08\u65E0\u5206\u89E3\uFF09";
|
|
35810
36255
|
const brief = node?.title ?? node?.description ?? "";
|
|
36256
|
+
const now = Date.now();
|
|
35811
36257
|
const caseFile = p2.diag.causes.length ? p2.diag.causes.map((c) => `> - [${c.category}\uFF5C\u6765\u6E90 ${c.source}\uFF5C\u5EFA\u8BAE\u5904\u7F6E ${c.handler}] ${c.detail}`).join("\n") : "> \uFF08\u673A\u68B0\u7A77\u4E3E\u65E0\u660E\u786E\u75C5\u56E0\u2014\u2014\u6B8B\u5DEE\uFF0C\u9700\u4F60\u4E8C\u6B21\u5F52\u56E0\uFF09";
|
|
36258
|
+
const sessionsLine = (id) => {
|
|
36259
|
+
const ss = this.deps.sessionsOf?.(id) ?? [];
|
|
36260
|
+
if (ss.length === 0) return "\u65E0\u5728\u8DD1\u4F1A\u8BDD";
|
|
36261
|
+
return `${ss.length} \u4E2A \u2014\u2014 ${ss.map((s2) => `${s2.action} run ${s2.runId}\uFF08\u5DF2\u8DD1 ${Math.round((now - Date.parse(s2.startedAt)) / 6e4)} \u5206\u949F\uFF09`).join("\u3001")}`;
|
|
36262
|
+
};
|
|
36263
|
+
const nodeBlock = (id) => {
|
|
36264
|
+
const a = m2.artifacts.get(id);
|
|
36265
|
+
if (!a) return `- **${id}**\uFF08\u4E0D\u5B58\u5728 / \u5DF2\u88AB\u6E05\u7406\uFF09`;
|
|
36266
|
+
const head = a.currentRev ? `head@${String(a.currentRev).slice(9, 17)}` : "\u65E0head";
|
|
36267
|
+
const blk = blockedOf(m2, id);
|
|
36268
|
+
const status = [lifecycleOf(m2, id), head, blk.blocked ? `\u963B\u585E[${blk.reasons.join("\uFF1B")}]` : null].filter(Boolean).join("\uFF5C");
|
|
36269
|
+
const label = a.title ?? a.description ?? "\uFF08\u65E0 brief\uFF09";
|
|
36270
|
+
return [
|
|
36271
|
+
`- **${id}**\uFF08type=${a.type}\uFF1A${label}\uFF09`,
|
|
36272
|
+
` - \u72B6\u6001\uFF1A${status}${a.parts?.length ? `\uFF5C\u90E8\u4EF6 [${a.parts.map((x2) => x2.name).join("\u3001")}]` : ""}`,
|
|
36273
|
+
` - \u5728\u8DD1\u4F1A\u8BDD\uFF1A${sessionsLine(id)}`
|
|
36274
|
+
].join("\n");
|
|
36275
|
+
};
|
|
36276
|
+
const upstreams = (node?.inputs ?? []).map((e) => e.to);
|
|
36277
|
+
const downstreams = consumersOf(m2, p2.id).map((a) => a.id);
|
|
35812
36278
|
const task = [
|
|
35813
36279
|
`## \u672C\u6B21\u4E3A\u4EC0\u4E48\u5524\u8D77\u4F60`,
|
|
35814
|
-
`\u8282\u70B9 **${p2.id}**${brief ? `\uFF08${brief}\uFF09` : ""} \u5DF2\u963B\u585E\u7EA6 **${mins} \u5206\u949F**\u6CA1\u8FDB\u5C55\u3002\u7CFB\u7EDF\u5DF2\u673A\u68B0\u8BCA\u65AD\u3001\u786E\u8BA4**\u8FD9\u662F\u56FE\u75C5\u6216\u673A\u68B0\u5B9A\u4F4D\u4E0D\u5230\u7684\u6B8B\u5DEE**\uFF08\u8D44\u6E90 / \u4EBA / \u8FD0\u884C\u65F6\u7C7B\u75C5\u56E0\u5DF2\
|
|
36280
|
+
`\u8282\u70B9 **${p2.id}**${brief ? `\uFF08${brief}\uFF09` : ""} \u5DF2\u963B\u585E\u7EA6 **${mins} \u5206\u949F**\u6CA1\u8FDB\u5C55\u3002\u7CFB\u7EDF\u5DF2\u673A\u68B0\u8BCA\u65AD\u3001\u786E\u8BA4**\u8FD9\u662F\u56FE\u75C5\u6216\u673A\u68B0\u5B9A\u4F4D\u4E0D\u5230\u7684\u6B8B\u5DEE**\uFF08\u8D44\u6E90 / \u4EBA / \u8FD0\u884C\u65F6\u7C7B\u75C5\u56E0\u5DF2\u6392\u9664\uFF09\u3002`,
|
|
35815
36281
|
``,
|
|
35816
36282
|
`## \u75C5\u5386\uFF08\u673A\u68B0\u8BCA\u65AD\uFF1B\u5148\u8BFB\u8FD9\u4E2A\uFF09`,
|
|
35817
36283
|
caseFile,
|
|
35818
|
-
`\u5148\u8FA8\
|
|
36284
|
+
`\u5148\u8FA8\u662F**\u771F\u6B7B\u9501 / \u8BE5\u6539\u56FE**\uFF0C\u8FD8\u662F**\u53EA\u662F\u5728\u7B49\u4E00\u4E2A\u5065\u5EB7\u5728\u4EA7\u7684\u4E0A\u6E38**\u2014\u2014**\u786E\u662F\u771F\u6B7B\u9501\u5C31\u679C\u65AD\u6539\u56FE\uFF08\u8FD9\u6B63\u662F\u8981\u4F60\u505A\u7684\uFF09\uFF1B\u53EA\u6709\u5065\u5EB7\u7B49\u5F85\u624D\u522B\u52A8**\u3002`,
|
|
36285
|
+
``,
|
|
36286
|
+
`## \u672C\u8282\u70B9 + \u90BB\u57DF`,
|
|
36287
|
+
`> \u5B57\u6BB5\u8BFB\u6CD5\uFF1Alifecycle\uFF08active=\u8FDB\u884C\u4E2D / sealed:frozen=\u51BB\u7ED3\u3001\u4EA7\u4E0D\u51FA\u65B0\u91CC\u7A0B\u7891 / sealed:cancelled=\u5DF2\u5E9F\u5F03 / sealed:accepted=\u5DF2\u5B9A\u7A3F\u7EC8\u6001\uFF09\uFF1Bhead=\u5F53\u524D\u5B9A\u7A3F\u7248\u672C\uFF08\u65E0head=\u8FD8\u6CA1\u4EA7\u51FA\u8FC7\u4EFB\u4F55\u91CC\u7A0B\u7891\uFF09\uFF1B\u963B\u585E[\u2026]=\u5361\u5728\u4EC0\u4E48\u4E0A\uFF1B\u5728\u8DD1\u4F1A\u8BDD=\u6B64\u523B\u6709\u51E0\u4E2A agent \u4F1A\u8BDD\u5728\u5E72\u5B83\u3001\u5404\u8DD1\u591A\u4E45\u3002`,
|
|
36288
|
+
``,
|
|
36289
|
+
`### \u672C\u8282\u70B9`,
|
|
36290
|
+
nodeBlock(p2.id),
|
|
35819
36291
|
``,
|
|
35820
|
-
|
|
35821
|
-
|
|
35822
|
-
|
|
36292
|
+
`### \u4E0A\u6E38\uFF08\u672C\u8282\u70B9\u4F9D\u8D56\u8C01\u2014\u2014\u5B83\u4EEC\u6CA1\u5C31\u7EEA\uFF0C\u672C\u8282\u70B9\u52A8\u4E0D\u4E86\uFF09`,
|
|
36293
|
+
upstreams.length ? upstreams.map(nodeBlock).join("\n") : "- \uFF08\u65E0\u4E0A\u6E38\u4F9D\u8D56\uFF09",
|
|
36294
|
+
``,
|
|
36295
|
+
`### \u4E0B\u6E38\uFF08\u8C01\u4F9D\u8D56\u672C\u8282\u70B9\u2014\u2014\u672C\u8282\u70B9\u5361\uFF0C\u5B83\u4EEC\u8DDF\u7740\u5361\uFF09`,
|
|
36296
|
+
downstreams.length ? downstreams.map(nodeBlock).join("\n") : "- \uFF08\u65E0\u4E0B\u6E38\u6D88\u8D39\u8005\uFF09",
|
|
35823
36297
|
``,
|
|
35824
36298
|
`## \u4F60\u7684\u52A8\u4F5C\uFF08\u6682\u5B58 \u2192 apply\uFF09`,
|
|
35825
36299
|
`- \u5728**\u7B49\u4E00\u4E2A active\u3001\u6709 head/\u5728\u4EA7\u7684\u4E0A\u6E38** \u2192 **\u4E0D\u662F\u6B7B\u9501\uFF0C\u522B\u52A8**\uFF0C\u6536\u5DE5\uFF08\u53EF \`oasis status <\u4E0A\u6E38>\` \u786E\u8BA4\uFF09\u3002`,
|
|
@@ -35907,6 +36381,7 @@ var init_src4 = __esm({
|
|
|
35907
36381
|
init_resolve();
|
|
35908
36382
|
init_worker();
|
|
35909
36383
|
init_drafts();
|
|
36384
|
+
init_workorder_drafts();
|
|
35910
36385
|
}
|
|
35911
36386
|
});
|
|
35912
36387
|
|
|
@@ -37478,11 +37953,11 @@ var require_pg_connection_string = __commonJS({
|
|
|
37478
37953
|
config2.client_encoding = result.searchParams.get("encoding");
|
|
37479
37954
|
return config2;
|
|
37480
37955
|
}
|
|
37481
|
-
const
|
|
37956
|
+
const hostname5 = dummyHost ? "" : result.hostname;
|
|
37482
37957
|
if (!config2.host) {
|
|
37483
|
-
config2.host = decodeURIComponent(
|
|
37484
|
-
} else if (
|
|
37485
|
-
result.pathname =
|
|
37958
|
+
config2.host = decodeURIComponent(hostname5);
|
|
37959
|
+
} else if (hostname5 && /^%2f/i.test(hostname5)) {
|
|
37960
|
+
result.pathname = hostname5 + result.pathname;
|
|
37486
37961
|
}
|
|
37487
37962
|
if (!config2.port) {
|
|
37488
37963
|
config2.port = result.port;
|
|
@@ -41092,7 +41567,6 @@ var init_trajectory = __esm({
|
|
|
41092
41567
|
});
|
|
41093
41568
|
|
|
41094
41569
|
// src/index.ts
|
|
41095
|
-
var import_node_crypto22 = require("node:crypto");
|
|
41096
41570
|
var fs15 = __toESM(require("node:fs"));
|
|
41097
41571
|
var os8 = __toESM(require("node:os"));
|
|
41098
41572
|
var path13 = __toESM(require("node:path"));
|
|
@@ -41874,6 +42348,9 @@ var CodexAdapter = class {
|
|
|
41874
42348
|
"-C",
|
|
41875
42349
|
dir,
|
|
41876
42350
|
...this.opts.model ? ["--model", this.opts.model] : [],
|
|
42351
|
+
// 打开 reasoning 摘要通道:默认 auto,让 codex 产出 reasoning item(→ thought → 前端思考块)。
|
|
42352
|
+
...this.opts.reasoningSummary !== "none" ? ["-c", `model_reasoning_summary=${this.opts.reasoningSummary ?? "auto"}`] : [],
|
|
42353
|
+
...this.opts.reasoningEffort ? ["-c", `model_reasoning_effort=${this.opts.reasoningEffort}`] : [],
|
|
41877
42354
|
...this.opts.extraArgs ?? [],
|
|
41878
42355
|
prompt
|
|
41879
42356
|
];
|
|
@@ -43907,6 +44384,10 @@ var PostgresArtifactStateStore = class _PostgresArtifactStateStore {
|
|
|
43907
44384
|
work_order_id text PRIMARY KEY,
|
|
43908
44385
|
project_id text NOT NULL
|
|
43909
44386
|
)`);
|
|
44387
|
+
await pool.query(`
|
|
44388
|
+
CREATE TABLE IF NOT EXISTS "${s2}".workspace_dispatch_hold (
|
|
44389
|
+
work_order_id text PRIMARY KEY
|
|
44390
|
+
)`);
|
|
43910
44391
|
return new _PostgresArtifactStateStore(pool, schema);
|
|
43911
44392
|
}
|
|
43912
44393
|
/* ---------- artifacts ---------- */
|
|
@@ -44180,14 +44661,29 @@ var PostgresArtifactStateStore = class _PostgresArtifactStateStore {
|
|
|
44180
44661
|
const r = await this.pool.query(`SELECT * FROM ${this.s}.workspace_bindings`);
|
|
44181
44662
|
return r.rows.map(rowToWorkspaceBinding);
|
|
44182
44663
|
}
|
|
44664
|
+
async setWorkspaceDispatchHold(workOrderId, held) {
|
|
44665
|
+
if (held) {
|
|
44666
|
+
await this.pool.query(
|
|
44667
|
+
`INSERT INTO ${this.s}.workspace_dispatch_hold (work_order_id) VALUES ($1) ON CONFLICT DO NOTHING`,
|
|
44668
|
+
[workOrderId]
|
|
44669
|
+
);
|
|
44670
|
+
} else {
|
|
44671
|
+
await this.pool.query(`DELETE FROM ${this.s}.workspace_dispatch_hold WHERE work_order_id = $1`, [workOrderId]);
|
|
44672
|
+
}
|
|
44673
|
+
}
|
|
44674
|
+
async listWorkspaceDispatchHeld() {
|
|
44675
|
+
const r = await this.pool.query(`SELECT work_order_id FROM ${this.s}.workspace_dispatch_hold`);
|
|
44676
|
+
return r.rows.map((row) => String(row.work_order_id));
|
|
44677
|
+
}
|
|
44183
44678
|
async listAll() {
|
|
44184
|
-
const [artifacts, revisions, events, annotations, outputs, bindings] = await Promise.all([
|
|
44679
|
+
const [artifacts, revisions, events, annotations, outputs, bindings, held] = await Promise.all([
|
|
44185
44680
|
this.pool.query(`SELECT * FROM ${this.s}.artifacts`),
|
|
44186
44681
|
this.pool.query(`SELECT * FROM ${this.s}.artifact_revisions`),
|
|
44187
44682
|
this.pool.query(`SELECT * FROM ${this.s}.artifact_events ORDER BY seq`),
|
|
44188
44683
|
this.pool.query(`SELECT * FROM ${this.s}.artifact_annotations`),
|
|
44189
44684
|
this.pool.query(`SELECT * FROM ${this.s}.node_outputs`),
|
|
44190
|
-
this.pool.query(`SELECT * FROM ${this.s}.workspace_bindings`)
|
|
44685
|
+
this.pool.query(`SELECT * FROM ${this.s}.workspace_bindings`),
|
|
44686
|
+
this.pool.query(`SELECT work_order_id FROM ${this.s}.workspace_dispatch_hold`)
|
|
44191
44687
|
]);
|
|
44192
44688
|
const nodeOutputs = [];
|
|
44193
44689
|
for (const row of outputs.rows) {
|
|
@@ -44203,7 +44699,8 @@ var PostgresArtifactStateStore = class _PostgresArtifactStateStore {
|
|
|
44203
44699
|
events: events.rows.map(rowToEvent),
|
|
44204
44700
|
annotations: annotations.rows.map(rowToAnnotation),
|
|
44205
44701
|
nodeOutputs,
|
|
44206
|
-
workspaceBindings: bindings.rows.map(rowToWorkspaceBinding)
|
|
44702
|
+
workspaceBindings: bindings.rows.map(rowToWorkspaceBinding),
|
|
44703
|
+
workspaceDispatchHeld: held.rows.map((row) => String(row.work_order_id))
|
|
44207
44704
|
};
|
|
44208
44705
|
}
|
|
44209
44706
|
/**
|
|
@@ -45249,13 +45746,6 @@ var PostgresChatSessionStore = class _PostgresChatSessionStore {
|
|
|
45249
45746
|
created_at timestamptz NOT NULL
|
|
45250
45747
|
)`);
|
|
45251
45748
|
await pool.query(`CREATE INDEX IF NOT EXISTS chat_messages_session_idx ON "${s2}".chat_messages (session_id, seq)`);
|
|
45252
|
-
await pool.query(`
|
|
45253
|
-
CREATE TABLE IF NOT EXISTS "${s2}".chat_session_work_orders (
|
|
45254
|
-
session_id text NOT NULL,
|
|
45255
|
-
work_order_id text NOT NULL,
|
|
45256
|
-
linked_at timestamptz NOT NULL DEFAULT now(),
|
|
45257
|
-
PRIMARY KEY (session_id, work_order_id)
|
|
45258
|
-
)`);
|
|
45259
45749
|
return new _PostgresChatSessionStore(pool, schema);
|
|
45260
45750
|
}
|
|
45261
45751
|
async createSession(s2) {
|
|
@@ -45319,19 +45809,6 @@ var PostgresChatSessionStore = class _PostgresChatSessionStore {
|
|
|
45319
45809
|
const rows = r.rows.map(rowToMessage);
|
|
45320
45810
|
return limit ? rows.reverse() : rows;
|
|
45321
45811
|
}
|
|
45322
|
-
async linkWorkOrder(sessionId, workOrderId) {
|
|
45323
|
-
await this.pool.query(
|
|
45324
|
-
`INSERT INTO ${this.s}.chat_session_work_orders (session_id, work_order_id) VALUES ($1,$2) ON CONFLICT DO NOTHING`,
|
|
45325
|
-
[sessionId, workOrderId]
|
|
45326
|
-
);
|
|
45327
|
-
}
|
|
45328
|
-
async listWorkOrdersForSession(sessionId) {
|
|
45329
|
-
const r = await this.pool.query(
|
|
45330
|
-
`SELECT work_order_id FROM ${this.s}.chat_session_work_orders WHERE session_id = $1 ORDER BY linked_at`,
|
|
45331
|
-
[sessionId]
|
|
45332
|
-
);
|
|
45333
|
-
return r.rows.map((row) => row.work_order_id);
|
|
45334
|
-
}
|
|
45335
45812
|
};
|
|
45336
45813
|
var rowToSession = (row) => ({
|
|
45337
45814
|
id: row.id,
|
|
@@ -45389,10 +45866,6 @@ var PostgresNodeStore = class _PostgresNodeStore {
|
|
|
45389
45866
|
last_seen_at timestamptz NOT NULL
|
|
45390
45867
|
)`);
|
|
45391
45868
|
await pool.query(`CREATE INDEX IF NOT EXISTS node_runtimes_node_idx ON "${s2}".node_runtimes (node_id)`);
|
|
45392
|
-
await pool.query(`
|
|
45393
|
-
CREATE TABLE IF NOT EXISTS "${s2}".deleted_runtime_ids (
|
|
45394
|
-
id text PRIMARY KEY
|
|
45395
|
-
)`);
|
|
45396
45869
|
return new _PostgresNodeStore(pool, schema);
|
|
45397
45870
|
}
|
|
45398
45871
|
async upsertNode(n) {
|
|
@@ -45409,8 +45882,6 @@ var PostgresNodeStore = class _PostgresNodeStore {
|
|
|
45409
45882
|
);
|
|
45410
45883
|
}
|
|
45411
45884
|
async upsertRuntime(r) {
|
|
45412
|
-
const deleted = await this.pool.query(`SELECT 1 FROM ${this.s}.deleted_runtime_ids WHERE id=$1`, [r.id]);
|
|
45413
|
-
if (deleted.rowCount) return;
|
|
45414
45885
|
await this.pool.query(
|
|
45415
45886
|
`INSERT INTO ${this.s}.node_runtimes (id,node_id,kind,hostname,runtime_binary,version,status,last_seen_at)
|
|
45416
45887
|
VALUES ($1,$2,$3,$4,$5,$6,$7,$8)
|
|
@@ -45423,8 +45894,18 @@ var PostgresNodeStore = class _PostgresNodeStore {
|
|
|
45423
45894
|
[r.id, r.nodeId, r.kind, r.hostname, r.binary ?? null, r.version ?? null, r.status, r.lastSeenAt]
|
|
45424
45895
|
);
|
|
45425
45896
|
}
|
|
45897
|
+
/** Insert only if absent (ON CONFLICT DO NOTHING). Returns true if a row was inserted. */
|
|
45898
|
+
async addRuntimeIfAbsent(r) {
|
|
45899
|
+
const res = await this.pool.query(
|
|
45900
|
+
`INSERT INTO ${this.s}.node_runtimes (id,node_id,kind,hostname,runtime_binary,version,status,last_seen_at)
|
|
45901
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8)
|
|
45902
|
+
ON CONFLICT (id) DO NOTHING`,
|
|
45903
|
+
[r.id, r.nodeId, r.kind, r.hostname, r.binary ?? null, r.version ?? null, r.status, r.lastSeenAt]
|
|
45904
|
+
);
|
|
45905
|
+
return (res.rowCount ?? 0) > 0;
|
|
45906
|
+
}
|
|
45426
45907
|
async setNodeOnline(id, online) {
|
|
45427
|
-
await this.pool.query(`UPDATE ${this.s}.nodes SET status=$2,last_seen_at=$3 WHERE id=$1`, [id, online ? "online" : "offline", (/* @__PURE__ */ new Date()).toISOString()]);
|
|
45908
|
+
await this.pool.query(`UPDATE ${this.s}.nodes SET status=$2,last_seen_at=$3 WHERE id=$1 AND status <> 'deleted'`, [id, online ? "online" : "offline", (/* @__PURE__ */ new Date()).toISOString()]);
|
|
45428
45909
|
}
|
|
45429
45910
|
async setNodeRuntimesOnline(nodeId, online) {
|
|
45430
45911
|
await this.pool.query(`UPDATE ${this.s}.node_runtimes SET status=$2,last_seen_at=$3 WHERE node_id=$1`, [nodeId, online ? "online" : "offline", (/* @__PURE__ */ new Date()).toISOString()]);
|
|
@@ -45437,7 +45918,7 @@ var PostgresNodeStore = class _PostgresNodeStore {
|
|
|
45437
45918
|
return r.rows[0] ? rowToNode(r.rows[0]) : null;
|
|
45438
45919
|
}
|
|
45439
45920
|
async listNodes() {
|
|
45440
|
-
const r = await this.pool.query(`SELECT * FROM ${this.s}.nodes ORDER BY enrolled_at ASC`);
|
|
45921
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.nodes WHERE status <> 'deleted' ORDER BY enrolled_at ASC`);
|
|
45441
45922
|
return r.rows.map(rowToNode);
|
|
45442
45923
|
}
|
|
45443
45924
|
async listRuntimes(nodeId) {
|
|
@@ -45446,24 +45927,24 @@ var PostgresNodeStore = class _PostgresNodeStore {
|
|
|
45446
45927
|
}
|
|
45447
45928
|
async deleteNode(id) {
|
|
45448
45929
|
await this.pool.query(`DELETE FROM ${this.s}.nodes WHERE id=$1`, [id]);
|
|
45449
|
-
|
|
45930
|
+
}
|
|
45931
|
+
/** Soft-delete: mark node as deleted (excluded from normal API, kept for audit) and drop its runtime rows. */
|
|
45932
|
+
async softDeleteNode(id) {
|
|
45933
|
+
await this.pool.query(`UPDATE ${this.s}.nodes SET status='deleted',last_seen_at=$2 WHERE id=$1`, [id, (/* @__PURE__ */ new Date()).toISOString()]);
|
|
45934
|
+
await this.pool.query(`DELETE FROM ${this.s}.node_runtimes WHERE node_id=$1`, [id]);
|
|
45450
45935
|
}
|
|
45451
45936
|
async deleteRuntime(id) {
|
|
45452
|
-
await this.pool.query(`INSERT INTO ${this.s}.deleted_runtime_ids (id) VALUES ($1) ON CONFLICT DO NOTHING`, [id]);
|
|
45453
45937
|
const existing = await this.pool.query(`SELECT node_id FROM ${this.s}.node_runtimes WHERE id=$1`, [id]);
|
|
45454
45938
|
await this.pool.query(`DELETE FROM ${this.s}.node_runtimes WHERE id=$1`, [id]);
|
|
45455
45939
|
if (!existing.rows[0]) return { nodeDeleted: false };
|
|
45456
45940
|
const nodeId = existing.rows[0].node_id;
|
|
45457
45941
|
const remaining = await this.pool.query(`SELECT 1 FROM ${this.s}.node_runtimes WHERE node_id=$1 LIMIT 1`, [nodeId]);
|
|
45458
45942
|
if (!remaining.rowCount) {
|
|
45459
|
-
await this.
|
|
45943
|
+
await this.softDeleteNode(nodeId);
|
|
45460
45944
|
return { nodeDeleted: true };
|
|
45461
45945
|
}
|
|
45462
45946
|
return { nodeDeleted: false };
|
|
45463
45947
|
}
|
|
45464
|
-
async clearDeletedRuntimesForNode(nodeId) {
|
|
45465
|
-
await this.pool.query(`DELETE FROM ${this.s}.deleted_runtime_ids WHERE id LIKE $1`, [`runtime:${nodeId}:%`]);
|
|
45466
|
-
}
|
|
45467
45948
|
};
|
|
45468
45949
|
var PostgresNodeTokenStore = class _PostgresNodeTokenStore {
|
|
45469
45950
|
constructor(pool, schema) {
|
|
@@ -45521,6 +46002,7 @@ var rowToRuntime = (row) => ({
|
|
|
45521
46002
|
// ../cli/src/serve.ts
|
|
45522
46003
|
init_src3();
|
|
45523
46004
|
init_trajectory();
|
|
46005
|
+
var DEFAULT_CHAT_SKILLS = ["build-workorder"];
|
|
45524
46006
|
var oasisWrapperCache;
|
|
45525
46007
|
function oasisWrapperScript() {
|
|
45526
46008
|
if (oasisWrapperCache && fs13.existsSync(oasisWrapperCache)) return oasisWrapperCache;
|
|
@@ -45634,6 +46116,7 @@ async function startServe(opts) {
|
|
|
45634
46116
|
if ((await typeStore.list()).length === 0) for (const d of schema) await typeStore.put(d);
|
|
45635
46117
|
{
|
|
45636
46118
|
const live = await typeStore.list();
|
|
46119
|
+
upgradeDefaultFieldDefs(live);
|
|
45637
46120
|
schema.length = 0;
|
|
45638
46121
|
schema.push(...live);
|
|
45639
46122
|
}
|
|
@@ -45800,6 +46283,7 @@ async function startServe(opts) {
|
|
|
45800
46283
|
return auth.sessionAccountId(headers);
|
|
45801
46284
|
};
|
|
45802
46285
|
const interventionDrafts = new InterventionDraftStore();
|
|
46286
|
+
const workorderDrafts = new WorkorderDraftStore();
|
|
45803
46287
|
const journalFile = path11.join(opts.dir, "dispatch-journal.ndjson");
|
|
45804
46288
|
const journalRing = [];
|
|
45805
46289
|
if (fs13.existsSync(journalFile)) {
|
|
@@ -45880,6 +46364,15 @@ async function startServe(opts) {
|
|
|
45880
46364
|
if (actorCtx?.config?.prompt) {
|
|
45881
46365
|
Object.assign(files, splitIdentityFiles3(actorCtx.config.prompt));
|
|
45882
46366
|
}
|
|
46367
|
+
Object.assign(files, await materializeSkillFiles({
|
|
46368
|
+
service: actors.service,
|
|
46369
|
+
blobs: assets,
|
|
46370
|
+
actorId: planner.id,
|
|
46371
|
+
runtimeKind: binding.runtimeKind
|
|
46372
|
+
}).catch((err) => {
|
|
46373
|
+
console.warn(`[planner] skill materialize failed for ${planner.id}: ${String(err)}`);
|
|
46374
|
+
return {};
|
|
46375
|
+
}));
|
|
45883
46376
|
const handle = await chatRemoteAdapter.spawn({
|
|
45884
46377
|
actor: planner.id,
|
|
45885
46378
|
actorToken: issuer.issue(planner.id),
|
|
@@ -45927,10 +46420,10 @@ async function startServe(opts) {
|
|
|
45927
46420
|
}),
|
|
45928
46421
|
trace.register,
|
|
45929
46422
|
automations.register,
|
|
45930
|
-
...chatSessionStore ? [createChatSessionsDomain({ store: chatSessionStore, registry: registryStore
|
|
46423
|
+
...chatSessionStore ? [createChatSessionsDomain({ store: chatSessionStore, registry: registryStore })] : []
|
|
45931
46424
|
],
|
|
45932
46425
|
drafts: interventionDrafts,
|
|
45933
|
-
|
|
46426
|
+
workorderDrafts,
|
|
45934
46427
|
// 暴露注册的 artifact 类型,给前端「发起工单」动态拉取根产物可选类型——schema.json 增改即自动扩展,
|
|
45935
46428
|
// 前端不再写死枚举。走通用 /api/view(generic data),非域路由契约,不进 gen:api。
|
|
45936
46429
|
views: {
|
|
@@ -45949,6 +46442,7 @@ async function startServe(opts) {
|
|
|
45949
46442
|
},
|
|
45950
46443
|
resolveActorEnv: async (actorId) => (await buildActorProvision(actors.service, actorId)).env,
|
|
45951
46444
|
resolveActorContext: (actorId) => buildActorContext(actors.service, actorId),
|
|
46445
|
+
materializeSkills: (actorId, runtimeKind) => materializeSkillFiles({ service: actors.service, blobs: assets, actorId, runtimeKind }),
|
|
45952
46446
|
dispatchProduce: async ({ artifactId, actorId, part }) => {
|
|
45953
46447
|
if (!dispatcher) {
|
|
45954
46448
|
throw new ApiError(503, "DISPATCH_NOT_ENABLED", "\u670D\u52A1\u7AEF\u672A\u542F\u7528 --dispatch true\uFF0C\u4E0D\u80FD\u62C9\u8D77\u771F\u5B9E\u667A\u80FD\u4F53\u4FEE\u6539");
|
|
@@ -45959,7 +46453,7 @@ async function startServe(opts) {
|
|
|
45959
46453
|
...part !== void 0 ? { part } : {}
|
|
45960
46454
|
});
|
|
45961
46455
|
},
|
|
45962
|
-
dispatchChat: async ({ actorId, message, sessionId
|
|
46456
|
+
dispatchChat: async ({ actorId, message, sessionId }) => {
|
|
45963
46457
|
const binding = await actors.service.resolveBinding()(actorId);
|
|
45964
46458
|
if (!binding) {
|
|
45965
46459
|
throw new ApiError(409, "ACTOR_BINDING_MISSING", `\u6570\u5B57\u5458\u5DE5 ${actorId} \u6CA1\u6709 active runtime \u7ED1\u5B9A`);
|
|
@@ -46049,6 +46543,16 @@ async function startServe(opts) {
|
|
|
46049
46543
|
if (actorCtx?.config?.prompt) {
|
|
46050
46544
|
Object.assign(files, splitIdentityFiles3(actorCtx.config.prompt));
|
|
46051
46545
|
}
|
|
46546
|
+
Object.assign(files, await materializeSkillFiles({
|
|
46547
|
+
service: actors.service,
|
|
46548
|
+
blobs: assets,
|
|
46549
|
+
actorId,
|
|
46550
|
+
runtimeKind: binding.runtimeKind,
|
|
46551
|
+
extraSkillIds: DEFAULT_CHAT_SKILLS
|
|
46552
|
+
}).catch((err) => {
|
|
46553
|
+
console.warn(`[chat] skill materialize failed for ${actorId}: ${String(err)}`);
|
|
46554
|
+
return {};
|
|
46555
|
+
}));
|
|
46052
46556
|
const handle = await chatRemoteAdapter.spawn({
|
|
46053
46557
|
actor: actorId,
|
|
46054
46558
|
actorToken: issuer.issue(actorId),
|
|
@@ -46060,7 +46564,7 @@ async function startServe(opts) {
|
|
|
46060
46564
|
server: { url: localUrl },
|
|
46061
46565
|
binding,
|
|
46062
46566
|
limits: { wallClockMs: 10 * 6e4 },
|
|
46063
|
-
env:
|
|
46567
|
+
env: provision.env,
|
|
46064
46568
|
wrapperPaths: provision.wrapperPaths
|
|
46065
46569
|
});
|
|
46066
46570
|
const buffered = [];
|
|
@@ -46183,11 +46687,12 @@ async function startServe(opts) {
|
|
|
46183
46687
|
resolveBinding: actors.service.resolveBinding(),
|
|
46184
46688
|
provision: (actorId) => buildActorProvision(actors.service, actorId),
|
|
46185
46689
|
resolveActorContext: (actorId) => buildActorContext(actors.service, actorId),
|
|
46690
|
+
materializeSkills: (actorId, runtimeKind) => materializeSkillFiles({ service: actors.service, blobs: assets, actorId, runtimeKind }),
|
|
46186
46691
|
schema: new Map(schema.map((d2) => [d2.name, d2])),
|
|
46187
46692
|
resolveCodeRepo,
|
|
46188
46693
|
// §4/C:异步从权威源(store)解析;dispatcher 装配前 await
|
|
46189
|
-
// #2 派单开关:工单"待派发"
|
|
46190
|
-
resolveDispatchHold: (ws) =>
|
|
46694
|
+
// #2 派单开关:工单"待派发"(控制面旁存标 held)时不派其活。held 集通常空、表极小,每次查可接受。
|
|
46695
|
+
resolveDispatchHold: async (ws) => (await artifactStateStore.listWorkspaceDispatchHeld()).includes(ws),
|
|
46191
46696
|
tokenFor: (actor) => issuer.issue(actor),
|
|
46192
46697
|
journal,
|
|
46193
46698
|
// TrajectoryEvent 是唯一事件模型;trace 旁账与 M7 蒸馏库并行消费同一流(§16.D 收口)。
|
|
@@ -46343,6 +46848,8 @@ async function startServe(opts) {
|
|
|
46343
46848
|
// 阻塞诊断(proposal D1/D3):派发器运行时信号(queued/fused/backoff/hasInFlight)注入诊断,
|
|
46344
46849
|
// 让协调者把"资源 / 运行时"病因和"图病"分开——资源排队的节点不会被误唤去砍流程。
|
|
46345
46850
|
runtimeSignals: (id) => dispatcher?.runtimeSignalsFor(id) ?? { hasInFlight: false },
|
|
46851
|
+
// 病历里展示节点的"在跑会话情况"(runId + 跑了多久):从派发器在途会话按 artifactId 过滤。
|
|
46852
|
+
sessionsOf: (id) => (dispatcher?.inFlightSessions() ?? []).filter((s2) => s2.artifactId === id).map((s2) => ({ runId: s2.sessionId, action: s2.action, startedAt: s2.startedAt })),
|
|
46346
46853
|
log: (m2) => console.log(m2)
|
|
46347
46854
|
});
|
|
46348
46855
|
coordTimer = setInterval(() => {
|
|
@@ -46473,6 +46980,7 @@ var DaemonWsClient = class {
|
|
|
46473
46980
|
ws.on("open", () => {
|
|
46474
46981
|
log2("[node-cli]", `connected id=${this.daemonId}`);
|
|
46475
46982
|
this.send({ type: "hello", daemonId: this.daemonId, meta: this.buildMeta() });
|
|
46983
|
+
this.reportRuntimes = false;
|
|
46476
46984
|
this.pingTimer = setInterval(() => this.send({ type: "pong" }), PING_INTERVAL_MS);
|
|
46477
46985
|
});
|
|
46478
46986
|
ws.on("message", (raw) => {
|
|
@@ -46636,7 +47144,7 @@ async function startNode(opts) {
|
|
|
46636
47144
|
claude
|
|
46637
47145
|
)
|
|
46638
47146
|
);
|
|
46639
|
-
const client = new DaemonWsClient(opts.serverUrl, opts.nodeId, sessions, adapters, opts.token, runtimes, opts.nodeName, opts.reportRuntimes ??
|
|
47147
|
+
const client = new DaemonWsClient(opts.serverUrl, opts.nodeId, sessions, adapters, opts.token, runtimes, opts.nodeName, opts.reportRuntimes ?? false);
|
|
46640
47148
|
console.log(`[oasis node] ${opts.nodeId} \u2192 ${opts.serverUrl}`);
|
|
46641
47149
|
client.start();
|
|
46642
47150
|
await new Promise((resolve3) => {
|
|
@@ -46672,8 +47180,9 @@ var USAGE = `oasis \u2014\u2014 artifact-centric \u534F\u4F5C\u5185\u6838 CLI\uF
|
|
|
46672
47180
|
token \u89E3\u6790\u987A\u5E8F: --token > $OASIS_TOKEN > <dir>/operator-token
|
|
46673
47181
|
|
|
46674
47182
|
\u7248\u672C\u673A\uFF08\xA75\uFF09
|
|
46675
|
-
spawn <artifactId> --type <t> [--title <\u77ED\u540D>] [--brief <\u89C4\u683C>] [--input <\u4E0A\u6E38id>[,<id>\u2026]] [--owner <actorId>] [--workspace ws] [--persistence temp|persistent] [--parts-file <json>]
|
|
47183
|
+
spawn <artifactId> --type <t> [--title <\u77ED\u540D>] [--brief <\u89C4\u683C>] [--input <\u4E0A\u6E38id>[,<id>\u2026]] [--owner <actorId>] [--workspace ws] [--persistence temp|persistent] [--parts-file <json>] [--<\u5B57\u6BB5> <\u503C>] [--set-json <json>]
|
|
46676
47184
|
# --parts-file\uFF1Aconvergent \u5E76\u884C\u5206\u89E3\u7684\u90E8\u4EF6\u6E05\u5355 JSON [{name, spec?, deps?}]\uFF08Stage 3\uFF0C\u7CFB\u7EDF\u6309\u90E8\u4EF6\u6D3E N \u8DEF\uFF09
|
|
47185
|
+
# --<\u5B57\u6BB5> <\u503C>\uFF1A\u8BE5\u7C7B\u578B\u3010\u52A8\u6001\u58F0\u660E\u3011\u7684\u81EA\u5B9A\u4E49\u5B57\u6BB5\uFF08\u4E0D\u662F\u56FA\u5B9A\u6E05\u5355\uFF09\u2014\u2014\u7528 "oasis artifact-types show <type>" \u770B\u53EF\u586B\u5B57\u6BB5\uFF1Bstring[]/\u6279\u91CF\u7528 --set-json\uFF1B\u7F3A\u5FC5\u586B\u4F1A\u62A5\u9519\u5217\u51FA
|
|
46677
47186
|
propose <artifactId> (--file <path> | --from-dir <path> | --git-commit <sha>) --reason <text> [--base <revisionId|none>] [--part <name>] [--rebase-of <revisionId>]
|
|
46678
47187
|
# --file \u5355\u6587\u4EF6\uFF1B--from-dir \u9012\u5F52\u6574\u76EE\u5F55\u4E3A\u591A\u6587\u4EF6 revision\uFF08\u88C5\u7BB1\u5355\uFF0C\u8DE8\u7248\u672C\u53BB\u91CD\uFF0CF1\uFF09\u3002
|
|
46679
47188
|
# --git-commit\uFF1AModel Y \u4EE3\u7801-git project\u2014\u2014\u5185\u5BB9\u6D3B\u5728 git\uFF0C\u53EA external-pin \u90A3\u4E2A commit sha\uFF08\u5148 push \u4F60\u7684 feat \u5206\u652F\uFF09\u3002
|
|
@@ -46705,8 +47214,12 @@ var USAGE = `oasis \u2014\u2014 artifact-centric \u534F\u4F5C\u5185\u6838 CLI\uF
|
|
|
46705
47214
|
log <artifactId> # \u8BE5\u4EA7\u7269\u7684**\u64CD\u4F5C\u65E5\u5FD7**\uFF1A\u5DF2\u53D1\u751F\u7684\u5185\u6838\u4E8B\u5B9E\uFF08spawn/propose/merge/conclude\u2026\uFF09\uFF0C\u4ECE oplog \u8BFB
|
|
46706
47215
|
session-log <runId|artifactId> [--n 20] # \u67D0 agent **\u4F1A\u8BDD\u7684\u8FD0\u884C\u65F6\u8F93\u51FA**\uFF08\u65C1\u8D26\uFF0C\u975E\u5185\u6838\u4E8B\u5B9E\uFF09\uFF1A\u6700\u65B0 N \u6761\u4E8B\u4EF6 + "\u8DDD\u4E0A\u6B21\u4E8B\u4EF6 X \u5206\u949F"\uFF08>10min\u2248\u5361\uFF09+ \u5728\u98DE tool_use\u2014\u2014\u5224"\u60F3 vs \u5361"\u3002\u6CE8\u610F\u2260 log\uFF1Alog \u770B\u4EA7\u7269\u7ECF\u5386\u4E86\u4EC0\u4E48\uFF0Csession-log \u770B agent \u6B64\u523B\u5728\u8BF4/\u5E72\u4EC0\u4E48
|
|
46707
47216
|
artifact-types [list | search <q>] # \u770B/\u68C0\u7D22\u5DF2\u6CE8\u518C\u7C7B\u578B\uFF08\u7528\u9014/owner/\u72B6\u6001\uFF09\u2014\u2014\u7ED9\u4EBA\u548C agent \u9009 type
|
|
47217
|
+
artifact-types show <type> # \u770B\u5355\u7C7B\u578B\u8BE6\u60C5\uFF1A\u542B\u3010\u81EA\u5B9A\u4E49\u5B57\u6BB5\u8868\u3011(\u540D/\u7C7B\u578B/\u5FC5\u586B/\u8BF4\u660E)\u2014\u2014spawn/edit \u524D\u67E5\u80FD\u586B\u5565
|
|
46708
47218
|
artifact-types add <name> (--content-type <t> --owner-role <r> | --file <json>) [--description \u2026] # \u65B0\u589E\uFF08--file \u6536\u5B8C\u6574 ArtifactTypeDef\uFF1Atriggers/reviews/grounding/budget/acl/mirror\u2026\uFF09
|
|
46709
|
-
artifact-types edit <name> [--file <json>] [--description \u2026] [--owner-role \u2026] [--content-type \u2026] # \u6539\
|
|
47219
|
+
artifact-types edit <name> [--file <json>] [--description \u2026] [--owner-role \u2026] [--content-type \u2026] # \u6539\u7C7B\u578B\u914D\u7F6E\uFF08name \u4E0D\u53EF\u6539\uFF1BcontentType \u6709\u5B58\u91CF\u5373\u62E6\uFF1B--file \u6536\u5B8C\u6574 patch\u3002\u6539\u5B57\u6BB5\u7528 *-field\uFF09
|
|
47220
|
+
artifact-types add-field <type> <\u5B57\u6BB5> --type <string|string[]|number|boolean> [--required true] [--description \u2026] # \u7ED9\u7C7B\u578B\u3010\u58F0\u660E\u3011\u4E00\u4E2A\u81EA\u5B9A\u4E49\u5B57\u6BB5\uFF08\u6539 schema\u3001\u5F71\u54CD\u8BE5 type \u6240\u6709\u8282\u70B9\u7684\u672A\u6765\u5199\u5165\uFF09
|
|
47221
|
+
artifact-types edit-field <type> <\u5B57\u6BB5> [--type \u2026] [--required true|false] [--description \u2026] # \u6539\u5B57\u6BB5\u58F0\u660E\u7684\u5C5E\u6027\uFF08\u4E0D\u53EF\u6539\u540D\uFF1B\u524D\u5411\u751F\u6548 + \u5F71\u54CD\u534A\u5F84\uFF09
|
|
47222
|
+
artifact-types drop-field <type> <\u5B57\u6BB5> # \u5220\u5B57\u6BB5\u58F0\u660E\uFF08deprecate\uFF1A\u5B58\u91CF\u503C\u7559\u3001\u65B0\u5199\u5165\u62D2\uFF1B\u5E26\u5F71\u54CD\u534A\u5F84\uFF09
|
|
46710
47223
|
artifact-types deprecate <name> | delete <name> # \u5F03\u7528\uFF08\u7981\u65B0\u5EFA\u3001\u5B58\u91CF\u4E0D\u52A8\uFF09/ \u786C\u5220\uFF08\u4EC5\u96F6\u5F15\u7528\uFF0C\u5426\u5219\u5F15\u5BFC deprecate\uFF09
|
|
46711
47224
|
|
|
46712
47225
|
\u6CBB\u7406\uFF08\xA711\uFF09
|
|
@@ -46767,10 +47280,7 @@ function projectSlug(value) {
|
|
|
46767
47280
|
return value.trim().toLowerCase().replace(/[^\w\u4e00-\u9fa5\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").slice(0, 32);
|
|
46768
47281
|
}
|
|
46769
47282
|
function makeClient(base, token) {
|
|
46770
|
-
const
|
|
46771
|
-
const chatSessionId = process.env["OASIS_CHAT_SESSION_ID"];
|
|
46772
|
-
if (chatSessionId) baseHeaders["x-chat-session-id"] = chatSessionId;
|
|
46773
|
-
const headers = baseHeaders;
|
|
47283
|
+
const headers = { authorization: `Bearer ${token}`, "content-type": "application/json" };
|
|
46774
47284
|
const fail = async (res) => {
|
|
46775
47285
|
const body = await res.json().catch(() => ({}));
|
|
46776
47286
|
const err = body.error;
|
|
@@ -46823,25 +47333,50 @@ async function runCli(argv, println = console.log) {
|
|
|
46823
47333
|
const serverUrl = flags.get("server-ws") ?? process.env["OASIS_SERVER_WS"] ?? "ws://127.0.0.1:7320/node-gateway";
|
|
46824
47334
|
const nodeName = flags.get("name") ?? process.env["OASIS_NODE_NAME"];
|
|
46825
47335
|
const nodeIdFile = path12.join(dir, "node-id");
|
|
46826
|
-
const
|
|
46827
|
-
const
|
|
46828
|
-
|
|
47336
|
+
const nodeTokenFile = path12.join(dir, "node-token");
|
|
47337
|
+
const enrollTokenFile = path12.join(dir, "enroll-token");
|
|
47338
|
+
const reportMarkerFile = path12.join(dir, "report-runtimes");
|
|
47339
|
+
let nodeId = fs14.existsSync(nodeIdFile) ? fs14.readFileSync(nodeIdFile, "utf8").trim() : null;
|
|
47340
|
+
let token2 = flags.get("token") ?? process.env["OASIS_NODE_TOKEN"] ?? (fs14.existsSync(nodeTokenFile) ? fs14.readFileSync(nodeTokenFile, "utf8").trim() : void 0);
|
|
47341
|
+
const enrollToken = flags.get("enroll-token") ?? process.env["OASIS_ENROLL_TOKEN"] ?? (fs14.existsSync(enrollTokenFile) ? fs14.readFileSync(enrollTokenFile, "utf8").trim() : void 0);
|
|
47342
|
+
let freshEnrollment = false;
|
|
47343
|
+
if ((!nodeId || !token2) && enrollToken) {
|
|
47344
|
+
const httpBase2 = serverUrl.replace(/^ws(s?):\/\//, "http$1://").replace(/\/node-gateway.*$/, "");
|
|
47345
|
+
const res = await fetch(`${httpBase2}/api/nodes/exchange`, {
|
|
47346
|
+
method: "POST",
|
|
47347
|
+
headers: { "content-type": "application/json" },
|
|
47348
|
+
body: JSON.stringify({ enrollToken, ...nodeId ? { nodeId } : {}, hostname: os7.hostname() })
|
|
47349
|
+
});
|
|
47350
|
+
if (!res.ok) throw new Error(`enroll token exchange failed: HTTP ${res.status}`);
|
|
47351
|
+
const body = await res.json();
|
|
47352
|
+
nodeId = body.nodeId;
|
|
47353
|
+
token2 = body.token;
|
|
46829
47354
|
fs14.mkdirSync(dir, { recursive: true });
|
|
46830
47355
|
fs14.writeFileSync(nodeIdFile, nodeId);
|
|
47356
|
+
fs14.writeFileSync(nodeTokenFile, token2, { mode: 384 });
|
|
47357
|
+
try {
|
|
47358
|
+
fs14.unlinkSync(enrollTokenFile);
|
|
47359
|
+
} catch {
|
|
47360
|
+
}
|
|
47361
|
+
freshEnrollment = true;
|
|
46831
47362
|
}
|
|
46832
|
-
|
|
46833
|
-
const
|
|
47363
|
+
if (!nodeId) throw new Error("no nodeId \u2014 provide --enroll-token <ent_...> on first run");
|
|
47364
|
+
const scriptRun = freshEnrollment || fs14.existsSync(reportMarkerFile);
|
|
46834
47365
|
if (flags.has("service")) {
|
|
46835
47366
|
const { spawnSync, execSync: execSync2 } = await import("node:child_process");
|
|
46836
47367
|
if (token2) {
|
|
46837
47368
|
fs14.mkdirSync(dir, { recursive: true });
|
|
46838
47369
|
fs14.writeFileSync(nodeTokenFile, token2, { mode: 384 });
|
|
46839
47370
|
}
|
|
47371
|
+
if (scriptRun) {
|
|
47372
|
+
fs14.mkdirSync(dir, { recursive: true });
|
|
47373
|
+
fs14.writeFileSync(reportMarkerFile, "");
|
|
47374
|
+
}
|
|
46840
47375
|
println("\u2192 Installing oasis_test globally...");
|
|
46841
47376
|
spawnSync("npm", ["install", "-g", "oasis_test@latest"], { stdio: "inherit" });
|
|
46842
47377
|
const prefix = execSync2("npm prefix -g").toString().trim();
|
|
46843
47378
|
const binPath = path12.join(prefix, "bin", "oasis_test");
|
|
46844
|
-
const svcArgs = `node --server-ws ${serverUrl} --
|
|
47379
|
+
const svcArgs = `node --server-ws ${serverUrl} --dir ${dir}${nodeName ? ` --name ${nodeName}` : ""}`;
|
|
46845
47380
|
if (process.platform === "linux") {
|
|
46846
47381
|
const svcFile = path12.join(os7.homedir(), ".config/systemd/user/oasis-node.service");
|
|
46847
47382
|
fs14.mkdirSync(path12.dirname(svcFile), { recursive: true });
|
|
@@ -46854,6 +47389,9 @@ async function runCli(argv, println = console.log) {
|
|
|
46854
47389
|
"",
|
|
46855
47390
|
"[Service]",
|
|
46856
47391
|
`ExecStart=${binPath} ${svcArgs}`,
|
|
47392
|
+
// Carry the install-time PATH so the detached daemon detects CLIs in user dirs
|
|
47393
|
+
// (~/.npm-global/bin, ~/.local/bin) — systemd --user otherwise gives a minimal PATH.
|
|
47394
|
+
`Environment=PATH=${process.env["PATH"] ?? ""}:${path12.join(os7.homedir(), ".npm-global/bin")}:${path12.join(os7.homedir(), ".local/bin")}:/usr/local/bin`,
|
|
46857
47395
|
"Restart=on-failure",
|
|
46858
47396
|
"RestartSec=5",
|
|
46859
47397
|
"StandardOutput=journal",
|
|
@@ -46874,7 +47412,7 @@ async function runCli(argv, println = console.log) {
|
|
|
46874
47412
|
const plist = path12.join(os7.homedir(), "Library/LaunchAgents/com.oasis.node.plist");
|
|
46875
47413
|
fs14.mkdirSync(path12.dirname(plist), { recursive: true });
|
|
46876
47414
|
spawnSync("launchctl", ["unload", plist], { stdio: "ignore" });
|
|
46877
|
-
const argXml = [binPath, "node", "--server-ws", serverUrl, "--
|
|
47415
|
+
const argXml = [binPath, "node", "--server-ws", serverUrl, "--dir", dir, ...nodeName ? ["--name", nodeName] : []].map((a) => `<string>${a}</string>`).join("");
|
|
46878
47416
|
fs14.writeFileSync(plist, `<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>Label</key><string>com.oasis.node</string><key>ProgramArguments</key><array>${argXml}</array><key>RunAtLoad</key><true/><key>KeepAlive</key><true/><key>StandardOutPath</key><string>${os7.homedir()}/.oasis-node.log</string><key>StandardErrorPath</key><string>${os7.homedir()}/.oasis-node.err</string></dict></plist>`);
|
|
46879
47417
|
spawnSync("launchctl", ["load", plist], { stdio: "inherit" });
|
|
46880
47418
|
println("\u2713 Oasis node service started (macOS LaunchAgent com.oasis.node)");
|
|
@@ -46887,7 +47425,13 @@ async function runCli(argv, println = console.log) {
|
|
|
46887
47425
|
}
|
|
46888
47426
|
return;
|
|
46889
47427
|
}
|
|
46890
|
-
|
|
47428
|
+
if (fs14.existsSync(reportMarkerFile)) {
|
|
47429
|
+
try {
|
|
47430
|
+
fs14.unlinkSync(reportMarkerFile);
|
|
47431
|
+
} catch {
|
|
47432
|
+
}
|
|
47433
|
+
}
|
|
47434
|
+
await startNode({ serverUrl, nodeId, ...token2 ? { token: token2 } : {}, ...nodeName ? { nodeName } : {}, reportRuntimes: scriptRun });
|
|
46891
47435
|
return;
|
|
46892
47436
|
}
|
|
46893
47437
|
if (command === "node-token") {
|
|
@@ -47029,6 +47573,30 @@ async function runCli(argv, println = console.log) {
|
|
|
47029
47573
|
return out;
|
|
47030
47574
|
};
|
|
47031
47575
|
const STAGE_COMMANDS = /* @__PURE__ */ new Set(["link", "spawn", "unlink", "cancel", "freeze", "cancel-part", "edit"]);
|
|
47576
|
+
const GLOBAL_NON_FIELD_FLAGS = /* @__PURE__ */ new Set(["server", "token", "dir", "set-json", "clear"]);
|
|
47577
|
+
const SPAWN_OWN_FLAGS = /* @__PURE__ */ new Set(["type", "owner", "workspace", "persistence", "title", "brief", "description", "input", "parts-file"]);
|
|
47578
|
+
const EDIT_OWN_FLAGS = /* @__PURE__ */ new Set(["title", "brief"]);
|
|
47579
|
+
const fieldsFromFlags = (ownFlags) => {
|
|
47580
|
+
const fields = {};
|
|
47581
|
+
for (const [k2, v2] of flags) {
|
|
47582
|
+
if (ownFlags.has(k2) || GLOBAL_NON_FIELD_FLAGS.has(k2)) continue;
|
|
47583
|
+
fields[k2] = v2;
|
|
47584
|
+
}
|
|
47585
|
+
const sj = flags.get("set-json");
|
|
47586
|
+
if (sj !== void 0) {
|
|
47587
|
+
const parsed = JSON.parse(sj);
|
|
47588
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
47589
|
+
throw new Error(`--set-json \u9700\u8981\u4E00\u4E2A JSON \u5BF9\u8C61\uFF0C\u5982 '{"goal":"..."}'`);
|
|
47590
|
+
}
|
|
47591
|
+
Object.assign(fields, parsed);
|
|
47592
|
+
}
|
|
47593
|
+
const clear = flags.get("clear");
|
|
47594
|
+
if (clear !== void 0) for (const k2 of clear.split(",")) {
|
|
47595
|
+
const t = k2.trim();
|
|
47596
|
+
if (t) fields[t] = null;
|
|
47597
|
+
}
|
|
47598
|
+
return Object.keys(fields).length > 0 ? fields : void 0;
|
|
47599
|
+
};
|
|
47032
47600
|
const buildStageOp = (cmd) => {
|
|
47033
47601
|
switch (cmd) {
|
|
47034
47602
|
case "link":
|
|
@@ -47038,15 +47606,18 @@ async function runCli(argv, println = console.log) {
|
|
|
47038
47606
|
to: need(flags, "to"),
|
|
47039
47607
|
...flags.get("required") !== void 0 ? { required: flags.get("required") === "true" } : {}
|
|
47040
47608
|
};
|
|
47041
|
-
case "spawn":
|
|
47609
|
+
case "spawn": {
|
|
47610
|
+
const fields = fieldsFromFlags(SPAWN_OWN_FLAGS);
|
|
47042
47611
|
return {
|
|
47043
47612
|
action: "spawn",
|
|
47044
47613
|
id: needPos(positional, 0, "oasis spawn <id> --type <t>"),
|
|
47045
47614
|
type: need(flags, "type"),
|
|
47046
47615
|
...flags.get("title") !== void 0 ? { title: flags.get("title") } : {},
|
|
47047
47616
|
...(flags.get("brief") ?? flags.get("description")) !== void 0 ? { description: flags.get("brief") ?? flags.get("description") } : {},
|
|
47048
|
-
...flags.get("input") !== void 0 ? { inputs: flags.get("input").split(",").map((to) => ({ to: to.trim() })) } : {}
|
|
47617
|
+
...flags.get("input") !== void 0 ? { inputs: flags.get("input").split(",").map((to) => ({ to: to.trim() })) } : {},
|
|
47618
|
+
...fields !== void 0 ? { fields } : {}
|
|
47049
47619
|
};
|
|
47620
|
+
}
|
|
47050
47621
|
case "unlink":
|
|
47051
47622
|
return { action: "unlink", artifactId: needPos(positional, 0, "oasis unlink <consumer> --to <upstream>"), to: need(flags, "to") };
|
|
47052
47623
|
case "cancel":
|
|
@@ -47059,13 +47630,16 @@ async function runCli(argv, println = console.log) {
|
|
|
47059
47630
|
};
|
|
47060
47631
|
case "cancel-part":
|
|
47061
47632
|
return { action: "cancelPart", artifactId: needPos(positional, 0, "oasis cancel-part <id> --part <name>"), part: need(flags, "part") };
|
|
47062
|
-
case "edit":
|
|
47633
|
+
case "edit": {
|
|
47634
|
+
const fields = fieldsFromFlags(EDIT_OWN_FLAGS);
|
|
47063
47635
|
return {
|
|
47064
47636
|
action: "edit",
|
|
47065
|
-
artifactId: needPos(positional, 0, "oasis edit <id> [--title \u2026] [--brief \u2026]"),
|
|
47637
|
+
artifactId: needPos(positional, 0, "oasis edit <id> [--title \u2026] [--brief \u2026] [--<\u5B57\u6BB5> <\u503C>] [--set-json <json>] [--clear <\u5B57\u6BB5,\u2026>]\uFF08--<\u5B57\u6BB5> \u662F\u8BE5\u7C7B\u578B\u52A8\u6001\u58F0\u660E\u7684\uFF0C\u7528 oasis show <id> / artifact-types show <type> \u770B\u53EF\u586B\u5B57\u6BB5\uFF09"),
|
|
47066
47638
|
...flags.get("title") !== void 0 ? { title: flags.get("title") } : {},
|
|
47067
|
-
...flags.get("brief") !== void 0 ? { description: flags.get("brief") } : {}
|
|
47639
|
+
...flags.get("brief") !== void 0 ? { description: flags.get("brief") } : {},
|
|
47640
|
+
...fields !== void 0 ? { fields } : {}
|
|
47068
47641
|
};
|
|
47642
|
+
}
|
|
47069
47643
|
default:
|
|
47070
47644
|
throw new Error(`\u672A\u77E5\u6682\u5B58\u547D\u4EE4: ${cmd}`);
|
|
47071
47645
|
}
|
|
@@ -47099,43 +47673,28 @@ async function runCli(argv, println = console.log) {
|
|
|
47099
47673
|
return;
|
|
47100
47674
|
}
|
|
47101
47675
|
switch (command) {
|
|
47102
|
-
case "
|
|
47103
|
-
const
|
|
47104
|
-
|
|
47105
|
-
|
|
47106
|
-
|
|
47107
|
-
|
|
47108
|
-
)
|
|
47109
|
-
|
|
47110
|
-
|
|
47111
|
-
|
|
47112
|
-
|
|
47113
|
-
}
|
|
47114
|
-
|
|
47115
|
-
|
|
47116
|
-
|
|
47117
|
-
|
|
47118
|
-
|
|
47119
|
-
|
|
47120
|
-
|
|
47121
|
-
|
|
47122
|
-
|
|
47123
|
-
}
|
|
47124
|
-
}
|
|
47125
|
-
break;
|
|
47126
|
-
}
|
|
47127
|
-
case "artifacts": {
|
|
47128
|
-
const workOrderId = positional[0];
|
|
47129
|
-
if (!workOrderId) {
|
|
47130
|
-
println("\u7528\u6CD5: oasis artifacts <workOrderId>");
|
|
47131
|
-
break;
|
|
47676
|
+
case "create-workorder": {
|
|
47677
|
+
const planRaw = flags.get("plan-file") !== void 0 ? fs14.readFileSync(flags.get("plan-file"), "utf8") : flags.get("plan");
|
|
47678
|
+
const acceptanceFlag = flags.get("acceptance");
|
|
47679
|
+
const args = {
|
|
47680
|
+
createdVia: "seed",
|
|
47681
|
+
stage: true,
|
|
47682
|
+
type: need(flags, "type"),
|
|
47683
|
+
workspace: need(flags, "workspace"),
|
|
47684
|
+
title: need(flags, "title"),
|
|
47685
|
+
...flags.get("brief") !== void 0 ? { brief: flags.get("brief") } : {},
|
|
47686
|
+
...flags.get("goal") !== void 0 ? { goal: flags.get("goal") } : {},
|
|
47687
|
+
...acceptanceFlag !== void 0 ? { acceptance_criteria: parseStringArrayFlag(acceptanceFlag, "acceptance") } : {},
|
|
47688
|
+
...planRaw !== void 0 ? { plan: parseJsonFlag(planRaw, "plan") } : {},
|
|
47689
|
+
...flags.get("project") !== void 0 ? { project_id: flags.get("project") } : {},
|
|
47690
|
+
...flags.get("dispatch") === "false" ? { dispatch: false } : {}
|
|
47691
|
+
};
|
|
47692
|
+
const res = await api.cmd("spawn", args);
|
|
47693
|
+
println(res.message);
|
|
47694
|
+
const data = res.data;
|
|
47695
|
+
if (data?.draftId) {
|
|
47696
|
+
println(`draftId=${data.draftId} workspace=${data.workspace} \u8282\u70B9=${data.spawned?.length ?? 0} \u89C4\u5212=${data.planning?.status ?? "?"}`);
|
|
47132
47697
|
}
|
|
47133
|
-
const res = await api.request(
|
|
47134
|
-
"GET",
|
|
47135
|
-
`/api/workorders/${encodeURIComponent(workOrderId)}`
|
|
47136
|
-
);
|
|
47137
|
-
println(`\u5DE5\u5355 ${res.summary.title} \u7684\u4EA7\u7269\uFF08${res.graph.nodes.length}\uFF09\uFF1A`);
|
|
47138
|
-
for (const n of res.graph.nodes) println(` ${n.id} [${n.type}] ${n.label} stage=${n.stage}`);
|
|
47139
47698
|
break;
|
|
47140
47699
|
}
|
|
47141
47700
|
case "projects": {
|
|
@@ -47326,6 +47885,7 @@ async function runCli(argv, println = console.log) {
|
|
|
47326
47885
|
}
|
|
47327
47886
|
case "spawn": {
|
|
47328
47887
|
const partsFile = flags.get("parts-file");
|
|
47888
|
+
const spawnFields = fieldsFromFlags(SPAWN_OWN_FLAGS);
|
|
47329
47889
|
const { message } = await api.cmd("spawn", {
|
|
47330
47890
|
id: positional[0],
|
|
47331
47891
|
type: need(flags, "type"),
|
|
@@ -47335,7 +47895,8 @@ async function runCli(argv, println = console.log) {
|
|
|
47335
47895
|
title: flags.get("title"),
|
|
47336
47896
|
description: flags.get("brief") ?? flags.get("description"),
|
|
47337
47897
|
inputs: flags.get("input")?.split(",").map((to) => ({ to: to.trim() })),
|
|
47338
|
-
...partsFile !== void 0 ? { parts: JSON.parse(readFileArg(partsFile)) } : {}
|
|
47898
|
+
...partsFile !== void 0 ? { parts: JSON.parse(readFileArg(partsFile)) } : {},
|
|
47899
|
+
...spawnFields !== void 0 ? { fields: spawnFields } : {}
|
|
47339
47900
|
});
|
|
47340
47901
|
println(message);
|
|
47341
47902
|
break;
|
|
@@ -47787,6 +48348,52 @@ async function runCli(argv, println = console.log) {
|
|
|
47787
48348
|
println(`\u5DF2\u5220\u9664 ${name}`);
|
|
47788
48349
|
break;
|
|
47789
48350
|
}
|
|
48351
|
+
if (sub === "add-field") {
|
|
48352
|
+
const name = needPos(positional, 1, "oasis artifact-types add-field <type> <\u5B57\u6BB5> --type <string|string[]|number|boolean> [--required true] [--description \u2026]");
|
|
48353
|
+
const field = needPos(positional, 2, "oasis artifact-types add-field <type> <\u5B57\u6BB5> --type <t> [--required true] [--description \u2026]");
|
|
48354
|
+
const spec = {
|
|
48355
|
+
name: field,
|
|
48356
|
+
type: need(flags, "type"),
|
|
48357
|
+
...flags.get("required") === "true" ? { required: true } : {},
|
|
48358
|
+
...flags.get("description") !== void 0 ? { description: flags.get("description") } : {}
|
|
48359
|
+
};
|
|
48360
|
+
const r = await api.request("POST", `/api/artifact-types/${encodeURIComponent(name)}/fields`, spec);
|
|
48361
|
+
println(`\u5DF2\u7ED9 ${name} \u52A0\u5B57\u6BB5 ${field}\uFF08${spec.type}${spec["required"] ? "\uFF0C\u5FC5\u586B" : ""}\uFF09${r.usage && r.usage.total > 0 ? `\u2014\u2014${r.usage.total} \u4E2A\u5B58\u91CF\u8282\u70B9\u6682\u65E0\u6B64\u5B57\u6BB5\uFF08\u524D\u5411\uFF1A\u65B0\u5199\u5165\u53EF\u586B\uFF09` : ""}`);
|
|
48362
|
+
break;
|
|
48363
|
+
}
|
|
48364
|
+
if (sub === "edit-field") {
|
|
48365
|
+
const name = needPos(positional, 1, "oasis artifact-types edit-field <type> <\u5B57\u6BB5> [--type \u2026] [--required true|false] [--description \u2026]\uFF08\u4E0D\u53EF\u6539\u540D\uFF09");
|
|
48366
|
+
const field = needPos(positional, 2, "oasis artifact-types edit-field <type> <\u5B57\u6BB5> [--type \u2026] [--required \u2026] [--description \u2026]");
|
|
48367
|
+
const attrs = {
|
|
48368
|
+
...flags.get("type") !== void 0 ? { type: flags.get("type") } : {},
|
|
48369
|
+
...flags.get("required") !== void 0 ? { required: flags.get("required") === "true" } : {},
|
|
48370
|
+
...flags.get("description") !== void 0 ? { description: flags.get("description") } : {}
|
|
48371
|
+
};
|
|
48372
|
+
const r = await api.request("PATCH", `/api/artifact-types/${encodeURIComponent(name)}/fields/${encodeURIComponent(field)}`, attrs);
|
|
48373
|
+
println(`\u5DF2\u6539 ${name}.${field}${r.usage && r.usage.total > 0 ? `\uFF08\u524D\u5411\u751F\u6548\uFF0C\u5F71\u54CD ${r.usage.total} \u4E2A\u5B58\u91CF\u8282\u70B9\uFF09` : ""}`);
|
|
48374
|
+
break;
|
|
48375
|
+
}
|
|
48376
|
+
if (sub === "drop-field") {
|
|
48377
|
+
const name = needPos(positional, 1, "oasis artifact-types drop-field <type> <\u5B57\u6BB5>");
|
|
48378
|
+
const field = needPos(positional, 2, "oasis artifact-types drop-field <type> <\u5B57\u6BB5>");
|
|
48379
|
+
const r = await api.request("DELETE", `/api/artifact-types/${encodeURIComponent(name)}/fields/${encodeURIComponent(field)}`, void 0);
|
|
48380
|
+
println(`\u5DF2\u5220\u5B57\u6BB5\u58F0\u660E ${name}.${field}${r.usage && r.usage.total > 0 ? `\u2014\u2014${r.usage.total} \u4E2A\u5B58\u91CF\u8282\u70B9\u4ECD\u4FDD\u7559\u8BE5\u5B57\u6BB5\u7684\u503C\uFF08\u6570\u636E\u672A\u52A8\uFF1B\u65B0\u5199\u5165\u5C06\u62D2\uFF09` : ""}`);
|
|
48381
|
+
break;
|
|
48382
|
+
}
|
|
48383
|
+
if (sub === "show") {
|
|
48384
|
+
const name = needPos(positional, 1, "oasis artifact-types show <type>");
|
|
48385
|
+
const { items } = await api.request("GET", "/api/artifact-types");
|
|
48386
|
+
const t = items.find((x2) => x2.name === name);
|
|
48387
|
+
if (!t) {
|
|
48388
|
+
println(`\uFF08\u65E0\u6B64\u7C7B\u578B: ${name}\uFF09`);
|
|
48389
|
+
break;
|
|
48390
|
+
}
|
|
48391
|
+
println(`${t.name}${t.status === "deprecated" ? " [\u5DF2\u5F03\u7528]" : ""} (${t.contentType}, owner=${t.ownerRole})${t.description ? ` \u2014\u2014 ${t.description}` : ""}`);
|
|
48392
|
+
const fds = t.fieldDefs ?? [];
|
|
48393
|
+
println(fds.length === 0 ? " \u81EA\u5B9A\u4E49\u5B57\u6BB5\uFF1A\u65E0" : " \u81EA\u5B9A\u4E49\u5B57\u6BB5\uFF1A");
|
|
48394
|
+
for (const f2 of fds) println(` ${f2.name} ${f2.type} ${f2.required ? "\u5FC5\u586B" : "\u53EF\u9009"}${f2.description ? ` ${f2.description}` : ""}`);
|
|
48395
|
+
break;
|
|
48396
|
+
}
|
|
47790
48397
|
const all = await api.view(
|
|
47791
48398
|
"artifact-types"
|
|
47792
48399
|
);
|
|
@@ -47878,7 +48485,7 @@ var isRunning = (pid) => {
|
|
|
47878
48485
|
return false;
|
|
47879
48486
|
}
|
|
47880
48487
|
};
|
|
47881
|
-
var httpBase = (wsUrl) => wsUrl.replace(/^
|
|
48488
|
+
var httpBase = (wsUrl) => wsUrl.replace(/^ws(s?):\/\//, "http$1://").replace(/\/node-gateway.*$/, "");
|
|
47882
48489
|
function installBinary() {
|
|
47883
48490
|
fs15.mkdirSync(path13.dirname(BIN_FILE), { recursive: true });
|
|
47884
48491
|
fs15.copyFileSync(process.argv[1], BIN_FILE);
|
|
@@ -47889,6 +48496,13 @@ exec node "${BIN_FILE}" "$@"
|
|
|
47889
48496
|
}
|
|
47890
48497
|
function setupAutostart() {
|
|
47891
48498
|
const cmd = `${process.execPath} "${BIN_FILE}" --_daemon`;
|
|
48499
|
+
const home = os8.homedir();
|
|
48500
|
+
const extraBins = [
|
|
48501
|
+
path13.join(home, ".npm-global", "bin"),
|
|
48502
|
+
path13.join(home, ".local", "bin"),
|
|
48503
|
+
"/usr/local/bin"
|
|
48504
|
+
];
|
|
48505
|
+
const daemonPath = [process.env["PATH"] ?? "", ...extraBins].filter(Boolean).join(":");
|
|
47892
48506
|
if (process.platform === "linux") {
|
|
47893
48507
|
const unitDir = path13.join(os8.homedir(), ".config", "systemd", "user");
|
|
47894
48508
|
fs15.mkdirSync(unitDir, { recursive: true });
|
|
@@ -47899,6 +48513,7 @@ function setupAutostart() {
|
|
|
47899
48513
|
"",
|
|
47900
48514
|
"[Service]",
|
|
47901
48515
|
`ExecStart=${cmd}`,
|
|
48516
|
+
`Environment=PATH=${daemonPath}`,
|
|
47902
48517
|
`StandardOutput=append:${LOG_FILE}`,
|
|
47903
48518
|
`StandardError=append:${LOG_FILE}`,
|
|
47904
48519
|
"Restart=on-failure",
|
|
@@ -47920,6 +48535,7 @@ function setupAutostart() {
|
|
|
47920
48535
|
<plist version="1.0"><dict>
|
|
47921
48536
|
<key>Label</key><string>com.oasis.node</string>
|
|
47922
48537
|
<key>ProgramArguments</key><array><string>/bin/sh</string><string>-c</string><string>${cmd}</string></array>
|
|
48538
|
+
<key>EnvironmentVariables</key><dict><key>PATH</key><string>${daemonPath}</string></dict>
|
|
47923
48539
|
<key>RunAtLoad</key><true/><key>KeepAlive</key><true/>
|
|
47924
48540
|
<key>StandardOutPath</key><string>${LOG_FILE}</string>
|
|
47925
48541
|
<key>StandardErrorPath</key><string>${LOG_FILE}</string>
|
|
@@ -48001,8 +48617,11 @@ void (async () => {
|
|
|
48001
48617
|
await startNode({
|
|
48002
48618
|
serverUrl: cfg.serverUrl,
|
|
48003
48619
|
token: cfg.token,
|
|
48004
|
-
nodeId: cfg.nodeId ??
|
|
48005
|
-
|
|
48620
|
+
nodeId: cfg.nodeId ?? (() => {
|
|
48621
|
+
console.error("[oasis] no nodeId in config \u2014 run `oasis start` first");
|
|
48622
|
+
process.exit(1);
|
|
48623
|
+
})(),
|
|
48624
|
+
nodeName: cfg.name ?? cfg.nodeId,
|
|
48006
48625
|
reportRuntimes
|
|
48007
48626
|
});
|
|
48008
48627
|
return;
|
|
@@ -48011,32 +48630,29 @@ void (async () => {
|
|
|
48011
48630
|
const prev = readConfig();
|
|
48012
48631
|
const serverUrl = flags.get("server-ws") ?? process.env["OASIS_SERVER_WS"] ?? prev?.serverUrl;
|
|
48013
48632
|
if (!serverUrl) {
|
|
48014
|
-
console.error("Usage: oasis start --server-ws <wss://...> (--enroll-token <ent_...> | --token <ont_...>) [--name <name>]
|
|
48633
|
+
console.error("Usage: oasis start --server-ws <wss://...> (--enroll-token <ent_...> | --token <ont_...>) [--name <name>]");
|
|
48015
48634
|
process.exit(1);
|
|
48016
48635
|
}
|
|
48017
|
-
const commandNodeId = flags.get("id");
|
|
48018
48636
|
const serverChanged = !!prev?.serverUrl && prev.serverUrl !== serverUrl;
|
|
48019
|
-
let nodeId = serverChanged ?
|
|
48637
|
+
let nodeId = serverChanged ? void 0 : prev?.nodeId;
|
|
48020
48638
|
let token = serverChanged ? flags.get("token") ?? process.env["OASIS_NODE_TOKEN"] : flags.get("token") ?? process.env["OASIS_NODE_TOKEN"] ?? prev?.token;
|
|
48021
48639
|
const enrollToken = flags.get("enroll-token") ?? process.env["OASIS_ENROLL_TOKEN"];
|
|
48022
|
-
if (
|
|
48023
|
-
|
|
48640
|
+
if (prev?.nodeId && prev?.token && !serverChanged) {
|
|
48641
|
+
nodeId = prev.nodeId;
|
|
48642
|
+
token = prev.token;
|
|
48643
|
+
} else if (enrollToken) {
|
|
48644
|
+
const res = await fetch(`${httpBase(serverUrl)}/api/nodes/exchange`, {
|
|
48024
48645
|
method: "POST",
|
|
48025
48646
|
headers: { "content-type": "application/json" },
|
|
48026
|
-
body: JSON.stringify({ enrollToken,
|
|
48647
|
+
body: JSON.stringify({ enrollToken, hostname: os8.hostname() })
|
|
48027
48648
|
});
|
|
48028
|
-
let res = await exchange(nodeId);
|
|
48029
|
-
if (!res.ok && commandNodeId && commandNodeId !== nodeId) {
|
|
48030
|
-
res = await exchange(commandNodeId);
|
|
48031
|
-
if (res.ok) nodeId = commandNodeId;
|
|
48032
|
-
}
|
|
48033
48649
|
if (!res.ok) throw new Error(`enroll token exchange failed: HTTP ${res.status}`);
|
|
48034
48650
|
const body = await res.json();
|
|
48035
48651
|
token = body.token;
|
|
48036
|
-
nodeId = body.nodeId
|
|
48652
|
+
nodeId = body.nodeId;
|
|
48037
48653
|
}
|
|
48038
|
-
if (!token) {
|
|
48039
|
-
console.error("Usage: oasis start --server-ws <wss://...> (--enroll-token <ent_...> | --token <ont_...>) [--name <name>]
|
|
48654
|
+
if (!token || !nodeId) {
|
|
48655
|
+
console.error("Usage: oasis start --server-ws <wss://...> (--enroll-token <ent_...> | --token <ont_...>) [--name <name>]");
|
|
48040
48656
|
process.exit(1);
|
|
48041
48657
|
}
|
|
48042
48658
|
const name = flags.get("name") ?? prev?.name;
|