oasis_test 0.1.3 → 0.1.4
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 +460 -151
- 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,8 @@ 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 } };
|
|
27218
27453
|
});
|
|
27219
27454
|
router.delete("/api/nodes/:nodeId", async (req) => {
|
|
27220
27455
|
const nodeId = req.params["nodeId"];
|
|
@@ -27276,7 +27511,7 @@ function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject,
|
|
|
27276
27511
|
return {
|
|
27277
27512
|
id: a.id,
|
|
27278
27513
|
type: a.type,
|
|
27279
|
-
label: a
|
|
27514
|
+
label: nodeLabel(a),
|
|
27280
27515
|
stage: deriveNodeStage(info),
|
|
27281
27516
|
currentRev: a.currentRev,
|
|
27282
27517
|
queue: info.queueLen,
|
|
@@ -27402,7 +27637,7 @@ function buildActivity(model, arts, ref) {
|
|
|
27402
27637
|
function buildAcceptance(model, arts) {
|
|
27403
27638
|
return arts.map((a) => ({
|
|
27404
27639
|
artifactId: a.id,
|
|
27405
|
-
label: a
|
|
27640
|
+
label: nodeLabel(a),
|
|
27406
27641
|
state: isConcluded(model, a.id) ? "done" : a.currentRev ? "active" : "pending"
|
|
27407
27642
|
}));
|
|
27408
27643
|
}
|
|
@@ -27420,7 +27655,7 @@ function buildInbox(model, me) {
|
|
|
27420
27655
|
const out = [];
|
|
27421
27656
|
for (const a of model.artifacts.values()) {
|
|
27422
27657
|
const ws = a.workspace;
|
|
27423
|
-
const label = a
|
|
27658
|
+
const label = nodeLabel(a);
|
|
27424
27659
|
const queue = queueOf(model, a.id);
|
|
27425
27660
|
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
27661
|
if (myOpenChangeRequest) {
|
|
@@ -27475,6 +27710,7 @@ var init_inbox = __esm({
|
|
|
27475
27710
|
"../server/src/domains/collab/inbox.ts"() {
|
|
27476
27711
|
"use strict";
|
|
27477
27712
|
init_src2();
|
|
27713
|
+
init_node_state();
|
|
27478
27714
|
}
|
|
27479
27715
|
});
|
|
27480
27716
|
|
|
@@ -27547,7 +27783,7 @@ async function buildDashboard(model, oplog, me, deps) {
|
|
|
27547
27783
|
inFlight.push({
|
|
27548
27784
|
artifactId: a.id,
|
|
27549
27785
|
workspace: a.workspace,
|
|
27550
|
-
label: a
|
|
27786
|
+
label: nodeLabel(a),
|
|
27551
27787
|
stage: deriveNodeStage(nodeInfo(model, a)),
|
|
27552
27788
|
heldMs,
|
|
27553
27789
|
onSlaClock,
|
|
@@ -29023,7 +29259,8 @@ var init_worker = __esm({
|
|
|
29023
29259
|
- \`oasis spawn <id> --type <t> --title "<\u77ED\u540D>" --brief "<\u89C4\u683C>" [--input <up>]\` \u2014\u2014 \u65B0\u5EFA\u8282\u70B9
|
|
29024
29260
|
- \`oasis edit <id> [--title "\u2026"] [--brief "\u2026"]\` \u2014\u2014 \u6539\u8282\u70B9\u6807\u9898/brief
|
|
29025
29261
|
- \`oasis unlink <consumer> --to <up>\` \u2014\u2014 \u65AD\u4F9D\u8D56\u8FB9
|
|
29026
|
-
- \`oasis
|
|
29262
|
+
- \`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
|
|
29263
|
+
- \`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
29264
|
- \`oasis cancel-part <id> --part <\u540D>\` \u2014\u2014 \u64A4\u90E8\u4EF6
|
|
29028
29265
|
- \`oasis staged\` \u2014\u2014 \u770B\u5F53\u524D\u7F13\u51B2\u5168\u90E8\u5F85\u63D0\u4EA4\u9879\uFF08+ \u63D0\u4EA4\u540E\u9884\u89C8\uFF09
|
|
29029
29266
|
- \`oasis apply --reason "\u4E3A\u4EC0\u4E48\u8FD9\u4E48\u6539"\` \u2014\u2014 \u4E00\u6B21\u63D0\u4EA4\u7F13\u51B2
|
|
@@ -29184,7 +29421,7 @@ var init_worker = __esm({
|
|
|
29184
29421
|
`## \u4F60\u7684\u52A8\u4F5C\uFF08\u6682\u5B58 \u2192 apply\uFF09`,
|
|
29185
29422
|
`- \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
29423
|
`- \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
|
|
29424
|
+
`- \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
29425
|
`- \u62FF\u4E0D\u51C6 \u2192 \u522B\u6682\u5B58\uFF0C\u4E00\u53E5\u8BDD\u8BF4\u660E\u7591\u8651\u6536\u5DE5\u3002`
|
|
29189
29426
|
].join("\n");
|
|
29190
29427
|
await this.runCoordinator(gap.artifactId, task, `reconcile ${gap.artifactId}: ${gap.description.slice(0, 50)}`);
|
|
@@ -38495,13 +38732,12 @@ async function startServe(opts) {
|
|
|
38495
38732
|
const blobs = new DirBlobStore(path11.join(opts.dir, "blobs"));
|
|
38496
38733
|
const assets = new DirBlobStore(path11.join(opts.dir, "assets"));
|
|
38497
38734
|
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"
|
|
38735
|
+
{ 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
38736
|
{
|
|
38500
38737
|
name: "prd",
|
|
38501
38738
|
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
38739
|
contentType: "text",
|
|
38503
38740
|
ownerRole: "pm",
|
|
38504
|
-
triggersOnMerge: [{ action: "spawn", type: "adr" }],
|
|
38505
38741
|
reviews: { gates: [{ on: "conclude", reviewers: ["architect", "designer"], quorum: 2 }] }
|
|
38506
38742
|
},
|
|
38507
38743
|
{ 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 +39718,7 @@ var USAGE = `oasis \u2014\u2014 artifact-centric \u534F\u4F5C\u5185\u6838 CLI\uF
|
|
|
39482
39718
|
token \u89E3\u6790\u987A\u5E8F: --token > $OASIS_TOKEN > <dir>/operator-token
|
|
39483
39719
|
|
|
39484
39720
|
\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>]
|
|
39721
|
+
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
39722
|
# --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
39723
|
propose <artifactId> (--file <path> | --from-dir <path> | --git-commit <sha>) --reason <text> [--base <revisionId|none>] [--part <name>] [--rebase-of <revisionId>]
|
|
39488
39724
|
# --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 +39741,9 @@ var USAGE = `oasis \u2014\u2014 artifact-centric \u534F\u4F5C\u5185\u6838 CLI\uF
|
|
|
39505
39741
|
pin <artifactId> --to <\u4E0A\u6E38> --rev <revisionId>
|
|
39506
39742
|
stale <artifactId>
|
|
39507
39743
|
blocked [artifactId]
|
|
39508
|
-
|
|
39744
|
+
cancel <artifactId> [--note "\u2026"] # \u5E9F\u5F03\u8282\u70B9\uFF1A\u7EA7\u8054\u65AD\u76F8\u5173\u8FB9\u3001\u91CD\u5524\u4E0B\u6E38
|
|
39745
|
+
freeze <artifactId> [--note "\u2026"] # \u51BB\u7ED3\u8282\u70B9\uFF1A\u4FDD\u7559\u8F93\u51FA\u3001\u505C\u6B62\u6539\u52A8
|
|
39746
|
+
accept <artifactId> [--note "\u2026"] # \u5B9A\u7A3F\uFF08\u5148 conclude \u518D accept\uFF09
|
|
39509
39747
|
assign <artifactId> --owner <actorId>
|
|
39510
39748
|
gc # \u5B64\u513F blob \u56DE\u6536\uFF08mark-and-sweep\uFF1B\u64CD\u4F5C\u8005\u547D\u4EE4\uFF0CF1\uFF09
|
|
39511
39749
|
\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 +39858,73 @@ async function runCli(argv, println = console.log) {
|
|
|
39620
39858
|
const dir = flags.get("dir") ?? process.env["OASIS_DIR"] ?? path12.resolve(".oasis");
|
|
39621
39859
|
if (command === "node" && positional[0] !== "output") {
|
|
39622
39860
|
const serverUrl = flags.get("server-ws") ?? process.env["OASIS_SERVER_WS"] ?? "ws://127.0.0.1:7320/node-gateway";
|
|
39623
|
-
const
|
|
39861
|
+
const nodeName = flags.get("name") ?? process.env["OASIS_NODE_NAME"];
|
|
39862
|
+
const nodeIdFile = path12.join(dir, "node-id");
|
|
39863
|
+
const storedNodeId = fs13.existsSync(nodeIdFile) ? fs13.readFileSync(nodeIdFile, "utf8").trim() : null;
|
|
39864
|
+
const nodeId = storedNodeId ?? flags.get("id") ?? process.env["OASIS_NODE_ID"] ?? `node-${(0, import_node_crypto20.randomUUID)()}`;
|
|
39865
|
+
if (!storedNodeId) {
|
|
39866
|
+
fs13.mkdirSync(dir, { recursive: true });
|
|
39867
|
+
fs13.writeFileSync(nodeIdFile, nodeId);
|
|
39868
|
+
}
|
|
39624
39869
|
const nodeTokenFile = path12.join(dir, "node-token");
|
|
39625
39870
|
const token2 = flags.get("token") ?? process.env["OASIS_NODE_TOKEN"] ?? (fs13.existsSync(nodeTokenFile) ? fs13.readFileSync(nodeTokenFile, "utf8").trim() : void 0);
|
|
39626
|
-
|
|
39871
|
+
if (flags.has("service")) {
|
|
39872
|
+
const { spawnSync, execSync: execSync2 } = await import("node:child_process");
|
|
39873
|
+
if (token2) {
|
|
39874
|
+
fs13.mkdirSync(dir, { recursive: true });
|
|
39875
|
+
fs13.writeFileSync(nodeTokenFile, token2, { mode: 384 });
|
|
39876
|
+
}
|
|
39877
|
+
println("\u2192 Installing oasis_test globally...");
|
|
39878
|
+
spawnSync("npm", ["install", "-g", "oasis_test@latest"], { stdio: "inherit" });
|
|
39879
|
+
const prefix = execSync2("npm prefix -g").toString().trim();
|
|
39880
|
+
const binPath = path12.join(prefix, "bin", "oasis_test");
|
|
39881
|
+
const svcArgs = `node --server-ws ${serverUrl} --id ${nodeId} --dir ${dir}${nodeName ? ` --name ${nodeName}` : ""}`;
|
|
39882
|
+
if (process.platform === "linux") {
|
|
39883
|
+
const svcFile = path12.join(os7.homedir(), ".config/systemd/user/oasis-node.service");
|
|
39884
|
+
fs13.mkdirSync(path12.dirname(svcFile), { recursive: true });
|
|
39885
|
+
if (spawnSync("systemctl", ["--user", "is-active", "--quiet", "oasis-node"], {}).status === 0)
|
|
39886
|
+
spawnSync("systemctl", ["--user", "stop", "oasis-node"], { stdio: "inherit" });
|
|
39887
|
+
fs13.writeFileSync(svcFile, [
|
|
39888
|
+
"[Unit]",
|
|
39889
|
+
"Description=Oasis Node Daemon",
|
|
39890
|
+
"After=network.target",
|
|
39891
|
+
"",
|
|
39892
|
+
"[Service]",
|
|
39893
|
+
`ExecStart=${binPath} ${svcArgs}`,
|
|
39894
|
+
"Restart=on-failure",
|
|
39895
|
+
"RestartSec=5",
|
|
39896
|
+
"StandardOutput=journal",
|
|
39897
|
+
"StandardError=journal",
|
|
39898
|
+
"",
|
|
39899
|
+
"[Install]",
|
|
39900
|
+
"WantedBy=default.target",
|
|
39901
|
+
""
|
|
39902
|
+
].join("\n"));
|
|
39903
|
+
spawnSync("systemctl", ["--user", "daemon-reload"], { stdio: "inherit" });
|
|
39904
|
+
spawnSync("systemctl", ["--user", "enable", "--now", "oasis-node"], { stdio: "inherit" });
|
|
39905
|
+
spawnSync("loginctl", ["enable-linger", os7.userInfo().username], { stdio: "ignore" });
|
|
39906
|
+
println("\u2713 Oasis node service started (systemd --user oasis-node)");
|
|
39907
|
+
println(" Status : systemctl --user status oasis-node");
|
|
39908
|
+
println(" Logs : journalctl --user -u oasis-node -f");
|
|
39909
|
+
println(" Stop : systemctl --user stop oasis-node");
|
|
39910
|
+
} else if (process.platform === "darwin") {
|
|
39911
|
+
const plist = path12.join(os7.homedir(), "Library/LaunchAgents/com.oasis.node.plist");
|
|
39912
|
+
fs13.mkdirSync(path12.dirname(plist), { recursive: true });
|
|
39913
|
+
spawnSync("launchctl", ["unload", plist], { stdio: "ignore" });
|
|
39914
|
+
const argXml = [binPath, "node", "--server-ws", serverUrl, "--id", nodeId, "--dir", dir, ...nodeName ? ["--name", nodeName] : []].map((a) => `<string>${a}</string>`).join("");
|
|
39915
|
+
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>`);
|
|
39916
|
+
spawnSync("launchctl", ["load", plist], { stdio: "inherit" });
|
|
39917
|
+
println("\u2713 Oasis node service started (macOS LaunchAgent com.oasis.node)");
|
|
39918
|
+
println(` Logs : tail -f ${os7.homedir()}/.oasis-node.log`);
|
|
39919
|
+
println(` Stop : launchctl unload ${plist}`);
|
|
39920
|
+
} else {
|
|
39921
|
+
spawnSync("sh", ["-c", `nohup ${binPath} ${svcArgs} > ${path12.join(os7.homedir(), ".oasis-node.log")} 2>&1 &`], { stdio: "inherit" });
|
|
39922
|
+
println(`\u2713 Oasis node started in background (nohup). Logs: ~/.oasis-node.log`);
|
|
39923
|
+
println(" Note: no automatic restart on reboot in this environment.");
|
|
39924
|
+
}
|
|
39925
|
+
return;
|
|
39926
|
+
}
|
|
39927
|
+
await startNode({ serverUrl, nodeId, ...token2 ? { token: token2 } : {}, ...nodeName ? { nodeName } : {} });
|
|
39627
39928
|
return;
|
|
39628
39929
|
}
|
|
39629
39930
|
if (command === "node-token") {
|
|
@@ -39762,7 +40063,7 @@ async function runCli(argv, println = console.log) {
|
|
|
39762
40063
|
if (out.length === 0) throw new Error(`\u76EE\u5F55\u4E3A\u7A7A\uFF1A${root}`);
|
|
39763
40064
|
return out;
|
|
39764
40065
|
};
|
|
39765
|
-
const STAGE_COMMANDS = /* @__PURE__ */ new Set(["link", "spawn", "unlink", "
|
|
40066
|
+
const STAGE_COMMANDS = /* @__PURE__ */ new Set(["link", "spawn", "unlink", "cancel", "freeze", "cancel-part", "edit"]);
|
|
39766
40067
|
const buildStageOp = (cmd) => {
|
|
39767
40068
|
switch (cmd) {
|
|
39768
40069
|
case "link":
|
|
@@ -39783,11 +40084,12 @@ async function runCli(argv, println = console.log) {
|
|
|
39783
40084
|
};
|
|
39784
40085
|
case "unlink":
|
|
39785
40086
|
return { action: "unlink", artifactId: needPos(positional, 0, "oasis unlink <consumer> --to <upstream>"), to: need(flags, "to") };
|
|
39786
|
-
case "
|
|
40087
|
+
case "cancel":
|
|
40088
|
+
case "freeze":
|
|
39787
40089
|
return {
|
|
39788
40090
|
action: "seal",
|
|
39789
|
-
artifactId: needPos(positional, 0,
|
|
39790
|
-
reason:
|
|
40091
|
+
artifactId: needPos(positional, 0, `oasis ${cmd} <id> [--note \u2026]`),
|
|
40092
|
+
reason: cmd === "cancel" ? "cancelled" : "frozen",
|
|
39791
40093
|
...flags.get("note") !== void 0 ? { note: flags.get("note") } : {}
|
|
39792
40094
|
};
|
|
39793
40095
|
case "cancel-part":
|
|
@@ -39812,7 +40114,7 @@ async function runCli(argv, println = console.log) {
|
|
|
39812
40114
|
case "unlink":
|
|
39813
40115
|
return `unlink ${op["artifactId"]} \u2715 ${op["to"]}`;
|
|
39814
40116
|
case "seal":
|
|
39815
|
-
return
|
|
40117
|
+
return `${op["reason"] === "cancelled" ? "cancel" : op["reason"] === "frozen" ? "freeze" : "accept"} ${op["artifactId"]}`;
|
|
39816
40118
|
case "cancelPart":
|
|
39817
40119
|
return `cancel-part ${op["artifactId"]} part=${op["part"]}`;
|
|
39818
40120
|
case "edit":
|
|
@@ -39824,7 +40126,11 @@ async function runCli(argv, println = console.log) {
|
|
|
39824
40126
|
if (process.env["OASIS_STAGE"] === "1" && STAGE_COMMANDS.has(command)) {
|
|
39825
40127
|
const op = buildStageOp(command);
|
|
39826
40128
|
const r = await api.post("/api/stage", { op });
|
|
39827
|
-
|
|
40129
|
+
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`;
|
|
40130
|
+
if (r.cascade && r.cascade.length > 0)
|
|
40131
|
+
msg += `
|
|
40132
|
+
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`;
|
|
40133
|
+
println(msg);
|
|
39828
40134
|
return;
|
|
39829
40135
|
}
|
|
39830
40136
|
switch (command) {
|
|
@@ -40135,10 +40441,12 @@ async function runCli(argv, println = console.log) {
|
|
|
40135
40441
|
println(message);
|
|
40136
40442
|
break;
|
|
40137
40443
|
}
|
|
40138
|
-
case "
|
|
40444
|
+
case "cancel":
|
|
40445
|
+
case "freeze":
|
|
40446
|
+
case "accept": {
|
|
40139
40447
|
const { message } = await api.cmd("seal", {
|
|
40140
|
-
artifactId: needPos(positional, 0,
|
|
40141
|
-
reason:
|
|
40448
|
+
artifactId: needPos(positional, 0, `oasis ${command} <artifactId> [--note \u2026]`),
|
|
40449
|
+
reason: command === "cancel" ? "cancelled" : command === "freeze" ? "frozen" : "accepted",
|
|
40142
40450
|
note: flags.get("note")
|
|
40143
40451
|
});
|
|
40144
40452
|
println(message);
|
|
@@ -40566,7 +40874,7 @@ void (async () => {
|
|
|
40566
40874
|
await startNode({
|
|
40567
40875
|
serverUrl: cfg.serverUrl,
|
|
40568
40876
|
token: cfg.token,
|
|
40569
|
-
nodeId: `node-${(0, import_node_crypto21.randomUUID)()}`,
|
|
40877
|
+
nodeId: cfg.nodeId ?? `node-${(0, import_node_crypto21.randomUUID)()}`,
|
|
40570
40878
|
...cfg.name ? { nodeName: cfg.name } : {}
|
|
40571
40879
|
});
|
|
40572
40880
|
return;
|
|
@@ -40578,7 +40886,8 @@ void (async () => {
|
|
|
40578
40886
|
console.error("Usage: oasis start --server-ws <wss://...> --token <token> [--name <name>]");
|
|
40579
40887
|
process.exit(1);
|
|
40580
40888
|
}
|
|
40581
|
-
|
|
40889
|
+
const nodeId = flags.get("id") ?? readConfig()?.nodeId ?? `node-${(0, import_node_crypto21.randomUUID)()}`;
|
|
40890
|
+
saveConfig({ serverUrl, token, name: flags.get("name") ?? readConfig()?.name, nodeId });
|
|
40582
40891
|
installBinary();
|
|
40583
40892
|
stopDaemon();
|
|
40584
40893
|
setupAutostart();
|