oasis_test 0.1.11 → 0.1.12
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 +1077 -549
- 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,26 @@ 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
|
+
var init_node_identity = __esm({
|
|
622
|
+
"../core/src/node-identity.ts"() {
|
|
623
|
+
"use strict";
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
|
|
604
627
|
// ../core/src/content-handler.ts
|
|
605
628
|
function defaultHandlersByContentType() {
|
|
606
629
|
return /* @__PURE__ */ new Map([
|
|
@@ -678,7 +701,7 @@ var init_content_type_handler = __esm({
|
|
|
678
701
|
const parts = ctx.queuedPartLabels.map((p2) => `\`${p2}\``).join("\u3001") || "\uFF08\u961F\u5217\u4E3A\u7A7A\uFF09";
|
|
679
702
|
return [
|
|
680
703
|
`\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
|
|
704
|
+
`\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
705
|
`\`oasis integrate ${ctx.artifactId} --from-dir deliverable/ --reason "<\u96C6\u6210\u8BF4\u660E>"\` \u2192 \`oasis conclude ${ctx.artifactId}\`\u3002`,
|
|
683
706
|
``,
|
|
684
707
|
`**\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 +752,7 @@ var init_content_type_handler = __esm({
|
|
|
729
752
|
`\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
753
|
`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
754
|
`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
|
|
755
|
+
`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
756
|
``,
|
|
734
757
|
`**\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
758
|
];
|
|
@@ -1248,6 +1271,48 @@ var init_grounding = __esm({
|
|
|
1248
1271
|
});
|
|
1249
1272
|
|
|
1250
1273
|
// ../core/src/kernel.ts
|
|
1274
|
+
function fieldValueMatches(val, type) {
|
|
1275
|
+
switch (type) {
|
|
1276
|
+
case "string":
|
|
1277
|
+
return typeof val === "string";
|
|
1278
|
+
case "number":
|
|
1279
|
+
return typeof val === "number";
|
|
1280
|
+
case "boolean":
|
|
1281
|
+
return typeof val === "boolean";
|
|
1282
|
+
case "string[]":
|
|
1283
|
+
return Array.isArray(val) && val.every((x2) => typeof x2 === "string");
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
function coerceFieldValue(key, val, type) {
|
|
1287
|
+
if (fieldValueMatches(val, type)) return val;
|
|
1288
|
+
if (typeof val === "string") {
|
|
1289
|
+
if (type === "number" && val.trim() !== "" && !Number.isNaN(Number(val))) return Number(val);
|
|
1290
|
+
if (type === "boolean" && (val === "true" || val === "false")) return val === "true";
|
|
1291
|
+
}
|
|
1292
|
+
throw new KernelError(`\u5B57\u6BB5 '${key}' \u7C7B\u578B\u5E94\u4E3A ${type}${type === "string[]" ? "\uFF08\u6570\u7EC4\u503C\u8BF7\u7528 --set-json\uFF09" : ""}`);
|
|
1293
|
+
}
|
|
1294
|
+
function validateArtifactFields(fields, specs, mode) {
|
|
1295
|
+
const declared = new Map((specs ?? []).map((s2) => [s2.name, s2]));
|
|
1296
|
+
let out;
|
|
1297
|
+
if (fields !== void 0) {
|
|
1298
|
+
out = {};
|
|
1299
|
+
for (const [key, val] of Object.entries(fields)) {
|
|
1300
|
+
const spec = declared.get(key);
|
|
1301
|
+
if (!spec) {
|
|
1302
|
+
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`);
|
|
1303
|
+
}
|
|
1304
|
+
out[key] = val === null ? null : coerceFieldValue(key, val, spec.type);
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
if (mode === "spawn") {
|
|
1308
|
+
for (const spec of specs ?? []) {
|
|
1309
|
+
if (spec.required && (fields?.[spec.name] === void 0 || fields[spec.name] === null)) {
|
|
1310
|
+
throw new KernelError(`\u7F3A\u5FC5\u586B\u5B57\u6BB5 '${spec.name}'\uFF08${spec.type}\uFF09`);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
return out;
|
|
1315
|
+
}
|
|
1251
1316
|
var import_node_crypto, SYSTEM_ACTOR, MAX_PREREQ_DEPTH, KernelError, FrozenError, Kernel;
|
|
1252
1317
|
var init_kernel = __esm({
|
|
1253
1318
|
"../core/src/kernel.ts"() {
|
|
@@ -1350,6 +1415,7 @@ var init_kernel = __esm({
|
|
|
1350
1415
|
);
|
|
1351
1416
|
}
|
|
1352
1417
|
this.enforceAcl("spawn", typeDef, args.actor);
|
|
1418
|
+
const fields = this.schema.size > 0 ? validateArtifactFields(args.fields, typeDef?.fieldDefs, "spawn") : args.fields;
|
|
1353
1419
|
const inputs = [];
|
|
1354
1420
|
for (const edge of args.inputs ?? []) {
|
|
1355
1421
|
if (edge.to === args.id) throw new KernelError("\u4E0D\u80FD\u4F9D\u8D56\u81EA\u5DF1");
|
|
@@ -1378,7 +1444,8 @@ var init_kernel = __esm({
|
|
|
1378
1444
|
...args.title !== void 0 ? { title: args.title } : {},
|
|
1379
1445
|
...args.description !== void 0 ? { description: args.description } : {},
|
|
1380
1446
|
...args.docType !== void 0 ? { docType: args.docType } : {},
|
|
1381
|
-
...args.parts !== void 0 ? { parts: args.parts } : {}
|
|
1447
|
+
...args.parts !== void 0 ? { parts: args.parts } : {},
|
|
1448
|
+
...fields !== void 0 ? { fields } : {}
|
|
1382
1449
|
};
|
|
1383
1450
|
await this.commit(args.id, args.actor, "spawn_artifact", args.id, payload);
|
|
1384
1451
|
return this.model.artifacts.get(args.id);
|
|
@@ -2206,6 +2273,7 @@ var init_kernel = __esm({
|
|
|
2206
2273
|
case "spawn": {
|
|
2207
2274
|
const typeDef = this.schema.get(op.type);
|
|
2208
2275
|
if (this.schema.size > 0 && !typeDef) throw new KernelError(`unknown ArtifactType: ${op.type}`);
|
|
2276
|
+
const spawnFields = this.schema.size > 0 ? validateArtifactFields(op.fields, typeDef?.fieldDefs, "spawn") : op.fields;
|
|
2209
2277
|
const owner = op.owner ?? this.resolveOwner(op.type, typeDef);
|
|
2210
2278
|
return {
|
|
2211
2279
|
artifactId: op.id,
|
|
@@ -2220,6 +2288,7 @@ var init_kernel = __esm({
|
|
|
2220
2288
|
...op.title !== void 0 ? { title: op.title } : {},
|
|
2221
2289
|
...op.description !== void 0 ? { description: op.description } : {},
|
|
2222
2290
|
...op.docType !== void 0 ? { docType: op.docType } : {},
|
|
2291
|
+
...spawnFields !== void 0 ? { fields: spawnFields } : {},
|
|
2223
2292
|
inputs: (op.inputs ?? []).map((e) => ({
|
|
2224
2293
|
to: e.to,
|
|
2225
2294
|
pinned: concludedHead(this.model, e.to),
|
|
@@ -2254,7 +2323,12 @@ var init_kernel = __esm({
|
|
|
2254
2323
|
...op.blocks !== void 0 ? { blocks: op.blocks } : {}
|
|
2255
2324
|
}
|
|
2256
2325
|
};
|
|
2257
|
-
case "edit":
|
|
2326
|
+
case "edit": {
|
|
2327
|
+
let editFields = op.fields;
|
|
2328
|
+
if (this.schema.size > 0 && op.fields !== void 0) {
|
|
2329
|
+
const a = this.model.artifacts.get(op.artifactId);
|
|
2330
|
+
editFields = validateArtifactFields(op.fields, a ? this.schema.get(a.type)?.fieldDefs : void 0, "edit");
|
|
2331
|
+
}
|
|
2258
2332
|
return {
|
|
2259
2333
|
artifactId: op.artifactId,
|
|
2260
2334
|
kind: "edit_artifact",
|
|
@@ -2262,9 +2336,11 @@ var init_kernel = __esm({
|
|
|
2262
2336
|
payload: {
|
|
2263
2337
|
...op.title !== void 0 ? { title: op.title } : {},
|
|
2264
2338
|
...op.description !== void 0 ? { description: op.description } : {},
|
|
2265
|
-
...op.docType !== void 0 ? { docType: op.docType } : {}
|
|
2339
|
+
...op.docType !== void 0 ? { docType: op.docType } : {},
|
|
2340
|
+
...editFields !== void 0 ? { fields: editFields } : {}
|
|
2266
2341
|
}
|
|
2267
2342
|
};
|
|
2343
|
+
}
|
|
2268
2344
|
}
|
|
2269
2345
|
});
|
|
2270
2346
|
}
|
|
@@ -2500,7 +2576,7 @@ function commandReference(opts) {
|
|
|
2500
2576
|
const write = [`**\u5199**`];
|
|
2501
2577
|
if (opts.action === "integrate") {
|
|
2502
2578
|
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
|
|
2579
|
+
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
2580
|
);
|
|
2505
2581
|
} else {
|
|
2506
2582
|
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 +2586,15 @@ function commandReference(opts) {
|
|
|
2510
2586
|
}
|
|
2511
2587
|
write.push(
|
|
2512
2588
|
`- \`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} --
|
|
2589
|
+
`- \`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
2590
|
`- \`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
2591
|
`- \`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
2592
|
);
|
|
2517
2593
|
if (opts.hasOpenAnnotations) {
|
|
2518
|
-
write.push(`- \`oasis resolve <annId> --
|
|
2594
|
+
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
2595
|
}
|
|
2520
2596
|
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`);
|
|
2597
|
+
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
2598
|
}
|
|
2523
2599
|
return [`## \u4F60\u80FD\u7528\u7684 oasis \u547D\u4EE4\uFF08\u672C\u6B21\uFF09`, ...write, ``, ...readFull];
|
|
2524
2600
|
}
|
|
@@ -2672,6 +2748,10 @@ async function assembleContext(args) {
|
|
|
2672
2748
|
}
|
|
2673
2749
|
}
|
|
2674
2750
|
}
|
|
2751
|
+
if (args.materializeSkills) {
|
|
2752
|
+
const skillFiles = await args.materializeSkills().catch(() => ({}));
|
|
2753
|
+
for (const [p2, c] of Object.entries(skillFiles)) files[p2] = c;
|
|
2754
|
+
}
|
|
2675
2755
|
const wsCodeRepo = args.codeRepo ?? null;
|
|
2676
2756
|
const isCodeArtifact = typeDef?.contentType === "code" && !!wsCodeRepo?.repos?.some((r) => r.remote);
|
|
2677
2757
|
files["TASK.md"] = [
|
|
@@ -2935,6 +3015,8 @@ var init_dispatcher = __esm({
|
|
|
2935
3015
|
inFlight = /* @__PURE__ */ new Map();
|
|
2936
3016
|
/** v1 调度准入:在途会话的 actor(jobKey → actorId),用于每 agent 并发计数。与 inFlight 同生同灭。 */
|
|
2937
3017
|
inFlightActor = /* @__PURE__ */ new Map();
|
|
3018
|
+
/** 在途会话的派发时刻(jobKey → ms),算"已跑多久"用(协调者病历的会话情况)。与 inFlight 同生同灭。 */
|
|
3019
|
+
inFlightStartedAt = /* @__PURE__ */ new Map();
|
|
2938
3020
|
strikes = /* @__PURE__ */ new Map();
|
|
2939
3021
|
/** v1 调度准入:上一轮 tick 中"够格但被并发上限挡下"的 produce(隐式队列;下个 tick 重评)。
|
|
2940
3022
|
* jobKey → 撞的是哪道闸(global 全局总量 / per-actor owner 自己到上限)+ 该 actor——
|
|
@@ -2979,8 +3061,9 @@ var init_dispatcher = __esm({
|
|
|
2979
3061
|
artifactId: this.artifactIdFromJobKey(jobKey),
|
|
2980
3062
|
sessionId: handle.id,
|
|
2981
3063
|
actor: this.inFlightActor.get(jobKey) ?? "",
|
|
2982
|
-
action: jobKey.slice(0, jobKey.indexOf("::"))
|
|
3064
|
+
action: jobKey.slice(0, jobKey.indexOf("::")),
|
|
2983
3065
|
// produce / review / integrate
|
|
3066
|
+
startedAt: new Date(this.inFlightStartedAt.get(jobKey) ?? Date.now()).toISOString()
|
|
2984
3067
|
});
|
|
2985
3068
|
}
|
|
2986
3069
|
return out;
|
|
@@ -3129,6 +3212,8 @@ var init_dispatcher = __esm({
|
|
|
3129
3212
|
const lastFailure = retry && retry.count > 0 && retry.lastReason ? { reason: retry.lastReason } : void 0;
|
|
3130
3213
|
const ws = kernel.model.artifacts.get(spec.artifactId)?.workspace;
|
|
3131
3214
|
const codeRepo = ws ? await this.opts.resolveCodeRepo?.(ws) ?? null : null;
|
|
3215
|
+
const binding = await this.opts.resolveBinding?.(spec.actor) ?? void 0;
|
|
3216
|
+
const materializeSkills = this.opts.materializeSkills ? () => this.opts.materializeSkills(spec.actor, binding?.runtimeKind ?? "claude") : void 0;
|
|
3132
3217
|
const bundle = await assembleContext({
|
|
3133
3218
|
model: kernel.model,
|
|
3134
3219
|
blobs: this.opts.blobs,
|
|
@@ -3140,9 +3225,9 @@ var init_dispatcher = __esm({
|
|
|
3140
3225
|
...lastFailure ? { lastFailure } : {},
|
|
3141
3226
|
...this.opts.schema !== void 0 ? { schema: this.opts.schema } : {},
|
|
3142
3227
|
...codeRepo ? { codeRepo } : {},
|
|
3143
|
-
...this.opts.resolveActorContext !== void 0 ? { resolveActorContext: this.opts.resolveActorContext } : {}
|
|
3228
|
+
...this.opts.resolveActorContext !== void 0 ? { resolveActorContext: this.opts.resolveActorContext } : {},
|
|
3229
|
+
...materializeSkills ? { materializeSkills } : {}
|
|
3144
3230
|
});
|
|
3145
|
-
const binding = await this.opts.resolveBinding?.(spec.actor) ?? void 0;
|
|
3146
3231
|
const provisioned = this.opts.provision ? await this.opts.provision(spec.actor) : void 0;
|
|
3147
3232
|
const job = {
|
|
3148
3233
|
actor: spec.actor,
|
|
@@ -3159,6 +3244,7 @@ var init_dispatcher = __esm({
|
|
|
3159
3244
|
this.inFlight.set(jobKey, session);
|
|
3160
3245
|
this.inFlightActor.set(jobKey, spec.actor);
|
|
3161
3246
|
const dispatchedAtMs = Date.now();
|
|
3247
|
+
this.inFlightStartedAt.set(jobKey, dispatchedAtMs);
|
|
3162
3248
|
const seqAtDispatch = seqNow;
|
|
3163
3249
|
const sink = this.opts.trajectory;
|
|
3164
3250
|
if (sink) {
|
|
@@ -3224,6 +3310,7 @@ var init_dispatcher = __esm({
|
|
|
3224
3310
|
}
|
|
3225
3311
|
this.inFlight.delete(jobKey);
|
|
3226
3312
|
this.inFlightActor.delete(jobKey);
|
|
3313
|
+
this.inFlightStartedAt.delete(jobKey);
|
|
3227
3314
|
const seqAtExit = kernel.model.lastSeq.get(spec.artifactId) ?? 0;
|
|
3228
3315
|
if (sink) {
|
|
3229
3316
|
void (async () => {
|
|
@@ -3363,6 +3450,7 @@ var init_src2 = __esm({
|
|
|
3363
3450
|
"../core/src/index.ts"() {
|
|
3364
3451
|
"use strict";
|
|
3365
3452
|
init_read_model();
|
|
3453
|
+
init_node_identity();
|
|
3366
3454
|
init_content_handler();
|
|
3367
3455
|
init_content_type_handler();
|
|
3368
3456
|
init_diff();
|
|
@@ -20363,6 +20451,36 @@ var init_drafts = __esm({
|
|
|
20363
20451
|
}
|
|
20364
20452
|
});
|
|
20365
20453
|
|
|
20454
|
+
// ../server/src/governance/workorder-drafts.ts
|
|
20455
|
+
var WorkorderDraftStore;
|
|
20456
|
+
var init_workorder_drafts = __esm({
|
|
20457
|
+
"../server/src/governance/workorder-drafts.ts"() {
|
|
20458
|
+
"use strict";
|
|
20459
|
+
WorkorderDraftStore = class {
|
|
20460
|
+
drafts = /* @__PURE__ */ new Map();
|
|
20461
|
+
seq = 0;
|
|
20462
|
+
submit(d) {
|
|
20463
|
+
const draft = { ...d, id: `wodraft:${++this.seq}`, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
20464
|
+
this.drafts.set(draft.id, draft);
|
|
20465
|
+
return draft;
|
|
20466
|
+
}
|
|
20467
|
+
list() {
|
|
20468
|
+
return [...this.drafts.values()];
|
|
20469
|
+
}
|
|
20470
|
+
/** 按 workspace 取最新一份:friday 用它发起建单时生成的 workspace id 取回草案,无需解析 chat 文本。 */
|
|
20471
|
+
forWorkspace(workspace) {
|
|
20472
|
+
return [...this.drafts.values()].reverse().find((d) => d.workspace === workspace);
|
|
20473
|
+
}
|
|
20474
|
+
get(id) {
|
|
20475
|
+
return this.drafts.get(id);
|
|
20476
|
+
}
|
|
20477
|
+
remove(id) {
|
|
20478
|
+
return this.drafts.delete(id);
|
|
20479
|
+
}
|
|
20480
|
+
};
|
|
20481
|
+
}
|
|
20482
|
+
});
|
|
20483
|
+
|
|
20366
20484
|
// ../server/src/domains/collab/planner.ts
|
|
20367
20485
|
function ensureDefaultWorkorderTypes(schema) {
|
|
20368
20486
|
const out = [...schema];
|
|
@@ -20371,18 +20489,17 @@ function ensureDefaultWorkorderTypes(schema) {
|
|
|
20371
20489
|
}
|
|
20372
20490
|
return out;
|
|
20373
20491
|
}
|
|
20374
|
-
function
|
|
20375
|
-
const
|
|
20376
|
-
|
|
20377
|
-
|
|
20378
|
-
const
|
|
20379
|
-
|
|
20380
|
-
|
|
20381
|
-
|
|
20492
|
+
function upgradeDefaultFieldDefs(schema) {
|
|
20493
|
+
const changed = [];
|
|
20494
|
+
for (const def of DEFAULT_TYPES) {
|
|
20495
|
+
if (!def.fieldDefs) continue;
|
|
20496
|
+
const cur = schema.find((item) => item.name === def.name);
|
|
20497
|
+
if (cur && !cur.fieldDefs) {
|
|
20498
|
+
cur.fieldDefs = def.fieldDefs;
|
|
20499
|
+
changed.push(def.name);
|
|
20500
|
+
}
|
|
20382
20501
|
}
|
|
20383
|
-
|
|
20384
|
-
function workorderSpecBody(spec) {
|
|
20385
|
-
return WORKORDER_SPEC_PREFIX + JSON.stringify(spec);
|
|
20502
|
+
return changed;
|
|
20386
20503
|
}
|
|
20387
20504
|
function parseWorkorderSpec(body) {
|
|
20388
20505
|
const first = body.split("\n", 1)[0] ?? "";
|
|
@@ -20420,6 +20537,12 @@ async function buildDynamicWorkorderPlan(input) {
|
|
|
20420
20537
|
return match;
|
|
20421
20538
|
};
|
|
20422
20539
|
const briefOwner = input.actor;
|
|
20540
|
+
const specFields = {};
|
|
20541
|
+
if (input.goal?.trim()) specFields["goal"] = input.goal.trim();
|
|
20542
|
+
if (input.acceptanceCriteria?.length) {
|
|
20543
|
+
const items = input.acceptanceCriteria.map((x2) => x2.trim()).filter(Boolean);
|
|
20544
|
+
if (items.length > 0) specFields["acceptanceCriteria"] = items;
|
|
20545
|
+
}
|
|
20423
20546
|
const ops = [
|
|
20424
20547
|
{
|
|
20425
20548
|
action: "spawn",
|
|
@@ -20429,23 +20552,12 @@ async function buildDynamicWorkorderPlan(input) {
|
|
|
20429
20552
|
workspace: input.workspace,
|
|
20430
20553
|
// 工单名 → brief.title;工单目标/brief → brief.description(别再把 name 塞进 description、丢掉 goal)
|
|
20431
20554
|
...input.title?.trim() ? { title: input.title.trim() } : {},
|
|
20432
|
-
description: input.description
|
|
20555
|
+
description: input.description,
|
|
20556
|
+
...Object.keys(specFields).length > 0 ? { fields: specFields } : {}
|
|
20433
20557
|
}
|
|
20434
20558
|
];
|
|
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
20559
|
const spawned = [{ id: briefId, type: "brief", role: "pm", owner: briefOwner }];
|
|
20445
20560
|
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
20561
|
if (input.agentPlan && !agentGraph) {
|
|
20450
20562
|
issues.push({
|
|
20451
20563
|
code: "planner_invalid",
|
|
@@ -20453,51 +20565,63 @@ async function buildDynamicWorkorderPlan(input) {
|
|
|
20453
20565
|
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
20566
|
});
|
|
20455
20567
|
}
|
|
20456
|
-
const
|
|
20568
|
+
for (const dup of agentGraph?.dupTitles ?? []) {
|
|
20569
|
+
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` });
|
|
20570
|
+
}
|
|
20571
|
+
const usedIds = /* @__PURE__ */ new Set([briefId]);
|
|
20457
20572
|
const availableNodes = [];
|
|
20458
|
-
|
|
20459
|
-
const
|
|
20460
|
-
|
|
20461
|
-
|
|
20462
|
-
code: "missing_artifact_type",
|
|
20463
|
-
|
|
20464
|
-
|
|
20465
|
-
|
|
20573
|
+
if (agentGraph) {
|
|
20574
|
+
for (const node of agentGraph.nodes) {
|
|
20575
|
+
const def = schemaByName.get(node.type);
|
|
20576
|
+
if (!def) {
|
|
20577
|
+
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` });
|
|
20578
|
+
continue;
|
|
20579
|
+
}
|
|
20580
|
+
const owner = await resolveRole(def.ownerRole, node.type);
|
|
20581
|
+
const ownerActor = owner.actor ?? `pending:role:${def.ownerRole}`;
|
|
20582
|
+
availableNodes.push({
|
|
20583
|
+
type: node.type,
|
|
20584
|
+
title: node.title,
|
|
20585
|
+
id: deriveArtifactId(node.type, input.workspace, node.title, usedIds),
|
|
20586
|
+
role: def.ownerRole,
|
|
20587
|
+
owner: ownerActor,
|
|
20588
|
+
dependsOn: node.dependsOn,
|
|
20589
|
+
...node.brief ? { brief: node.brief } : {}
|
|
20466
20590
|
});
|
|
20467
|
-
continue;
|
|
20468
20591
|
}
|
|
20469
|
-
|
|
20470
|
-
const
|
|
20471
|
-
|
|
20472
|
-
|
|
20473
|
-
|
|
20592
|
+
} else {
|
|
20593
|
+
const desired = desiredArtifacts(primaryType, input.description);
|
|
20594
|
+
for (const type of desired) {
|
|
20595
|
+
const def = schemaByName.get(type);
|
|
20596
|
+
if (!def) {
|
|
20597
|
+
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` });
|
|
20598
|
+
continue;
|
|
20599
|
+
}
|
|
20600
|
+
const owner = await resolveRole(def.ownerRole, type);
|
|
20601
|
+
const ownerActor = owner.actor ?? (type === primaryType ? input.actor : `pending:role:${def.ownerRole}`);
|
|
20602
|
+
const id = type === primaryType ? primaryId : `artifact:${type}:${slug4}`;
|
|
20603
|
+
availableNodes.push({ type, id, role: def.ownerRole, owner: ownerActor });
|
|
20604
|
+
}
|
|
20474
20605
|
}
|
|
20475
20606
|
for (const node of availableNodes) {
|
|
20476
|
-
const inputs =
|
|
20607
|
+
const inputs = agentGraph ? agentEdgesFor(node.dependsOn ?? [], availableNodes, briefId, issues) : ruleEdgesFor(node.type, availableNodes, briefId);
|
|
20477
20608
|
ops.push({
|
|
20478
20609
|
action: "spawn",
|
|
20479
20610
|
id: node.id,
|
|
20480
20611
|
type: node.type,
|
|
20481
20612
|
owner: node.owner,
|
|
20482
20613
|
workspace: input.workspace,
|
|
20614
|
+
...node.title ? { title: node.title } : {},
|
|
20483
20615
|
inputs,
|
|
20484
|
-
description: node.type === "prd" && issues.length > 0 ? `\u964D\u7EA7\u65B9\u6848\u6587\u6863\uFF1A${displayTitle}` : `${labelForType(node.type)}\uFF1A${displayTitle}`
|
|
20616
|
+
description: !agentGraph && node.type === "prd" && issues.length > 0 ? `\u964D\u7EA7\u65B9\u6848\u6587\u6863\uFF1A${displayTitle}` : `${labelForType(node.type)}\uFF1A${node.title ?? displayTitle}`
|
|
20485
20617
|
});
|
|
20486
20618
|
ops.push({
|
|
20487
20619
|
action: "annotate",
|
|
20488
20620
|
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),
|
|
20621
|
+
body: node.brief ?? taskBookFor(node.type, input.description, node.role, issues, agentGraph?.notes),
|
|
20499
20622
|
blocks: false
|
|
20500
20623
|
});
|
|
20624
|
+
spawned.push({ id: node.id, type: node.type, role: node.role, owner: node.owner });
|
|
20501
20625
|
}
|
|
20502
20626
|
return {
|
|
20503
20627
|
plan: {
|
|
@@ -20543,18 +20667,21 @@ function buildPmAgentPlannerPrompt(input) {
|
|
|
20543
20667
|
"",
|
|
20544
20668
|
JSON.stringify({
|
|
20545
20669
|
nodes: [
|
|
20546
|
-
{ type: "prd",
|
|
20547
|
-
{ type: "
|
|
20670
|
+
{ 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" },
|
|
20671
|
+
{ type: "research", title: "\u7ADE\u54C1\u8C03\u7814", dependsOn: ["brief"], brief: "\u2026" },
|
|
20672
|
+
{ type: "dev", title: "\u540E\u7AEF\u5B9E\u73B0", dependsOn: ["prd:\u9700\u6C42\u4E0E\u8303\u56F4\u8BF4\u660E"], brief: "\u2026" }
|
|
20548
20673
|
],
|
|
20549
20674
|
notes: ["\u53EF\u9009\uFF1A\u89C4\u5212\u7406\u7531\u6216\u98CE\u9669\u63D0\u793A"]
|
|
20550
20675
|
}, null, 2),
|
|
20551
20676
|
"",
|
|
20552
20677
|
"\u89C4\u5219\uFF1A",
|
|
20553
20678
|
"- 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
|
-
"-
|
|
20679
|
+
"- 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",
|
|
20680
|
+
"- \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",
|
|
20681
|
+
'- \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',
|
|
20682
|
+
"- \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",
|
|
20683
|
+
'- 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',
|
|
20684
|
+
"- \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
20685
|
].join("\n");
|
|
20559
20686
|
}
|
|
20560
20687
|
function parseAgentWorkorderPlan(text) {
|
|
@@ -20652,16 +20779,28 @@ function desiredArtifacts(primaryType, description) {
|
|
|
20652
20779
|
}
|
|
20653
20780
|
function normalizeAgentPlan(plan, validTypes) {
|
|
20654
20781
|
const allowed = validTypes.map((t) => t.trim().toLowerCase()).filter((t) => t !== "brief");
|
|
20655
|
-
const
|
|
20656
|
-
const
|
|
20782
|
+
const nodes = [];
|
|
20783
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20784
|
+
const dupTitles = [];
|
|
20657
20785
|
for (const node of plan.nodes) {
|
|
20658
20786
|
const type = node.type.trim().toLowerCase();
|
|
20659
20787
|
if (type === "brief") continue;
|
|
20660
|
-
if (!
|
|
20661
|
-
|
|
20788
|
+
if (!allowed.includes(type)) continue;
|
|
20789
|
+
const title = (node.title?.trim() || labelForType(type)).slice(0, 40);
|
|
20790
|
+
const key = `${type}:${title.toLowerCase()}`;
|
|
20791
|
+
if (seen.has(key)) {
|
|
20792
|
+
dupTitles.push(`${type}:${title}`);
|
|
20793
|
+
continue;
|
|
20794
|
+
}
|
|
20795
|
+
seen.add(key);
|
|
20796
|
+
nodes.push({
|
|
20797
|
+
type,
|
|
20798
|
+
title,
|
|
20799
|
+
dependsOn: node.dependsOn?.map((d) => d.trim()).filter(Boolean) ?? [],
|
|
20800
|
+
...node.brief ? { brief: node.brief } : {}
|
|
20801
|
+
});
|
|
20662
20802
|
}
|
|
20663
|
-
|
|
20664
|
-
return filtered.length > 0 ? { types: filtered, dependsOn, ...plan.notes?.length ? { notes: plan.notes } : {} } : null;
|
|
20803
|
+
return nodes.length > 0 ? { nodes, dupTitles, ...plan.notes?.length ? { notes: plan.notes } : {} } : null;
|
|
20665
20804
|
}
|
|
20666
20805
|
function extractJson(text) {
|
|
20667
20806
|
const fence = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
@@ -20674,23 +20813,30 @@ function extractJson(text) {
|
|
|
20674
20813
|
if (arrStart >= 0 && arrEnd > arrStart) return text.slice(arrStart, arrEnd + 1);
|
|
20675
20814
|
return null;
|
|
20676
20815
|
}
|
|
20677
|
-
function
|
|
20678
|
-
const idOf = (t) => nodes.find((node) => node.type === t)?.id;
|
|
20816
|
+
function agentEdgesFor(dependsOn, nodes, briefId, issues) {
|
|
20679
20817
|
const edges = /* @__PURE__ */ new Set();
|
|
20680
|
-
const
|
|
20681
|
-
|
|
20682
|
-
|
|
20683
|
-
|
|
20684
|
-
|
|
20685
|
-
|
|
20686
|
-
if (id) edges.add(id);
|
|
20687
|
-
}
|
|
20818
|
+
for (const dep of dependsOn) {
|
|
20819
|
+
const d = dep.trim();
|
|
20820
|
+
if (!d) continue;
|
|
20821
|
+
if (d.toLowerCase() === "brief") {
|
|
20822
|
+
edges.add(briefId);
|
|
20823
|
+
continue;
|
|
20688
20824
|
}
|
|
20825
|
+
const sep = d.indexOf(":");
|
|
20826
|
+
const depType = (sep >= 0 ? d.slice(0, sep) : d).trim().toLowerCase();
|
|
20827
|
+
const depTitle = sep >= 0 ? d.slice(sep + 1).trim().toLowerCase() : null;
|
|
20828
|
+
const match = nodes.find((n) => n.type === depType && (depTitle === null || (n.title ?? "").toLowerCase() === depTitle));
|
|
20829
|
+
if (match) edges.add(match.id);
|
|
20830
|
+
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
20831
|
}
|
|
20690
|
-
if (edges.size === 0)
|
|
20691
|
-
|
|
20692
|
-
|
|
20693
|
-
|
|
20832
|
+
if (edges.size === 0) edges.add(briefId);
|
|
20833
|
+
return [...edges].map((to) => ({ to, required: true }));
|
|
20834
|
+
}
|
|
20835
|
+
function ruleEdgesFor(type, nodes, briefId) {
|
|
20836
|
+
const idOf = (t) => nodes.find((node) => node.type === t)?.id;
|
|
20837
|
+
const edges = /* @__PURE__ */ new Set();
|
|
20838
|
+
if (type !== "prd") edges.add(idOf("prd") ?? briefId);
|
|
20839
|
+
else edges.add(briefId);
|
|
20694
20840
|
if (type === "adr") {
|
|
20695
20841
|
const research = idOf("research");
|
|
20696
20842
|
if (research) edges.add(research);
|
|
@@ -20727,17 +20873,6 @@ ${plannerNotes.map((note) => `- ${note}`).join("\n")}` : "";
|
|
|
20727
20873
|
notes
|
|
20728
20874
|
].join("\n");
|
|
20729
20875
|
}
|
|
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
20876
|
function dedupeIssues(issues) {
|
|
20742
20877
|
const seen = /* @__PURE__ */ new Set();
|
|
20743
20878
|
const out = [];
|
|
@@ -20763,16 +20898,19 @@ function labelForType(type) {
|
|
|
20763
20898
|
}
|
|
20764
20899
|
function artifactSlug(idOrWorkspace, description) {
|
|
20765
20900
|
const source = idOrWorkspace.split(":").pop() ?? description;
|
|
20766
|
-
|
|
20767
|
-
return slug4 || `wo-${Date.now()}`;
|
|
20901
|
+
return slugify(source, 40) || `wo-${Date.now()}`;
|
|
20768
20902
|
}
|
|
20769
|
-
var
|
|
20903
|
+
var DEFAULT_TYPES, WORKORDER_SPEC_PREFIX;
|
|
20770
20904
|
var init_planner = __esm({
|
|
20771
20905
|
"../server/src/domains/collab/planner.ts"() {
|
|
20772
20906
|
"use strict";
|
|
20773
|
-
|
|
20907
|
+
init_src2();
|
|
20774
20908
|
DEFAULT_TYPES = [
|
|
20775
|
-
|
|
20909
|
+
// brief 的 goal/acceptanceCriteria 现在是【自定义字段】(Phase 3,替代旧 OASIS_WORKORDER_SPEC 批注,铲 #1 死锁根)
|
|
20910
|
+
{ name: "brief", contentType: "text", ownerRole: "pm", fieldDefs: [
|
|
20911
|
+
{ name: "goal", type: "string", description: "\u4E00\u53E5\u8BDD\u76EE\u6807\uFF1A\u8FD9\u4E2A\u5DE5\u5355\u8981\u8FBE\u6210\u4EC0\u4E48" },
|
|
20912
|
+
{ name: "acceptanceCriteria", type: "string[]", description: "\u9A8C\u6536\u6761\u76EE\uFF08\u6EE1\u8DB3\u5373\u7B97\u5B8C\u6210\uFF09" }
|
|
20913
|
+
] },
|
|
20776
20914
|
{ name: "prd", contentType: "text", ownerRole: "pm" },
|
|
20777
20915
|
{ name: "research", contentType: "text", ownerRole: "research" },
|
|
20778
20916
|
{ name: "design", contentType: "text", ownerRole: "designer" },
|
|
@@ -20784,145 +20922,6 @@ var init_planner = __esm({
|
|
|
20784
20922
|
}
|
|
20785
20923
|
});
|
|
20786
20924
|
|
|
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
20925
|
// ../server/src/server.ts
|
|
20927
20926
|
function wantsChatParts(req) {
|
|
20928
20927
|
const accept = req.headers.accept;
|
|
@@ -21183,6 +21182,10 @@ async function runCommand(kernel, blobs, oplog, actor, command, args, ctx = {})
|
|
|
21183
21182
|
...p2.spec !== void 0 ? { spec: String(p2.spec) } : {},
|
|
21184
21183
|
...Array.isArray(p2.deps) ? { deps: p2.deps.map(String) } : {}
|
|
21185
21184
|
}));
|
|
21185
|
+
const rawFields = args["fields"];
|
|
21186
|
+
if (rawFields !== void 0 && (typeof rawFields !== "object" || rawFields === null || Array.isArray(rawFields))) {
|
|
21187
|
+
throw new Error("\u53C2\u6570 fields \u5FC5\u987B\u662F\u5BF9\u8C61 {key: value}");
|
|
21188
|
+
}
|
|
21186
21189
|
const createdViaArg = optStr(args, "createdVia");
|
|
21187
21190
|
if (createdViaArg !== void 0 && createdViaArg !== "seed" && createdViaArg !== "manual-spawn") {
|
|
21188
21191
|
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 +21277,33 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
|
|
|
21274
21277
|
if (!preview2.endState.ok) {
|
|
21275
21278
|
throw new Error(`\u52A8\u6001\u89C4\u5212\u7EC8\u6001\u6821\u9A8C\u4E0D\u8FC7\uFF1A${preview2.endState.violations.join("\uFF1B")}`);
|
|
21276
21279
|
}
|
|
21280
|
+
if (args["stage"] === true && ctx.workorderDrafts) {
|
|
21281
|
+
const draft = ctx.workorderDrafts.submit({
|
|
21282
|
+
workspace,
|
|
21283
|
+
plan: planned.plan,
|
|
21284
|
+
rootArtifactId: planned.rootArtifactId,
|
|
21285
|
+
seed: {
|
|
21286
|
+
brief,
|
|
21287
|
+
...goal !== void 0 ? { goal } : {},
|
|
21288
|
+
...acceptanceCriteria && acceptanceCriteria.length > 0 ? { acceptanceCriteria } : {},
|
|
21289
|
+
...args["dispatch"] === false ? { dispatch: false } : {}
|
|
21290
|
+
},
|
|
21291
|
+
draftedBy: actor
|
|
21292
|
+
});
|
|
21293
|
+
return {
|
|
21294
|
+
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`,
|
|
21295
|
+
data: {
|
|
21296
|
+
draftId: draft.id,
|
|
21297
|
+
workspace,
|
|
21298
|
+
rootArtifactId: planned.rootArtifactId,
|
|
21299
|
+
spawned: planned.spawned,
|
|
21300
|
+
planning: {
|
|
21301
|
+
status: planned.issues.length > 0 ? "needs_staffing" : "ready",
|
|
21302
|
+
issues: planned.issues
|
|
21303
|
+
}
|
|
21304
|
+
}
|
|
21305
|
+
};
|
|
21306
|
+
}
|
|
21277
21307
|
const applied = await kernel.applyIntervention(planned.plan, preview2.baseSeqs, actor);
|
|
21278
21308
|
const briefArtifactId = planned.rootArtifactId;
|
|
21279
21309
|
if (brief && brief.trim()) {
|
|
@@ -21283,7 +21313,7 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
|
|
|
21283
21313
|
await kernel.conclude({ artifactId: briefArtifactId, actor, note: "\u5EFA\u5355\u65F6\u7531\u53D1\u8D77\u4EBA\u62CD\u677F\u5B9A\u7A3F" });
|
|
21284
21314
|
}
|
|
21285
21315
|
if (args["dispatch"] === false) {
|
|
21286
|
-
await
|
|
21316
|
+
await ctx.artifactState?.setWorkspaceDispatchHold(workspace, true);
|
|
21287
21317
|
}
|
|
21288
21318
|
return {
|
|
21289
21319
|
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 +21342,8 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
|
|
|
21312
21342
|
...optStr(args, "description") !== void 0 ? { description: optStr(args, "description") } : {},
|
|
21313
21343
|
...optStr(args, "docType") !== void 0 ? { docType: optStr(args, "docType") } : {},
|
|
21314
21344
|
...inputs !== void 0 ? { inputs } : {},
|
|
21315
|
-
...parts !== void 0 ? { parts } : {}
|
|
21345
|
+
...parts !== void 0 ? { parts } : {},
|
|
21346
|
+
...rawFields !== void 0 ? { fields: rawFields } : {}
|
|
21316
21347
|
});
|
|
21317
21348
|
return { message: `spawned ${artifact.id}\uFF08owner=${artifact.owner}${artifact.parts?.length ? `\uFF0Cparts=${artifact.parts.map((p2) => p2.name).join("/")}` : ""}\uFF09`, data: artifact };
|
|
21318
21349
|
}
|
|
@@ -21836,17 +21867,9 @@ async function startOasisServer(opts) {
|
|
|
21836
21867
|
...opts.planWorkorderWithAgent ? { planWorkorderWithAgent: opts.planWorkorderWithAgent } : {},
|
|
21837
21868
|
...opts.resolveCodeRepo ? { resolveCodeRepo: opts.resolveCodeRepo } : {},
|
|
21838
21869
|
...opts.dispatchProduce ? { dispatchProduce: opts.dispatchProduce } : {},
|
|
21870
|
+
...opts.workorderDrafts ? { workorderDrafts: opts.workorderDrafts } : {},
|
|
21839
21871
|
...opts.gitEnv ? { gitEnv: opts.gitEnv } : {}
|
|
21840
21872
|
});
|
|
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
21873
|
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify(result));
|
|
21851
21874
|
} catch (err) {
|
|
21852
21875
|
const status = err instanceof FrozenError ? 409 : 400;
|
|
@@ -21883,6 +21906,40 @@ async function startOasisServer(opts) {
|
|
|
21883
21906
|
}
|
|
21884
21907
|
return;
|
|
21885
21908
|
}
|
|
21909
|
+
if (url.pathname === "/api/workorder/draft" && req.method === "GET" && opts.workorderDrafts) {
|
|
21910
|
+
const workspace = url.searchParams.get("workspace");
|
|
21911
|
+
const draft = workspace ? opts.workorderDrafts.forWorkspace(workspace) : void 0;
|
|
21912
|
+
if (!draft) {
|
|
21913
|
+
res.writeHead(404, { "content-type": "application/json" }).end(JSON.stringify({ error: "no staged workorder draft" }));
|
|
21914
|
+
return;
|
|
21915
|
+
}
|
|
21916
|
+
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ data: draft }));
|
|
21917
|
+
return;
|
|
21918
|
+
}
|
|
21919
|
+
if (url.pathname === "/api/workorder/apply" && req.method === "POST") {
|
|
21920
|
+
try {
|
|
21921
|
+
const body = JSON.parse(rawBody);
|
|
21922
|
+
const result = await engine.kernel.applyIntervention(body.plan, body.baseSeqs, actor);
|
|
21923
|
+
const brief = engine.kernel.model.artifacts.get(body.briefArtifactId);
|
|
21924
|
+
if (brief && brief.description && brief.description.trim()) {
|
|
21925
|
+
const briefRef = await engine.blobs.put(new TextEncoder().encode(brief.description));
|
|
21926
|
+
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" });
|
|
21927
|
+
await engine.kernel.advanceQueue(body.briefArtifactId, actor);
|
|
21928
|
+
await engine.kernel.conclude({ artifactId: body.briefArtifactId, actor, note: "\u5EFA\u5355\u65F6\u7531\u53D1\u8D77\u4EBA\u62CD\u677F\u5B9A\u7A3F" });
|
|
21929
|
+
}
|
|
21930
|
+
if (body.dispatch === false && brief?.workspace) {
|
|
21931
|
+
await opts.artifactState?.setWorkspaceDispatchHold(brief.workspace, true);
|
|
21932
|
+
}
|
|
21933
|
+
res.writeHead(200, { "content-type": "application/json" }).end(
|
|
21934
|
+
JSON.stringify({ ...result, briefArtifactId: body.briefArtifactId, workspace: brief?.workspace ?? null })
|
|
21935
|
+
);
|
|
21936
|
+
} catch (err) {
|
|
21937
|
+
res.writeHead(409, { "content-type": "application/json" }).end(
|
|
21938
|
+
JSON.stringify({ error: err instanceof Error ? err.message : String(err) })
|
|
21939
|
+
);
|
|
21940
|
+
}
|
|
21941
|
+
return;
|
|
21942
|
+
}
|
|
21886
21943
|
if (url.pathname === "/api/intervention/draft" && req.method === "POST" && opts.drafts) {
|
|
21887
21944
|
try {
|
|
21888
21945
|
const plan = JSON.parse(rawBody);
|
|
@@ -22129,8 +22186,7 @@ async function startOasisServer(opts) {
|
|
|
22129
22186
|
const session = await opts.dispatchChat({
|
|
22130
22187
|
actorId: body.actorId,
|
|
22131
22188
|
message: body.message,
|
|
22132
|
-
...body.sessionId ? { sessionId: body.sessionId } : {}
|
|
22133
|
-
...body.chatSessionId ? { chatSessionId: body.chatSessionId } : {}
|
|
22189
|
+
...body.sessionId ? { sessionId: body.sessionId } : {}
|
|
22134
22190
|
});
|
|
22135
22191
|
let finished = false;
|
|
22136
22192
|
res.on("close", () => {
|
|
@@ -22216,6 +22272,14 @@ async function startOasisServer(opts) {
|
|
|
22216
22272
|
});
|
|
22217
22273
|
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
22274
|
}
|
|
22275
|
+
if (opts.materializeSkills) {
|
|
22276
|
+
const skillFiles = await opts.materializeSkills(body.actorId, "claude").catch(() => ({}));
|
|
22277
|
+
for (const [rel, content] of Object.entries(skillFiles)) {
|
|
22278
|
+
const file = join16(dir, rel);
|
|
22279
|
+
mkdirSync13(dirname13(file), { recursive: true });
|
|
22280
|
+
writeFileSync14(file, content);
|
|
22281
|
+
}
|
|
22282
|
+
}
|
|
22219
22283
|
const activeConnectors = actorCtx.connectors.filter(
|
|
22220
22284
|
(c) => c.status === "connected" || c.status === "verifying"
|
|
22221
22285
|
);
|
|
@@ -22231,8 +22295,8 @@ async function startOasisServer(opts) {
|
|
|
22231
22295
|
}
|
|
22232
22296
|
}
|
|
22233
22297
|
const { spawn: spawn6 } = await import("node:child_process");
|
|
22234
|
-
const { randomUUID:
|
|
22235
|
-
const sessionId = body.sessionId ??
|
|
22298
|
+
const { randomUUID: randomUUID14 } = await import("node:crypto");
|
|
22299
|
+
const sessionId = body.sessionId ?? randomUUID14();
|
|
22236
22300
|
const args = [
|
|
22237
22301
|
"-p",
|
|
22238
22302
|
body.message,
|
|
@@ -22243,7 +22307,7 @@ async function startOasisServer(opts) {
|
|
|
22243
22307
|
];
|
|
22244
22308
|
const child = spawn6(opts.claudeBin ?? "claude", args, {
|
|
22245
22309
|
...spawnCwd ? { cwd: spawnCwd } : {},
|
|
22246
|
-
env: { ...process.env, ...env
|
|
22310
|
+
env: { ...process.env, ...env },
|
|
22247
22311
|
stdio: ["ignore", "pipe", "pipe"]
|
|
22248
22312
|
});
|
|
22249
22313
|
if (typedParts) {
|
|
@@ -22535,8 +22599,8 @@ var init_server3 = __esm({
|
|
|
22535
22599
|
init_mcp2();
|
|
22536
22600
|
init_router();
|
|
22537
22601
|
init_drafts();
|
|
22602
|
+
init_workorder_drafts();
|
|
22538
22603
|
init_planner();
|
|
22539
|
-
init_workorders();
|
|
22540
22604
|
defaultResolveActor = (token) => token.startsWith("token:") ? token.slice("token:".length) : null;
|
|
22541
22605
|
enc = (s2) => new TextEncoder().encode(s2);
|
|
22542
22606
|
CHAT_OUTPUT_LIMIT = 16e3;
|
|
@@ -22614,11 +22678,11 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
|
|
|
22614
22678
|
return m2;
|
|
22615
22679
|
};
|
|
22616
22680
|
return {
|
|
22617
|
-
issue(
|
|
22681
|
+
issue(name, ttlMs = defaultTtlMs) {
|
|
22618
22682
|
const m2 = prune();
|
|
22619
22683
|
const token = `ent_${(0, import_node_crypto3.randomBytes)(24).toString("base64url")}`;
|
|
22620
22684
|
const expiresAt = Date.now() + ttlMs;
|
|
22621
|
-
m2.set(token, {
|
|
22685
|
+
m2.set(token, { name, expiresAt });
|
|
22622
22686
|
write(m2);
|
|
22623
22687
|
return { token, expiresAt: new Date(expiresAt).toISOString() };
|
|
22624
22688
|
},
|
|
@@ -22626,7 +22690,7 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
|
|
|
22626
22690
|
const m2 = prune();
|
|
22627
22691
|
const entry = m2.get(token);
|
|
22628
22692
|
if (!entry) return null;
|
|
22629
|
-
return {
|
|
22693
|
+
return { name: entry.name, ...entry.issuedNodeId ? { issuedNodeId: entry.issuedNodeId } : {} };
|
|
22630
22694
|
},
|
|
22631
22695
|
consume(token) {
|
|
22632
22696
|
const m2 = prune();
|
|
@@ -22634,7 +22698,15 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
|
|
|
22634
22698
|
if (!entry) return null;
|
|
22635
22699
|
m2.delete(token);
|
|
22636
22700
|
write(m2);
|
|
22637
|
-
return {
|
|
22701
|
+
return { name: entry.name, ...entry.issuedNodeId ? { issuedNodeId: entry.issuedNodeId } : {} };
|
|
22702
|
+
},
|
|
22703
|
+
setIssuedNodeId(token, nodeId) {
|
|
22704
|
+
const m2 = read();
|
|
22705
|
+
const entry = m2.get(token);
|
|
22706
|
+
if (entry) {
|
|
22707
|
+
entry.issuedNodeId = nodeId;
|
|
22708
|
+
write(m2);
|
|
22709
|
+
}
|
|
22638
22710
|
}
|
|
22639
22711
|
};
|
|
22640
22712
|
}
|
|
@@ -23746,6 +23818,147 @@ var init_projection = __esm({
|
|
|
23746
23818
|
}
|
|
23747
23819
|
});
|
|
23748
23820
|
|
|
23821
|
+
// ../server/src/domains/collab/node-state.ts
|
|
23822
|
+
function nodeLabel(a) {
|
|
23823
|
+
if (a.title) return a.title;
|
|
23824
|
+
if (a.description) return a.description;
|
|
23825
|
+
const tail = a.id.split(":").slice(2).join(":");
|
|
23826
|
+
return tail ? `\u672A\u547D\u540D ${a.type}\uFF08${tail}\uFF09` : `\u672A\u547D\u540D ${a.type}`;
|
|
23827
|
+
}
|
|
23828
|
+
function nodeInfo(model, a) {
|
|
23829
|
+
const lifecycle = lifecycleOf(model, a.id);
|
|
23830
|
+
const sealed = lifecycle !== "active";
|
|
23831
|
+
const concluded = isConcluded(model, a.id);
|
|
23832
|
+
const mark = sealed ? lifecycle : pendingConcludeAttempt(model, a.id) ? "gate-pending" : blockedOf(model, a.id).blocked ? "blocked" : concluded ? "concluded" : a.currentRev ? "active" : "embryo";
|
|
23833
|
+
const hasWorking = revisionsOf(model, a.id).some((r) => r.state === "working");
|
|
23834
|
+
return { mark, queueLen: queueOf(model, a.id).length, concluded, sealed, hasWorking };
|
|
23835
|
+
}
|
|
23836
|
+
function deriveStage(members) {
|
|
23837
|
+
if (members.length > 0 && members.every((m2) => m2.sealed)) return "sealed";
|
|
23838
|
+
const live = members.filter((m2) => !m2.sealed);
|
|
23839
|
+
if (live.length > 0 && live.every((m2) => m2.concluded)) return "concluded";
|
|
23840
|
+
const reviewing = live.some((m2) => m2.mark === "gate-pending" || m2.mark === "active" && m2.queueLen > 0);
|
|
23841
|
+
if (reviewing) return "review";
|
|
23842
|
+
const progressing = live.some((m2) => m2.mark === "active" || m2.mark === "embryo");
|
|
23843
|
+
const blocked = live.some((m2) => m2.mark === "blocked");
|
|
23844
|
+
if (blocked && !progressing) return "blocked";
|
|
23845
|
+
return "executing";
|
|
23846
|
+
}
|
|
23847
|
+
function deriveNodeStage(info) {
|
|
23848
|
+
if (info.sealed) return "sealed";
|
|
23849
|
+
switch (info.mark) {
|
|
23850
|
+
case "concluded":
|
|
23851
|
+
return "concluded";
|
|
23852
|
+
case "gate-pending":
|
|
23853
|
+
return "gate";
|
|
23854
|
+
case "blocked":
|
|
23855
|
+
return "blocked";
|
|
23856
|
+
case "embryo":
|
|
23857
|
+
return "embryo";
|
|
23858
|
+
default:
|
|
23859
|
+
if (info.hasWorking) return "editing";
|
|
23860
|
+
if (info.queueLen > 0) return "review";
|
|
23861
|
+
return "merged";
|
|
23862
|
+
}
|
|
23863
|
+
}
|
|
23864
|
+
var init_node_state = __esm({
|
|
23865
|
+
"../server/src/domains/collab/node-state.ts"() {
|
|
23866
|
+
"use strict";
|
|
23867
|
+
init_src2();
|
|
23868
|
+
}
|
|
23869
|
+
});
|
|
23870
|
+
|
|
23871
|
+
// ../server/src/domains/collab/workorders.ts
|
|
23872
|
+
function buildWorkorderSummaries(model, resolveActor, resolveProject, dispatchPausedOf) {
|
|
23873
|
+
const byWorkspace = /* @__PURE__ */ new Map();
|
|
23874
|
+
for (const a of model.artifacts.values()) {
|
|
23875
|
+
const arr = byWorkspace.get(a.workspace) ?? [];
|
|
23876
|
+
arr.push(a);
|
|
23877
|
+
byWorkspace.set(a.workspace, arr);
|
|
23878
|
+
}
|
|
23879
|
+
const ref = (id) => {
|
|
23880
|
+
const extra = resolveActor?.(id);
|
|
23881
|
+
return {
|
|
23882
|
+
id,
|
|
23883
|
+
...extra?.name ? { name: extra.name } : {},
|
|
23884
|
+
...extra?.role ? { role: extra.role } : {},
|
|
23885
|
+
...extra?.avatar ? { avatar: extra.avatar } : {}
|
|
23886
|
+
};
|
|
23887
|
+
};
|
|
23888
|
+
const out = [];
|
|
23889
|
+
for (const [workspace, arts] of byWorkspace) {
|
|
23890
|
+
const members = arts.map((a) => nodeInfo(model, a));
|
|
23891
|
+
const seed = arts.find((a) => a.inputs.length === 0) ?? arts[0];
|
|
23892
|
+
const spec = workorderSpecOf(model, arts);
|
|
23893
|
+
const concluded = members.filter((m2) => m2.concluded).length;
|
|
23894
|
+
const lastOps = arts.map((a) => model.lastOpAt.get(a.id)).filter((t) => Boolean(t)).sort();
|
|
23895
|
+
const participantIds = [...new Set(arts.map((a) => a.owner))];
|
|
23896
|
+
const woTitle = seed.title ?? seed.description ?? workspace;
|
|
23897
|
+
out.push({
|
|
23898
|
+
id: workspace,
|
|
23899
|
+
title: woTitle,
|
|
23900
|
+
// 工单完整内容 = 根产物 description;仅当与标题不同(根产物有独立 title)时给出,避免与 title 重复
|
|
23901
|
+
...seed.description && seed.description !== woTitle ? { description: seed.description } : {},
|
|
23902
|
+
// 一句话目标:优先取结构化 spec.goal;缺省时前端回退 description 首段(投影不替前端做回退,只给真值)
|
|
23903
|
+
...spec?.goal ? { goal: spec.goal } : {},
|
|
23904
|
+
stage: deriveStage(members),
|
|
23905
|
+
owner: ref(seed.owner),
|
|
23906
|
+
participants: participantIds.map(ref),
|
|
23907
|
+
artifactCount: arts.length,
|
|
23908
|
+
progress: { concluded, total: arts.length },
|
|
23909
|
+
updatedAt: lastOps[lastOps.length - 1] ?? "",
|
|
23910
|
+
projectId: resolveProject?.(workspace) ?? null,
|
|
23911
|
+
planning: planningStatusOf(model, arts),
|
|
23912
|
+
...dispatchPausedOf?.(workspace) ? { dispatchPaused: true } : {}
|
|
23913
|
+
});
|
|
23914
|
+
}
|
|
23915
|
+
return out.sort((a, b2) => a.updatedAt < b2.updatedAt ? 1 : a.updatedAt > b2.updatedAt ? -1 : 0);
|
|
23916
|
+
}
|
|
23917
|
+
function planningStatusOf(_model, arts) {
|
|
23918
|
+
const issues = [];
|
|
23919
|
+
const seenRoles = /* @__PURE__ */ new Set();
|
|
23920
|
+
for (const a of arts) {
|
|
23921
|
+
const m2 = /^pending:role:(.+)$/.exec(a.owner);
|
|
23922
|
+
if (!m2) continue;
|
|
23923
|
+
const role = m2[1];
|
|
23924
|
+
if (seenRoles.has(role)) continue;
|
|
23925
|
+
seenRoles.add(role);
|
|
23926
|
+
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` });
|
|
23927
|
+
}
|
|
23928
|
+
return issues.length > 0 ? { status: "needs_staffing", issues } : { status: "ready", issues: [] };
|
|
23929
|
+
}
|
|
23930
|
+
function specFromFields(fields) {
|
|
23931
|
+
if (!fields) return null;
|
|
23932
|
+
const out = {};
|
|
23933
|
+
const goal = fields["goal"];
|
|
23934
|
+
if (typeof goal === "string" && goal.trim()) out.goal = goal.trim();
|
|
23935
|
+
const ac = fields["acceptanceCriteria"];
|
|
23936
|
+
if (Array.isArray(ac)) {
|
|
23937
|
+
const items = ac.filter((x2) => typeof x2 === "string" && x2.trim().length > 0).map((x2) => x2.trim());
|
|
23938
|
+
if (items.length > 0) out.acceptanceCriteria = items;
|
|
23939
|
+
}
|
|
23940
|
+
return out.goal !== void 0 || out.acceptanceCriteria !== void 0 ? out : null;
|
|
23941
|
+
}
|
|
23942
|
+
function workorderSpecOf(model, arts) {
|
|
23943
|
+
const seed = arts.find((a) => a.type === "brief") ?? arts.find((a) => a.inputs.length === 0);
|
|
23944
|
+
if (!seed) return null;
|
|
23945
|
+
const fromFields = specFromFields(seed.fields);
|
|
23946
|
+
if (fromFields) return fromFields;
|
|
23947
|
+
for (const annotation of model.annotations.values()) {
|
|
23948
|
+
if (annotation.anchor.target !== seed.id) continue;
|
|
23949
|
+
const spec = parseWorkorderSpec(annotation.body);
|
|
23950
|
+
if (spec) return spec;
|
|
23951
|
+
}
|
|
23952
|
+
return null;
|
|
23953
|
+
}
|
|
23954
|
+
var init_workorders = __esm({
|
|
23955
|
+
"../server/src/domains/collab/workorders.ts"() {
|
|
23956
|
+
"use strict";
|
|
23957
|
+
init_node_state();
|
|
23958
|
+
init_planner();
|
|
23959
|
+
}
|
|
23960
|
+
});
|
|
23961
|
+
|
|
23749
23962
|
// ../server/src/domains/projects/service.ts
|
|
23750
23963
|
function namedError(name, message) {
|
|
23751
23964
|
const err = new Error(message);
|
|
@@ -24003,6 +24216,8 @@ var init_service = __esm({
|
|
|
24003
24216
|
nodeOutputs = /* @__PURE__ */ new Map();
|
|
24004
24217
|
// B5:工单→项目绑定(key=workOrderId)。与 node_outputs 同属合法薄旁存——reset 不清、重放时种回。
|
|
24005
24218
|
workspaceBindings = /* @__PURE__ */ new Map();
|
|
24219
|
+
// #2 派单门:处于"待派发"的工单 id(薄旁存,同 binding——dispatcher/工单视图读它,不进 oplog)。
|
|
24220
|
+
dispatchHeld = /* @__PURE__ */ new Set();
|
|
24006
24221
|
/**
|
|
24007
24222
|
* B2 清空式重建用:清掉 oplog 派生的事实投影(artifact/revision/event/annotation),
|
|
24008
24223
|
* 保留 node_output(合法薄投影,投影正依赖它补 source*;由 registerNodeOutput 独立维护)。
|
|
@@ -24070,6 +24285,13 @@ var init_service = __esm({
|
|
|
24070
24285
|
async listWorkspaceBindings() {
|
|
24071
24286
|
return [...this.workspaceBindings.values()].map((binding) => ({ ...binding }));
|
|
24072
24287
|
}
|
|
24288
|
+
async setWorkspaceDispatchHold(workOrderId, held) {
|
|
24289
|
+
if (held) this.dispatchHeld.add(workOrderId);
|
|
24290
|
+
else this.dispatchHeld.delete(workOrderId);
|
|
24291
|
+
}
|
|
24292
|
+
async listWorkspaceDispatchHeld() {
|
|
24293
|
+
return [...this.dispatchHeld];
|
|
24294
|
+
}
|
|
24073
24295
|
async listAll() {
|
|
24074
24296
|
return {
|
|
24075
24297
|
artifacts: [...this.artifacts.values()].map(cloneArtifact),
|
|
@@ -24077,7 +24299,8 @@ var init_service = __esm({
|
|
|
24077
24299
|
events: [...this.events.values()].map(cloneEvent),
|
|
24078
24300
|
annotations: [...this.annotations.values()].map(cloneAnnotation),
|
|
24079
24301
|
nodeOutputs: [...this.nodeOutputs.values()].map(cloneNodeOutput),
|
|
24080
|
-
workspaceBindings: [...this.workspaceBindings.values()].map((binding) => ({ ...binding }))
|
|
24302
|
+
workspaceBindings: [...this.workspaceBindings.values()].map((binding) => ({ ...binding })),
|
|
24303
|
+
workspaceDispatchHeld: [...this.dispatchHeld]
|
|
24081
24304
|
};
|
|
24082
24305
|
}
|
|
24083
24306
|
};
|
|
@@ -25205,9 +25428,10 @@ var init_dev_store = __esm({
|
|
|
25205
25428
|
for (const annotation of snap.annotations ?? []) await MemoryArtifactStateStore.prototype.upsertAnnotation.call(store, annotation);
|
|
25206
25429
|
for (const output of snap.nodeOutputs ?? []) await MemoryArtifactStateStore.prototype.upsertNodeOutput.call(store, output);
|
|
25207
25430
|
for (const binding of snap.workspaceBindings ?? []) await MemoryArtifactStateStore.prototype.upsertWorkspaceBinding.call(store, binding);
|
|
25431
|
+
for (const ws of snap.workspaceDispatchHeld ?? []) await MemoryArtifactStateStore.prototype.setWorkspaceDispatchHold.call(store, ws, true);
|
|
25208
25432
|
} else {
|
|
25209
25433
|
fs2.mkdirSync(path.dirname(file), { recursive: true });
|
|
25210
|
-
fs2.writeFileSync(file, JSON.stringify({ artifacts: [], revisions: [], events: [], annotations: [], nodeOutputs: [], workspaceBindings: [] }, null, 2));
|
|
25434
|
+
fs2.writeFileSync(file, JSON.stringify({ artifacts: [], revisions: [], events: [], annotations: [], nodeOutputs: [], workspaceBindings: [], workspaceDispatchHeld: [] }, null, 2));
|
|
25211
25435
|
}
|
|
25212
25436
|
return store;
|
|
25213
25437
|
}
|
|
@@ -25235,6 +25459,10 @@ var init_dev_store = __esm({
|
|
|
25235
25459
|
await super.upsertWorkspaceBinding(binding);
|
|
25236
25460
|
await this.save();
|
|
25237
25461
|
}
|
|
25462
|
+
async setWorkspaceDispatchHold(workOrderId, held) {
|
|
25463
|
+
await super.setWorkspaceDispatchHold(workOrderId, held);
|
|
25464
|
+
await this.save();
|
|
25465
|
+
}
|
|
25238
25466
|
async save() {
|
|
25239
25467
|
fs2.writeFileSync(this.file, JSON.stringify(await this.listAll(), null, 2));
|
|
25240
25468
|
}
|
|
@@ -25321,7 +25549,6 @@ var init_dev_store = __esm({
|
|
|
25321
25549
|
MemoryChatSessionStore = class {
|
|
25322
25550
|
sessions = /* @__PURE__ */ new Map();
|
|
25323
25551
|
messages = /* @__PURE__ */ new Map();
|
|
25324
|
-
sessionWorkOrders = /* @__PURE__ */ new Map();
|
|
25325
25552
|
async createSession(s2) {
|
|
25326
25553
|
this.sessions.set(s2.id, s2);
|
|
25327
25554
|
}
|
|
@@ -25338,7 +25565,6 @@ var init_dev_store = __esm({
|
|
|
25338
25565
|
async deleteSession(id) {
|
|
25339
25566
|
this.sessions.delete(id);
|
|
25340
25567
|
this.messages.delete(id);
|
|
25341
|
-
this.sessionWorkOrders.delete(id);
|
|
25342
25568
|
}
|
|
25343
25569
|
async appendMessage(m2) {
|
|
25344
25570
|
const list = this.messages.get(m2.sessionId) ?? [];
|
|
@@ -25349,14 +25575,6 @@ var init_dev_store = __esm({
|
|
|
25349
25575
|
const list = (this.messages.get(sessionId) ?? []).sort((a, b2) => a.seq - b2.seq);
|
|
25350
25576
|
return limit ? list.slice(-limit) : list;
|
|
25351
25577
|
}
|
|
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
25578
|
};
|
|
25361
25579
|
}
|
|
25362
25580
|
});
|
|
@@ -25474,12 +25692,36 @@ function typeDefError(def, mode) {
|
|
|
25474
25692
|
}
|
|
25475
25693
|
return null;
|
|
25476
25694
|
}
|
|
25477
|
-
var TYPE_DEF_SCHEMA, TypesAdminService;
|
|
25695
|
+
var RESERVED_FIELD_NAMES, TYPE_DEF_SCHEMA, TypesAdminService;
|
|
25478
25696
|
var init_types_admin = __esm({
|
|
25479
25697
|
"../server/src/types-admin.ts"() {
|
|
25480
25698
|
"use strict";
|
|
25481
25699
|
init_zod();
|
|
25482
25700
|
init_src2();
|
|
25701
|
+
RESERVED_FIELD_NAMES = /* @__PURE__ */ new Set([
|
|
25702
|
+
// Artifact 内置字段
|
|
25703
|
+
"id",
|
|
25704
|
+
"type",
|
|
25705
|
+
"title",
|
|
25706
|
+
"description",
|
|
25707
|
+
"docType",
|
|
25708
|
+
"currentRev",
|
|
25709
|
+
"owner",
|
|
25710
|
+
"workspace",
|
|
25711
|
+
"persistence",
|
|
25712
|
+
"createdVia",
|
|
25713
|
+
"inputs",
|
|
25714
|
+
"parts",
|
|
25715
|
+
"fields",
|
|
25716
|
+
// edit/spawn 自己的 flag + 全局 infra flag(--<字段> 转发要避开)
|
|
25717
|
+
"brief",
|
|
25718
|
+
"input",
|
|
25719
|
+
"set-json",
|
|
25720
|
+
"clear",
|
|
25721
|
+
"server",
|
|
25722
|
+
"token",
|
|
25723
|
+
"dir"
|
|
25724
|
+
]);
|
|
25483
25725
|
TYPE_DEF_SCHEMA = external_exports.object({
|
|
25484
25726
|
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
25727
|
description: external_exports.string(),
|
|
@@ -25505,8 +25747,16 @@ var init_types_admin = __esm({
|
|
|
25505
25747
|
budget: external_exports.object({ wallClockMinutes: external_exports.number().positive().optional() }).strict(),
|
|
25506
25748
|
cascade: external_exports.enum(["auto", "manual"]),
|
|
25507
25749
|
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())
|
|
25750
|
+
mirror: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
25509
25751
|
// MirrorConfig 是连接器侧复杂类型——只要求是对象、内部松校验
|
|
25752
|
+
fieldDefs: external_exports.array(
|
|
25753
|
+
external_exports.object({
|
|
25754
|
+
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"),
|
|
25755
|
+
type: external_exports.enum(["string", "string[]", "number", "boolean"]),
|
|
25756
|
+
required: external_exports.boolean().optional(),
|
|
25757
|
+
description: external_exports.string().optional()
|
|
25758
|
+
}).strict()
|
|
25759
|
+
)
|
|
25510
25760
|
}).partial().strict();
|
|
25511
25761
|
TypesAdminService = class {
|
|
25512
25762
|
constructor(store, kernels) {
|
|
@@ -25576,6 +25826,36 @@ var init_types_admin = __esm({
|
|
|
25576
25826
|
for (const k2 of this.kernels()) k2.removeType(name);
|
|
25577
25827
|
return { ok: true };
|
|
25578
25828
|
}
|
|
25829
|
+
// —— B 平面:自定义字段声明(fieldDefs)的增/改/删。都落到 edit(复用 zod 校验 + 传播 + usage 影响半径)。
|
|
25830
|
+
// 改名禁、删=deprecate(存量值留、新写入拒)都由"只有这三个动词、且 name 钉死"在结构上保证。
|
|
25831
|
+
/** 加一条字段声明。name 已声明 / 撞保留集(edit 的 zod 把住)则拒。 */
|
|
25832
|
+
async addField(typeName, spec) {
|
|
25833
|
+
const cur = (await this.store.list()).find((t) => t.name === typeName);
|
|
25834
|
+
if (!cur) return { ok: false, error: `\u7C7B\u578B\u4E0D\u5B58\u5728: ${typeName}` };
|
|
25835
|
+
if ((cur.fieldDefs ?? []).some((f2) => f2.name === spec.name)) {
|
|
25836
|
+
return { ok: false, error: `\u5B57\u6BB5\u5DF2\u58F0\u660E: ${spec.name}\uFF08\u6539\u5C5E\u6027\u7528 edit-field\u3001\u5220\u7528 drop-field\uFF09` };
|
|
25837
|
+
}
|
|
25838
|
+
return this.edit(typeName, { fieldDefs: [...cur.fieldDefs ?? [], spec] });
|
|
25839
|
+
}
|
|
25840
|
+
/** 改一条字段声明的属性(type/required/description);**name 钉死、不可改名**。 */
|
|
25841
|
+
async editField(typeName, fieldName, attrs) {
|
|
25842
|
+
const cur = (await this.store.list()).find((t) => t.name === typeName);
|
|
25843
|
+
if (!cur) return { ok: false, error: `\u7C7B\u578B\u4E0D\u5B58\u5728: ${typeName}` };
|
|
25844
|
+
const idx = (cur.fieldDefs ?? []).findIndex((f2) => f2.name === fieldName);
|
|
25845
|
+
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` };
|
|
25846
|
+
const nextDefs = [...cur.fieldDefs];
|
|
25847
|
+
nextDefs[idx] = { ...nextDefs[idx], ...attrs, name: fieldName };
|
|
25848
|
+
return this.edit(typeName, { fieldDefs: nextDefs });
|
|
25849
|
+
}
|
|
25850
|
+
/** 删一条字段声明(deprecate 语义:存量值留、新写入拒)。返回 usage 供影响半径预览。 */
|
|
25851
|
+
async dropField(typeName, fieldName) {
|
|
25852
|
+
const cur = (await this.store.list()).find((t) => t.name === typeName);
|
|
25853
|
+
if (!cur) return { ok: false, error: `\u7C7B\u578B\u4E0D\u5B58\u5728: ${typeName}` };
|
|
25854
|
+
if (!(cur.fieldDefs ?? []).some((f2) => f2.name === fieldName)) {
|
|
25855
|
+
return { ok: false, error: `\u5B57\u6BB5\u672A\u58F0\u660E: ${fieldName}` };
|
|
25856
|
+
}
|
|
25857
|
+
return this.edit(typeName, { fieldDefs: (cur.fieldDefs ?? []).filter((f2) => f2.name !== fieldName) });
|
|
25858
|
+
}
|
|
25579
25859
|
};
|
|
25580
25860
|
}
|
|
25581
25861
|
});
|
|
@@ -30047,6 +30327,10 @@ ${input.description}
|
|
|
30047
30327
|
if (enabledKeys.size === 0) return [];
|
|
30048
30328
|
return all.filter((s2) => enabledKeys.has(s2.id) || enabledKeys.has(s2.name));
|
|
30049
30329
|
}
|
|
30330
|
+
/** 全局已装技能平铺列表(「默认挂」materialize 用;installedSkillsWithAssignments 带分配关系太重)。 */
|
|
30331
|
+
async listInstalledSkills() {
|
|
30332
|
+
return this.opts.store.listInstalledSkills();
|
|
30333
|
+
}
|
|
30050
30334
|
/* ---------- 变量(2.8):AES-256-GCM,读侧只见掩码 ---------- */
|
|
30051
30335
|
/**
|
|
30052
30336
|
* 写入变量。
|
|
@@ -30565,6 +30849,61 @@ var init_routes = __esm({
|
|
|
30565
30849
|
}
|
|
30566
30850
|
});
|
|
30567
30851
|
|
|
30852
|
+
// ../server/src/domains/actors/skill-materializer.ts
|
|
30853
|
+
function skillsDirForRuntime(runtimeKind) {
|
|
30854
|
+
switch (runtimeKind) {
|
|
30855
|
+
case "claude":
|
|
30856
|
+
case "claude-code":
|
|
30857
|
+
return ".claude/skills";
|
|
30858
|
+
default:
|
|
30859
|
+
return ".agent_context/skills";
|
|
30860
|
+
}
|
|
30861
|
+
}
|
|
30862
|
+
function sanitizeSkillDir(name) {
|
|
30863
|
+
const s2 = name.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
30864
|
+
return s2 || "skill";
|
|
30865
|
+
}
|
|
30866
|
+
async function materializeSkillFiles(args) {
|
|
30867
|
+
const own = await args.service.listInstalledSkillsForActor(args.actorId);
|
|
30868
|
+
let extras = [];
|
|
30869
|
+
if (args.extraSkillIds && args.extraSkillIds.length > 0) {
|
|
30870
|
+
const ownIds = new Set(own.map((s2) => s2.id));
|
|
30871
|
+
const wanted = new Set(args.extraSkillIds);
|
|
30872
|
+
extras = (await args.service.listInstalledSkills()).filter((s2) => wanted.has(s2.id) && !ownIds.has(s2.id));
|
|
30873
|
+
}
|
|
30874
|
+
const skills = [...own, ...extras];
|
|
30875
|
+
if (skills.length === 0) return {};
|
|
30876
|
+
const prefix = skillsDirForRuntime(args.runtimeKind);
|
|
30877
|
+
const out = {};
|
|
30878
|
+
const dec2 = new TextDecoder();
|
|
30879
|
+
const usedDirs = /* @__PURE__ */ new Set();
|
|
30880
|
+
for (const skill of skills) {
|
|
30881
|
+
const files = await args.service.listSkillFiles(skill.id);
|
|
30882
|
+
if (files.length === 0) continue;
|
|
30883
|
+
let dir = sanitizeSkillDir(skill.name);
|
|
30884
|
+
if (usedDirs.has(dir)) {
|
|
30885
|
+
let n = 2;
|
|
30886
|
+
while (usedDirs.has(`${dir}-${n}`)) n++;
|
|
30887
|
+
dir = `${dir}-${n}`;
|
|
30888
|
+
}
|
|
30889
|
+
usedDirs.add(dir);
|
|
30890
|
+
for (const f2 of files) {
|
|
30891
|
+
try {
|
|
30892
|
+
const bytes = await args.blobs.get(f2.blobHash);
|
|
30893
|
+
out[`${prefix}/${dir}/${f2.path}`] = dec2.decode(bytes);
|
|
30894
|
+
} catch (err) {
|
|
30895
|
+
console.warn(`[skill-materializer] skip ${skill.id}/${f2.path} (blob ${f2.blobHash.slice(0, 12)}\u2026): ${String(err)}`);
|
|
30896
|
+
}
|
|
30897
|
+
}
|
|
30898
|
+
}
|
|
30899
|
+
return out;
|
|
30900
|
+
}
|
|
30901
|
+
var init_skill_materializer = __esm({
|
|
30902
|
+
"../server/src/domains/actors/skill-materializer.ts"() {
|
|
30903
|
+
"use strict";
|
|
30904
|
+
}
|
|
30905
|
+
});
|
|
30906
|
+
|
|
30568
30907
|
// ../server/src/domains/actors/index.ts
|
|
30569
30908
|
async function syncRegistryRolesToKernel(store, kernel) {
|
|
30570
30909
|
for (const actor of await store.listActors()) {
|
|
@@ -30622,6 +30961,7 @@ var init_actors = __esm({
|
|
|
30622
30961
|
init_service2();
|
|
30623
30962
|
init_routes();
|
|
30624
30963
|
init_stats();
|
|
30964
|
+
init_skill_materializer();
|
|
30625
30965
|
}
|
|
30626
30966
|
});
|
|
30627
30967
|
|
|
@@ -32111,14 +32451,20 @@ OASIS_DIR="$(pwd)"
|
|
|
32111
32451
|
NODE_DIR="$OASIS_DIR/${dir}"
|
|
32112
32452
|
NODE_CMD="$OASIS_DIR/node_modules/.bin/tsx $OASIS_DIR/packages/cli/src/main.ts"
|
|
32113
32453
|
|
|
32114
|
-
# \u5199\u5165\
|
|
32115
|
-
# CLI \u7684 oasis node \u4F1A\
|
|
32454
|
+
# \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
|
|
32455
|
+
# CLI \u7684 oasis node \u9996\u6B21\u542F\u52A8\u4F1A\u7528\u5B83\u5411\u670D\u52A1\u7AEF exchange \u53D6\u56DE nodeId + \u957F\u671F node-token\uFF0C
|
|
32456
|
+
# \u4E4B\u540E\u843D\u76D8\u5230 <dir>/node-id \u4E0E <dir>/node-token\uFF0C\u91CD\u542F\u4E0D\u518D exchange\u3002
|
|
32116
32457
|
mkdir -p "$NODE_DIR"
|
|
32117
|
-
( umask 077; cat > "$NODE_DIR/
|
|
32458
|
+
( umask 077; cat > "$NODE_DIR/enroll-token" <<'OASIS_ENROLL_TOKEN_EOF'
|
|
32118
32459
|
${p2.token}
|
|
32119
|
-
|
|
32460
|
+
OASIS_ENROLL_TOKEN_EOF
|
|
32120
32461
|
)
|
|
32121
32462
|
|
|
32463
|
+
# "Script was run" marker: tells the daemon to (re)report its available runtimes once on
|
|
32464
|
+
# next start. Adapters are picked up only on an explicit script run \u2014 never on plain
|
|
32465
|
+
# reconnects/restarts \u2014 so admin-side runtime deletions stay deleted until you re-run this.
|
|
32466
|
+
touch "$NODE_DIR/report-runtimes"
|
|
32467
|
+
|
|
32122
32468
|
# \u7528 systemd \u7528\u6237\u670D\u52A1\uFF08Linux\uFF09\u4FDD\u6301\u540E\u53F0\u5E38\u9A7B + \u5F00\u673A\u81EA\u542F
|
|
32123
32469
|
if command -v systemctl &>/dev/null && systemctl --user daemon-reload &>/dev/null 2>&1; then
|
|
32124
32470
|
SERVICE_FILE=~/.config/systemd/user/oasis-node.service
|
|
@@ -32133,11 +32479,11 @@ if command -v systemctl &>/dev/null && systemctl --user daemon-reload &>/dev/nul
|
|
|
32133
32479
|
echo '\u2192 \u5199\u5165 systemd \u7528\u6237\u670D\u52A1\u914D\u7F6E...'
|
|
32134
32480
|
cat > "$SERVICE_FILE" << EOF
|
|
32135
32481
|
[Unit]
|
|
32136
|
-
Description=Oasis Node Daemon
|
|
32482
|
+
Description=Oasis Node Daemon
|
|
32137
32483
|
After=network.target
|
|
32138
32484
|
|
|
32139
32485
|
[Service]
|
|
32140
|
-
ExecStart=$NODE_CMD node --server-ws ${p2.gatewayUrl} --
|
|
32486
|
+
ExecStart=$NODE_CMD node --server-ws ${p2.gatewayUrl} --dir $NODE_DIR
|
|
32141
32487
|
WorkingDirectory=$OASIS_DIR
|
|
32142
32488
|
Restart=on-failure
|
|
32143
32489
|
RestartSec=5
|
|
@@ -32176,7 +32522,6 @@ elif [[ "$(uname)" == "Darwin" ]]; then
|
|
|
32176
32522
|
<string>$MAIN</string>
|
|
32177
32523
|
<string>node</string>
|
|
32178
32524
|
<string>--server-ws</string><string>${p2.gatewayUrl}</string>
|
|
32179
|
-
<string>--id</string><string>${p2.nodeId}</string>
|
|
32180
32525
|
<string>--dir</string><string>$NODE_DIR</string>
|
|
32181
32526
|
</array>
|
|
32182
32527
|
<key>WorkingDirectory</key><string>$OASIS_DIR</string>
|
|
@@ -32204,7 +32549,7 @@ else
|
|
|
32204
32549
|
sleep 1
|
|
32205
32550
|
fi
|
|
32206
32551
|
echo '\u2192 \u542F\u52A8\u8282\u70B9\uFF08nohup \u540E\u53F0\uFF09...'
|
|
32207
|
-
nohup $NODE_CMD node --server-ws ${p2.gatewayUrl} --
|
|
32552
|
+
nohup $NODE_CMD node --server-ws ${p2.gatewayUrl} --dir $NODE_DIR > ~/.oasis-node.log 2>&1 &
|
|
32208
32553
|
echo "\u2713 \u8282\u70B9\u5DF2\u5728\u540E\u53F0\u542F\u52A8\uFF08PID $!\uFF09"
|
|
32209
32554
|
echo ' \u65E5\u5FD7\uFF1Atail -f ~/.oasis-node.log'
|
|
32210
32555
|
echo ' \u505C\u6B62\uFF1Apkill -f "oasis node"'
|
|
@@ -32239,9 +32584,10 @@ if (-not (Test-Path "node_modules")) {
|
|
|
32239
32584
|
Write-Host "\u2192 Dependencies already present, skipping."
|
|
32240
32585
|
}
|
|
32241
32586
|
|
|
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
|
|
32587
|
+
# \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
|
|
32588
|
+
# \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
32589
|
New-Item -ItemType Directory -Force -Path $NODE_DIR | Out-Null
|
|
32244
|
-
$tokenPath = "$NODE_DIR\\
|
|
32590
|
+
$tokenPath = "$NODE_DIR\\enroll-token"
|
|
32245
32591
|
Set-Content -Path $tokenPath -Value "${p2.token}" -NoNewline -Encoding utf8
|
|
32246
32592
|
$acl = Get-Acl $tokenPath
|
|
32247
32593
|
$acl.SetAccessRuleProtection($true, $false)
|
|
@@ -32250,10 +32596,15 @@ $rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
|
|
|
32250
32596
|
$acl.AddAccessRule($rule)
|
|
32251
32597
|
Set-Acl $tokenPath $acl
|
|
32252
32598
|
|
|
32599
|
+
# "Script was run" marker: tells the daemon to (re)report its available runtimes once on
|
|
32600
|
+
# next start. Adapters are picked up only on an explicit script run \u2014 never on plain
|
|
32601
|
+
# reconnects/restarts \u2014 so admin-side runtime deletions stay deleted until you re-run this.
|
|
32602
|
+
Set-Content -Path "$NODE_DIR\\report-runtimes" -Value "" -NoNewline -Encoding utf8
|
|
32603
|
+
|
|
32253
32604
|
# \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
32605
|
$tsx = "$INSTALL_DIR\\node_modules\\.bin\\tsx.cmd"
|
|
32255
32606
|
$main = "$INSTALL_DIR\\packages\\cli\\src\\main.ts"
|
|
32256
|
-
$args = "\`"$main\`" node --server-ws ${p2.gatewayUrl} --
|
|
32607
|
+
$args = "\`"$main\`" node --server-ws ${p2.gatewayUrl} --dir \`"$NODE_DIR\`""
|
|
32257
32608
|
|
|
32258
32609
|
$action = New-ScheduledTaskAction -Execute $tsx -Argument $args -WorkingDirectory $INSTALL_DIR
|
|
32259
32610
|
$trigger = New-ScheduledTaskTrigger -AtLogOn -User $env:USERNAME
|
|
@@ -32261,23 +32612,23 @@ $settings = New-ScheduledTaskSettingsSet -RestartCount 5 \`
|
|
|
32261
32612
|
-RestartInterval (New-TimeSpan -Minutes 1) \`
|
|
32262
32613
|
-ExecutionTimeLimit ([TimeSpan]::Zero) \`
|
|
32263
32614
|
-StartWhenAvailable $true
|
|
32264
|
-
Register-ScheduledTask -TaskName "OasisNode
|
|
32615
|
+
Register-ScheduledTask -TaskName "OasisNode" \`
|
|
32265
32616
|
-Action $action -Trigger $trigger -Settings $settings -Force | Out-Null
|
|
32266
32617
|
|
|
32267
32618
|
# \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
|
|
32619
|
+
Start-ScheduledTask -TaskName "OasisNode"
|
|
32269
32620
|
Pop-Location
|
|
32270
32621
|
|
|
32271
32622
|
Write-Host ""
|
|
32272
32623
|
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
|
|
32624
|
+
Write-Host " Status : Get-ScheduledTask -TaskName 'OasisNode'"
|
|
32625
|
+
Write-Host " Stop : Stop-ScheduledTask -TaskName 'OasisNode'"
|
|
32626
|
+
Write-Host " Remove : Unregister-ScheduledTask -TaskName 'OasisNode' -Confirm:$false"
|
|
32276
32627
|
`;
|
|
32277
32628
|
}
|
|
32278
32629
|
function buildNpxCmd(p2) {
|
|
32279
32630
|
const namePart = p2.nodeName ? ` --name ${p2.nodeName}` : "";
|
|
32280
|
-
return `npx -y oasis_test@latest start --server-ws ${p2.gatewayUrl}
|
|
32631
|
+
return `npx -y oasis_test@latest start --server-ws ${p2.gatewayUrl}${namePart} --enroll-token ${p2.token}`;
|
|
32281
32632
|
}
|
|
32282
32633
|
var init_connect_script = __esm({
|
|
32283
32634
|
"../server/src/domains/nodes/connect-script.ts"() {
|
|
@@ -32287,7 +32638,6 @@ var init_connect_script = __esm({
|
|
|
32287
32638
|
|
|
32288
32639
|
// ../server/src/domains/nodes/routes.ts
|
|
32289
32640
|
function nodesDomain(deps) {
|
|
32290
|
-
const nodeNames = /* @__PURE__ */ new Map();
|
|
32291
32641
|
let hubWired = false;
|
|
32292
32642
|
function wireHub(hub) {
|
|
32293
32643
|
if (hubWired) return;
|
|
@@ -32298,19 +32648,20 @@ function nodesDomain(deps) {
|
|
|
32298
32648
|
if (msg.type !== "hello") return;
|
|
32299
32649
|
const d = hub.connectedDaemons().find((c) => c.daemonId === daemonId);
|
|
32300
32650
|
if (!d) return;
|
|
32301
|
-
|
|
32302
|
-
|
|
32303
|
-
|
|
32304
|
-
|
|
32305
|
-
|
|
32306
|
-
|
|
32307
|
-
|
|
32308
|
-
|
|
32309
|
-
|
|
32310
|
-
|
|
32311
|
-
|
|
32312
|
-
|
|
32313
|
-
|
|
32651
|
+
if (msg.meta.runtimes) {
|
|
32652
|
+
const existing = await nodeStore.getNode(daemonId);
|
|
32653
|
+
await nodeStore.upsertNode({
|
|
32654
|
+
id: daemonId,
|
|
32655
|
+
hostname: d.meta.hostname,
|
|
32656
|
+
nodeVersion: d.meta.nodeVersion ?? "unknown",
|
|
32657
|
+
status: "online",
|
|
32658
|
+
// revives a previously soft-deleted node
|
|
32659
|
+
lastSeenAt: now(),
|
|
32660
|
+
enrolledAt: existing?.enrolledAt ?? now(),
|
|
32661
|
+
...d.meta.nodeName ? { name: d.meta.nodeName } : existing?.name ? { name: existing.name } : {}
|
|
32662
|
+
});
|
|
32663
|
+
for (const cap of msg.meta.runtimes) {
|
|
32664
|
+
await nodeStore.addRuntimeIfAbsent({
|
|
32314
32665
|
id: `runtime:${daemonId}:${cap.kind}`,
|
|
32315
32666
|
nodeId: daemonId,
|
|
32316
32667
|
kind: cap.kind,
|
|
@@ -32321,7 +32672,9 @@ function nodesDomain(deps) {
|
|
|
32321
32672
|
lastSeenAt: now()
|
|
32322
32673
|
});
|
|
32323
32674
|
}
|
|
32675
|
+
await nodeStore.setNodeRuntimesOnline(daemonId, true);
|
|
32324
32676
|
} else {
|
|
32677
|
+
await nodeStore.setNodeOnline(daemonId, true);
|
|
32325
32678
|
await nodeStore.setNodeRuntimesOnline(daemonId, true);
|
|
32326
32679
|
}
|
|
32327
32680
|
});
|
|
@@ -32331,35 +32684,24 @@ function nodesDomain(deps) {
|
|
|
32331
32684
|
};
|
|
32332
32685
|
}
|
|
32333
32686
|
return (router) => {
|
|
32687
|
+
let wirePoll = null;
|
|
32688
|
+
const tryWire = () => {
|
|
32689
|
+
const hub = deps.getHub();
|
|
32690
|
+
if (hub) {
|
|
32691
|
+
wireHub(hub);
|
|
32692
|
+
if (wirePoll) {
|
|
32693
|
+
clearInterval(wirePoll);
|
|
32694
|
+
wirePoll = null;
|
|
32695
|
+
}
|
|
32696
|
+
}
|
|
32697
|
+
};
|
|
32698
|
+
tryWire();
|
|
32699
|
+
if (!hubWired) wirePoll = setInterval(tryWire, 100);
|
|
32334
32700
|
async function syncConnectedDaemon(daemonId, hub) {
|
|
32335
32701
|
const d = hub.connectedDaemons().find((c) => c.daemonId === daemonId);
|
|
32336
32702
|
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
|
-
}
|
|
32703
|
+
await deps.nodeStore.setNodeOnline(daemonId, true);
|
|
32704
|
+
await deps.nodeStore.setNodeRuntimesOnline(daemonId, true);
|
|
32363
32705
|
}
|
|
32364
32706
|
router.get("/api/nodes", async () => {
|
|
32365
32707
|
const hub = deps.getHub();
|
|
@@ -32414,16 +32756,11 @@ function nodesDomain(deps) {
|
|
|
32414
32756
|
router.post("/api/nodes/enroll", async (req) => {
|
|
32415
32757
|
const body = req.body ?? {};
|
|
32416
32758
|
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 };
|
|
32759
|
+
const enroll = deps.enrollTokens.issue(nodeName ?? "");
|
|
32760
|
+
const params = { gatewayUrl: deps.gatewayUrl, token: enroll.token, repoRemote: deps.repoRemote };
|
|
32424
32761
|
const script = body.platform === "windows" ? buildWindowsConnectScript(params) : buildConnectScript(params);
|
|
32425
|
-
const npxCmd = buildNpxCmd({ gatewayUrl: deps.gatewayUrl,
|
|
32426
|
-
return { status: 200, body: {
|
|
32762
|
+
const npxCmd = buildNpxCmd({ gatewayUrl: deps.gatewayUrl, token: enroll.token, nodeName });
|
|
32763
|
+
return { status: 200, body: { nodeName, token: enroll.token, script, gatewayUrl: deps.gatewayUrl, npxCmd } };
|
|
32427
32764
|
});
|
|
32428
32765
|
router.post("/api/nodes/exchange", async (req) => {
|
|
32429
32766
|
const body = req.body ?? {};
|
|
@@ -32431,15 +32768,22 @@ function nodesDomain(deps) {
|
|
|
32431
32768
|
if (!enrollToken) return { status: 400, body: { error: "missing enrollToken" } };
|
|
32432
32769
|
const entry = deps.enrollTokens.peek(enrollToken);
|
|
32433
32770
|
if (!entry) return { status: 401, body: { error: "invalid or expired enroll token" } };
|
|
32434
|
-
const
|
|
32435
|
-
const
|
|
32436
|
-
|
|
32437
|
-
|
|
32438
|
-
|
|
32439
|
-
|
|
32440
|
-
|
|
32441
|
-
|
|
32442
|
-
|
|
32771
|
+
const incomingNodeId = body.nodeId?.trim();
|
|
32772
|
+
const ownsIncoming = !!incomingNodeId && (deps.nodeTokens.list().some((e) => e.nodeId === incomingNodeId) || await deps.nodeStore.getNode(incomingNodeId) !== null);
|
|
32773
|
+
let nodeId;
|
|
32774
|
+
if (entry.issuedNodeId) {
|
|
32775
|
+
if (incomingNodeId && incomingNodeId !== entry.issuedNodeId && !ownsIncoming)
|
|
32776
|
+
return { status: 409, body: { error: "nodeId mismatch" } };
|
|
32777
|
+
nodeId = entry.issuedNodeId;
|
|
32778
|
+
} else if (ownsIncoming) {
|
|
32779
|
+
nodeId = incomingNodeId;
|
|
32780
|
+
deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
|
|
32781
|
+
} else {
|
|
32782
|
+
nodeId = `node-${(0, import_node_crypto9.randomUUID)().slice(0, 8)}`;
|
|
32783
|
+
deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
|
|
32784
|
+
}
|
|
32785
|
+
deps.enrollTokens.consume(enrollToken);
|
|
32786
|
+
if (entry.name) await deps.nodeStore.updateNodeName(nodeId, entry.name);
|
|
32443
32787
|
const existing = deps.nodeTokens.list().find((e) => e.nodeId === nodeId);
|
|
32444
32788
|
const token = existing?.token ?? deps.nodeTokens.issue(nodeId);
|
|
32445
32789
|
return { status: 200, body: { nodeId, token } };
|
|
@@ -32505,6 +32849,24 @@ function typesDomain(deps) {
|
|
|
32505
32849
|
const r = await deps.admin.delete(name);
|
|
32506
32850
|
return r.ok ? { status: 200, body: { ok: true } } : { status: 400, body: { error: r.error, ...r.usage ? { usage: r.usage } : {} } };
|
|
32507
32851
|
});
|
|
32852
|
+
router.post("/api/artifact-types/:name/fields", async (req) => {
|
|
32853
|
+
const name = req.params["name"];
|
|
32854
|
+
if (!name) return { status: 400, body: { error: "missing name" } };
|
|
32855
|
+
const r = await deps.admin.addField(name, req.body ?? {});
|
|
32856
|
+
return r.ok ? { status: 200, body: { ok: true, ...r.usage ? { usage: r.usage } : {} } } : { status: 400, body: { error: r.error } };
|
|
32857
|
+
});
|
|
32858
|
+
router.patch("/api/artifact-types/:name/fields/:field", async (req) => {
|
|
32859
|
+
const { name, field } = req.params;
|
|
32860
|
+
if (!name || !field) return { status: 400, body: { error: "missing name/field" } };
|
|
32861
|
+
const r = await deps.admin.editField(name, field, req.body ?? {});
|
|
32862
|
+
return r.ok ? { status: 200, body: { ok: true, ...r.usage ? { usage: r.usage } : {} } } : { status: 400, body: { error: r.error } };
|
|
32863
|
+
});
|
|
32864
|
+
router.delete("/api/artifact-types/:name/fields/:field", async (req) => {
|
|
32865
|
+
const { name, field } = req.params;
|
|
32866
|
+
if (!name || !field) return { status: 400, body: { error: "missing name/field" } };
|
|
32867
|
+
const r = await deps.admin.dropField(name, field);
|
|
32868
|
+
return r.ok ? { status: 200, body: { ok: true, ...r.usage ? { usage: r.usage } : {} } } : { status: 400, body: { error: r.error } };
|
|
32869
|
+
});
|
|
32508
32870
|
};
|
|
32509
32871
|
}
|
|
32510
32872
|
var init_types3 = __esm({
|
|
@@ -32522,17 +32884,20 @@ var init_node_store = __esm({
|
|
|
32522
32884
|
MemoryNodeStore = class {
|
|
32523
32885
|
nodes = /* @__PURE__ */ new Map();
|
|
32524
32886
|
runtimes = /* @__PURE__ */ new Map();
|
|
32525
|
-
deletedRuntimeIds = /* @__PURE__ */ new Set();
|
|
32526
32887
|
async upsertNode(node) {
|
|
32527
32888
|
this.nodes.set(node.id, { ...this.nodes.get(node.id), ...node });
|
|
32528
32889
|
}
|
|
32529
32890
|
async upsertRuntime(rt) {
|
|
32530
|
-
if (this.deletedRuntimeIds.has(rt.id)) return;
|
|
32531
32891
|
this.runtimes.set(rt.id, { ...this.runtimes.get(rt.id), ...rt });
|
|
32532
32892
|
}
|
|
32893
|
+
async addRuntimeIfAbsent(rt) {
|
|
32894
|
+
if (this.runtimes.has(rt.id)) return false;
|
|
32895
|
+
this.runtimes.set(rt.id, rt);
|
|
32896
|
+
return true;
|
|
32897
|
+
}
|
|
32533
32898
|
async setNodeOnline(id, online) {
|
|
32534
32899
|
const n = this.nodes.get(id);
|
|
32535
|
-
if (n) {
|
|
32900
|
+
if (n && n.status !== "deleted") {
|
|
32536
32901
|
n.status = online ? "online" : "offline";
|
|
32537
32902
|
n.lastSeenAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
32538
32903
|
}
|
|
@@ -32554,7 +32919,7 @@ var init_node_store = __esm({
|
|
|
32554
32919
|
return this.nodes.get(id) ?? null;
|
|
32555
32920
|
}
|
|
32556
32921
|
async listNodes() {
|
|
32557
|
-
return [...this.nodes.values()];
|
|
32922
|
+
return [...this.nodes.values()].filter((n) => n.status !== "deleted");
|
|
32558
32923
|
}
|
|
32559
32924
|
async listRuntimes(nodeId) {
|
|
32560
32925
|
const all = [...this.runtimes.values()];
|
|
@@ -32565,23 +32930,24 @@ var init_node_store = __esm({
|
|
|
32565
32930
|
for (const [k2, rt] of this.runtimes) {
|
|
32566
32931
|
if (rt.nodeId === id) this.runtimes.delete(k2);
|
|
32567
32932
|
}
|
|
32568
|
-
for (const k2 of [...this.deletedRuntimeIds]) {
|
|
32569
|
-
if (k2.startsWith(`runtime:${id}:`)) this.deletedRuntimeIds.delete(k2);
|
|
32570
|
-
}
|
|
32571
32933
|
}
|
|
32572
|
-
async
|
|
32573
|
-
|
|
32574
|
-
|
|
32934
|
+
async softDeleteNode(id) {
|
|
32935
|
+
const n = this.nodes.get(id);
|
|
32936
|
+
if (n) {
|
|
32937
|
+
n.status = "deleted";
|
|
32938
|
+
n.lastSeenAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
32939
|
+
}
|
|
32940
|
+
for (const [k2, rt] of this.runtimes) {
|
|
32941
|
+
if (rt.nodeId === id) this.runtimes.delete(k2);
|
|
32575
32942
|
}
|
|
32576
32943
|
}
|
|
32577
32944
|
async deleteRuntime(id) {
|
|
32578
|
-
this.deletedRuntimeIds.add(id);
|
|
32579
32945
|
const rt = this.runtimes.get(id);
|
|
32580
32946
|
this.runtimes.delete(id);
|
|
32581
32947
|
if (!rt) return { nodeDeleted: false };
|
|
32582
32948
|
const remaining = [...this.runtimes.values()].filter((r) => r.nodeId === rt.nodeId);
|
|
32583
32949
|
if (remaining.length === 0) {
|
|
32584
|
-
this.
|
|
32950
|
+
await this.softDeleteNode(rt.nodeId);
|
|
32585
32951
|
return { nodeDeleted: true };
|
|
32586
32952
|
}
|
|
32587
32953
|
return { nodeDeleted: false };
|
|
@@ -32598,7 +32964,6 @@ var init_node_store = __esm({
|
|
|
32598
32964
|
const snap = JSON.parse(fs5.readFileSync(file, "utf8"));
|
|
32599
32965
|
for (const n of snap.nodes ?? []) s2.nodes.set(n.id, n);
|
|
32600
32966
|
for (const r of snap.runtimes ?? []) s2.runtimes.set(r.id, r);
|
|
32601
|
-
for (const id of snap.deletedRuntimeIds ?? []) s2.deletedRuntimeIds.add(id);
|
|
32602
32967
|
} catch {
|
|
32603
32968
|
fs5.writeFileSync(file, JSON.stringify({ nodes: [], runtimes: [] }, null, 2));
|
|
32604
32969
|
}
|
|
@@ -32607,8 +32972,7 @@ var init_node_store = __esm({
|
|
|
32607
32972
|
flush() {
|
|
32608
32973
|
fs5.writeFileSync(this.file, JSON.stringify({
|
|
32609
32974
|
nodes: [...this.nodes.values()],
|
|
32610
|
-
runtimes: [...this.runtimes.values()]
|
|
32611
|
-
deletedRuntimeIds: [...this.deletedRuntimeIds]
|
|
32975
|
+
runtimes: [...this.runtimes.values()]
|
|
32612
32976
|
}, null, 2));
|
|
32613
32977
|
}
|
|
32614
32978
|
async upsertNode(node) {
|
|
@@ -32619,6 +32983,11 @@ var init_node_store = __esm({
|
|
|
32619
32983
|
await super.upsertRuntime(rt);
|
|
32620
32984
|
this.flush();
|
|
32621
32985
|
}
|
|
32986
|
+
async addRuntimeIfAbsent(rt) {
|
|
32987
|
+
const r = await super.addRuntimeIfAbsent(rt);
|
|
32988
|
+
if (r) this.flush();
|
|
32989
|
+
return r;
|
|
32990
|
+
}
|
|
32622
32991
|
async setNodeOnline(id, online) {
|
|
32623
32992
|
await super.setNodeOnline(id, online);
|
|
32624
32993
|
this.flush();
|
|
@@ -32635,8 +33004,8 @@ var init_node_store = __esm({
|
|
|
32635
33004
|
await super.deleteNode(id);
|
|
32636
33005
|
this.flush();
|
|
32637
33006
|
}
|
|
32638
|
-
async
|
|
32639
|
-
await super.
|
|
33007
|
+
async softDeleteNode(id) {
|
|
33008
|
+
await super.softDeleteNode(id);
|
|
32640
33009
|
this.flush();
|
|
32641
33010
|
}
|
|
32642
33011
|
async deleteRuntime(id) {
|
|
@@ -32649,10 +33018,10 @@ var init_node_store = __esm({
|
|
|
32649
33018
|
});
|
|
32650
33019
|
|
|
32651
33020
|
// ../server/src/domains/collab/workorder-detail.ts
|
|
32652
|
-
function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject, dispatchJournal = []) {
|
|
33021
|
+
function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject, dispatchJournal = [], dispatchPausedOf) {
|
|
32653
33022
|
const arts = [...model.artifacts.values()].filter((a) => a.workspace === workspaceId);
|
|
32654
33023
|
if (arts.length === 0) return null;
|
|
32655
|
-
const summary = buildWorkorderSummaries(model, resolveActor, resolveProject).find((w2) => w2.id === workspaceId);
|
|
33024
|
+
const summary = buildWorkorderSummaries(model, resolveActor, resolveProject, dispatchPausedOf).find((w2) => w2.id === workspaceId);
|
|
32656
33025
|
if (!summary) return null;
|
|
32657
33026
|
const ref = (id) => {
|
|
32658
33027
|
const extra = resolveActor?.(id);
|
|
@@ -32823,6 +33192,26 @@ var init_workorder_detail = __esm({
|
|
|
32823
33192
|
// ../server/src/domains/collab/inbox.ts
|
|
32824
33193
|
function buildInbox(model, me) {
|
|
32825
33194
|
const out = [];
|
|
33195
|
+
const creatorOf = /* @__PURE__ */ new Map();
|
|
33196
|
+
for (const a of model.artifacts.values()) {
|
|
33197
|
+
if (a.type === "brief") creatorOf.set(a.workspace, a.owner);
|
|
33198
|
+
}
|
|
33199
|
+
const staffingSeen = /* @__PURE__ */ new Set();
|
|
33200
|
+
for (const a of model.artifacts.values()) {
|
|
33201
|
+
const pendingRole = /^pending:role:(.+)$/.exec(a.owner);
|
|
33202
|
+
if (!pendingRole || creatorOf.get(a.workspace) !== me) continue;
|
|
33203
|
+
const key = `${a.workspace}:${pendingRole[1]}`;
|
|
33204
|
+
if (staffingSeen.has(key)) continue;
|
|
33205
|
+
staffingSeen.add(key);
|
|
33206
|
+
out.push({
|
|
33207
|
+
kind: "needs-staffing",
|
|
33208
|
+
artifactId: a.id,
|
|
33209
|
+
workspace: a.workspace,
|
|
33210
|
+
since: model.lastOpAt.get(a.id) ?? "",
|
|
33211
|
+
actionRequired: true,
|
|
33212
|
+
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`
|
|
33213
|
+
});
|
|
33214
|
+
}
|
|
32826
33215
|
for (const a of model.artifacts.values()) {
|
|
32827
33216
|
const ws = a.workspace;
|
|
32828
33217
|
const label = nodeLabel(a);
|
|
@@ -33018,6 +33407,11 @@ async function buildProjectResolver(artifacts) {
|
|
|
33018
33407
|
const byWorkOrder = new Map(bindings.map((binding) => [binding.workOrderId, binding.projectId]));
|
|
33019
33408
|
return (workOrderId) => byWorkOrder.get(workOrderId) ?? null;
|
|
33020
33409
|
}
|
|
33410
|
+
async function buildDispatchPausedResolver(artifacts) {
|
|
33411
|
+
if (!artifacts) return void 0;
|
|
33412
|
+
const held = new Set(await artifacts.listWorkspaceDispatchHeld());
|
|
33413
|
+
return (workspace) => held.has(workspace);
|
|
33414
|
+
}
|
|
33021
33415
|
function collabDomain(opts) {
|
|
33022
33416
|
const defaultCtx = {
|
|
33023
33417
|
kernel: opts.kernel,
|
|
@@ -33044,28 +33438,30 @@ function collabDomain(opts) {
|
|
|
33044
33438
|
const { kernel, registry: registry2, artifacts } = await resolveCtx(req.auth.companyId);
|
|
33045
33439
|
const resolve3 = await buildResolver(registry2);
|
|
33046
33440
|
const resolveProject = await buildProjectResolver(artifacts);
|
|
33047
|
-
|
|
33441
|
+
const dispatchPausedOf = await buildDispatchPausedResolver(artifacts);
|
|
33442
|
+
return { status: 200, body: { items: buildWorkorderSummaries(kernel.model, resolve3, resolveProject, dispatchPausedOf) } };
|
|
33048
33443
|
});
|
|
33049
33444
|
router.get("/api/workorders/:id", async (req) => {
|
|
33050
33445
|
const { kernel, registry: registry2, artifacts } = await resolveCtx(req.auth.companyId);
|
|
33051
33446
|
const resolve3 = await buildResolver(registry2);
|
|
33052
33447
|
const resolveProject = await buildProjectResolver(artifacts);
|
|
33053
|
-
const
|
|
33448
|
+
const dispatchPausedOf = await buildDispatchPausedResolver(artifacts);
|
|
33449
|
+
const detail = buildWorkorderDetail(kernel.model, req.params.id, resolve3, resolveProject, opts.dispatchJournal?.() ?? [], dispatchPausedOf);
|
|
33054
33450
|
if (!detail) {
|
|
33055
33451
|
return { status: 404, body: { error: { code: "not_found", message: `\u5DE5\u5355\u4E0D\u5B58\u5728: ${req.params.id}` } } };
|
|
33056
33452
|
}
|
|
33057
33453
|
return { status: 200, body: detail };
|
|
33058
33454
|
});
|
|
33059
33455
|
router.post("/api/workorders/:id/dispatch", async (req) => {
|
|
33060
|
-
const { kernel } = await resolveCtx(req.auth.companyId);
|
|
33456
|
+
const { kernel, artifacts } = await resolveCtx(req.auth.companyId);
|
|
33061
33457
|
const ws = req.params.id;
|
|
33062
33458
|
const arts = [...kernel.model.artifacts.values()].filter((a) => a.workspace === ws);
|
|
33063
33459
|
if (arts.length === 0) {
|
|
33064
33460
|
return { status: 404, body: { error: { code: "not_found", message: `\u5DE5\u5355\u4E0D\u5B58\u5728: ${ws}` } } };
|
|
33065
33461
|
}
|
|
33066
|
-
const
|
|
33067
|
-
if (!
|
|
33068
|
-
await
|
|
33462
|
+
const held = artifacts ? (await artifacts.listWorkspaceDispatchHeld()).includes(ws) : false;
|
|
33463
|
+
if (!held) return { status: 200, body: { dispatched: false } };
|
|
33464
|
+
await artifacts.setWorkspaceDispatchHold(ws, false);
|
|
33069
33465
|
return { status: 200, body: { dispatched: true } };
|
|
33070
33466
|
});
|
|
33071
33467
|
router.get("/api/inbox", async (req) => {
|
|
@@ -34016,23 +34412,6 @@ function createChatSessionsDomain(opts) {
|
|
|
34016
34412
|
await store.updateSession(req.params.id, { touchedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
34017
34413
|
return { status: 201, body: msg };
|
|
34018
34414
|
});
|
|
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
34415
|
};
|
|
34037
34416
|
}
|
|
34038
34417
|
var import_node_crypto10;
|
|
@@ -34041,7 +34420,6 @@ var init_chat_sessions = __esm({
|
|
|
34041
34420
|
"use strict";
|
|
34042
34421
|
import_node_crypto10 = require("node:crypto");
|
|
34043
34422
|
init_router();
|
|
34044
|
-
init_workorders();
|
|
34045
34423
|
}
|
|
34046
34424
|
});
|
|
34047
34425
|
|
|
@@ -35582,16 +35960,16 @@ var init_worker = __esm({
|
|
|
35582
35960
|
\u2461 **\u786E\u5C5E\u56FE\u75C5\u624D\u4E0B\u5200**\uFF08link / spawn / cancel-part / \u6539\u8FDE\uFF09\uFF1B
|
|
35583
35961
|
\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
35962
|
|
|
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 "
|
|
35963
|
+
\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
|
|
35964
|
+
- \`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
|
|
35965
|
+
- \`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
|
|
35966
|
+
- \`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
|
|
35967
|
+
- \`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
|
|
35968
|
+
- \`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
|
|
35969
|
+
- \`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
|
|
35970
|
+
- \`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
|
|
35971
|
+
- \`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
|
|
35972
|
+
- \`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
35973
|
\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
35974
|
- \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
35975
|
- \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 +35977,10 @@ var init_worker = __esm({
|
|
|
35599
35977
|
- \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
35978
|
|
|
35601
35979
|
\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\
|
|
35980
|
+
- \`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
|
|
35981
|
+
- \`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
|
|
35982
|
+
- \`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
|
|
35983
|
+
- \`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
35984
|
CoordinatorWorker = class {
|
|
35607
35985
|
constructor(deps) {
|
|
35608
35986
|
this.deps = deps;
|
|
@@ -35751,15 +36129,6 @@ var init_worker = __esm({
|
|
|
35751
36129
|
gaps.length > 0 ? `\u672A\u89E3\u6790gap\xD7${gaps.length}` : null
|
|
35752
36130
|
].filter(Boolean).join("\u3001");
|
|
35753
36131
|
}
|
|
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
36132
|
/** 结构摘要(reconcile 去重用):id / type / 是否已交付 / title+brief / 依赖——不含进度态(去重只看语义)。 */
|
|
35764
36133
|
structDigest(exclude) {
|
|
35765
36134
|
const m2 = this.deps.kernel.model;
|
|
@@ -35804,22 +36173,51 @@ var init_worker = __esm({
|
|
|
35804
36173
|
* 真死锁且某 part 卡住整条集成 → 起草 cancelPart(C 类,人确认);能解就解;拿不准 / 非图病 → 别动、交人。
|
|
35805
36174
|
*/
|
|
35806
36175
|
async reconcileDeadlock(p2) {
|
|
35807
|
-
const
|
|
36176
|
+
const m2 = this.deps.kernel.model;
|
|
36177
|
+
const node = m2.artifacts.get(p2.id);
|
|
35808
36178
|
const mins = Math.round(p2.staleMs / 6e4);
|
|
35809
|
-
const parts = node?.parts?.map((x2) => x2.name).join("\u3001") || "\uFF08\u65E0\u5206\u89E3\uFF09";
|
|
35810
36179
|
const brief = node?.title ?? node?.description ?? "";
|
|
36180
|
+
const now = Date.now();
|
|
35811
36181
|
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";
|
|
36182
|
+
const sessionsLine = (id) => {
|
|
36183
|
+
const ss = this.deps.sessionsOf?.(id) ?? [];
|
|
36184
|
+
if (ss.length === 0) return "\u65E0\u5728\u8DD1\u4F1A\u8BDD";
|
|
36185
|
+
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")}`;
|
|
36186
|
+
};
|
|
36187
|
+
const nodeBlock = (id) => {
|
|
36188
|
+
const a = m2.artifacts.get(id);
|
|
36189
|
+
if (!a) return `- **${id}**\uFF08\u4E0D\u5B58\u5728 / \u5DF2\u88AB\u6E05\u7406\uFF09`;
|
|
36190
|
+
const head = a.currentRev ? `head@${String(a.currentRev).slice(9, 17)}` : "\u65E0head";
|
|
36191
|
+
const blk = blockedOf(m2, id);
|
|
36192
|
+
const status = [lifecycleOf(m2, id), head, blk.blocked ? `\u963B\u585E[${blk.reasons.join("\uFF1B")}]` : null].filter(Boolean).join("\uFF5C");
|
|
36193
|
+
const label = a.title ?? a.description ?? "\uFF08\u65E0 brief\uFF09";
|
|
36194
|
+
return [
|
|
36195
|
+
`- **${id}**\uFF08type=${a.type}\uFF1A${label}\uFF09`,
|
|
36196
|
+
` - \u72B6\u6001\uFF1A${status}${a.parts?.length ? `\uFF5C\u90E8\u4EF6 [${a.parts.map((x2) => x2.name).join("\u3001")}]` : ""}`,
|
|
36197
|
+
` - \u5728\u8DD1\u4F1A\u8BDD\uFF1A${sessionsLine(id)}`
|
|
36198
|
+
].join("\n");
|
|
36199
|
+
};
|
|
36200
|
+
const upstreams = (node?.inputs ?? []).map((e) => e.to);
|
|
36201
|
+
const downstreams = consumersOf(m2, p2.id).map((a) => a.id);
|
|
35812
36202
|
const task = [
|
|
35813
36203
|
`## \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\
|
|
36204
|
+
`\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
36205
|
``,
|
|
35816
36206
|
`## \u75C5\u5386\uFF08\u673A\u68B0\u8BCA\u65AD\uFF1B\u5148\u8BFB\u8FD9\u4E2A\uFF09`,
|
|
35817
36207
|
caseFile,
|
|
35818
|
-
`\u5148\u8FA8\
|
|
36208
|
+
`\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`,
|
|
36209
|
+
``,
|
|
36210
|
+
`## \u672C\u8282\u70B9 + \u90BB\u57DF`,
|
|
36211
|
+
`> \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`,
|
|
36212
|
+
``,
|
|
36213
|
+
`### \u672C\u8282\u70B9`,
|
|
36214
|
+
nodeBlock(p2.id),
|
|
36215
|
+
``,
|
|
36216
|
+
`### \u4E0A\u6E38\uFF08\u672C\u8282\u70B9\u4F9D\u8D56\u8C01\u2014\u2014\u5B83\u4EEC\u6CA1\u5C31\u7EEA\uFF0C\u672C\u8282\u70B9\u52A8\u4E0D\u4E86\uFF09`,
|
|
36217
|
+
upstreams.length ? upstreams.map(nodeBlock).join("\n") : "- \uFF08\u65E0\u4E0A\u6E38\u4F9D\u8D56\uFF09",
|
|
35819
36218
|
``,
|
|
35820
|
-
|
|
35821
|
-
|
|
35822
|
-
this.graphDigest(p2.id),
|
|
36219
|
+
`### \u4E0B\u6E38\uFF08\u8C01\u4F9D\u8D56\u672C\u8282\u70B9\u2014\u2014\u672C\u8282\u70B9\u5361\uFF0C\u5B83\u4EEC\u8DDF\u7740\u5361\uFF09`,
|
|
36220
|
+
downstreams.length ? downstreams.map(nodeBlock).join("\n") : "- \uFF08\u65E0\u4E0B\u6E38\u6D88\u8D39\u8005\uFF09",
|
|
35823
36221
|
``,
|
|
35824
36222
|
`## \u4F60\u7684\u52A8\u4F5C\uFF08\u6682\u5B58 \u2192 apply\uFF09`,
|
|
35825
36223
|
`- \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 +36305,7 @@ var init_src4 = __esm({
|
|
|
35907
36305
|
init_resolve();
|
|
35908
36306
|
init_worker();
|
|
35909
36307
|
init_drafts();
|
|
36308
|
+
init_workorder_drafts();
|
|
35910
36309
|
}
|
|
35911
36310
|
});
|
|
35912
36311
|
|
|
@@ -37478,11 +37877,11 @@ var require_pg_connection_string = __commonJS({
|
|
|
37478
37877
|
config2.client_encoding = result.searchParams.get("encoding");
|
|
37479
37878
|
return config2;
|
|
37480
37879
|
}
|
|
37481
|
-
const
|
|
37880
|
+
const hostname5 = dummyHost ? "" : result.hostname;
|
|
37482
37881
|
if (!config2.host) {
|
|
37483
|
-
config2.host = decodeURIComponent(
|
|
37484
|
-
} else if (
|
|
37485
|
-
result.pathname =
|
|
37882
|
+
config2.host = decodeURIComponent(hostname5);
|
|
37883
|
+
} else if (hostname5 && /^%2f/i.test(hostname5)) {
|
|
37884
|
+
result.pathname = hostname5 + result.pathname;
|
|
37486
37885
|
}
|
|
37487
37886
|
if (!config2.port) {
|
|
37488
37887
|
config2.port = result.port;
|
|
@@ -41092,7 +41491,6 @@ var init_trajectory = __esm({
|
|
|
41092
41491
|
});
|
|
41093
41492
|
|
|
41094
41493
|
// src/index.ts
|
|
41095
|
-
var import_node_crypto22 = require("node:crypto");
|
|
41096
41494
|
var fs15 = __toESM(require("node:fs"));
|
|
41097
41495
|
var os8 = __toESM(require("node:os"));
|
|
41098
41496
|
var path13 = __toESM(require("node:path"));
|
|
@@ -41874,6 +42272,9 @@ var CodexAdapter = class {
|
|
|
41874
42272
|
"-C",
|
|
41875
42273
|
dir,
|
|
41876
42274
|
...this.opts.model ? ["--model", this.opts.model] : [],
|
|
42275
|
+
// 打开 reasoning 摘要通道:默认 auto,让 codex 产出 reasoning item(→ thought → 前端思考块)。
|
|
42276
|
+
...this.opts.reasoningSummary !== "none" ? ["-c", `model_reasoning_summary=${this.opts.reasoningSummary ?? "auto"}`] : [],
|
|
42277
|
+
...this.opts.reasoningEffort ? ["-c", `model_reasoning_effort=${this.opts.reasoningEffort}`] : [],
|
|
41877
42278
|
...this.opts.extraArgs ?? [],
|
|
41878
42279
|
prompt
|
|
41879
42280
|
];
|
|
@@ -43907,6 +44308,10 @@ var PostgresArtifactStateStore = class _PostgresArtifactStateStore {
|
|
|
43907
44308
|
work_order_id text PRIMARY KEY,
|
|
43908
44309
|
project_id text NOT NULL
|
|
43909
44310
|
)`);
|
|
44311
|
+
await pool.query(`
|
|
44312
|
+
CREATE TABLE IF NOT EXISTS "${s2}".workspace_dispatch_hold (
|
|
44313
|
+
work_order_id text PRIMARY KEY
|
|
44314
|
+
)`);
|
|
43910
44315
|
return new _PostgresArtifactStateStore(pool, schema);
|
|
43911
44316
|
}
|
|
43912
44317
|
/* ---------- artifacts ---------- */
|
|
@@ -44180,14 +44585,29 @@ var PostgresArtifactStateStore = class _PostgresArtifactStateStore {
|
|
|
44180
44585
|
const r = await this.pool.query(`SELECT * FROM ${this.s}.workspace_bindings`);
|
|
44181
44586
|
return r.rows.map(rowToWorkspaceBinding);
|
|
44182
44587
|
}
|
|
44588
|
+
async setWorkspaceDispatchHold(workOrderId, held) {
|
|
44589
|
+
if (held) {
|
|
44590
|
+
await this.pool.query(
|
|
44591
|
+
`INSERT INTO ${this.s}.workspace_dispatch_hold (work_order_id) VALUES ($1) ON CONFLICT DO NOTHING`,
|
|
44592
|
+
[workOrderId]
|
|
44593
|
+
);
|
|
44594
|
+
} else {
|
|
44595
|
+
await this.pool.query(`DELETE FROM ${this.s}.workspace_dispatch_hold WHERE work_order_id = $1`, [workOrderId]);
|
|
44596
|
+
}
|
|
44597
|
+
}
|
|
44598
|
+
async listWorkspaceDispatchHeld() {
|
|
44599
|
+
const r = await this.pool.query(`SELECT work_order_id FROM ${this.s}.workspace_dispatch_hold`);
|
|
44600
|
+
return r.rows.map((row) => String(row.work_order_id));
|
|
44601
|
+
}
|
|
44183
44602
|
async listAll() {
|
|
44184
|
-
const [artifacts, revisions, events, annotations, outputs, bindings] = await Promise.all([
|
|
44603
|
+
const [artifacts, revisions, events, annotations, outputs, bindings, held] = await Promise.all([
|
|
44185
44604
|
this.pool.query(`SELECT * FROM ${this.s}.artifacts`),
|
|
44186
44605
|
this.pool.query(`SELECT * FROM ${this.s}.artifact_revisions`),
|
|
44187
44606
|
this.pool.query(`SELECT * FROM ${this.s}.artifact_events ORDER BY seq`),
|
|
44188
44607
|
this.pool.query(`SELECT * FROM ${this.s}.artifact_annotations`),
|
|
44189
44608
|
this.pool.query(`SELECT * FROM ${this.s}.node_outputs`),
|
|
44190
|
-
this.pool.query(`SELECT * FROM ${this.s}.workspace_bindings`)
|
|
44609
|
+
this.pool.query(`SELECT * FROM ${this.s}.workspace_bindings`),
|
|
44610
|
+
this.pool.query(`SELECT work_order_id FROM ${this.s}.workspace_dispatch_hold`)
|
|
44191
44611
|
]);
|
|
44192
44612
|
const nodeOutputs = [];
|
|
44193
44613
|
for (const row of outputs.rows) {
|
|
@@ -44203,7 +44623,8 @@ var PostgresArtifactStateStore = class _PostgresArtifactStateStore {
|
|
|
44203
44623
|
events: events.rows.map(rowToEvent),
|
|
44204
44624
|
annotations: annotations.rows.map(rowToAnnotation),
|
|
44205
44625
|
nodeOutputs,
|
|
44206
|
-
workspaceBindings: bindings.rows.map(rowToWorkspaceBinding)
|
|
44626
|
+
workspaceBindings: bindings.rows.map(rowToWorkspaceBinding),
|
|
44627
|
+
workspaceDispatchHeld: held.rows.map((row) => String(row.work_order_id))
|
|
44207
44628
|
};
|
|
44208
44629
|
}
|
|
44209
44630
|
/**
|
|
@@ -45249,13 +45670,6 @@ var PostgresChatSessionStore = class _PostgresChatSessionStore {
|
|
|
45249
45670
|
created_at timestamptz NOT NULL
|
|
45250
45671
|
)`);
|
|
45251
45672
|
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
45673
|
return new _PostgresChatSessionStore(pool, schema);
|
|
45260
45674
|
}
|
|
45261
45675
|
async createSession(s2) {
|
|
@@ -45319,19 +45733,6 @@ var PostgresChatSessionStore = class _PostgresChatSessionStore {
|
|
|
45319
45733
|
const rows = r.rows.map(rowToMessage);
|
|
45320
45734
|
return limit ? rows.reverse() : rows;
|
|
45321
45735
|
}
|
|
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
45736
|
};
|
|
45336
45737
|
var rowToSession = (row) => ({
|
|
45337
45738
|
id: row.id,
|
|
@@ -45389,10 +45790,6 @@ var PostgresNodeStore = class _PostgresNodeStore {
|
|
|
45389
45790
|
last_seen_at timestamptz NOT NULL
|
|
45390
45791
|
)`);
|
|
45391
45792
|
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
45793
|
return new _PostgresNodeStore(pool, schema);
|
|
45397
45794
|
}
|
|
45398
45795
|
async upsertNode(n) {
|
|
@@ -45409,8 +45806,6 @@ var PostgresNodeStore = class _PostgresNodeStore {
|
|
|
45409
45806
|
);
|
|
45410
45807
|
}
|
|
45411
45808
|
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
45809
|
await this.pool.query(
|
|
45415
45810
|
`INSERT INTO ${this.s}.node_runtimes (id,node_id,kind,hostname,runtime_binary,version,status,last_seen_at)
|
|
45416
45811
|
VALUES ($1,$2,$3,$4,$5,$6,$7,$8)
|
|
@@ -45423,8 +45818,18 @@ var PostgresNodeStore = class _PostgresNodeStore {
|
|
|
45423
45818
|
[r.id, r.nodeId, r.kind, r.hostname, r.binary ?? null, r.version ?? null, r.status, r.lastSeenAt]
|
|
45424
45819
|
);
|
|
45425
45820
|
}
|
|
45821
|
+
/** Insert only if absent (ON CONFLICT DO NOTHING). Returns true if a row was inserted. */
|
|
45822
|
+
async addRuntimeIfAbsent(r) {
|
|
45823
|
+
const res = await this.pool.query(
|
|
45824
|
+
`INSERT INTO ${this.s}.node_runtimes (id,node_id,kind,hostname,runtime_binary,version,status,last_seen_at)
|
|
45825
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8)
|
|
45826
|
+
ON CONFLICT (id) DO NOTHING`,
|
|
45827
|
+
[r.id, r.nodeId, r.kind, r.hostname, r.binary ?? null, r.version ?? null, r.status, r.lastSeenAt]
|
|
45828
|
+
);
|
|
45829
|
+
return (res.rowCount ?? 0) > 0;
|
|
45830
|
+
}
|
|
45426
45831
|
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()]);
|
|
45832
|
+
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
45833
|
}
|
|
45429
45834
|
async setNodeRuntimesOnline(nodeId, online) {
|
|
45430
45835
|
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 +45842,7 @@ var PostgresNodeStore = class _PostgresNodeStore {
|
|
|
45437
45842
|
return r.rows[0] ? rowToNode(r.rows[0]) : null;
|
|
45438
45843
|
}
|
|
45439
45844
|
async listNodes() {
|
|
45440
|
-
const r = await this.pool.query(`SELECT * FROM ${this.s}.nodes ORDER BY enrolled_at ASC`);
|
|
45845
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.nodes WHERE status <> 'deleted' ORDER BY enrolled_at ASC`);
|
|
45441
45846
|
return r.rows.map(rowToNode);
|
|
45442
45847
|
}
|
|
45443
45848
|
async listRuntimes(nodeId) {
|
|
@@ -45446,24 +45851,24 @@ var PostgresNodeStore = class _PostgresNodeStore {
|
|
|
45446
45851
|
}
|
|
45447
45852
|
async deleteNode(id) {
|
|
45448
45853
|
await this.pool.query(`DELETE FROM ${this.s}.nodes WHERE id=$1`, [id]);
|
|
45449
|
-
|
|
45854
|
+
}
|
|
45855
|
+
/** Soft-delete: mark node as deleted (excluded from normal API, kept for audit) and drop its runtime rows. */
|
|
45856
|
+
async softDeleteNode(id) {
|
|
45857
|
+
await this.pool.query(`UPDATE ${this.s}.nodes SET status='deleted',last_seen_at=$2 WHERE id=$1`, [id, (/* @__PURE__ */ new Date()).toISOString()]);
|
|
45858
|
+
await this.pool.query(`DELETE FROM ${this.s}.node_runtimes WHERE node_id=$1`, [id]);
|
|
45450
45859
|
}
|
|
45451
45860
|
async deleteRuntime(id) {
|
|
45452
|
-
await this.pool.query(`INSERT INTO ${this.s}.deleted_runtime_ids (id) VALUES ($1) ON CONFLICT DO NOTHING`, [id]);
|
|
45453
45861
|
const existing = await this.pool.query(`SELECT node_id FROM ${this.s}.node_runtimes WHERE id=$1`, [id]);
|
|
45454
45862
|
await this.pool.query(`DELETE FROM ${this.s}.node_runtimes WHERE id=$1`, [id]);
|
|
45455
45863
|
if (!existing.rows[0]) return { nodeDeleted: false };
|
|
45456
45864
|
const nodeId = existing.rows[0].node_id;
|
|
45457
45865
|
const remaining = await this.pool.query(`SELECT 1 FROM ${this.s}.node_runtimes WHERE node_id=$1 LIMIT 1`, [nodeId]);
|
|
45458
45866
|
if (!remaining.rowCount) {
|
|
45459
|
-
await this.
|
|
45867
|
+
await this.softDeleteNode(nodeId);
|
|
45460
45868
|
return { nodeDeleted: true };
|
|
45461
45869
|
}
|
|
45462
45870
|
return { nodeDeleted: false };
|
|
45463
45871
|
}
|
|
45464
|
-
async clearDeletedRuntimesForNode(nodeId) {
|
|
45465
|
-
await this.pool.query(`DELETE FROM ${this.s}.deleted_runtime_ids WHERE id LIKE $1`, [`runtime:${nodeId}:%`]);
|
|
45466
|
-
}
|
|
45467
45872
|
};
|
|
45468
45873
|
var PostgresNodeTokenStore = class _PostgresNodeTokenStore {
|
|
45469
45874
|
constructor(pool, schema) {
|
|
@@ -45521,6 +45926,7 @@ var rowToRuntime = (row) => ({
|
|
|
45521
45926
|
// ../cli/src/serve.ts
|
|
45522
45927
|
init_src3();
|
|
45523
45928
|
init_trajectory();
|
|
45929
|
+
var DEFAULT_CHAT_SKILLS = ["build-workorder"];
|
|
45524
45930
|
var oasisWrapperCache;
|
|
45525
45931
|
function oasisWrapperScript() {
|
|
45526
45932
|
if (oasisWrapperCache && fs13.existsSync(oasisWrapperCache)) return oasisWrapperCache;
|
|
@@ -45634,6 +46040,7 @@ async function startServe(opts) {
|
|
|
45634
46040
|
if ((await typeStore.list()).length === 0) for (const d of schema) await typeStore.put(d);
|
|
45635
46041
|
{
|
|
45636
46042
|
const live = await typeStore.list();
|
|
46043
|
+
upgradeDefaultFieldDefs(live);
|
|
45637
46044
|
schema.length = 0;
|
|
45638
46045
|
schema.push(...live);
|
|
45639
46046
|
}
|
|
@@ -45800,6 +46207,7 @@ async function startServe(opts) {
|
|
|
45800
46207
|
return auth.sessionAccountId(headers);
|
|
45801
46208
|
};
|
|
45802
46209
|
const interventionDrafts = new InterventionDraftStore();
|
|
46210
|
+
const workorderDrafts = new WorkorderDraftStore();
|
|
45803
46211
|
const journalFile = path11.join(opts.dir, "dispatch-journal.ndjson");
|
|
45804
46212
|
const journalRing = [];
|
|
45805
46213
|
if (fs13.existsSync(journalFile)) {
|
|
@@ -45880,6 +46288,15 @@ async function startServe(opts) {
|
|
|
45880
46288
|
if (actorCtx?.config?.prompt) {
|
|
45881
46289
|
Object.assign(files, splitIdentityFiles3(actorCtx.config.prompt));
|
|
45882
46290
|
}
|
|
46291
|
+
Object.assign(files, await materializeSkillFiles({
|
|
46292
|
+
service: actors.service,
|
|
46293
|
+
blobs: assets,
|
|
46294
|
+
actorId: planner.id,
|
|
46295
|
+
runtimeKind: binding.runtimeKind
|
|
46296
|
+
}).catch((err) => {
|
|
46297
|
+
console.warn(`[planner] skill materialize failed for ${planner.id}: ${String(err)}`);
|
|
46298
|
+
return {};
|
|
46299
|
+
}));
|
|
45883
46300
|
const handle = await chatRemoteAdapter.spawn({
|
|
45884
46301
|
actor: planner.id,
|
|
45885
46302
|
actorToken: issuer.issue(planner.id),
|
|
@@ -45927,10 +46344,10 @@ async function startServe(opts) {
|
|
|
45927
46344
|
}),
|
|
45928
46345
|
trace.register,
|
|
45929
46346
|
automations.register,
|
|
45930
|
-
...chatSessionStore ? [createChatSessionsDomain({ store: chatSessionStore, registry: registryStore
|
|
46347
|
+
...chatSessionStore ? [createChatSessionsDomain({ store: chatSessionStore, registry: registryStore })] : []
|
|
45931
46348
|
],
|
|
45932
46349
|
drafts: interventionDrafts,
|
|
45933
|
-
|
|
46350
|
+
workorderDrafts,
|
|
45934
46351
|
// 暴露注册的 artifact 类型,给前端「发起工单」动态拉取根产物可选类型——schema.json 增改即自动扩展,
|
|
45935
46352
|
// 前端不再写死枚举。走通用 /api/view(generic data),非域路由契约,不进 gen:api。
|
|
45936
46353
|
views: {
|
|
@@ -45949,6 +46366,7 @@ async function startServe(opts) {
|
|
|
45949
46366
|
},
|
|
45950
46367
|
resolveActorEnv: async (actorId) => (await buildActorProvision(actors.service, actorId)).env,
|
|
45951
46368
|
resolveActorContext: (actorId) => buildActorContext(actors.service, actorId),
|
|
46369
|
+
materializeSkills: (actorId, runtimeKind) => materializeSkillFiles({ service: actors.service, blobs: assets, actorId, runtimeKind }),
|
|
45952
46370
|
dispatchProduce: async ({ artifactId, actorId, part }) => {
|
|
45953
46371
|
if (!dispatcher) {
|
|
45954
46372
|
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 +46377,7 @@ async function startServe(opts) {
|
|
|
45959
46377
|
...part !== void 0 ? { part } : {}
|
|
45960
46378
|
});
|
|
45961
46379
|
},
|
|
45962
|
-
dispatchChat: async ({ actorId, message, sessionId
|
|
46380
|
+
dispatchChat: async ({ actorId, message, sessionId }) => {
|
|
45963
46381
|
const binding = await actors.service.resolveBinding()(actorId);
|
|
45964
46382
|
if (!binding) {
|
|
45965
46383
|
throw new ApiError(409, "ACTOR_BINDING_MISSING", `\u6570\u5B57\u5458\u5DE5 ${actorId} \u6CA1\u6709 active runtime \u7ED1\u5B9A`);
|
|
@@ -46049,6 +46467,16 @@ async function startServe(opts) {
|
|
|
46049
46467
|
if (actorCtx?.config?.prompt) {
|
|
46050
46468
|
Object.assign(files, splitIdentityFiles3(actorCtx.config.prompt));
|
|
46051
46469
|
}
|
|
46470
|
+
Object.assign(files, await materializeSkillFiles({
|
|
46471
|
+
service: actors.service,
|
|
46472
|
+
blobs: assets,
|
|
46473
|
+
actorId,
|
|
46474
|
+
runtimeKind: binding.runtimeKind,
|
|
46475
|
+
extraSkillIds: DEFAULT_CHAT_SKILLS
|
|
46476
|
+
}).catch((err) => {
|
|
46477
|
+
console.warn(`[chat] skill materialize failed for ${actorId}: ${String(err)}`);
|
|
46478
|
+
return {};
|
|
46479
|
+
}));
|
|
46052
46480
|
const handle = await chatRemoteAdapter.spawn({
|
|
46053
46481
|
actor: actorId,
|
|
46054
46482
|
actorToken: issuer.issue(actorId),
|
|
@@ -46060,7 +46488,7 @@ async function startServe(opts) {
|
|
|
46060
46488
|
server: { url: localUrl },
|
|
46061
46489
|
binding,
|
|
46062
46490
|
limits: { wallClockMs: 10 * 6e4 },
|
|
46063
|
-
env:
|
|
46491
|
+
env: provision.env,
|
|
46064
46492
|
wrapperPaths: provision.wrapperPaths
|
|
46065
46493
|
});
|
|
46066
46494
|
const buffered = [];
|
|
@@ -46183,11 +46611,12 @@ async function startServe(opts) {
|
|
|
46183
46611
|
resolveBinding: actors.service.resolveBinding(),
|
|
46184
46612
|
provision: (actorId) => buildActorProvision(actors.service, actorId),
|
|
46185
46613
|
resolveActorContext: (actorId) => buildActorContext(actors.service, actorId),
|
|
46614
|
+
materializeSkills: (actorId, runtimeKind) => materializeSkillFiles({ service: actors.service, blobs: assets, actorId, runtimeKind }),
|
|
46186
46615
|
schema: new Map(schema.map((d2) => [d2.name, d2])),
|
|
46187
46616
|
resolveCodeRepo,
|
|
46188
46617
|
// §4/C:异步从权威源(store)解析;dispatcher 装配前 await
|
|
46189
|
-
// #2 派单开关:工单"待派发"
|
|
46190
|
-
resolveDispatchHold: (ws) =>
|
|
46618
|
+
// #2 派单开关:工单"待派发"(控制面旁存标 held)时不派其活。held 集通常空、表极小,每次查可接受。
|
|
46619
|
+
resolveDispatchHold: async (ws) => (await artifactStateStore.listWorkspaceDispatchHeld()).includes(ws),
|
|
46191
46620
|
tokenFor: (actor) => issuer.issue(actor),
|
|
46192
46621
|
journal,
|
|
46193
46622
|
// TrajectoryEvent 是唯一事件模型;trace 旁账与 M7 蒸馏库并行消费同一流(§16.D 收口)。
|
|
@@ -46343,6 +46772,8 @@ async function startServe(opts) {
|
|
|
46343
46772
|
// 阻塞诊断(proposal D1/D3):派发器运行时信号(queued/fused/backoff/hasInFlight)注入诊断,
|
|
46344
46773
|
// 让协调者把"资源 / 运行时"病因和"图病"分开——资源排队的节点不会被误唤去砍流程。
|
|
46345
46774
|
runtimeSignals: (id) => dispatcher?.runtimeSignalsFor(id) ?? { hasInFlight: false },
|
|
46775
|
+
// 病历里展示节点的"在跑会话情况"(runId + 跑了多久):从派发器在途会话按 artifactId 过滤。
|
|
46776
|
+
sessionsOf: (id) => (dispatcher?.inFlightSessions() ?? []).filter((s2) => s2.artifactId === id).map((s2) => ({ runId: s2.sessionId, action: s2.action, startedAt: s2.startedAt })),
|
|
46346
46777
|
log: (m2) => console.log(m2)
|
|
46347
46778
|
});
|
|
46348
46779
|
coordTimer = setInterval(() => {
|
|
@@ -46473,6 +46904,7 @@ var DaemonWsClient = class {
|
|
|
46473
46904
|
ws.on("open", () => {
|
|
46474
46905
|
log2("[node-cli]", `connected id=${this.daemonId}`);
|
|
46475
46906
|
this.send({ type: "hello", daemonId: this.daemonId, meta: this.buildMeta() });
|
|
46907
|
+
this.reportRuntimes = false;
|
|
46476
46908
|
this.pingTimer = setInterval(() => this.send({ type: "pong" }), PING_INTERVAL_MS);
|
|
46477
46909
|
});
|
|
46478
46910
|
ws.on("message", (raw) => {
|
|
@@ -46636,7 +47068,7 @@ async function startNode(opts) {
|
|
|
46636
47068
|
claude
|
|
46637
47069
|
)
|
|
46638
47070
|
);
|
|
46639
|
-
const client = new DaemonWsClient(opts.serverUrl, opts.nodeId, sessions, adapters, opts.token, runtimes, opts.nodeName, opts.reportRuntimes ??
|
|
47071
|
+
const client = new DaemonWsClient(opts.serverUrl, opts.nodeId, sessions, adapters, opts.token, runtimes, opts.nodeName, opts.reportRuntimes ?? false);
|
|
46640
47072
|
console.log(`[oasis node] ${opts.nodeId} \u2192 ${opts.serverUrl}`);
|
|
46641
47073
|
client.start();
|
|
46642
47074
|
await new Promise((resolve3) => {
|
|
@@ -46672,8 +47104,9 @@ var USAGE = `oasis \u2014\u2014 artifact-centric \u534F\u4F5C\u5185\u6838 CLI\uF
|
|
|
46672
47104
|
token \u89E3\u6790\u987A\u5E8F: --token > $OASIS_TOKEN > <dir>/operator-token
|
|
46673
47105
|
|
|
46674
47106
|
\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>]
|
|
47107
|
+
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
47108
|
# --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
|
|
47109
|
+
# --<\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
47110
|
propose <artifactId> (--file <path> | --from-dir <path> | --git-commit <sha>) --reason <text> [--base <revisionId|none>] [--part <name>] [--rebase-of <revisionId>]
|
|
46678
47111
|
# --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
47112
|
# --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 +47138,12 @@ var USAGE = `oasis \u2014\u2014 artifact-centric \u534F\u4F5C\u5185\u6838 CLI\uF
|
|
|
46705
47138
|
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
47139
|
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
47140
|
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
|
|
47141
|
+
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
47142
|
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\
|
|
47143
|
+
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
|
|
47144
|
+
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
|
|
47145
|
+
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
|
|
47146
|
+
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
47147
|
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
47148
|
|
|
46712
47149
|
\u6CBB\u7406\uFF08\xA711\uFF09
|
|
@@ -46767,10 +47204,7 @@ function projectSlug(value) {
|
|
|
46767
47204
|
return value.trim().toLowerCase().replace(/[^\w\u4e00-\u9fa5\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").slice(0, 32);
|
|
46768
47205
|
}
|
|
46769
47206
|
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;
|
|
47207
|
+
const headers = { authorization: `Bearer ${token}`, "content-type": "application/json" };
|
|
46774
47208
|
const fail = async (res) => {
|
|
46775
47209
|
const body = await res.json().catch(() => ({}));
|
|
46776
47210
|
const err = body.error;
|
|
@@ -46823,25 +47257,50 @@ async function runCli(argv, println = console.log) {
|
|
|
46823
47257
|
const serverUrl = flags.get("server-ws") ?? process.env["OASIS_SERVER_WS"] ?? "ws://127.0.0.1:7320/node-gateway";
|
|
46824
47258
|
const nodeName = flags.get("name") ?? process.env["OASIS_NODE_NAME"];
|
|
46825
47259
|
const nodeIdFile = path12.join(dir, "node-id");
|
|
46826
|
-
const
|
|
46827
|
-
const
|
|
46828
|
-
|
|
47260
|
+
const nodeTokenFile = path12.join(dir, "node-token");
|
|
47261
|
+
const enrollTokenFile = path12.join(dir, "enroll-token");
|
|
47262
|
+
const reportMarkerFile = path12.join(dir, "report-runtimes");
|
|
47263
|
+
let nodeId = fs14.existsSync(nodeIdFile) ? fs14.readFileSync(nodeIdFile, "utf8").trim() : null;
|
|
47264
|
+
let token2 = flags.get("token") ?? process.env["OASIS_NODE_TOKEN"] ?? (fs14.existsSync(nodeTokenFile) ? fs14.readFileSync(nodeTokenFile, "utf8").trim() : void 0);
|
|
47265
|
+
const enrollToken = flags.get("enroll-token") ?? process.env["OASIS_ENROLL_TOKEN"] ?? (fs14.existsSync(enrollTokenFile) ? fs14.readFileSync(enrollTokenFile, "utf8").trim() : void 0);
|
|
47266
|
+
let freshEnrollment = false;
|
|
47267
|
+
if ((!nodeId || !token2) && enrollToken) {
|
|
47268
|
+
const httpBase2 = serverUrl.replace(/^ws(s?):\/\//, "http$1://").replace(/\/node-gateway.*$/, "");
|
|
47269
|
+
const res = await fetch(`${httpBase2}/api/nodes/exchange`, {
|
|
47270
|
+
method: "POST",
|
|
47271
|
+
headers: { "content-type": "application/json" },
|
|
47272
|
+
body: JSON.stringify({ enrollToken, ...nodeId ? { nodeId } : {}, hostname: os7.hostname() })
|
|
47273
|
+
});
|
|
47274
|
+
if (!res.ok) throw new Error(`enroll token exchange failed: HTTP ${res.status}`);
|
|
47275
|
+
const body = await res.json();
|
|
47276
|
+
nodeId = body.nodeId;
|
|
47277
|
+
token2 = body.token;
|
|
46829
47278
|
fs14.mkdirSync(dir, { recursive: true });
|
|
46830
47279
|
fs14.writeFileSync(nodeIdFile, nodeId);
|
|
47280
|
+
fs14.writeFileSync(nodeTokenFile, token2, { mode: 384 });
|
|
47281
|
+
try {
|
|
47282
|
+
fs14.unlinkSync(enrollTokenFile);
|
|
47283
|
+
} catch {
|
|
47284
|
+
}
|
|
47285
|
+
freshEnrollment = true;
|
|
46831
47286
|
}
|
|
46832
|
-
|
|
46833
|
-
const
|
|
47287
|
+
if (!nodeId) throw new Error("no nodeId \u2014 provide --enroll-token <ent_...> on first run");
|
|
47288
|
+
const scriptRun = freshEnrollment || fs14.existsSync(reportMarkerFile);
|
|
46834
47289
|
if (flags.has("service")) {
|
|
46835
47290
|
const { spawnSync, execSync: execSync2 } = await import("node:child_process");
|
|
46836
47291
|
if (token2) {
|
|
46837
47292
|
fs14.mkdirSync(dir, { recursive: true });
|
|
46838
47293
|
fs14.writeFileSync(nodeTokenFile, token2, { mode: 384 });
|
|
46839
47294
|
}
|
|
47295
|
+
if (scriptRun) {
|
|
47296
|
+
fs14.mkdirSync(dir, { recursive: true });
|
|
47297
|
+
fs14.writeFileSync(reportMarkerFile, "");
|
|
47298
|
+
}
|
|
46840
47299
|
println("\u2192 Installing oasis_test globally...");
|
|
46841
47300
|
spawnSync("npm", ["install", "-g", "oasis_test@latest"], { stdio: "inherit" });
|
|
46842
47301
|
const prefix = execSync2("npm prefix -g").toString().trim();
|
|
46843
47302
|
const binPath = path12.join(prefix, "bin", "oasis_test");
|
|
46844
|
-
const svcArgs = `node --server-ws ${serverUrl} --
|
|
47303
|
+
const svcArgs = `node --server-ws ${serverUrl} --dir ${dir}${nodeName ? ` --name ${nodeName}` : ""}`;
|
|
46845
47304
|
if (process.platform === "linux") {
|
|
46846
47305
|
const svcFile = path12.join(os7.homedir(), ".config/systemd/user/oasis-node.service");
|
|
46847
47306
|
fs14.mkdirSync(path12.dirname(svcFile), { recursive: true });
|
|
@@ -46874,7 +47333,7 @@ async function runCli(argv, println = console.log) {
|
|
|
46874
47333
|
const plist = path12.join(os7.homedir(), "Library/LaunchAgents/com.oasis.node.plist");
|
|
46875
47334
|
fs14.mkdirSync(path12.dirname(plist), { recursive: true });
|
|
46876
47335
|
spawnSync("launchctl", ["unload", plist], { stdio: "ignore" });
|
|
46877
|
-
const argXml = [binPath, "node", "--server-ws", serverUrl, "--
|
|
47336
|
+
const argXml = [binPath, "node", "--server-ws", serverUrl, "--dir", dir, ...nodeName ? ["--name", nodeName] : []].map((a) => `<string>${a}</string>`).join("");
|
|
46878
47337
|
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
47338
|
spawnSync("launchctl", ["load", plist], { stdio: "inherit" });
|
|
46880
47339
|
println("\u2713 Oasis node service started (macOS LaunchAgent com.oasis.node)");
|
|
@@ -46887,7 +47346,13 @@ async function runCli(argv, println = console.log) {
|
|
|
46887
47346
|
}
|
|
46888
47347
|
return;
|
|
46889
47348
|
}
|
|
46890
|
-
|
|
47349
|
+
if (fs14.existsSync(reportMarkerFile)) {
|
|
47350
|
+
try {
|
|
47351
|
+
fs14.unlinkSync(reportMarkerFile);
|
|
47352
|
+
} catch {
|
|
47353
|
+
}
|
|
47354
|
+
}
|
|
47355
|
+
await startNode({ serverUrl, nodeId, ...token2 ? { token: token2 } : {}, ...nodeName ? { nodeName } : {}, reportRuntimes: scriptRun });
|
|
46891
47356
|
return;
|
|
46892
47357
|
}
|
|
46893
47358
|
if (command === "node-token") {
|
|
@@ -47029,6 +47494,30 @@ async function runCli(argv, println = console.log) {
|
|
|
47029
47494
|
return out;
|
|
47030
47495
|
};
|
|
47031
47496
|
const STAGE_COMMANDS = /* @__PURE__ */ new Set(["link", "spawn", "unlink", "cancel", "freeze", "cancel-part", "edit"]);
|
|
47497
|
+
const GLOBAL_NON_FIELD_FLAGS = /* @__PURE__ */ new Set(["server", "token", "dir", "set-json", "clear"]);
|
|
47498
|
+
const SPAWN_OWN_FLAGS = /* @__PURE__ */ new Set(["type", "owner", "workspace", "persistence", "title", "brief", "description", "input", "parts-file"]);
|
|
47499
|
+
const EDIT_OWN_FLAGS = /* @__PURE__ */ new Set(["title", "brief"]);
|
|
47500
|
+
const fieldsFromFlags = (ownFlags) => {
|
|
47501
|
+
const fields = {};
|
|
47502
|
+
for (const [k2, v2] of flags) {
|
|
47503
|
+
if (ownFlags.has(k2) || GLOBAL_NON_FIELD_FLAGS.has(k2)) continue;
|
|
47504
|
+
fields[k2] = v2;
|
|
47505
|
+
}
|
|
47506
|
+
const sj = flags.get("set-json");
|
|
47507
|
+
if (sj !== void 0) {
|
|
47508
|
+
const parsed = JSON.parse(sj);
|
|
47509
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
47510
|
+
throw new Error(`--set-json \u9700\u8981\u4E00\u4E2A JSON \u5BF9\u8C61\uFF0C\u5982 '{"goal":"..."}'`);
|
|
47511
|
+
}
|
|
47512
|
+
Object.assign(fields, parsed);
|
|
47513
|
+
}
|
|
47514
|
+
const clear = flags.get("clear");
|
|
47515
|
+
if (clear !== void 0) for (const k2 of clear.split(",")) {
|
|
47516
|
+
const t = k2.trim();
|
|
47517
|
+
if (t) fields[t] = null;
|
|
47518
|
+
}
|
|
47519
|
+
return Object.keys(fields).length > 0 ? fields : void 0;
|
|
47520
|
+
};
|
|
47032
47521
|
const buildStageOp = (cmd) => {
|
|
47033
47522
|
switch (cmd) {
|
|
47034
47523
|
case "link":
|
|
@@ -47038,15 +47527,18 @@ async function runCli(argv, println = console.log) {
|
|
|
47038
47527
|
to: need(flags, "to"),
|
|
47039
47528
|
...flags.get("required") !== void 0 ? { required: flags.get("required") === "true" } : {}
|
|
47040
47529
|
};
|
|
47041
|
-
case "spawn":
|
|
47530
|
+
case "spawn": {
|
|
47531
|
+
const fields = fieldsFromFlags(SPAWN_OWN_FLAGS);
|
|
47042
47532
|
return {
|
|
47043
47533
|
action: "spawn",
|
|
47044
47534
|
id: needPos(positional, 0, "oasis spawn <id> --type <t>"),
|
|
47045
47535
|
type: need(flags, "type"),
|
|
47046
47536
|
...flags.get("title") !== void 0 ? { title: flags.get("title") } : {},
|
|
47047
47537
|
...(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() })) } : {}
|
|
47538
|
+
...flags.get("input") !== void 0 ? { inputs: flags.get("input").split(",").map((to) => ({ to: to.trim() })) } : {},
|
|
47539
|
+
...fields !== void 0 ? { fields } : {}
|
|
47049
47540
|
};
|
|
47541
|
+
}
|
|
47050
47542
|
case "unlink":
|
|
47051
47543
|
return { action: "unlink", artifactId: needPos(positional, 0, "oasis unlink <consumer> --to <upstream>"), to: need(flags, "to") };
|
|
47052
47544
|
case "cancel":
|
|
@@ -47059,13 +47551,16 @@ async function runCli(argv, println = console.log) {
|
|
|
47059
47551
|
};
|
|
47060
47552
|
case "cancel-part":
|
|
47061
47553
|
return { action: "cancelPart", artifactId: needPos(positional, 0, "oasis cancel-part <id> --part <name>"), part: need(flags, "part") };
|
|
47062
|
-
case "edit":
|
|
47554
|
+
case "edit": {
|
|
47555
|
+
const fields = fieldsFromFlags(EDIT_OWN_FLAGS);
|
|
47063
47556
|
return {
|
|
47064
47557
|
action: "edit",
|
|
47065
|
-
artifactId: needPos(positional, 0, "oasis edit <id> [--title \u2026] [--brief \u2026]"),
|
|
47558
|
+
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
47559
|
...flags.get("title") !== void 0 ? { title: flags.get("title") } : {},
|
|
47067
|
-
...flags.get("brief") !== void 0 ? { description: flags.get("brief") } : {}
|
|
47560
|
+
...flags.get("brief") !== void 0 ? { description: flags.get("brief") } : {},
|
|
47561
|
+
...fields !== void 0 ? { fields } : {}
|
|
47068
47562
|
};
|
|
47563
|
+
}
|
|
47069
47564
|
default:
|
|
47070
47565
|
throw new Error(`\u672A\u77E5\u6682\u5B58\u547D\u4EE4: ${cmd}`);
|
|
47071
47566
|
}
|
|
@@ -47099,43 +47594,28 @@ async function runCli(argv, println = console.log) {
|
|
|
47099
47594
|
return;
|
|
47100
47595
|
}
|
|
47101
47596
|
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;
|
|
47597
|
+
case "create-workorder": {
|
|
47598
|
+
const planRaw = flags.get("plan-file") !== void 0 ? fs14.readFileSync(flags.get("plan-file"), "utf8") : flags.get("plan");
|
|
47599
|
+
const acceptanceFlag = flags.get("acceptance");
|
|
47600
|
+
const args = {
|
|
47601
|
+
createdVia: "seed",
|
|
47602
|
+
stage: true,
|
|
47603
|
+
type: need(flags, "type"),
|
|
47604
|
+
workspace: need(flags, "workspace"),
|
|
47605
|
+
title: need(flags, "title"),
|
|
47606
|
+
...flags.get("brief") !== void 0 ? { brief: flags.get("brief") } : {},
|
|
47607
|
+
...flags.get("goal") !== void 0 ? { goal: flags.get("goal") } : {},
|
|
47608
|
+
...acceptanceFlag !== void 0 ? { acceptance_criteria: parseStringArrayFlag(acceptanceFlag, "acceptance") } : {},
|
|
47609
|
+
...planRaw !== void 0 ? { plan: parseJsonFlag(planRaw, "plan") } : {},
|
|
47610
|
+
...flags.get("project") !== void 0 ? { project_id: flags.get("project") } : {},
|
|
47611
|
+
...flags.get("dispatch") === "false" ? { dispatch: false } : {}
|
|
47612
|
+
};
|
|
47613
|
+
const res = await api.cmd("spawn", args);
|
|
47614
|
+
println(res.message);
|
|
47615
|
+
const data = res.data;
|
|
47616
|
+
if (data?.draftId) {
|
|
47617
|
+
println(`draftId=${data.draftId} workspace=${data.workspace} \u8282\u70B9=${data.spawned?.length ?? 0} \u89C4\u5212=${data.planning?.status ?? "?"}`);
|
|
47132
47618
|
}
|
|
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
47619
|
break;
|
|
47140
47620
|
}
|
|
47141
47621
|
case "projects": {
|
|
@@ -47326,6 +47806,7 @@ async function runCli(argv, println = console.log) {
|
|
|
47326
47806
|
}
|
|
47327
47807
|
case "spawn": {
|
|
47328
47808
|
const partsFile = flags.get("parts-file");
|
|
47809
|
+
const spawnFields = fieldsFromFlags(SPAWN_OWN_FLAGS);
|
|
47329
47810
|
const { message } = await api.cmd("spawn", {
|
|
47330
47811
|
id: positional[0],
|
|
47331
47812
|
type: need(flags, "type"),
|
|
@@ -47335,7 +47816,8 @@ async function runCli(argv, println = console.log) {
|
|
|
47335
47816
|
title: flags.get("title"),
|
|
47336
47817
|
description: flags.get("brief") ?? flags.get("description"),
|
|
47337
47818
|
inputs: flags.get("input")?.split(",").map((to) => ({ to: to.trim() })),
|
|
47338
|
-
...partsFile !== void 0 ? { parts: JSON.parse(readFileArg(partsFile)) } : {}
|
|
47819
|
+
...partsFile !== void 0 ? { parts: JSON.parse(readFileArg(partsFile)) } : {},
|
|
47820
|
+
...spawnFields !== void 0 ? { fields: spawnFields } : {}
|
|
47339
47821
|
});
|
|
47340
47822
|
println(message);
|
|
47341
47823
|
break;
|
|
@@ -47787,6 +48269,52 @@ async function runCli(argv, println = console.log) {
|
|
|
47787
48269
|
println(`\u5DF2\u5220\u9664 ${name}`);
|
|
47788
48270
|
break;
|
|
47789
48271
|
}
|
|
48272
|
+
if (sub === "add-field") {
|
|
48273
|
+
const name = needPos(positional, 1, "oasis artifact-types add-field <type> <\u5B57\u6BB5> --type <string|string[]|number|boolean> [--required true] [--description \u2026]");
|
|
48274
|
+
const field = needPos(positional, 2, "oasis artifact-types add-field <type> <\u5B57\u6BB5> --type <t> [--required true] [--description \u2026]");
|
|
48275
|
+
const spec = {
|
|
48276
|
+
name: field,
|
|
48277
|
+
type: need(flags, "type"),
|
|
48278
|
+
...flags.get("required") === "true" ? { required: true } : {},
|
|
48279
|
+
...flags.get("description") !== void 0 ? { description: flags.get("description") } : {}
|
|
48280
|
+
};
|
|
48281
|
+
const r = await api.request("POST", `/api/artifact-types/${encodeURIComponent(name)}/fields`, spec);
|
|
48282
|
+
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` : ""}`);
|
|
48283
|
+
break;
|
|
48284
|
+
}
|
|
48285
|
+
if (sub === "edit-field") {
|
|
48286
|
+
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");
|
|
48287
|
+
const field = needPos(positional, 2, "oasis artifact-types edit-field <type> <\u5B57\u6BB5> [--type \u2026] [--required \u2026] [--description \u2026]");
|
|
48288
|
+
const attrs = {
|
|
48289
|
+
...flags.get("type") !== void 0 ? { type: flags.get("type") } : {},
|
|
48290
|
+
...flags.get("required") !== void 0 ? { required: flags.get("required") === "true" } : {},
|
|
48291
|
+
...flags.get("description") !== void 0 ? { description: flags.get("description") } : {}
|
|
48292
|
+
};
|
|
48293
|
+
const r = await api.request("PATCH", `/api/artifact-types/${encodeURIComponent(name)}/fields/${encodeURIComponent(field)}`, attrs);
|
|
48294
|
+
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` : ""}`);
|
|
48295
|
+
break;
|
|
48296
|
+
}
|
|
48297
|
+
if (sub === "drop-field") {
|
|
48298
|
+
const name = needPos(positional, 1, "oasis artifact-types drop-field <type> <\u5B57\u6BB5>");
|
|
48299
|
+
const field = needPos(positional, 2, "oasis artifact-types drop-field <type> <\u5B57\u6BB5>");
|
|
48300
|
+
const r = await api.request("DELETE", `/api/artifact-types/${encodeURIComponent(name)}/fields/${encodeURIComponent(field)}`, void 0);
|
|
48301
|
+
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` : ""}`);
|
|
48302
|
+
break;
|
|
48303
|
+
}
|
|
48304
|
+
if (sub === "show") {
|
|
48305
|
+
const name = needPos(positional, 1, "oasis artifact-types show <type>");
|
|
48306
|
+
const { items } = await api.request("GET", "/api/artifact-types");
|
|
48307
|
+
const t = items.find((x2) => x2.name === name);
|
|
48308
|
+
if (!t) {
|
|
48309
|
+
println(`\uFF08\u65E0\u6B64\u7C7B\u578B: ${name}\uFF09`);
|
|
48310
|
+
break;
|
|
48311
|
+
}
|
|
48312
|
+
println(`${t.name}${t.status === "deprecated" ? " [\u5DF2\u5F03\u7528]" : ""} (${t.contentType}, owner=${t.ownerRole})${t.description ? ` \u2014\u2014 ${t.description}` : ""}`);
|
|
48313
|
+
const fds = t.fieldDefs ?? [];
|
|
48314
|
+
println(fds.length === 0 ? " \u81EA\u5B9A\u4E49\u5B57\u6BB5\uFF1A\u65E0" : " \u81EA\u5B9A\u4E49\u5B57\u6BB5\uFF1A");
|
|
48315
|
+
for (const f2 of fds) println(` ${f2.name} ${f2.type} ${f2.required ? "\u5FC5\u586B" : "\u53EF\u9009"}${f2.description ? ` ${f2.description}` : ""}`);
|
|
48316
|
+
break;
|
|
48317
|
+
}
|
|
47790
48318
|
const all = await api.view(
|
|
47791
48319
|
"artifact-types"
|
|
47792
48320
|
);
|
|
@@ -47878,7 +48406,7 @@ var isRunning = (pid) => {
|
|
|
47878
48406
|
return false;
|
|
47879
48407
|
}
|
|
47880
48408
|
};
|
|
47881
|
-
var httpBase = (wsUrl) => wsUrl.replace(/^
|
|
48409
|
+
var httpBase = (wsUrl) => wsUrl.replace(/^ws(s?):\/\//, "http$1://").replace(/\/node-gateway.*$/, "");
|
|
47882
48410
|
function installBinary() {
|
|
47883
48411
|
fs15.mkdirSync(path13.dirname(BIN_FILE), { recursive: true });
|
|
47884
48412
|
fs15.copyFileSync(process.argv[1], BIN_FILE);
|
|
@@ -48001,8 +48529,11 @@ void (async () => {
|
|
|
48001
48529
|
await startNode({
|
|
48002
48530
|
serverUrl: cfg.serverUrl,
|
|
48003
48531
|
token: cfg.token,
|
|
48004
|
-
nodeId: cfg.nodeId ??
|
|
48005
|
-
|
|
48532
|
+
nodeId: cfg.nodeId ?? (() => {
|
|
48533
|
+
console.error("[oasis] no nodeId in config \u2014 run `oasis start` first");
|
|
48534
|
+
process.exit(1);
|
|
48535
|
+
})(),
|
|
48536
|
+
nodeName: cfg.name ?? cfg.nodeId,
|
|
48006
48537
|
reportRuntimes
|
|
48007
48538
|
});
|
|
48008
48539
|
return;
|
|
@@ -48011,32 +48542,29 @@ void (async () => {
|
|
|
48011
48542
|
const prev = readConfig();
|
|
48012
48543
|
const serverUrl = flags.get("server-ws") ?? process.env["OASIS_SERVER_WS"] ?? prev?.serverUrl;
|
|
48013
48544
|
if (!serverUrl) {
|
|
48014
|
-
console.error("Usage: oasis start --server-ws <wss://...> (--enroll-token <ent_...> | --token <ont_...>) [--name <name>]
|
|
48545
|
+
console.error("Usage: oasis start --server-ws <wss://...> (--enroll-token <ent_...> | --token <ont_...>) [--name <name>]");
|
|
48015
48546
|
process.exit(1);
|
|
48016
48547
|
}
|
|
48017
|
-
const commandNodeId = flags.get("id");
|
|
48018
48548
|
const serverChanged = !!prev?.serverUrl && prev.serverUrl !== serverUrl;
|
|
48019
|
-
let nodeId = serverChanged ?
|
|
48549
|
+
let nodeId = serverChanged ? void 0 : prev?.nodeId;
|
|
48020
48550
|
let token = serverChanged ? flags.get("token") ?? process.env["OASIS_NODE_TOKEN"] : flags.get("token") ?? process.env["OASIS_NODE_TOKEN"] ?? prev?.token;
|
|
48021
48551
|
const enrollToken = flags.get("enroll-token") ?? process.env["OASIS_ENROLL_TOKEN"];
|
|
48022
|
-
if (
|
|
48023
|
-
|
|
48552
|
+
if (prev?.nodeId && prev?.token && !serverChanged) {
|
|
48553
|
+
nodeId = prev.nodeId;
|
|
48554
|
+
token = prev.token;
|
|
48555
|
+
} else if (enrollToken) {
|
|
48556
|
+
const res = await fetch(`${httpBase(serverUrl)}/api/nodes/exchange`, {
|
|
48024
48557
|
method: "POST",
|
|
48025
48558
|
headers: { "content-type": "application/json" },
|
|
48026
|
-
body: JSON.stringify({ enrollToken,
|
|
48559
|
+
body: JSON.stringify({ enrollToken, hostname: os8.hostname() })
|
|
48027
48560
|
});
|
|
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
48561
|
if (!res.ok) throw new Error(`enroll token exchange failed: HTTP ${res.status}`);
|
|
48034
48562
|
const body = await res.json();
|
|
48035
48563
|
token = body.token;
|
|
48036
|
-
nodeId = body.nodeId
|
|
48564
|
+
nodeId = body.nodeId;
|
|
48037
48565
|
}
|
|
48038
|
-
if (!token) {
|
|
48039
|
-
console.error("Usage: oasis start --server-ws <wss://...> (--enroll-token <ent_...> | --token <ont_...>) [--name <name>]
|
|
48566
|
+
if (!token || !nodeId) {
|
|
48567
|
+
console.error("Usage: oasis start --server-ws <wss://...> (--enroll-token <ent_...> | --token <ont_...>) [--name <name>]");
|
|
48040
48568
|
process.exit(1);
|
|
48041
48569
|
}
|
|
48042
48570
|
const name = flags.get("name") ?? prev?.name;
|