oasis_test 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +501 -164
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -955,13 +955,6 @@ var init_chat = __esm({
|
|
|
955
955
|
}
|
|
956
956
|
});
|
|
957
957
|
|
|
958
|
-
// ../contract/src/chat-session.ts
|
|
959
|
-
var init_chat_session = __esm({
|
|
960
|
-
"../contract/src/chat-session.ts"() {
|
|
961
|
-
"use strict";
|
|
962
|
-
}
|
|
963
|
-
});
|
|
964
|
-
|
|
965
958
|
// ../contract/src/http-api.ts
|
|
966
959
|
var init_http_api = __esm({
|
|
967
960
|
"../contract/src/http-api.ts"() {
|
|
@@ -995,7 +988,6 @@ var init_src = __esm({
|
|
|
995
988
|
init_telemetry();
|
|
996
989
|
init_trace();
|
|
997
990
|
init_chat();
|
|
998
|
-
init_chat_session();
|
|
999
991
|
init_http_api();
|
|
1000
992
|
}
|
|
1001
993
|
});
|
|
@@ -1785,6 +1777,11 @@ var init_kernel = __esm({
|
|
|
1785
1777
|
async seal(args) {
|
|
1786
1778
|
this.ensureActive(args.artifactId);
|
|
1787
1779
|
this.artifactOrThrow(args.artifactId);
|
|
1780
|
+
if (args.reason === "cancelled") {
|
|
1781
|
+
for (const edge of this.edgesTouching(args.artifactId)) {
|
|
1782
|
+
await this.unlinkInput({ artifactId: edge.artifactId, to: edge.to, actor: args.actor });
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1788
1785
|
const payload = {
|
|
1789
1786
|
reason: args.reason,
|
|
1790
1787
|
...args.note !== void 0 ? { note: args.note } : {}
|
|
@@ -1878,6 +1875,14 @@ var init_kernel = __esm({
|
|
|
1878
1875
|
for (const a of fork.artifacts.values()) {
|
|
1879
1876
|
if (lifecycleOf(fork, a.id) === "active" && !a.owner) violations.push(`active \u65E0 owner: ${a.id}`);
|
|
1880
1877
|
}
|
|
1878
|
+
for (const a of fork.artifacts.values()) {
|
|
1879
|
+
if (lifecycleOf(fork, a.id) !== "active") continue;
|
|
1880
|
+
for (const e of a.inputs) {
|
|
1881
|
+
if (lifecycleOf(fork, e.to) === "sealed:cancelled") {
|
|
1882
|
+
violations.push(`\u4F9D\u8D56\u5E9F\u5F03\u8282\u70B9: ${a.id} \u2192 ${e.to}\uFF08cancelled \u4E0D\u53EF\u88AB\u4F9D\u8D56\uFF1B\u6539\u6295\u6216\u64A4\u8FB9\uFF09`);
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1881
1886
|
for (const id of touched) {
|
|
1882
1887
|
if (fork.artifacts.get(id)?.createdVia === "prereq" && this.prereqChainDepth(fork, id) > MAX_PREREQ_DEPTH) {
|
|
1883
1888
|
violations.push(`\u524D\u7F6E\u94FE\u8D85\u4E0A\u9650 ${MAX_PREREQ_DEPTH}\uFF1A${id}\uFF08\u7591\u4F3C\u5931\u63A7\u9012\u5F52\uFF0C\u505C\u5728\u6B64\u3001\u5347\u4EBA\uFF09`);
|
|
@@ -1900,10 +1905,14 @@ var init_kernel = __esm({
|
|
|
1900
1905
|
if (edge?.required) advisory.push(`\u65AD\u5F00 required \u8FB9 ${op.artifactId} \u2192 ${op.to}\uFF08\u627F\u91CD\u53EF\u80FD\uFF1A\u6D88\u8D39\u65B9\u518D\u6D3E\u751F\u5C06\u5931\u53BB\u6B64\u8F93\u5165\uFF09`);
|
|
1901
1906
|
}
|
|
1902
1907
|
if (op.action === "seal" && op.reason === "cancelled") {
|
|
1903
|
-
const
|
|
1904
|
-
|
|
1908
|
+
const edges = this.edgesTouching(op.artifactId).length;
|
|
1909
|
+
const consumers = consumersOf(this.model, op.artifactId).length;
|
|
1910
|
+
if (edges > 0)
|
|
1911
|
+
advisory.push(
|
|
1912
|
+
`cancel ${op.artifactId}\uFF1A\u81EA\u52A8\u65AD\u5F00 ${edges} \u6761\u76F8\u5173\u8FB9\uFF08\u53CC\u5411\uFF09` + (consumers > 0 ? `\uFF1B${consumers} \u4E2A\u6D88\u8D39\u8005\u5C06\u88AB\u91CD\u5524\u53BB\u6389\u5BF9\u5B83\u7684\u9002\u914D\u2014\u2014\u6309\u9700\u6539\u6295(link\u2192\u66FF\u4EE3)/\u7EA7\u8054 cancel` : "")
|
|
1913
|
+
);
|
|
1905
1914
|
}
|
|
1906
|
-
if (op.action === "seal" && !isConcluded(this.model, op.artifactId)) {
|
|
1915
|
+
if (op.action === "seal" && op.reason !== "cancelled" && !isConcluded(this.model, op.artifactId)) {
|
|
1907
1916
|
const strandedConsumers = consumersOf(this.model, op.artifactId).filter(
|
|
1908
1917
|
(c) => c.inputs.some((e) => e.to === op.artifactId && e.required)
|
|
1909
1918
|
);
|
|
@@ -1930,37 +1939,6 @@ var init_kernel = __esm({
|
|
|
1930
1939
|
advisory.push(`\u8282\u70B9\u4FE1\u606F\u4E3A\u7A7A\uFF1A${id} \u6CA1\u6709 title/description\u2014\u2014\u5EFA\u4E86\u4E2A\u6CA1\u8BF4\u660E\u7684\u8282\u70B9\uFF1F\uFF08\u4ECD\u53EF\u63D0\u4EA4\uFF0C\u8BF7\u786E\u8BA4\uFF09`);
|
|
1931
1940
|
}
|
|
1932
1941
|
}
|
|
1933
|
-
const touchedWorkspaces = new Set(
|
|
1934
|
-
touched.map((id) => fork.artifacts.get(id)?.workspace).filter((w) => !!w)
|
|
1935
|
-
);
|
|
1936
|
-
for (const ws of touchedWorkspaces) {
|
|
1937
|
-
const members = [...fork.artifacts.values()].filter((a) => a.workspace === ws);
|
|
1938
|
-
if (members.length < 2) continue;
|
|
1939
|
-
const root = members.find((a) => a.type === "brief") ?? members.find((a) => a.inputs.length === 0);
|
|
1940
|
-
if (!root) continue;
|
|
1941
|
-
const seen = /* @__PURE__ */ new Set([root.id]);
|
|
1942
|
-
const stack = [root.id];
|
|
1943
|
-
while (stack.length > 0) {
|
|
1944
|
-
const cur = stack.pop();
|
|
1945
|
-
const neighbors = [
|
|
1946
|
-
...fork.artifacts.get(cur)?.inputs.map((e) => e.to) ?? [],
|
|
1947
|
-
// 上游
|
|
1948
|
-
...consumersOf(fork, cur).map((c) => c.id)
|
|
1949
|
-
// 下游
|
|
1950
|
-
];
|
|
1951
|
-
for (const n of neighbors) {
|
|
1952
|
-
if (fork.artifacts.has(n) && !seen.has(n)) {
|
|
1953
|
-
seen.add(n);
|
|
1954
|
-
stack.push(n);
|
|
1955
|
-
}
|
|
1956
|
-
}
|
|
1957
|
-
}
|
|
1958
|
-
for (const a of members) {
|
|
1959
|
-
if (lifecycleOf(fork, a.id) === "active" && !seen.has(a.id)) {
|
|
1960
|
-
advisory.push(`\u5B64\u5C9B\u8282\u70B9\uFF1A${a.id} \u4E0E\u6839 brief \u4E0D\u8FDE\u901A\u2014\u2014\u6F0F\u8FDE\u4F9D\u8D56\u8FB9\uFF1F\uFF08\u4ECD\u53EF\u63D0\u4EA4\uFF0C\u8BF7\u786E\u8BA4\u4E0D\u662F\u8BEF\u5EFA\uFF09`);
|
|
1961
|
-
}
|
|
1962
|
-
}
|
|
1963
|
-
}
|
|
1964
1942
|
const closure = /* @__PURE__ */ new Set();
|
|
1965
1943
|
const expand = (id) => {
|
|
1966
1944
|
for (const c of consumersOf(this.model, id)) {
|
|
@@ -2032,8 +2010,42 @@ var init_kernel = __esm({
|
|
|
2032
2010
|
}
|
|
2033
2011
|
return { interventionId, applied: constituents.length };
|
|
2034
2012
|
}
|
|
2035
|
-
/**
|
|
2013
|
+
/** D2:cancel 一个节点要断的所有相关边(双向)——消费者→它(C.inputs∋id)、它→上游(id.inputs∋U)。 */
|
|
2014
|
+
edgesTouching(id) {
|
|
2015
|
+
const out = [];
|
|
2016
|
+
for (const c of consumersOf(this.model, id)) out.push({ artifactId: c.id, to: id });
|
|
2017
|
+
for (const e of this.model.artifacts.get(id)?.inputs ?? []) out.push({ artifactId: id, to: e.to });
|
|
2018
|
+
return out;
|
|
2019
|
+
}
|
|
2020
|
+
/** D2:cancel(id) 会级联断掉的相关边(双向)——供 stage 回显/preview 列明"断了哪些边"。 */
|
|
2021
|
+
cancelCascadeEdges(id) {
|
|
2022
|
+
return this.edgesTouching(id);
|
|
2023
|
+
}
|
|
2024
|
+
/**
|
|
2025
|
+
* 编译 InterventionOp → oplog 构件。D2(决策 0017):seal(cancelled) 级联展开——在该 seal **之前**
|
|
2026
|
+
* 插入"断掉与本节点相关的所有边(双向)"的 unlink(趁本节点还 active:X→U 的 unlink 需 X active),再 seal;
|
|
2027
|
+
* 去重 plan 里已显式 unlink 的边。accepted/frozen 不级联(输出保留、消费者继续依赖)。
|
|
2028
|
+
*/
|
|
2036
2029
|
buildConstituents(plan) {
|
|
2030
|
+
const already = new Set(
|
|
2031
|
+
plan.ops.filter((o) => o.action === "unlink").map((o) => `${o.artifactId} ${o.to}`)
|
|
2032
|
+
);
|
|
2033
|
+
const out = [];
|
|
2034
|
+
for (const c of this.buildConstituentsRaw(plan)) {
|
|
2035
|
+
if (c.kind === "seal" && c.payload.reason === "cancelled") {
|
|
2036
|
+
for (const edge of this.edgesTouching(c.artifactId)) {
|
|
2037
|
+
const key = `${edge.artifactId} ${edge.to}`;
|
|
2038
|
+
if (already.has(key)) continue;
|
|
2039
|
+
already.add(key);
|
|
2040
|
+
out.push({ artifactId: edge.artifactId, kind: "unlink_input", target: edge.artifactId, payload: { to: edge.to } });
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
out.push(c);
|
|
2044
|
+
}
|
|
2045
|
+
return out;
|
|
2046
|
+
}
|
|
2047
|
+
/** 把 InterventionOp 词汇编译成按依赖序排列的 oplog 构件(spawn 先、seal 最后) */
|
|
2048
|
+
buildConstituentsRaw(plan) {
|
|
2037
2049
|
const order = { spawn: 0, edit: 1, reopen: 2, assign: 3, link: 4, pin: 5, unlink: 6, cancelPart: 7, seal: 8, annotate: 9 };
|
|
2038
2050
|
const sorted = [...plan.ops].sort((a, b) => order[a.action] - order[b.action]);
|
|
2039
2051
|
const spawnedHere = new Set(plan.ops.flatMap((op) => op.action === "spawn" ? [op.id] : []));
|
|
@@ -2373,6 +2385,18 @@ async function assembleContext(args) {
|
|
|
2373
2385
|
const headRev = head !== null ? model.revisions.get(head) : null;
|
|
2374
2386
|
const headSpread = headRev !== null ? await spreadRevisionContent(blobs, headRev, files, "deliverable") : null;
|
|
2375
2387
|
if (headSpread?.inlineText != null) files["deliverable/CONTENT.md"] = headSpread.inlineText;
|
|
2388
|
+
let orphanBriefNote = null;
|
|
2389
|
+
if (artifact.inputs.length === 0 && artifact.type !== "brief") {
|
|
2390
|
+
const brief = [...model.artifacts.values()].find(
|
|
2391
|
+
(a) => a.workspace === artifact.workspace && a.type === "brief" && a.id !== artifactId
|
|
2392
|
+
);
|
|
2393
|
+
const briefHead = brief?.currentRev ? model.revisions.get(brief.currentRev) ?? null : null;
|
|
2394
|
+
if (brief && briefHead) {
|
|
2395
|
+
const spread = await spreadRevisionContent(blobs, briefHead, files, "inputs/work-order");
|
|
2396
|
+
if (spread.inlineText != null) files["inputs/work-order/CONTENT.md"] = spread.inlineText;
|
|
2397
|
+
orphanBriefNote = `\u4F60\u6CA1\u6709\u4E0A\u6E38\u4F9D\u8D56\u2014\u2014\u4EE5\u5DE5\u5355\u6839 brief \`${brief.id}\` \u4E3A\u51C6\uFF08\u5DF2\u653E\u8FDB \`inputs/work-order/\`\uFF09\u3002`;
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2376
2400
|
const integrating = args.action === "integrate";
|
|
2377
2401
|
const queuedParts = [];
|
|
2378
2402
|
if (integrating) {
|
|
@@ -2410,11 +2434,16 @@ async function assembleContext(args) {
|
|
|
2410
2434
|
whatLines.push(`\u628A\u4F60\u6D3E\u51FA\u53BB\u7684\u5404\u90E8\u4EF6\u6536\u53E3\u6210**\u4E00\u7248\u5B8C\u6574\u3001\u81EA\u6D3D\u7684 R\\***\u2014\u2014\u5404\u90E8\u4EF6\u5728\u54EA\u3001\u4EE5\u4EC0\u4E48\u4E3A\u5E95\u600E\u4E48\u5408\uFF0C\u89C1\u300C\u600E\u4E48\u63D0\u4EA4\u300D\uFF08\u968F\u5185\u5BB9\u7C7B\u578B\u4E0D\u540C\uFF09\u3002`);
|
|
2411
2435
|
} else {
|
|
2412
2436
|
if (unfoundedHere.length > 0) {
|
|
2413
|
-
whyLines.push(`**\u8FD4\u5DE5\uFF1A\u4E0A\u6E38\
|
|
2437
|
+
whyLines.push(`**\u8FD4\u5DE5\uFF1A\u4F60\u4F9D\u8D56\u7684\u4E0A\u6E38\u53D8\u4E86**\u2014\u2014\u4F60\u5F53\u524D head \u57FA\u4E8E\u65E7\u60C5\u51B5\uFF1B\u4E0B\u5217\u4E0A\u6E38\u6709\u53D8\uFF08\u66F4\u65B0\uFF0F\u88AB\u65AD\u6216\u5E9F\u5F03\uFF09\uFF0C\u636E\u6B64\u4FEE\u8BA2\u4F60\u7684\u4EA7\u7269\uFF1A`);
|
|
2414
2438
|
for (const u of unfoundedHere) {
|
|
2415
2439
|
const oldC = await contentMapOf(u.basedOn ? model.revisions.get(u.basedOn) ?? null : null);
|
|
2416
2440
|
const newC = u.pinned ? await contentMapOf(model.revisions.get(u.pinned) ?? null) : /* @__PURE__ */ new Map();
|
|
2417
|
-
|
|
2441
|
+
if (u.pinned === null) {
|
|
2442
|
+
const status = lifecycleOf(model, u.upstream) === "sealed:cancelled" ? "\u5DF2\u5E9F\u5F03(cancelled)" : "\u5DF2\u65AD\u5F00(unlink)";
|
|
2443
|
+
whyLines.push(`- \u4E0A\u6E38 ${u.upstream} **${status}**\uFF08\u4F60\u66FE\u57FA\u4E8E\u5B83\u7684 ${(u.basedOn ?? "\u2205").slice(9, 17)} \u7248\uFF09\uFF1A**\u53BB\u6389\u4F60\u4EA7\u7269\u91CC\u4F9D\u8D56\u5B83\u7684\u90E8\u5206**\uFF08\u5B83\u7684\u65E7\u5185\u5BB9\u89C1\u4E0B\uFF09\uFF1A`);
|
|
2444
|
+
} else {
|
|
2445
|
+
whyLines.push(`- \u4E0A\u6E38 ${u.upstream}\uFF08${(u.basedOn ?? "\u2205").slice(9, 17)} \u2192 ${(u.pinned ?? "\u2205").slice(9, 17)}\uFF09\uFF1A`);
|
|
2446
|
+
}
|
|
2418
2447
|
whyLines.push(oldC !== null && newC !== null ? manifestDiff(oldC, newC) : `\uFF08\u4EE3\u7801-git\uFF1A\u7528 \`git log -p\` \u81EA\u67E5\uFF1B\u65B0\u7248\u4E5F\u5728 inputs/ \u91CC\uFF09`);
|
|
2419
2448
|
}
|
|
2420
2449
|
whatLines.push(`\u636E\u4E0A\u6E38\u65B0\u7248**\u66F4\u65B0\u53D7\u5F71\u54CD\u7684\u90E8\u5206**\u3001\u91CD\u65B0\u63D0\u4EA4\uFF1B\u4E0D\u53D7\u5F71\u54CD\u7684\u522B\u52A8\uFF08\u600E\u4E48\u843D\u89C1\u300C\u600E\u4E48\u63D0\u4EA4\u300D\uFF09\u3002`);
|
|
@@ -2500,6 +2529,7 @@ async function assembleContext(args) {
|
|
|
2500
2529
|
`- type: ${artifact.type}${typeDef ? `\uFF08contentType=${typeDef.contentType}\uFF09` : ""}`,
|
|
2501
2530
|
`- \u4F60\u662F **${typeDef?.ownerRole ?? "owner"}**\uFF08\u4F60\u7684\u4E13\u957F/\u98CE\u683C\u89C1 \`identity/\`\uFF09`,
|
|
2502
2531
|
`- \u5F53\u524D head: ${head ?? "\u2205\uFF08\u80DA\u80CE\u671F\u2014\u2014\u8BF7\u4EA7\u51FA\u9996\u4E2A revision\uFF09"}`,
|
|
2532
|
+
...orphanBriefNote ? [`- \u2693 ${orphanBriefNote}`] : [],
|
|
2503
2533
|
...whyLines.length > 0 ? [``, `## \u672C\u6B21\u4E3A\u4EC0\u4E48\u5524\u8D77\u4F60`, ...whyLines] : [],
|
|
2504
2534
|
...whatLines.length > 0 ? [``, `## \u9700\u8981\u4F60\u505A\u4EC0\u4E48`, ...whatLines] : [],
|
|
2505
2535
|
``,
|
|
@@ -2784,6 +2814,7 @@ var init_dispatcher = __esm({
|
|
|
2784
2814
|
async tick() {
|
|
2785
2815
|
const { kernel } = this.opts;
|
|
2786
2816
|
let started = 0;
|
|
2817
|
+
await this.autoConcludeReadyMergedArtifacts();
|
|
2787
2818
|
for (const job of computeProduceJobs(kernel.model)) {
|
|
2788
2819
|
if (kernel.isFrozen(job.artifactId)) continue;
|
|
2789
2820
|
const artifact = kernel.model.artifacts.get(job.artifactId);
|
|
@@ -2923,74 +2954,134 @@ var init_dispatcher = __esm({
|
|
|
2923
2954
|
wallClockMs: job.limits.wallClockMs
|
|
2924
2955
|
});
|
|
2925
2956
|
session.onExit((info) => {
|
|
2926
|
-
this.
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2957
|
+
void this.handleSessionExit({
|
|
2958
|
+
jobKey,
|
|
2959
|
+
spec,
|
|
2960
|
+
session,
|
|
2961
|
+
info,
|
|
2962
|
+
sink,
|
|
2963
|
+
seqAtDispatch,
|
|
2964
|
+
dispatchedAtMs
|
|
2965
|
+
});
|
|
2966
|
+
});
|
|
2967
|
+
return 1;
|
|
2968
|
+
}
|
|
2969
|
+
async handleSessionExit(args) {
|
|
2970
|
+
const { kernel } = this.opts;
|
|
2971
|
+
const { jobKey, spec, session, info, sink, seqAtDispatch, dispatchedAtMs } = args;
|
|
2972
|
+
const seqBeforeAutoConclude = kernel.model.lastSeq.get(spec.artifactId) ?? 0;
|
|
2973
|
+
if (info.code === 0 && seqBeforeAutoConclude !== seqAtDispatch) {
|
|
2974
|
+
await this.maybeAutoConclude(spec);
|
|
2975
|
+
}
|
|
2976
|
+
this.inFlight.delete(jobKey);
|
|
2977
|
+
const seqAtExit = kernel.model.lastSeq.get(spec.artifactId) ?? 0;
|
|
2978
|
+
if (sink) {
|
|
2979
|
+
void (async () => {
|
|
2980
|
+
const ops = await this.opts.oplog.read(spec.artifactId);
|
|
2981
|
+
const opRefs = ops.filter((o) => o.seq > seqAtDispatch && o.seq <= seqAtExit).map((o) => ({ seq: o.seq, kind: o.kind, id: o.id, target: o.target }));
|
|
2982
|
+
sink.end(session.id, {
|
|
2983
|
+
exit: {
|
|
2984
|
+
code: info.code,
|
|
2985
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2986
|
+
durationMs: Date.now() - dispatchedAtMs,
|
|
2987
|
+
wallclockKilled: info.code === null,
|
|
2988
|
+
// 适配器从 stream-json 抽出的 token/成本(含 cache);不带则缺省。
|
|
2989
|
+
...info.usage ? { usage: info.usage } : {}
|
|
2990
|
+
},
|
|
2991
|
+
opRefs
|
|
2944
2992
|
});
|
|
2945
|
-
}
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2993
|
+
})().catch(() => {
|
|
2994
|
+
});
|
|
2995
|
+
}
|
|
2996
|
+
const progressed = seqAtExit !== seqAtDispatch;
|
|
2997
|
+
let consecutive = 0;
|
|
2998
|
+
if (progressed) {
|
|
2999
|
+
this.strikes.delete(jobKey);
|
|
3000
|
+
} else {
|
|
3001
|
+
consecutive = (this.strikes.get(jobKey)?.count ?? 0) + 1;
|
|
3002
|
+
const max = this.opts.backoff?.maxConsecutiveFailures ?? 3;
|
|
3003
|
+
const base = this.opts.backoff?.baseMs ?? 3e4;
|
|
3004
|
+
const fused = consecutive >= max;
|
|
3005
|
+
this.strikes.set(jobKey, {
|
|
3006
|
+
artifactId: spec.artifactId,
|
|
3007
|
+
actor: spec.actor,
|
|
3008
|
+
action: spec.action,
|
|
3009
|
+
count: consecutive,
|
|
3010
|
+
nextEligibleAt: Date.now() + base * 2 ** (consecutive - 1),
|
|
3011
|
+
fused,
|
|
3012
|
+
...fused ? { fusedAt: (/* @__PURE__ */ new Date()).toISOString() } : {},
|
|
3013
|
+
seqAtVerdict: seqAtExit,
|
|
3014
|
+
...info.reason ? { lastReason: info.reason } : {}
|
|
3015
|
+
});
|
|
3016
|
+
if (fused) {
|
|
3017
|
+
this.emitJournal({
|
|
3018
|
+
kind: "fused",
|
|
3019
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3020
|
+
jobKey,
|
|
2956
3021
|
artifactId: spec.artifactId,
|
|
2957
3022
|
actor: spec.actor,
|
|
2958
3023
|
action: spec.action,
|
|
2959
|
-
|
|
2960
|
-
nextEligibleAt: Date.now() + base * 2 ** (consecutive - 1),
|
|
2961
|
-
fused,
|
|
2962
|
-
...fused ? { fusedAt: (/* @__PURE__ */ new Date()).toISOString() } : {},
|
|
2963
|
-
seqAtVerdict: seqAtExit,
|
|
2964
|
-
...info.reason ? { lastReason: info.reason } : {}
|
|
3024
|
+
consecutiveFailures: consecutive
|
|
2965
3025
|
});
|
|
2966
|
-
if (fused) {
|
|
2967
|
-
this.emitJournal({
|
|
2968
|
-
kind: "fused",
|
|
2969
|
-
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2970
|
-
jobKey,
|
|
2971
|
-
artifactId: spec.artifactId,
|
|
2972
|
-
actor: spec.actor,
|
|
2973
|
-
action: spec.action,
|
|
2974
|
-
consecutiveFailures: consecutive
|
|
2975
|
-
});
|
|
2976
|
-
}
|
|
2977
3026
|
}
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
3027
|
+
}
|
|
3028
|
+
this.emitJournal({
|
|
3029
|
+
kind: "exited",
|
|
3030
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3031
|
+
jobKey,
|
|
3032
|
+
artifactId: spec.artifactId,
|
|
3033
|
+
actor: spec.actor,
|
|
3034
|
+
action: spec.action,
|
|
3035
|
+
code: info.code,
|
|
3036
|
+
...info.reason ? { reason: info.reason } : {},
|
|
3037
|
+
// BUG-5:结构化退出归因
|
|
3038
|
+
durationMs: Date.now() - dispatchedAtMs,
|
|
3039
|
+
progressed,
|
|
3040
|
+
consecutiveFailures: consecutive
|
|
3041
|
+
});
|
|
3042
|
+
}
|
|
3043
|
+
async maybeAutoConclude(spec) {
|
|
3044
|
+
if (!this.shouldAutoConclude(spec)) return;
|
|
3045
|
+
try {
|
|
3046
|
+
await this.opts.kernel.conclude({
|
|
2982
3047
|
artifactId: spec.artifactId,
|
|
2983
3048
|
actor: spec.actor,
|
|
2984
|
-
|
|
2985
|
-
code: info.code,
|
|
2986
|
-
...info.reason ? { reason: info.reason } : {},
|
|
2987
|
-
// BUG-5:结构化退出归因
|
|
2988
|
-
durationMs: Date.now() - dispatchedAtMs,
|
|
2989
|
-
progressed,
|
|
2990
|
-
consecutiveFailures: consecutive
|
|
3049
|
+
note: "auto-conclude after successful dispatch"
|
|
2991
3050
|
});
|
|
2992
|
-
}
|
|
2993
|
-
|
|
3051
|
+
} catch {
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
async autoConcludeReadyMergedArtifacts() {
|
|
3055
|
+
for (const artifact of this.opts.kernel.model.artifacts.values()) {
|
|
3056
|
+
if (!artifact.owner.startsWith("actor:agent:")) continue;
|
|
3057
|
+
await this.maybeAutoConclude({
|
|
3058
|
+
artifactId: artifact.id,
|
|
3059
|
+
actor: artifact.owner,
|
|
3060
|
+
action: (artifact.parts ?? []).length > 0 ? "integrate" : "produce"
|
|
3061
|
+
});
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
shouldAutoConclude(spec) {
|
|
3065
|
+
if (spec.action !== "produce" && spec.action !== "integrate") return false;
|
|
3066
|
+
if (spec.part !== void 0) return false;
|
|
3067
|
+
const model = this.opts.kernel.model;
|
|
3068
|
+
const artifact = model.artifacts.get(spec.artifactId);
|
|
3069
|
+
if (!artifact) return false;
|
|
3070
|
+
if (!artifact.owner.startsWith("actor:agent:")) return false;
|
|
3071
|
+
if (lifecycleOf(model, spec.artifactId) !== "active") return false;
|
|
3072
|
+
if (isConcluded(model, spec.artifactId)) return false;
|
|
3073
|
+
if (pendingConcludeAttempt(model, spec.artifactId)) return false;
|
|
3074
|
+
if (!artifact.currentRev) return false;
|
|
3075
|
+
if (queueOf(model, spec.artifactId).length > 0) return false;
|
|
3076
|
+
if (unresolvedGapsOf(model, spec.artifactId).length > 0) return false;
|
|
3077
|
+
if (annotationsOf(model, spec.artifactId).some((a) => a.state === "open" && a.raisedFrom === spec.artifactId)) return false;
|
|
3078
|
+
if (hasOpenHumanChangeRequest(model, spec.artifactId)) return false;
|
|
3079
|
+
const parts = artifact.parts ?? [];
|
|
3080
|
+
if (parts.length === 0) return true;
|
|
3081
|
+
if (spec.action !== "integrate") return false;
|
|
3082
|
+
return parts.every(
|
|
3083
|
+
(p) => revisionsOf(model, spec.artifactId).some((r) => r.part === p.name && r.state !== "rejected")
|
|
3084
|
+
);
|
|
2994
3085
|
}
|
|
2995
3086
|
/** per-type 预算优先(schema.budget),其次 Dispatcher 配置,最后 10min 兜底 */
|
|
2996
3087
|
wallClockFor(artifactId) {
|
|
@@ -16475,6 +16566,29 @@ async function closeHumanChangeRequestForRevision(kernel, oplog, artifactId, rev
|
|
|
16475
16566
|
await kernel.resolveAnnotation({ annotationId, actor, resolution, note });
|
|
16476
16567
|
}
|
|
16477
16568
|
}
|
|
16569
|
+
async function mergeApprovedRevision(kernel, args) {
|
|
16570
|
+
if (args.revisionId === void 0) {
|
|
16571
|
+
const artifact2 = await kernel.merge({ artifactId: args.artifactId, actor: args.actor });
|
|
16572
|
+
return { artifact: artifact2, mergedRevisionId: artifact2.currentRev, approvedRevisionId: artifact2.currentRev };
|
|
16573
|
+
}
|
|
16574
|
+
const queue = queueOf(kernel.model, args.artifactId);
|
|
16575
|
+
const target = kernel.model.revisions.get(args.revisionId);
|
|
16576
|
+
if (queue.length === 1 && queue[0]?.id === args.revisionId && target?.state === "proposed" && isStale(kernel.model, target)) {
|
|
16577
|
+
const successor = await kernel.proposeRevision({
|
|
16578
|
+
artifactId: args.artifactId,
|
|
16579
|
+
actor: args.actor,
|
|
16580
|
+
contentRef: target.contentRef,
|
|
16581
|
+
contentKind: target.contentKind,
|
|
16582
|
+
reason: `\u6279\u51C6\u8FC7\u671F\u4FEE\u8BA2\u540E\u91CD\u653E\uFF1A${target.reason}`,
|
|
16583
|
+
...target.part !== void 0 ? { part: target.part } : {},
|
|
16584
|
+
rebasedFrom: target.id
|
|
16585
|
+
});
|
|
16586
|
+
const artifact2 = await kernel.merge({ artifactId: args.artifactId, actor: args.actor, revisionId: successor.id });
|
|
16587
|
+
return { artifact: artifact2, mergedRevisionId: successor.id, approvedRevisionId: target.id, rebasedFrom: target.id };
|
|
16588
|
+
}
|
|
16589
|
+
const artifact = await kernel.merge({ artifactId: args.artifactId, actor: args.actor, revisionId: args.revisionId });
|
|
16590
|
+
return { artifact, mergedRevisionId: args.revisionId, approvedRevisionId: args.revisionId };
|
|
16591
|
+
}
|
|
16478
16592
|
function splitIdentityFiles2(prompt) {
|
|
16479
16593
|
const SEP = /<!-- FILE: (.+?) -->\n?/g;
|
|
16480
16594
|
const parts = prompt.split(SEP);
|
|
@@ -16690,22 +16804,24 @@ async function runCommand(kernel, blobs, oplog, actor, command, args, ctx = {})
|
|
|
16690
16804
|
case "merge": {
|
|
16691
16805
|
const revisionId = optStr(args, "revisionId");
|
|
16692
16806
|
const artifactId = str(args, "artifactId");
|
|
16693
|
-
const
|
|
16807
|
+
const merged = await mergeApprovedRevision(kernel, {
|
|
16694
16808
|
artifactId,
|
|
16695
16809
|
actor,
|
|
16696
16810
|
...revisionId !== void 0 ? { revisionId } : {}
|
|
16697
16811
|
});
|
|
16698
|
-
const mergedRevisionId = revisionId ?? artifact.currentRev;
|
|
16699
16812
|
await closeHumanChangeRequestForRevision(
|
|
16700
16813
|
kernel,
|
|
16701
16814
|
oplog,
|
|
16702
16815
|
artifactId,
|
|
16703
|
-
|
|
16816
|
+
merged.approvedRevisionId,
|
|
16704
16817
|
actor,
|
|
16705
16818
|
"resolved",
|
|
16706
|
-
`\u5BA1\u6838\u901A\u8FC7${revisionId ? `\uFF1A${revisionId}` : ""}\uFF0C\u53D8\u66F4\u8BF7\u6C42\u5DF2\u89E3\u51B3\u3002`
|
|
16819
|
+
merged.rebasedFrom ? `\u5BA1\u6838\u901A\u8FC7\uFF1A${merged.rebasedFrom}\uFF08\u5DF2\u91CD\u653E\u4E3A ${merged.mergedRevisionId}\uFF09\uFF0C\u53D8\u66F4\u8BF7\u6C42\u5DF2\u89E3\u51B3\u3002` : `\u5BA1\u6838\u901A\u8FC7${revisionId ? `\uFF1A${revisionId}` : ""}\uFF0C\u53D8\u66F4\u8BF7\u6C42\u5DF2\u89E3\u51B3\u3002`
|
|
16707
16820
|
);
|
|
16708
|
-
return {
|
|
16821
|
+
return {
|
|
16822
|
+
message: merged.rebasedFrom ? `merged(rebase)\uFF1Ahead \u2192 ${merged.artifact.currentRev}\uFF1Bsuperseded ${merged.rebasedFrom}` : `merged\uFF1Ahead \u2192 ${merged.artifact.currentRev}`,
|
|
16823
|
+
data: merged.rebasedFrom ? { rebasedFrom: merged.rebasedFrom, revisionId: merged.mergedRevisionId } : void 0
|
|
16824
|
+
};
|
|
16709
16825
|
}
|
|
16710
16826
|
case "advance": {
|
|
16711
16827
|
const result = await kernel.advanceQueue(str(args, "artifactId"), actor);
|
|
@@ -17271,7 +17387,10 @@ async function startOasisServer(opts) {
|
|
|
17271
17387
|
const buf = stagingBuffers.get(sessionKey) ?? [];
|
|
17272
17388
|
buf.push(op);
|
|
17273
17389
|
stagingBuffers.set(sessionKey, buf);
|
|
17274
|
-
|
|
17390
|
+
const cascade = op.action === "seal" && op.reason === "cancelled" ? engine.kernel.cancelCascadeEdges(op.artifactId) : void 0;
|
|
17391
|
+
res.writeHead(200, { "content-type": "application/json" }).end(
|
|
17392
|
+
JSON.stringify({ count: buf.length, staged: op, ...cascade && cascade.length > 0 ? { cascade } : {} })
|
|
17393
|
+
);
|
|
17275
17394
|
} catch (err) {
|
|
17276
17395
|
res.writeHead(400, { "content-type": "application/json" }).end(
|
|
17277
17396
|
JSON.stringify({ error: err instanceof Error ? err.message : String(err) })
|
|
@@ -17497,13 +17616,13 @@ async function startOasisServer(opts) {
|
|
|
17497
17616
|
const actorCtx = await opts.resolveActorContext(body.actorId).catch(() => null);
|
|
17498
17617
|
if (actorCtx) {
|
|
17499
17618
|
const { mkdtempSync: mkdtempSync6, mkdirSync: mkdirSync13, writeFileSync: writeFileSync13 } = await import("node:fs");
|
|
17500
|
-
const { join: join16, dirname:
|
|
17619
|
+
const { join: join16, dirname: dirname13 } = await import("node:path");
|
|
17501
17620
|
const { tmpdir: tmpdir8 } = await import("node:os");
|
|
17502
17621
|
const dir = mkdtempSync6(join16(tmpdir8(), "oasis-chat-"));
|
|
17503
17622
|
if (actorCtx.config?.prompt) {
|
|
17504
17623
|
for (const [rel, content] of Object.entries(splitIdentityFiles2(actorCtx.config.prompt))) {
|
|
17505
17624
|
const file = join16(dir, rel);
|
|
17506
|
-
mkdirSync13(
|
|
17625
|
+
mkdirSync13(dirname13(file), { recursive: true });
|
|
17507
17626
|
writeFileSync13(file, content);
|
|
17508
17627
|
}
|
|
17509
17628
|
}
|
|
@@ -18595,11 +18714,11 @@ function deriveRevisionStatus(revision, currentRev) {
|
|
|
18595
18714
|
return { status: "editing" };
|
|
18596
18715
|
}
|
|
18597
18716
|
}
|
|
18598
|
-
function deriveArtifactStatus(currentRev, revisions) {
|
|
18599
|
-
if (
|
|
18717
|
+
function deriveArtifactStatus(model, artifactId, currentRev, revisions) {
|
|
18718
|
+
if (isConcluded(model, artifactId)) return "current";
|
|
18600
18719
|
if (revisions.some((r) => r.state === "proposed")) return "pending_review";
|
|
18601
18720
|
if (revisions.length > 0 && revisions.every((r) => r.state === "rejected")) return "rejected";
|
|
18602
|
-
if (revisions.length > 0) return "editing";
|
|
18721
|
+
if (currentRev || revisions.length > 0) return "editing";
|
|
18603
18722
|
return "pending";
|
|
18604
18723
|
}
|
|
18605
18724
|
function deriveActorKind(actor) {
|
|
@@ -18640,7 +18759,7 @@ function buildArtifactProjection(input) {
|
|
|
18640
18759
|
projectId,
|
|
18641
18760
|
type: artifact.docType ?? descriptor.docType ?? artifact.type,
|
|
18642
18761
|
title: artifact.title ?? descriptor.title ?? artifactDerivation?.title ?? artifactId,
|
|
18643
|
-
status: deriveArtifactStatus(currentRev, ordered),
|
|
18762
|
+
status: deriveArtifactStatus(model, artifactId, currentRev, ordered),
|
|
18644
18763
|
...currentRev ? { currentRevisionId: currentRev } : {},
|
|
18645
18764
|
...latest ? { latestRevisionId: latest.id } : {},
|
|
18646
18765
|
...artifactDerivation ? { createdFromWorkOrderId: artifactDerivation.workOrderId } : {},
|
|
@@ -18854,6 +18973,12 @@ var init_projection = __esm({
|
|
|
18854
18973
|
});
|
|
18855
18974
|
|
|
18856
18975
|
// ../server/src/domains/collab/node-state.ts
|
|
18976
|
+
function nodeLabel(a) {
|
|
18977
|
+
if (a.title) return a.title;
|
|
18978
|
+
if (a.description) return a.description;
|
|
18979
|
+
const tail = a.id.split(":").slice(2).join(":");
|
|
18980
|
+
return tail ? `\u672A\u547D\u540D ${a.type}\uFF08${tail}\uFF09` : `\u672A\u547D\u540D ${a.type}`;
|
|
18981
|
+
}
|
|
18857
18982
|
function nodeInfo(model, a) {
|
|
18858
18983
|
const lifecycle = lifecycleOf(model, a.id);
|
|
18859
18984
|
const sealed = lifecycle !== "active";
|
|
@@ -18993,10 +19118,10 @@ function renderIndexYaml(index) {
|
|
|
18993
19118
|
}
|
|
18994
19119
|
function summarizeArtifacts(artifacts) {
|
|
18995
19120
|
const total = artifacts.length;
|
|
18996
|
-
const current = artifacts.filter((artifact) => artifact.status === "
|
|
18997
|
-
const editing = artifacts.filter((artifact) => artifact.
|
|
18998
|
-
const pendingReview = artifacts.filter((artifact) => artifact.status === "
|
|
18999
|
-
const rejected = artifacts.filter((artifact) => artifact.status === "rejected").length;
|
|
19121
|
+
const current = artifacts.filter((artifact) => (artifact.businessStatus ?? businessStatusFromLegacyStatus(artifact.status)) === "completed").length;
|
|
19122
|
+
const editing = artifacts.filter((artifact) => ["producing", "produced", "blocked"].includes(artifact.businessStatus ?? businessStatusFromLegacyStatus(artifact.status))).length;
|
|
19123
|
+
const pendingReview = artifacts.filter((artifact) => (artifact.businessStatus ?? businessStatusFromLegacyStatus(artifact.status)) === "review").length;
|
|
19124
|
+
const rejected = artifacts.filter((artifact) => (artifact.businessStatus ?? businessStatusFromLegacyStatus(artifact.status)) === "rejected").length;
|
|
19000
19125
|
return {
|
|
19001
19126
|
total,
|
|
19002
19127
|
current,
|
|
@@ -19006,14 +19131,80 @@ function summarizeArtifacts(artifacts) {
|
|
|
19006
19131
|
progressPercent: total === 0 ? 0 : Math.round(current / total * 100)
|
|
19007
19132
|
};
|
|
19008
19133
|
}
|
|
19009
|
-
function
|
|
19010
|
-
|
|
19134
|
+
function businessStatusFromLegacyStatus(status) {
|
|
19135
|
+
switch (status) {
|
|
19136
|
+
case "current":
|
|
19137
|
+
case "history":
|
|
19138
|
+
return "completed";
|
|
19139
|
+
case "pending_review":
|
|
19140
|
+
return "review";
|
|
19141
|
+
case "rejected":
|
|
19142
|
+
return "rejected";
|
|
19143
|
+
case "archived":
|
|
19144
|
+
return "archived";
|
|
19145
|
+
case "producing":
|
|
19146
|
+
return "producing";
|
|
19147
|
+
case "editing":
|
|
19148
|
+
return "produced";
|
|
19149
|
+
case "pending":
|
|
19150
|
+
default:
|
|
19151
|
+
return "not_started";
|
|
19152
|
+
}
|
|
19153
|
+
}
|
|
19154
|
+
function artifactStatusFromNode(info, revisions) {
|
|
19011
19155
|
const stage = deriveNodeStage(info);
|
|
19156
|
+
if (stage === "concluded") return "current";
|
|
19012
19157
|
if (stage === "review" || stage === "gate") return "pending_review";
|
|
19013
19158
|
if (stage === "sealed") return "archived";
|
|
19159
|
+
if (hasProposedRevision(revisions) && !info.concluded) return "pending_review";
|
|
19160
|
+
if (hasOnlyRejectedRevisions(revisions) && !info.concluded) return "rejected";
|
|
19014
19161
|
if (stage === "editing" || stage === "merged" || stage === "blocked") return "editing";
|
|
19015
19162
|
return "producing";
|
|
19016
19163
|
}
|
|
19164
|
+
function artifactOutputStatusFromNode(info, revisions) {
|
|
19165
|
+
const stage = deriveNodeStage(info);
|
|
19166
|
+
if (hasProposedRevision(revisions) || hasOnlyRejectedRevisions(revisions)) return "produced";
|
|
19167
|
+
if (stage === "editing") return "producing";
|
|
19168
|
+
if (stage === "merged" || stage === "review" || stage === "gate" || stage === "concluded") return "produced";
|
|
19169
|
+
if (stage === "blocked" && info.hasWorking) return "producing";
|
|
19170
|
+
if (stage === "blocked" && info.queueLen > 0) return "produced";
|
|
19171
|
+
if (stage === "blocked" && info.concluded) return "produced";
|
|
19172
|
+
if (stage === "sealed" && info.concluded) return "produced";
|
|
19173
|
+
return "not_started";
|
|
19174
|
+
}
|
|
19175
|
+
function artifactAcceptanceStatusFromNode(info, revisions) {
|
|
19176
|
+
const stage = deriveNodeStage(info);
|
|
19177
|
+
if (stage === "concluded" || stage === "sealed" && info.concluded) return "accepted";
|
|
19178
|
+
if (stage === "review" || stage === "gate") return "review_pending";
|
|
19179
|
+
if (hasProposedRevision(revisions) && !info.concluded) return "review_pending";
|
|
19180
|
+
if (hasOnlyRejectedRevisions(revisions) && !info.concluded) return "rejected";
|
|
19181
|
+
if (stage === "blocked") return "blocked";
|
|
19182
|
+
return "not_ready";
|
|
19183
|
+
}
|
|
19184
|
+
function hasProposedRevision(revisions) {
|
|
19185
|
+
return revisions.some((revision) => revision.state === "proposed");
|
|
19186
|
+
}
|
|
19187
|
+
function hasOnlyRejectedRevisions(revisions) {
|
|
19188
|
+
return revisions.length > 0 && revisions.every((revision) => revision.state === "rejected");
|
|
19189
|
+
}
|
|
19190
|
+
function artifactBusinessStatusFromLayers(outputStatus, acceptanceStatus, info) {
|
|
19191
|
+
if (info.sealed) return "archived";
|
|
19192
|
+
switch (acceptanceStatus) {
|
|
19193
|
+
case "accepted":
|
|
19194
|
+
return "completed";
|
|
19195
|
+
case "review_pending":
|
|
19196
|
+
return "review";
|
|
19197
|
+
case "blocked":
|
|
19198
|
+
return "blocked";
|
|
19199
|
+
case "rejected":
|
|
19200
|
+
return "rejected";
|
|
19201
|
+
case "not_ready":
|
|
19202
|
+
default:
|
|
19203
|
+
if (outputStatus === "produced" || outputStatus === "stale") return "produced";
|
|
19204
|
+
if (outputStatus === "producing") return "producing";
|
|
19205
|
+
return "not_started";
|
|
19206
|
+
}
|
|
19207
|
+
}
|
|
19017
19208
|
function actorToProjectMember(projectId, actor, addedAt) {
|
|
19018
19209
|
return {
|
|
19019
19210
|
projectId,
|
|
@@ -19438,11 +19629,14 @@ var init_service = __esm({
|
|
|
19438
19629
|
async getProjectContents(projectId) {
|
|
19439
19630
|
const project = await this.getProject(projectId);
|
|
19440
19631
|
if (!project) return null;
|
|
19441
|
-
const artifacts = (await this.
|
|
19632
|
+
const artifacts = (await this.listAllProjectArtifacts(projectId)).map((a) => ({
|
|
19442
19633
|
artifactId: a.artifactId,
|
|
19443
19634
|
type: a.type,
|
|
19444
19635
|
title: a.title,
|
|
19445
19636
|
status: a.status,
|
|
19637
|
+
...a.outputStatus !== void 0 ? { outputStatus: a.outputStatus } : {},
|
|
19638
|
+
...a.acceptanceStatus !== void 0 ? { acceptanceStatus: a.acceptanceStatus } : {},
|
|
19639
|
+
...a.businessStatus !== void 0 ? { businessStatus: a.businessStatus } : {},
|
|
19446
19640
|
...a.currentRevisionId !== void 0 ? { currentRevisionId: a.currentRevisionId } : {},
|
|
19447
19641
|
updatedAt: a.updatedAt
|
|
19448
19642
|
}));
|
|
@@ -19713,6 +19907,10 @@ var init_service = __esm({
|
|
|
19713
19907
|
} else if (kernelRev?.state !== "merged") {
|
|
19714
19908
|
throw new Error(`revision not approvable (state=${kernelRev?.state ?? "missing"}): ${input.revisionId}`);
|
|
19715
19909
|
}
|
|
19910
|
+
const artifactAfterMerge = this.kernel.model.artifacts.get(input.artifactId);
|
|
19911
|
+
if (artifactAfterMerge?.currentRev === input.revisionId && concludedHead(this.kernel.model, input.artifactId) !== input.revisionId) {
|
|
19912
|
+
await this.kernel.conclude({ artifactId: input.artifactId, actor, note: "conclude after artifact revision approval" });
|
|
19913
|
+
}
|
|
19716
19914
|
await this.syncProjection();
|
|
19717
19915
|
const updatedArtifact = await this.artifacts.getArtifact(input.artifactId);
|
|
19718
19916
|
const approved = await this.artifacts.getRevision(input.revisionId);
|
|
@@ -19948,18 +20146,29 @@ var init_service = __esm({
|
|
|
19948
20146
|
const projectByWorkorder = new Map(bindings.map((binding) => [binding.workOrderId, binding.projectId]));
|
|
19949
20147
|
for (const artifact of this.kernel.model.artifacts.values()) {
|
|
19950
20148
|
if (projectByWorkorder.get(artifact.workspace) !== projectId) continue;
|
|
19951
|
-
if (byArtifact.has(artifact.id)) continue;
|
|
19952
20149
|
const descriptor = decodeDocumentDescription(artifact.description);
|
|
19953
20150
|
const info = nodeInfo(this.kernel.model, artifact);
|
|
20151
|
+
const revisions = revisionsOf(this.kernel.model, artifact.id);
|
|
19954
20152
|
const updatedAt = this.kernel.model.lastOpAt.get(artifact.id) ?? (/* @__PURE__ */ new Date(0)).toISOString();
|
|
20153
|
+
const existing = byArtifact.get(artifact.id);
|
|
20154
|
+
const status = artifactStatusFromNode(info, revisions);
|
|
20155
|
+
const outputStatus = artifactOutputStatusFromNode(info, revisions);
|
|
20156
|
+
const acceptanceStatus = artifactAcceptanceStatusFromNode(info, revisions);
|
|
20157
|
+
const businessStatus = artifactBusinessStatusFromLayers(outputStatus, acceptanceStatus, info);
|
|
19955
20158
|
byArtifact.set(artifact.id, {
|
|
20159
|
+
...existing ?? {},
|
|
19956
20160
|
artifactId: artifact.id,
|
|
19957
20161
|
projectId,
|
|
19958
20162
|
type: artifact.docType ?? descriptor.docType ?? artifact.type,
|
|
19959
20163
|
title: artifact.title ?? descriptor.title ?? artifact.id,
|
|
19960
|
-
status
|
|
20164
|
+
status,
|
|
20165
|
+
outputStatus,
|
|
20166
|
+
acceptanceStatus,
|
|
20167
|
+
businessStatus,
|
|
20168
|
+
...artifact.currentRev ? { currentRevisionId: artifact.currentRev } : {},
|
|
20169
|
+
...existing?.latestRevisionId !== void 0 ? { latestRevisionId: existing.latestRevisionId } : {},
|
|
19961
20170
|
createdFromWorkOrderId: artifact.workspace,
|
|
19962
|
-
createdAt: updatedAt,
|
|
20171
|
+
createdAt: existing?.createdAt ?? updatedAt,
|
|
19963
20172
|
updatedAt
|
|
19964
20173
|
});
|
|
19965
20174
|
}
|
|
@@ -26049,6 +26258,9 @@ function toApiArtifact(artifact) {
|
|
|
26049
26258
|
type: artifact.type,
|
|
26050
26259
|
title: artifact.title,
|
|
26051
26260
|
status: artifact.status,
|
|
26261
|
+
output_status: artifact.outputStatus,
|
|
26262
|
+
acceptance_status: artifact.acceptanceStatus,
|
|
26263
|
+
business_status: artifact.businessStatus,
|
|
26052
26264
|
current_revision_id: artifact.currentRevisionId,
|
|
26053
26265
|
latest_revision_id: artifact.latestRevisionId,
|
|
26054
26266
|
source_work_order_id: artifact.createdFromWorkOrderId,
|
|
@@ -27155,6 +27367,10 @@ Write-Host " Stop : Stop-ScheduledTask -TaskName 'OasisNode-${p.nodeId}'"
|
|
|
27155
27367
|
Write-Host " Remove : Unregister-ScheduledTask -TaskName 'OasisNode-${p.nodeId}' -Confirm:$false"
|
|
27156
27368
|
`;
|
|
27157
27369
|
}
|
|
27370
|
+
function buildNpxCmd(p) {
|
|
27371
|
+
const namePart = p.nodeName ? ` --name ${p.nodeName}` : "";
|
|
27372
|
+
return `npx -y oasis_test@latest start --server-ws ${p.gatewayUrl} --id ${p.nodeId}${namePart} --token ${p.token}`;
|
|
27373
|
+
}
|
|
27158
27374
|
var init_connect_script = __esm({
|
|
27159
27375
|
"../server/src/domains/nodes/connect-script.ts"() {
|
|
27160
27376
|
"use strict";
|
|
@@ -27190,8 +27406,20 @@ function nodesDomain(deps) {
|
|
|
27190
27406
|
}));
|
|
27191
27407
|
return { status: 200, body: { items: [...online, ...offline] } };
|
|
27192
27408
|
});
|
|
27409
|
+
const suppressedRuntimes = /* @__PURE__ */ new Set();
|
|
27410
|
+
let hubListenerAdded = false;
|
|
27193
27411
|
router.get("/api/runtimes", async () => {
|
|
27194
27412
|
const hub = deps.getHub();
|
|
27413
|
+
if (hub && !hubListenerAdded) {
|
|
27414
|
+
hubListenerAdded = true;
|
|
27415
|
+
hub.addMessageListener((daemonId, msg) => {
|
|
27416
|
+
if (msg.type === "hello") {
|
|
27417
|
+
for (const id of suppressedRuntimes) {
|
|
27418
|
+
if (id.startsWith(`runtime:${daemonId}:`)) suppressedRuntimes.delete(id);
|
|
27419
|
+
}
|
|
27420
|
+
}
|
|
27421
|
+
});
|
|
27422
|
+
}
|
|
27195
27423
|
const online = (hub?.connectedDaemons() ?? []).flatMap((d) => {
|
|
27196
27424
|
if (d.meta.nodeName) nodeNames.set(d.daemonId, d.meta.nodeName);
|
|
27197
27425
|
return runtimeInfos(d.daemonId, d.meta, "online");
|
|
@@ -27200,7 +27428,13 @@ function nodesDomain(deps) {
|
|
|
27200
27428
|
const offline = (hub?.recentlyDisconnected() ?? []).flatMap(
|
|
27201
27429
|
(g) => runtimeInfos(g.daemonId, g.meta, "offline").filter((r) => !onlineIds.has(r.id))
|
|
27202
27430
|
);
|
|
27203
|
-
return { status: 200, body: { items: [...online, ...offline] } };
|
|
27431
|
+
return { status: 200, body: { items: [...online, ...offline].filter((r) => !suppressedRuntimes.has(r.id)) } };
|
|
27432
|
+
});
|
|
27433
|
+
router.delete("/api/runtimes/:runtimeId", async (req) => {
|
|
27434
|
+
const runtimeId = req.params["runtimeId"];
|
|
27435
|
+
if (!runtimeId) return { status: 400, body: { error: "missing runtimeId" } };
|
|
27436
|
+
suppressedRuntimes.add(runtimeId);
|
|
27437
|
+
return { status: 200, body: { ok: true } };
|
|
27204
27438
|
});
|
|
27205
27439
|
router.post("/api/nodes/enroll", async (req) => {
|
|
27206
27440
|
const body = req.body ?? {};
|
|
@@ -27214,7 +27448,15 @@ function nodesDomain(deps) {
|
|
|
27214
27448
|
nodeNames.set(nodeId, nodeName);
|
|
27215
27449
|
const params = { gatewayUrl: deps.gatewayUrl, nodeId, token, repoRemote: deps.repoRemote };
|
|
27216
27450
|
const script = body.platform === "windows" ? buildWindowsConnectScript(params) : buildConnectScript(params);
|
|
27217
|
-
|
|
27451
|
+
const npxCmd = buildNpxCmd({ gatewayUrl: deps.gatewayUrl, nodeId, token, nodeName });
|
|
27452
|
+
return { status: 200, body: { nodeId, nodeName, token, script, gatewayUrl: deps.gatewayUrl, npxCmd } };
|
|
27453
|
+
});
|
|
27454
|
+
router.patch("/api/nodes/:nodeId", async (req) => {
|
|
27455
|
+
const nodeId = req.params["nodeId"];
|
|
27456
|
+
if (!nodeId) return { status: 400, body: { error: "missing nodeId" } };
|
|
27457
|
+
const body = req.body ?? {};
|
|
27458
|
+
if (body.name) nodeNames.set(nodeId, body.name);
|
|
27459
|
+
return { status: 200, body: { ok: true } };
|
|
27218
27460
|
});
|
|
27219
27461
|
router.delete("/api/nodes/:nodeId", async (req) => {
|
|
27220
27462
|
const nodeId = req.params["nodeId"];
|
|
@@ -27276,7 +27518,7 @@ function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject,
|
|
|
27276
27518
|
return {
|
|
27277
27519
|
id: a.id,
|
|
27278
27520
|
type: a.type,
|
|
27279
|
-
label: a
|
|
27521
|
+
label: nodeLabel(a),
|
|
27280
27522
|
stage: deriveNodeStage(info),
|
|
27281
27523
|
currentRev: a.currentRev,
|
|
27282
27524
|
queue: info.queueLen,
|
|
@@ -27402,7 +27644,7 @@ function buildActivity(model, arts, ref) {
|
|
|
27402
27644
|
function buildAcceptance(model, arts) {
|
|
27403
27645
|
return arts.map((a) => ({
|
|
27404
27646
|
artifactId: a.id,
|
|
27405
|
-
label: a
|
|
27647
|
+
label: nodeLabel(a),
|
|
27406
27648
|
state: isConcluded(model, a.id) ? "done" : a.currentRev ? "active" : "pending"
|
|
27407
27649
|
}));
|
|
27408
27650
|
}
|
|
@@ -27420,7 +27662,7 @@ function buildInbox(model, me) {
|
|
|
27420
27662
|
const out = [];
|
|
27421
27663
|
for (const a of model.artifacts.values()) {
|
|
27422
27664
|
const ws = a.workspace;
|
|
27423
|
-
const label = a
|
|
27665
|
+
const label = nodeLabel(a);
|
|
27424
27666
|
const queue = queueOf(model, a.id);
|
|
27425
27667
|
const myOpenChangeRequest = queue.length > 0 ? annotationsOf(model, a.id).find((an) => an.state === "open" && an.author === me && an.author.startsWith("actor:human:")) : void 0;
|
|
27426
27668
|
if (myOpenChangeRequest) {
|
|
@@ -27475,6 +27717,7 @@ var init_inbox = __esm({
|
|
|
27475
27717
|
"../server/src/domains/collab/inbox.ts"() {
|
|
27476
27718
|
"use strict";
|
|
27477
27719
|
init_src2();
|
|
27720
|
+
init_node_state();
|
|
27478
27721
|
}
|
|
27479
27722
|
});
|
|
27480
27723
|
|
|
@@ -27547,7 +27790,7 @@ async function buildDashboard(model, oplog, me, deps) {
|
|
|
27547
27790
|
inFlight.push({
|
|
27548
27791
|
artifactId: a.id,
|
|
27549
27792
|
workspace: a.workspace,
|
|
27550
|
-
label: a
|
|
27793
|
+
label: nodeLabel(a),
|
|
27551
27794
|
stage: deriveNodeStage(nodeInfo(model, a)),
|
|
27552
27795
|
heldMs,
|
|
27553
27796
|
onSlaClock,
|
|
@@ -29023,7 +29266,8 @@ var init_worker = __esm({
|
|
|
29023
29266
|
- \`oasis spawn <id> --type <t> --title "<\u77ED\u540D>" --brief "<\u89C4\u683C>" [--input <up>]\` \u2014\u2014 \u65B0\u5EFA\u8282\u70B9
|
|
29024
29267
|
- \`oasis edit <id> [--title "\u2026"] [--brief "\u2026"]\` \u2014\u2014 \u6539\u8282\u70B9\u6807\u9898/brief
|
|
29025
29268
|
- \`oasis unlink <consumer> --to <up>\` \u2014\u2014 \u65AD\u4F9D\u8D56\u8FB9
|
|
29026
|
-
- \`oasis
|
|
29269
|
+
- \`oasis cancel <id> [--note "\u2026"]\` \u2014\u2014 \u5E9F\u5F03\u8282\u70B9\uFF08\u81EA\u52A8\u7EA7\u8054\u65AD\u5B83\u7684\u6240\u6709\u76F8\u5173\u8FB9\u3001\u91CD\u5524\u4E0B\u6E38\u53BB\u6389\u5BF9\u5B83\u7684\u9002\u914D\uFF09
|
|
29270
|
+
- \`oasis freeze <id> [--note "\u2026"]\` \u2014\u2014 \u51BB\u7ED3\u8282\u70B9\uFF08\u4FDD\u7559\u8F93\u51FA\u3001\u505C\u6B62\u6539\u52A8\uFF1B\u8FB9\u4E0D\u52A8\uFF09
|
|
29027
29271
|
- \`oasis cancel-part <id> --part <\u540D>\` \u2014\u2014 \u64A4\u90E8\u4EF6
|
|
29028
29272
|
- \`oasis staged\` \u2014\u2014 \u770B\u5F53\u524D\u7F13\u51B2\u5168\u90E8\u5F85\u63D0\u4EA4\u9879\uFF08+ \u63D0\u4EA4\u540E\u9884\u89C8\uFF09
|
|
29029
29273
|
- \`oasis apply --reason "\u4E3A\u4EC0\u4E48\u8FD9\u4E48\u6539"\` \u2014\u2014 \u4E00\u6B21\u63D0\u4EA4\u7F13\u51B2
|
|
@@ -29184,7 +29428,7 @@ var init_worker = __esm({
|
|
|
29184
29428
|
`## \u4F60\u7684\u52A8\u4F5C\uFF08\u6682\u5B58 \u2192 apply\uFF09`,
|
|
29185
29429
|
`- \u67D0\u65E2\u6709\u6EE1\u8DB3\u8FD9\u9700\u6C42 \u2192 \`oasis link ${gap.artifactId} --to <\u65E2\u6709id>\`\uFF0C\`oasis apply --reason "\u2026"\`\uFF08apply \u540E\u81EA\u52A8\u89E3\u8FD9\u6761 gap\uFF09\u3002`,
|
|
29186
29430
|
`- \u786E\u65E0\u5339\u914D\u3001\u65B0\u9700\u6C42 \u2192 \`oasis spawn <\u65B0id> --type <\u7C7B\u578B> --title "<\u77ED\u540D>" --brief "${gap.description}"\` + \`oasis link ${gap.artifactId} --to <\u65B0id>\` \u2192 apply\u3002`,
|
|
29187
|
-
`- \u987A\u624B\u53D1\u73B0\u4E24\u4E2A\u4EA7\u7269\u91CD\u590D \u2192 **\u5148\u628A\u4E0A\u9762\u89E3 gap \u90A3\u7B14\u5355\u72EC apply \u6389**\uFF0C\u518D\u53E6\u8D77\u4E00\u7B14\uFF1A\`oasis edit\` \u6269\u4FDD\u7559\u4EF6 brief + \`oasis link\` \u6539\u8FDE + \`oasis
|
|
29431
|
+
`- \u987A\u624B\u53D1\u73B0\u4E24\u4E2A\u4EA7\u7269\u91CD\u590D \u2192 **\u5148\u628A\u4E0A\u9762\u89E3 gap \u90A3\u7B14\u5355\u72EC apply \u6389**\uFF0C\u518D\u53E6\u8D77\u4E00\u7B14\uFF1A\`oasis edit\` \u6269\u4FDD\u7559\u4EF6 brief + \`oasis link\` \u628A\u6D88\u8D39\u8005\u6539\u8FDE\u5230\u4FDD\u7559\u4EF6 + \`oasis cancel\` \u5E9F\u5F03\u4EF6\uFF08\u81EA\u52A8\u65AD\u5B83\u7684\u8FB9\u3001\u91CD\u5524\u88AB\u6539\u8FDE\u7684\u6D88\u8D39\u8005\uFF09\uFF0C\u4E00\u8D77\u6682\u5B58\u518D apply\u3002`,
|
|
29188
29432
|
`- \u62FF\u4E0D\u51C6 \u2192 \u522B\u6682\u5B58\uFF0C\u4E00\u53E5\u8BDD\u8BF4\u660E\u7591\u8651\u6536\u5DE5\u3002`
|
|
29189
29433
|
].join("\n");
|
|
29190
29434
|
await this.runCoordinator(gap.artifactId, task, `reconcile ${gap.artifactId}: ${gap.description.slice(0, 50)}`);
|
|
@@ -38495,13 +38739,12 @@ async function startServe(opts) {
|
|
|
38495
38739
|
const blobs = new DirBlobStore(path11.join(opts.dir, "blobs"));
|
|
38496
38740
|
const assets = new DirBlobStore(path11.join(opts.dir, "assets"));
|
|
38497
38741
|
const defaultRootSchema = [
|
|
38498
|
-
{ name: "brief", description: "\u5DE5\u5355\u603B\u7EB2\uFF1A\u53D1\u8D77\u65B9\u5BF9\u9F50\u7684\u603B\u76EE\u6807\u4E0E\u8303\u56F4\uFF0C\u6574\u5F20\u534F\u4F5C\u56FE\u7684\u6839\u3002", contentType: "text", ownerRole: "founder"
|
|
38742
|
+
{ name: "brief", description: "\u5DE5\u5355\u603B\u7EB2\uFF1A\u53D1\u8D77\u65B9\u5BF9\u9F50\u7684\u603B\u76EE\u6807\u4E0E\u8303\u56F4\uFF0C\u6574\u5F20\u534F\u4F5C\u56FE\u7684\u6839\u3002", contentType: "text", ownerRole: "founder" },
|
|
38499
38743
|
{
|
|
38500
38744
|
name: "prd",
|
|
38501
38745
|
description: "\u4EA7\u54C1\u9700\u6C42\u6587\u6863\uFF1A\u8981\u505A\u4EC0\u4E48\u3001\u7ED9\u8C01\u3001\u6838\u5FC3\u573A\u666F\u4E0E\u9A8C\u6536\u6807\u51C6\u3002",
|
|
38502
38746
|
contentType: "text",
|
|
38503
38747
|
ownerRole: "pm",
|
|
38504
|
-
triggersOnMerge: [{ action: "spawn", type: "adr" }],
|
|
38505
38748
|
reviews: { gates: [{ on: "conclude", reviewers: ["architect", "designer"], quorum: 2 }] }
|
|
38506
38749
|
},
|
|
38507
38750
|
{ name: "adr", description: "\u67B6\u6784\u51B3\u7B56\u8BB0\u5F55\uFF1A\u6280\u672F\u65B9\u6848\u9009\u578B\u4E0E\u53D6\u820D\u7406\u7531\u3002", contentType: "text", ownerRole: "architect" },
|
|
@@ -39482,7 +39725,7 @@ var USAGE = `oasis \u2014\u2014 artifact-centric \u534F\u4F5C\u5185\u6838 CLI\uF
|
|
|
39482
39725
|
token \u89E3\u6790\u987A\u5E8F: --token > $OASIS_TOKEN > <dir>/operator-token
|
|
39483
39726
|
|
|
39484
39727
|
\u7248\u672C\u673A\uFF08\xA75\uFF09
|
|
39485
|
-
spawn <artifactId> --type <t> [--input <\u4E0A\u6E38id>[,<id>\u2026]] [--owner <actorId>] [--workspace ws] [--persistence temp|persistent] [--parts-file <json>]
|
|
39728
|
+
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>]
|
|
39486
39729
|
# --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
|
|
39487
39730
|
propose <artifactId> (--file <path> | --from-dir <path> | --git-commit <sha>) --reason <text> [--base <revisionId|none>] [--part <name>] [--rebase-of <revisionId>]
|
|
39488
39731
|
# --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
|
|
@@ -39505,7 +39748,9 @@ var USAGE = `oasis \u2014\u2014 artifact-centric \u534F\u4F5C\u5185\u6838 CLI\uF
|
|
|
39505
39748
|
pin <artifactId> --to <\u4E0A\u6E38> --rev <revisionId>
|
|
39506
39749
|
stale <artifactId>
|
|
39507
39750
|
blocked [artifactId]
|
|
39508
|
-
|
|
39751
|
+
cancel <artifactId> [--note "\u2026"] # \u5E9F\u5F03\u8282\u70B9\uFF1A\u7EA7\u8054\u65AD\u76F8\u5173\u8FB9\u3001\u91CD\u5524\u4E0B\u6E38
|
|
39752
|
+
freeze <artifactId> [--note "\u2026"] # \u51BB\u7ED3\u8282\u70B9\uFF1A\u4FDD\u7559\u8F93\u51FA\u3001\u505C\u6B62\u6539\u52A8
|
|
39753
|
+
accept <artifactId> [--note "\u2026"] # \u5B9A\u7A3F\uFF08\u5148 conclude \u518D accept\uFF09
|
|
39509
39754
|
assign <artifactId> --owner <actorId>
|
|
39510
39755
|
gc # \u5B64\u513F blob \u56DE\u6536\uFF08mark-and-sweep\uFF1B\u64CD\u4F5C\u8005\u547D\u4EE4\uFF0CF1\uFF09
|
|
39511
39756
|
\u8BFB\u4EA7\u7269: status <id>\uFF08\u72B6\u6001\u7D22\u5F15\uFF0C\u4E0D\u542B\u6B63\u6587\uFF09/ content <id> [path]\uFF08\u5B9E\u9645\u5185\u5BB9\uFF0C\u591A\u6587\u4EF6\u7ED9 path \u53D6\u5355\u4E2A\uFF09/ project <id>\uFF08\u9879\u76EE\u76EE\u5F55\uFF09 # \u7ED9 agent\uFF0C\u6E10\u8FDB\u62AB\u9732
|
|
@@ -39620,10 +39865,73 @@ async function runCli(argv, println = console.log) {
|
|
|
39620
39865
|
const dir = flags.get("dir") ?? process.env["OASIS_DIR"] ?? path12.resolve(".oasis");
|
|
39621
39866
|
if (command === "node" && positional[0] !== "output") {
|
|
39622
39867
|
const serverUrl = flags.get("server-ws") ?? process.env["OASIS_SERVER_WS"] ?? "ws://127.0.0.1:7320/node-gateway";
|
|
39623
|
-
const
|
|
39868
|
+
const nodeName = flags.get("name") ?? process.env["OASIS_NODE_NAME"];
|
|
39869
|
+
const nodeIdFile = path12.join(dir, "node-id");
|
|
39870
|
+
const storedNodeId = fs13.existsSync(nodeIdFile) ? fs13.readFileSync(nodeIdFile, "utf8").trim() : null;
|
|
39871
|
+
const nodeId = storedNodeId ?? flags.get("id") ?? process.env["OASIS_NODE_ID"] ?? `node-${(0, import_node_crypto20.randomUUID)()}`;
|
|
39872
|
+
if (!storedNodeId) {
|
|
39873
|
+
fs13.mkdirSync(dir, { recursive: true });
|
|
39874
|
+
fs13.writeFileSync(nodeIdFile, nodeId);
|
|
39875
|
+
}
|
|
39624
39876
|
const nodeTokenFile = path12.join(dir, "node-token");
|
|
39625
39877
|
const token2 = flags.get("token") ?? process.env["OASIS_NODE_TOKEN"] ?? (fs13.existsSync(nodeTokenFile) ? fs13.readFileSync(nodeTokenFile, "utf8").trim() : void 0);
|
|
39626
|
-
|
|
39878
|
+
if (flags.has("service")) {
|
|
39879
|
+
const { spawnSync, execSync: execSync2 } = await import("node:child_process");
|
|
39880
|
+
if (token2) {
|
|
39881
|
+
fs13.mkdirSync(dir, { recursive: true });
|
|
39882
|
+
fs13.writeFileSync(nodeTokenFile, token2, { mode: 384 });
|
|
39883
|
+
}
|
|
39884
|
+
println("\u2192 Installing oasis_test globally...");
|
|
39885
|
+
spawnSync("npm", ["install", "-g", "oasis_test@latest"], { stdio: "inherit" });
|
|
39886
|
+
const prefix = execSync2("npm prefix -g").toString().trim();
|
|
39887
|
+
const binPath = path12.join(prefix, "bin", "oasis_test");
|
|
39888
|
+
const svcArgs = `node --server-ws ${serverUrl} --id ${nodeId} --dir ${dir}${nodeName ? ` --name ${nodeName}` : ""}`;
|
|
39889
|
+
if (process.platform === "linux") {
|
|
39890
|
+
const svcFile = path12.join(os7.homedir(), ".config/systemd/user/oasis-node.service");
|
|
39891
|
+
fs13.mkdirSync(path12.dirname(svcFile), { recursive: true });
|
|
39892
|
+
if (spawnSync("systemctl", ["--user", "is-active", "--quiet", "oasis-node"], {}).status === 0)
|
|
39893
|
+
spawnSync("systemctl", ["--user", "stop", "oasis-node"], { stdio: "inherit" });
|
|
39894
|
+
fs13.writeFileSync(svcFile, [
|
|
39895
|
+
"[Unit]",
|
|
39896
|
+
"Description=Oasis Node Daemon",
|
|
39897
|
+
"After=network.target",
|
|
39898
|
+
"",
|
|
39899
|
+
"[Service]",
|
|
39900
|
+
`ExecStart=${binPath} ${svcArgs}`,
|
|
39901
|
+
"Restart=on-failure",
|
|
39902
|
+
"RestartSec=5",
|
|
39903
|
+
"StandardOutput=journal",
|
|
39904
|
+
"StandardError=journal",
|
|
39905
|
+
"",
|
|
39906
|
+
"[Install]",
|
|
39907
|
+
"WantedBy=default.target",
|
|
39908
|
+
""
|
|
39909
|
+
].join("\n"));
|
|
39910
|
+
spawnSync("systemctl", ["--user", "daemon-reload"], { stdio: "inherit" });
|
|
39911
|
+
spawnSync("systemctl", ["--user", "enable", "--now", "oasis-node"], { stdio: "inherit" });
|
|
39912
|
+
spawnSync("loginctl", ["enable-linger", os7.userInfo().username], { stdio: "ignore" });
|
|
39913
|
+
println("\u2713 Oasis node service started (systemd --user oasis-node)");
|
|
39914
|
+
println(" Status : systemctl --user status oasis-node");
|
|
39915
|
+
println(" Logs : journalctl --user -u oasis-node -f");
|
|
39916
|
+
println(" Stop : systemctl --user stop oasis-node");
|
|
39917
|
+
} else if (process.platform === "darwin") {
|
|
39918
|
+
const plist = path12.join(os7.homedir(), "Library/LaunchAgents/com.oasis.node.plist");
|
|
39919
|
+
fs13.mkdirSync(path12.dirname(plist), { recursive: true });
|
|
39920
|
+
spawnSync("launchctl", ["unload", plist], { stdio: "ignore" });
|
|
39921
|
+
const argXml = [binPath, "node", "--server-ws", serverUrl, "--id", nodeId, "--dir", dir, ...nodeName ? ["--name", nodeName] : []].map((a) => `<string>${a}</string>`).join("");
|
|
39922
|
+
fs13.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>`);
|
|
39923
|
+
spawnSync("launchctl", ["load", plist], { stdio: "inherit" });
|
|
39924
|
+
println("\u2713 Oasis node service started (macOS LaunchAgent com.oasis.node)");
|
|
39925
|
+
println(` Logs : tail -f ${os7.homedir()}/.oasis-node.log`);
|
|
39926
|
+
println(` Stop : launchctl unload ${plist}`);
|
|
39927
|
+
} else {
|
|
39928
|
+
spawnSync("sh", ["-c", `nohup ${binPath} ${svcArgs} > ${path12.join(os7.homedir(), ".oasis-node.log")} 2>&1 &`], { stdio: "inherit" });
|
|
39929
|
+
println(`\u2713 Oasis node started in background (nohup). Logs: ~/.oasis-node.log`);
|
|
39930
|
+
println(" Note: no automatic restart on reboot in this environment.");
|
|
39931
|
+
}
|
|
39932
|
+
return;
|
|
39933
|
+
}
|
|
39934
|
+
await startNode({ serverUrl, nodeId, ...token2 ? { token: token2 } : {}, ...nodeName ? { nodeName } : {} });
|
|
39627
39935
|
return;
|
|
39628
39936
|
}
|
|
39629
39937
|
if (command === "node-token") {
|
|
@@ -39762,7 +40070,7 @@ async function runCli(argv, println = console.log) {
|
|
|
39762
40070
|
if (out.length === 0) throw new Error(`\u76EE\u5F55\u4E3A\u7A7A\uFF1A${root}`);
|
|
39763
40071
|
return out;
|
|
39764
40072
|
};
|
|
39765
|
-
const STAGE_COMMANDS = /* @__PURE__ */ new Set(["link", "spawn", "unlink", "
|
|
40073
|
+
const STAGE_COMMANDS = /* @__PURE__ */ new Set(["link", "spawn", "unlink", "cancel", "freeze", "cancel-part", "edit"]);
|
|
39766
40074
|
const buildStageOp = (cmd) => {
|
|
39767
40075
|
switch (cmd) {
|
|
39768
40076
|
case "link":
|
|
@@ -39783,11 +40091,12 @@ async function runCli(argv, println = console.log) {
|
|
|
39783
40091
|
};
|
|
39784
40092
|
case "unlink":
|
|
39785
40093
|
return { action: "unlink", artifactId: needPos(positional, 0, "oasis unlink <consumer> --to <upstream>"), to: need(flags, "to") };
|
|
39786
|
-
case "
|
|
40094
|
+
case "cancel":
|
|
40095
|
+
case "freeze":
|
|
39787
40096
|
return {
|
|
39788
40097
|
action: "seal",
|
|
39789
|
-
artifactId: needPos(positional, 0,
|
|
39790
|
-
reason:
|
|
40098
|
+
artifactId: needPos(positional, 0, `oasis ${cmd} <id> [--note \u2026]`),
|
|
40099
|
+
reason: cmd === "cancel" ? "cancelled" : "frozen",
|
|
39791
40100
|
...flags.get("note") !== void 0 ? { note: flags.get("note") } : {}
|
|
39792
40101
|
};
|
|
39793
40102
|
case "cancel-part":
|
|
@@ -39812,7 +40121,7 @@ async function runCli(argv, println = console.log) {
|
|
|
39812
40121
|
case "unlink":
|
|
39813
40122
|
return `unlink ${op["artifactId"]} \u2715 ${op["to"]}`;
|
|
39814
40123
|
case "seal":
|
|
39815
|
-
return
|
|
40124
|
+
return `${op["reason"] === "cancelled" ? "cancel" : op["reason"] === "frozen" ? "freeze" : "accept"} ${op["artifactId"]}`;
|
|
39816
40125
|
case "cancelPart":
|
|
39817
40126
|
return `cancel-part ${op["artifactId"]} part=${op["part"]}`;
|
|
39818
40127
|
case "edit":
|
|
@@ -39824,7 +40133,11 @@ async function runCli(argv, println = console.log) {
|
|
|
39824
40133
|
if (process.env["OASIS_STAGE"] === "1" && STAGE_COMMANDS.has(command)) {
|
|
39825
40134
|
const op = buildStageOp(command);
|
|
39826
40135
|
const r = await api.post("/api/stage", { op });
|
|
39827
|
-
|
|
40136
|
+
let msg = `\u5DF2\u6682\u5B58\uFF1A${describeStageOp(op)}\uFF08\u7F13\u51B2 ${r.count} \u9879\uFF1B\u6539\u5B8C \`oasis apply --reason "\u2026"\` \u63D0\u4EA4\u3001\`oasis staged\` \u770B\u5168\u90E8\uFF09`;
|
|
40137
|
+
if (r.cascade && r.cascade.length > 0)
|
|
40138
|
+
msg += `
|
|
40139
|
+
apply \u65F6\u7EA7\u8054\u65AD\u8FB9\uFF1A${r.cascade.map((e) => `${e.artifactId}\u2192${e.to}`).join("\u3001")}\u2014\u2014\u6D88\u8D39\u8005\u4F1A\u88AB\u91CD\u5524\uFF1B\u6309\u9700\u6539\u6295(link\u2192\u66FF\u4EE3)/\u7EA7\u8054 cancel`;
|
|
40140
|
+
println(msg);
|
|
39828
40141
|
return;
|
|
39829
40142
|
}
|
|
39830
40143
|
switch (command) {
|
|
@@ -40135,10 +40448,12 @@ async function runCli(argv, println = console.log) {
|
|
|
40135
40448
|
println(message);
|
|
40136
40449
|
break;
|
|
40137
40450
|
}
|
|
40138
|
-
case "
|
|
40451
|
+
case "cancel":
|
|
40452
|
+
case "freeze":
|
|
40453
|
+
case "accept": {
|
|
40139
40454
|
const { message } = await api.cmd("seal", {
|
|
40140
|
-
artifactId: needPos(positional, 0,
|
|
40141
|
-
reason:
|
|
40455
|
+
artifactId: needPos(positional, 0, `oasis ${command} <artifactId> [--note \u2026]`),
|
|
40456
|
+
reason: command === "cancel" ? "cancelled" : command === "freeze" ? "frozen" : "accepted",
|
|
40142
40457
|
note: flags.get("note")
|
|
40143
40458
|
});
|
|
40144
40459
|
println(message);
|
|
@@ -40457,6 +40772,7 @@ var isRunning = (pid) => {
|
|
|
40457
40772
|
return false;
|
|
40458
40773
|
}
|
|
40459
40774
|
};
|
|
40775
|
+
var httpBase = (wsUrl) => wsUrl.replace(/^wss?:\/\//, "http://").replace(/\/node-gateway.*$/, "");
|
|
40460
40776
|
function installBinary() {
|
|
40461
40777
|
fs14.mkdirSync(path13.dirname(BIN_FILE), { recursive: true });
|
|
40462
40778
|
fs14.copyFileSync(process.argv[1], BIN_FILE);
|
|
@@ -40521,22 +40837,24 @@ function spawnDaemon() {
|
|
|
40521
40837
|
}
|
|
40522
40838
|
function stopDaemon() {
|
|
40523
40839
|
const pid = readPid();
|
|
40524
|
-
if (
|
|
40840
|
+
if (pid && isRunning(pid)) {
|
|
40525
40841
|
try {
|
|
40526
|
-
|
|
40842
|
+
process.kill(pid, "SIGTERM");
|
|
40527
40843
|
} catch {
|
|
40528
40844
|
}
|
|
40529
|
-
return false;
|
|
40530
40845
|
}
|
|
40531
40846
|
try {
|
|
40532
|
-
|
|
40847
|
+
fs14.unlinkSync(PID_FILE);
|
|
40533
40848
|
} catch {
|
|
40534
40849
|
}
|
|
40535
40850
|
try {
|
|
40536
|
-
|
|
40851
|
+
(0, import_node_child_process10.execSync)('pkill -f "oasis.js --_daemon"', { stdio: "ignore" });
|
|
40852
|
+
} catch {
|
|
40853
|
+
}
|
|
40854
|
+
try {
|
|
40855
|
+
(0, import_node_child_process10.execSync)("systemctl --user stop oasis-node 2>/dev/null", { stdio: "ignore" });
|
|
40537
40856
|
} catch {
|
|
40538
40857
|
}
|
|
40539
|
-
return true;
|
|
40540
40858
|
}
|
|
40541
40859
|
void (async () => {
|
|
40542
40860
|
const [, , cmd] = process.argv;
|
|
@@ -40563,34 +40881,48 @@ void (async () => {
|
|
|
40563
40881
|
cleanup();
|
|
40564
40882
|
process.exit(0);
|
|
40565
40883
|
});
|
|
40884
|
+
if (cfg.nodeId && cfg.name) {
|
|
40885
|
+
const base = httpBase(cfg.serverUrl);
|
|
40886
|
+
fetch(`${base}/api/nodes/${cfg.nodeId}`, {
|
|
40887
|
+
method: "PATCH",
|
|
40888
|
+
headers: { authorization: `Bearer ${cfg.token}`, "content-type": "application/json" },
|
|
40889
|
+
body: JSON.stringify({ name: cfg.name })
|
|
40890
|
+
}).catch(() => {
|
|
40891
|
+
});
|
|
40892
|
+
}
|
|
40566
40893
|
await startNode({
|
|
40567
40894
|
serverUrl: cfg.serverUrl,
|
|
40568
40895
|
token: cfg.token,
|
|
40569
|
-
nodeId: `node-${(0, import_node_crypto21.randomUUID)()}`,
|
|
40896
|
+
nodeId: cfg.nodeId ?? `node-${(0, import_node_crypto21.randomUUID)()}`,
|
|
40570
40897
|
...cfg.name ? { nodeName: cfg.name } : {}
|
|
40571
40898
|
});
|
|
40572
40899
|
return;
|
|
40573
40900
|
}
|
|
40574
40901
|
if (cmd === "start") {
|
|
40575
|
-
const
|
|
40576
|
-
const
|
|
40902
|
+
const prev = readConfig();
|
|
40903
|
+
const serverUrl = flags.get("server-ws") ?? process.env["OASIS_SERVER_WS"] ?? prev?.serverUrl;
|
|
40904
|
+
const token = flags.get("token") ?? process.env["OASIS_NODE_TOKEN"] ?? prev?.token;
|
|
40577
40905
|
if (!serverUrl || !token) {
|
|
40578
|
-
console.error("Usage: oasis start --server-ws <wss://...> --token <token> [--name <name>]");
|
|
40906
|
+
console.error("Usage: oasis start --server-ws <wss://...> --token <token> [--name <name>] [--id <nodeId>]");
|
|
40579
40907
|
process.exit(1);
|
|
40580
40908
|
}
|
|
40581
|
-
|
|
40909
|
+
const nodeId = prev?.nodeId ?? flags.get("id") ?? `node-${(0, import_node_crypto21.randomUUID)()}`;
|
|
40910
|
+
const name = flags.get("name") ?? prev?.name;
|
|
40911
|
+
const nameChanged = prev?.name !== void 0 && name !== prev.name;
|
|
40912
|
+
saveConfig({ serverUrl, token, nodeId, ...name ? { name } : {} });
|
|
40582
40913
|
installBinary();
|
|
40583
40914
|
stopDaemon();
|
|
40584
40915
|
setupAutostart();
|
|
40585
40916
|
const pid = spawnDaemon();
|
|
40586
|
-
console.log(`\u2713 daemon started (PID ${pid})
|
|
40587
|
-
logs: tail -f ${LOG_FILE}`);
|
|
40917
|
+
console.log(`\u2713 daemon started (PID ${pid})${nameChanged ? ` [name updated: ${prev.name} \u2192 ${name}]` : ""}`);
|
|
40918
|
+
console.log(` logs: tail -f ${LOG_FILE}`);
|
|
40588
40919
|
if (!process.env["PATH"]?.includes(path13.dirname(LOCAL_BIN)))
|
|
40589
40920
|
console.log(` add to PATH: echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc && source ~/.bashrc`);
|
|
40590
40921
|
return;
|
|
40591
40922
|
}
|
|
40592
40923
|
if (cmd === "stop") {
|
|
40593
|
-
|
|
40924
|
+
stopDaemon();
|
|
40925
|
+
console.log("\u2713 stopped");
|
|
40594
40926
|
return;
|
|
40595
40927
|
}
|
|
40596
40928
|
if (cmd === "restart") {
|
|
@@ -40605,7 +40937,12 @@ void (async () => {
|
|
|
40605
40937
|
}
|
|
40606
40938
|
if (cmd === "status") {
|
|
40607
40939
|
const pid = readPid();
|
|
40608
|
-
|
|
40940
|
+
const cfg = readConfig();
|
|
40941
|
+
if (pid && isRunning(pid)) {
|
|
40942
|
+
console.log(`running PID=${pid} nodeId=${cfg?.nodeId ?? "?"} name=${cfg?.name ?? "(none)"}`);
|
|
40943
|
+
} else {
|
|
40944
|
+
console.log("not running");
|
|
40945
|
+
}
|
|
40609
40946
|
return;
|
|
40610
40947
|
}
|
|
40611
40948
|
await runCli(process.argv.slice(2));
|