oasis_test_v2 2.2.16 → 2.2.17
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/_base/wrapper-base.sh +27 -8
- package/dist/index.js +291 -123
- package/package.json +2 -2
|
@@ -17,6 +17,18 @@
|
|
|
17
17
|
|
|
18
18
|
set -euo pipefail
|
|
19
19
|
|
|
20
|
+
# 这个候选是不是一个 oasis wrapper?
|
|
21
|
+
#
|
|
22
|
+
# 判据与 TS 侧的 `isOasisWrapperPath` **同一条**:解析到底之后路径以 `/wrapper.sh` 结尾。
|
|
23
|
+
# 所有连接器的 wrapper 都是「以 commandName 之名软链到 <connector>/wrapper.sh」,
|
|
24
|
+
# 所以这一条既认得出自己、也认得出**别的会话/别的部署根** stage 出来的那些。
|
|
25
|
+
oasis_is_wrapper() {
|
|
26
|
+
case "$(realpath "$1" 2>/dev/null)" in
|
|
27
|
+
*/wrapper.sh) return 0 ;;
|
|
28
|
+
*) return 1 ;;
|
|
29
|
+
esac
|
|
30
|
+
}
|
|
31
|
+
|
|
20
32
|
# Resolve the real binary path (avoid recursion into self).
|
|
21
33
|
# If ${OASIS_BIN_VAR} is already set (injected by inject()), use it — unless it points
|
|
22
34
|
# back at this very wrapper. Otherwise scan PATH for the first executable that isn't us.
|
|
@@ -24,9 +36,6 @@ oasis_resolve_bin() {
|
|
|
24
36
|
local bin_var="${OASIS_BIN_VAR:?OASIS_BIN_VAR must be set}"
|
|
25
37
|
local cmd_name="${OASIS_CMD_NAME:?OASIS_CMD_NAME must be set}"
|
|
26
38
|
|
|
27
|
-
local SELF
|
|
28
|
-
SELF="$(realpath "$0" 2>/dev/null || echo "$0")"
|
|
29
|
-
|
|
30
39
|
# If already set by inject() AND executable on THIS machine, skip the PATH scan —
|
|
31
40
|
# but **only if it isn't this wrapper itself**.
|
|
32
41
|
#
|
|
@@ -36,18 +45,28 @@ oasis_resolve_bin() {
|
|
|
36
45
|
# 触发更新就会把 /tmp/oasis-conn-* 带进去)。于是 GH_BIN 指向 wrapper 自己 →
|
|
37
46
|
# 这里的快路径直接 return → 后面 exec "$GH_BIN" 就是无限递归调用自身。
|
|
38
47
|
# 症状还特别误导:递归被 oasis_check_available 拦下,报的是「cli is upgrading」。
|
|
39
|
-
|
|
40
|
-
|
|
48
|
+
# ⚠ 判据是「是不是 wrapper」,不是「是不是**我**」:daemon 的 PATH 被污染时,
|
|
49
|
+
# inject() 解出来的可能是**另一个**会话的 wrapper,信了照样递归。
|
|
50
|
+
if [ -n "${!bin_var:-}" ] && [ -x "${!bin_var:-}" ] && ! oasis_is_wrapper "${!bin_var}"; then
|
|
41
51
|
return 0
|
|
42
52
|
fi
|
|
43
53
|
# 空 / 不可执行 / 指向自己:清掉,让下面的 PATH 扫描重新填。
|
|
44
54
|
unset "$bin_var" 2>/dev/null || true
|
|
45
55
|
|
|
46
|
-
# Fallback: scan PATH
|
|
56
|
+
# Fallback: scan PATH —— **跳过所有 oasis wrapper,不只是自己**。
|
|
57
|
+
#
|
|
58
|
+
# ⚠ 把 `oasis_is_wrapper` 换回 `!= "$SELF"` 会炸机(2026-09-11 两次实测,进程数冲到
|
|
59
|
+
# 1.1 万、load 391)。同一台机器上常常同时挂着**多个** staged wrapper 目录
|
|
60
|
+
# (不同会话、不同部署根),它们各自是**不同的 wrapper.sh 文件**,realpath 互不相等;
|
|
61
|
+
# 只比 `$0` 的话,A 把 B 当「真 CLI」exec 过去,B 再把 A 当真 CLI,
|
|
62
|
+
# 叠上 `oasis_check_available` 的 `--version` 自检,每跳再 fork 一次 → 指数级。
|
|
63
|
+
#
|
|
64
|
+
# 平时看不出来,是因为 inject() 会把 LARK_BIN/GH_BIN 直接设好、走上面那条快路径,
|
|
65
|
+
# 根本不扫 PATH。那个绝对路径**一旦失效**(文件被挪走/升级换名)就立刻掉进这里。
|
|
47
66
|
local candidate
|
|
48
67
|
while IFS= read -r dir; do
|
|
49
68
|
candidate="$dir/$cmd_name"
|
|
50
|
-
if [ -x "$candidate" ] &&
|
|
69
|
+
if [ -x "$candidate" ] && ! oasis_is_wrapper "$candidate"; then
|
|
51
70
|
printf -v "$bin_var" '%s' "$candidate"
|
|
52
71
|
export "$bin_var"
|
|
53
72
|
return 0
|
|
@@ -67,7 +86,7 @@ oasis_resolve_bin() {
|
|
|
67
86
|
# 还能被找回来,不至于「按 <cmd> 扫 PATH 一个都找不到」。**别删它**,也别据此重新引入自动挪动。
|
|
68
87
|
while IFS= read -r dir; do
|
|
69
88
|
candidate="$dir/$cmd_name.oasis-displaced"
|
|
70
|
-
if [ -x "$candidate" ] &&
|
|
89
|
+
if [ -x "$candidate" ] && ! oasis_is_wrapper "$candidate"; then
|
|
71
90
|
printf -v "$bin_var" '%s' "$candidate"
|
|
72
91
|
export "$bin_var"
|
|
73
92
|
return 0
|
package/dist/index.js
CHANGED
|
@@ -25452,7 +25452,9 @@ var init_codex = __esm({
|
|
|
25452
25452
|
const reasoningEffort = this.opts.reasoningEffort;
|
|
25453
25453
|
return runProtocolSession(job, {
|
|
25454
25454
|
bin: this.opts.codexBin ?? "codex",
|
|
25455
|
-
|
|
25455
|
+
// 同 exec 那条路:关掉登录 shell,否则 `~/.profile` 会把 `$HOME/.local/bin` 前置到
|
|
25456
|
+
// PATH 最前,压过注入的连接器 wrapper(理由与实证见 exec 分支里那段长注释)。
|
|
25457
|
+
args: ["app-server", "--stdio", "-c", "allow_login_shell=false"],
|
|
25456
25458
|
extraArgs: this.opts.extraArgs,
|
|
25457
25459
|
blockedFlags: CODEX_APP_SERVER_BLOCKED_FLAGS,
|
|
25458
25460
|
workRoot: this.opts.workRoot,
|
|
@@ -25633,6 +25635,24 @@ ${task}` : task;
|
|
|
25633
25635
|
// 打开 reasoning 摘要通道:默认 auto,让 codex 产出 reasoning item(→ thought → 前端思考块)。
|
|
25634
25636
|
...this.opts.reasoningSummary !== "none" ? ["-c", `model_reasoning_summary=${this.opts.reasoningSummary ?? "auto"}`] : [],
|
|
25635
25637
|
...this.opts.reasoningEffort ? ["-c", `model_reasoning_effort=${this.opts.reasoningEffort}`] : [],
|
|
25638
|
+
/* ★ 关掉登录 shell(2026-09-11)。
|
|
25639
|
+
codex 的 shell 工具默认把每条命令丢进 `/bin/bash -lc`——本机 codex 自己的
|
|
25640
|
+
thread_history 里逐字记着 `"command":"/bin/bash -lc \"pwd && rg …"`。
|
|
25641
|
+
登录 shell 会重跑 `~/.profile`,而 Ubuntu 的 profile 里是
|
|
25642
|
+
`PATH="$HOME/.local/bin:$PATH"`(**前置**);于是任何装在 `~/.local/bin` 的同名 CLI
|
|
25643
|
+
都会抢到注入的连接器 wrapper 前面。实测后果:飞书连接器凭据、连接记录全对,
|
|
25644
|
+
dev-agent(codex)跑 `lark-cli whoami` 却回 `not_configured`——wrapper 被绕过、
|
|
25645
|
+
而隔离键 `LARKSUITE_CLI_CONFIG_DIR` 照旧生效指着空目录,两头落空。
|
|
25646
|
+
同机同用户的 claude-code agent 一切正常,差别只有这一个 `-l`。
|
|
25647
|
+
|
|
25648
|
+
`allow_login_shell` 是 codex 的正式配置键(本机实测:给它传整数会报
|
|
25649
|
+
`invalid type: integer, expected a boolean in \`allow_login_shell\``,
|
|
25650
|
+
而不存在的键是被静默忽略的),exec 与 app-server 两条路都吃 `-c` 覆盖。
|
|
25651
|
+
|
|
25652
|
+
⚠ 未验证的一格:这个键**是否真的**就是那个 `-l` 的开关,只能等下一次
|
|
25653
|
+
codex 派单后在节点上 `ps` 看是 `bash -lc` 还是 `bash -c`。设错了是 no-op,不会更坏。 */
|
|
25654
|
+
"-c",
|
|
25655
|
+
"allow_login_shell=false",
|
|
25636
25656
|
// ADR「agent CLI 启动参数员工级可配置化」:员工配置 job.extraArgs(在前)+ opts.extraArgs(静态默认,在后),
|
|
25637
25657
|
// 过 CODEX_BLOCKED_FLAGS 拦截。追加在**位置 prompt 之前**(codex exec 的 [PROMPT] 必须是末位参数)。
|
|
25638
25658
|
...prepareExtraArgs(
|
|
@@ -28663,7 +28683,7 @@ function projectsIntoContent(kind, role, origin, telemetryTextInContent) {
|
|
|
28663
28683
|
return true;
|
|
28664
28684
|
}
|
|
28665
28685
|
}
|
|
28666
|
-
var import_node_crypto15, CHAT_ITEMS_DOUBLE_WRITE_ENV, ChatItemLedger;
|
|
28686
|
+
var import_node_crypto15, CHAT_ITEMS_DOUBLE_WRITE_ENV, SEGMENT_END_NOTE, ChatItemLedger;
|
|
28667
28687
|
var init_chat_item_ledger = __esm({
|
|
28668
28688
|
"../server/src/chat-item-ledger.ts"() {
|
|
28669
28689
|
"use strict";
|
|
@@ -28671,6 +28691,7 @@ var init_chat_item_ledger = __esm({
|
|
|
28671
28691
|
init_chat_item();
|
|
28672
28692
|
import_node_crypto15 = require("node:crypto");
|
|
28673
28693
|
CHAT_ITEMS_DOUBLE_WRITE_ENV = "OASIS_CHAT_ITEMS_DOUBLE_WRITE";
|
|
28694
|
+
SEGMENT_END_NOTE = "\u672C\u6BB5\u65E0\u8F93\u51FA\uFF08\u4EBA\u5728\u9996\u4E2A\u6B63\u6587\u4E4B\u524D\u63D2\u4E86\u8BDD\uFF09";
|
|
28674
28695
|
ChatItemLedger = class {
|
|
28675
28696
|
constructor(deps) {
|
|
28676
28697
|
this.deps = deps;
|
|
@@ -28699,6 +28720,8 @@ var init_chat_item_ledger = __esm({
|
|
|
28699
28720
|
log;
|
|
28700
28721
|
persistEnabled;
|
|
28701
28722
|
telemetryTextInContent;
|
|
28723
|
+
/** 本段的「无输出」注解已经提前落过(`closeSegmentBeforeUserInput`),`beginSegment` 别再落一条。 */
|
|
28724
|
+
segmentEndEmitted = false;
|
|
28702
28725
|
/** **不变量 1**:`providerItemKey → itemId`,本账本唯一持有。切段/收尾清空。 */
|
|
28703
28726
|
itemKeyMap = /* @__PURE__ */ new Map();
|
|
28704
28727
|
/** 内存账本:itemId → 行。插入顺序即 seq 顺序。 */
|
|
@@ -28916,19 +28939,11 @@ var init_chat_item_ledger = __esm({
|
|
|
28916
28939
|
* 映射一并清空——新段是新行,复用旧 itemId 会把新文本写进上一条消息。
|
|
28917
28940
|
*/
|
|
28918
28941
|
beginSegment() {
|
|
28919
|
-
this.
|
|
28920
|
-
|
|
28921
|
-
|
|
28922
|
-
|
|
28923
|
-
|
|
28924
|
-
status: "completed",
|
|
28925
|
-
origin: "server",
|
|
28926
|
-
text: "",
|
|
28927
|
-
attrs: {},
|
|
28928
|
-
inContent: false,
|
|
28929
|
-
persist: true,
|
|
28930
|
-
payload: { code: "segment_end", note: "\u672C\u6BB5\u65E0\u8F93\u51FA\uFF08\u4EBA\u5728\u9996\u4E2A\u6B63\u6587\u4E4B\u524D\u63D2\u4E86\u8BDD\uFF09" }
|
|
28931
|
-
});
|
|
28942
|
+
if (this.segmentEndEmitted) {
|
|
28943
|
+
this.segmentEndEmitted = false;
|
|
28944
|
+
} else {
|
|
28945
|
+
this.closeStreamingRows("completed");
|
|
28946
|
+
if (this.segmentItemCount === 0 && this.messageId) this.insertSegmentEnd();
|
|
28932
28947
|
}
|
|
28933
28948
|
this.itemKeyMap.clear();
|
|
28934
28949
|
this.lastTurnId = null;
|
|
@@ -28936,6 +28951,41 @@ var init_chat_item_ledger = __esm({
|
|
|
28936
28951
|
this.segment += 1;
|
|
28937
28952
|
this.segmentItemCount = 0;
|
|
28938
28953
|
}
|
|
28954
|
+
/**
|
|
28955
|
+
* 人插话时,先把「这一段到此为止」落定;**必须在那条 user 行之前调用**。
|
|
28956
|
+
*
|
|
28957
|
+
* 展示序(`ord`)由落库那一刻的表级序列派,所以**谁先落库谁在上面**。此前这条注解跟在
|
|
28958
|
+
* `beginSegment()` 里、而 `beginSegment` 由 `onUserInput` 回调在 user 行之后触发,于是
|
|
28959
|
+
* 「本段无输出」排到了人说的那句话**下面**——发起人 2026-09-11 的现场(会话 `eca44da4`:
|
|
28960
|
+
* user 行 ord 8503、注解 ord 8504)。他要的顺序是:开启这一轮的那条消息 → agent 头像块
|
|
28961
|
+
* (里面就是这条注解)→ 人插的那句话 → agent 接着说。
|
|
28962
|
+
*
|
|
28963
|
+
* 返回落下的那一行(`null` = 本段有输出、或还没有 assistant 行可归属,不需要注解),
|
|
28964
|
+
* 调用方据它把这条推上 v3 流——不推的话它只在下一次快照才出现。
|
|
28965
|
+
*/
|
|
28966
|
+
closeSegmentBeforeUserInput() {
|
|
28967
|
+
this.closeStreamingRows("completed");
|
|
28968
|
+
if (this.segmentEndEmitted) return null;
|
|
28969
|
+
if (this.segmentItemCount !== 0 || !this.messageId) return null;
|
|
28970
|
+
const id = this.insertSegmentEnd();
|
|
28971
|
+
this.segmentEndEmitted = true;
|
|
28972
|
+
const row = this.rows.get(id);
|
|
28973
|
+
return { itemId: id, version: row.wireVersion, startedVersion: row.startedVersion, ord: row.ord };
|
|
28974
|
+
}
|
|
28975
|
+
/** 零输出段兜底(S1 评审 B1):那条 assistant 行库里已经在了,一条 item 都没有会被对账抓成假红。 */
|
|
28976
|
+
insertSegmentEnd() {
|
|
28977
|
+
return this.insert({
|
|
28978
|
+
kind: "control",
|
|
28979
|
+
role: "assistant",
|
|
28980
|
+
status: "completed",
|
|
28981
|
+
origin: "server",
|
|
28982
|
+
text: "",
|
|
28983
|
+
attrs: {},
|
|
28984
|
+
inContent: false,
|
|
28985
|
+
persist: true,
|
|
28986
|
+
payload: { code: "segment_end", note: SEGMENT_END_NOTE }
|
|
28987
|
+
});
|
|
28988
|
+
}
|
|
28939
28989
|
/** 行落库了,把本段还挂着 NULL 的 assistant item 认领回去,并让后续 item 直接带上它。 */
|
|
28940
28990
|
bindMessage(messageId) {
|
|
28941
28991
|
this.messageId = messageId;
|
|
@@ -159847,6 +159897,7 @@ var DEFAULT_BUFFER_MAX, DEFAULT_GRACE_MS, DEFAULT_LIVE_COALESCE_MS, DEFAULT_LIVE
|
|
|
159847
159897
|
var init_live_chat = __esm({
|
|
159848
159898
|
"../server/src/live-chat.ts"() {
|
|
159849
159899
|
"use strict";
|
|
159900
|
+
init_chat_item_ledger();
|
|
159850
159901
|
DEFAULT_BUFFER_MAX = 4e3;
|
|
159851
159902
|
DEFAULT_GRACE_MS = 6e4;
|
|
159852
159903
|
DEFAULT_LIVE_COALESCE_MS = 24;
|
|
@@ -160593,6 +160644,24 @@ var init_live_chat = __esm({
|
|
|
160593
160644
|
text: shown,
|
|
160594
160645
|
...opts?.attachments?.length ? { attachments: opts.attachments } : {}
|
|
160595
160646
|
};
|
|
160647
|
+
const segmentEnd = (() => {
|
|
160648
|
+
try {
|
|
160649
|
+
return turn.items?.closeSegmentBeforeUserInput() ?? null;
|
|
160650
|
+
} catch {
|
|
160651
|
+
return null;
|
|
160652
|
+
}
|
|
160653
|
+
})();
|
|
160654
|
+
if (segmentEnd) {
|
|
160655
|
+
this.publishServerItem(turn, {
|
|
160656
|
+
itemId: segmentEnd.itemId,
|
|
160657
|
+
itemType: "control",
|
|
160658
|
+
startedVersion: segmentEnd.startedVersion,
|
|
160659
|
+
version: segmentEnd.version,
|
|
160660
|
+
ord: segmentEnd.ord,
|
|
160661
|
+
status: "completed",
|
|
160662
|
+
payload: { code: "segment_end", note: SEGMENT_END_NOTE }
|
|
160663
|
+
});
|
|
160664
|
+
}
|
|
160596
160665
|
try {
|
|
160597
160666
|
turn.items?.recordUserInput(shown, opts?.attachments, opts?.clientKeys);
|
|
160598
160667
|
} catch {
|
|
@@ -212756,7 +212825,15 @@ var init_service4 = __esm({
|
|
|
212756
212825
|
* 亲手提交过至少一版」。这是仓库里唯一一条真实存在的「产物 ↔ 人」关系;
|
|
212757
212826
|
* 「谁 own 这个节点」不用(节点 owner 是**派给谁**,派了没干完的也会被算成产出,那是假数据)。
|
|
212758
212827
|
*
|
|
212759
|
-
*
|
|
212828
|
+
* **空轮不算产出**(ADR 0539 补记):内核把**每一轮执行**都投影成一版 revision,这一轮一个产出文件
|
|
212829
|
+
* 都没有时 bridge 如实给 `contentKind="empty"`(运行中占位 / 被限流打死的空跑 / 只留结论没产正文,
|
|
212830
|
+
* 见 `bridge-readmodel.workToRevision` 与 ADR-0134)。那种版本这里一律跳过——它不是「他交了什么」,
|
|
212831
|
+
* 展示层拿到只会得到一行没有大小、点开写着「还没有可预览的正文」的空行。
|
|
212832
|
+
* 项目页「相关文件」早就不列这类轮次(`collectEngineFilesForProject` 的
|
|
212833
|
+
* `latestAcceptId` / `cancelledAt` / `arts.length === 0` 三道闸),本栏此前漏了这条口径。
|
|
212834
|
+
*
|
|
212835
|
+
* 于是 `revisionCount` 记的是**他交付过几版**、`latestRevisionId`/`updatedAt` 取**他最近一次真的
|
|
212836
|
+
* 交了东西的那一版**;一版都没交过内容的产物整条不出现。
|
|
212760
212837
|
*
|
|
212761
212838
|
* 数据同 `artifactDetail`:从 `kernel.model` 现算投影(新引擎 oplog 已退役、派生 store 恒空),
|
|
212762
212839
|
* 不读 `artifact_revisions` 表——读那张表在新引擎上永远是空列表。
|
|
@@ -212774,6 +212851,7 @@ var init_service4 = __esm({
|
|
|
212774
212851
|
const byArtifact = /* @__PURE__ */ new Map();
|
|
212775
212852
|
for (const revision of projection.revisions) {
|
|
212776
212853
|
if (revision.actorId !== actorId) continue;
|
|
212854
|
+
if (isEmptyContent(this.kernel.model.revisions.get(revision.revisionId)?.contentKind)) continue;
|
|
212777
212855
|
const artifact = artifactById.get(revision.artifactId);
|
|
212778
212856
|
if (!artifact) continue;
|
|
212779
212857
|
const prev = byArtifact.get(revision.artifactId);
|
|
@@ -212816,8 +212894,10 @@ var init_service4 = __esm({
|
|
|
212816
212894
|
* 同一个 {@link contentByteSize}**,不另写一套判据):装箱单摊成每个物理文件一行,
|
|
212817
212895
|
* 其余(inline-blob / external-pin / 装箱单解析失败)整份产物一行。
|
|
212818
212896
|
*
|
|
212819
|
-
*
|
|
212820
|
-
*
|
|
212897
|
+
* 取哪一版:**他自己最近交付过内容的那一版**(`latestRevisionId`,空轮已在
|
|
212898
|
+
* {@link listActorArtifacts} 里滤掉),不是产物的 current head——这一栏回答的是「他交了什么」,
|
|
212899
|
+
* 拿别人后来覆盖的版本冒充他的产出就是假数据。该版本 id 随行发给前端(`revisionId`),
|
|
212900
|
+
* 预览栏据它定位同一版:行与预览必须指着同一个东西,否则会出现「行上有 20 KB、点开说没有正文」。
|
|
212821
212901
|
*/
|
|
212822
212902
|
async listActorFiles(actorId) {
|
|
212823
212903
|
const artifacts = await this.listActorArtifacts(actorId);
|
|
@@ -212829,6 +212909,7 @@ var init_service4 = __esm({
|
|
|
212829
212909
|
const base = {
|
|
212830
212910
|
artifactId: item.artifactId,
|
|
212831
212911
|
projectId: item.projectId,
|
|
212912
|
+
revisionId: item.latestRevisionId,
|
|
212832
212913
|
type: item.type,
|
|
212833
212914
|
updatedAt: item.updatedAt,
|
|
212834
212915
|
...contentKind ? { contentKind } : {},
|
|
@@ -220147,6 +220228,20 @@ ${input.description}
|
|
|
220147
220228
|
};
|
|
220148
220229
|
});
|
|
220149
220230
|
}
|
|
220231
|
+
/**
|
|
220232
|
+
* 这一行变量是谁建的(子 PRD《密钥管理》§1 的权限判据)。**只读元数据、不解密**——
|
|
220233
|
+
* `listVariables()` 会把每一行都解一遍出掩码,写路径上没必要付那个钱。
|
|
220234
|
+
*
|
|
220235
|
+
* 返回 undefined 有两种含义,调用方按「放行」处理:行不存在(新建),
|
|
220236
|
+
* 或行是 2026-09-10 之前落的(那时还没有 created_by 这一格)。
|
|
220237
|
+
*/
|
|
220238
|
+
async variableCreatedBy(key, selector = {}) {
|
|
220239
|
+
const rows = await this.opts.store.listVariables();
|
|
220240
|
+
const hit = rows.find(
|
|
220241
|
+
(r) => r.key === key && (r.actorId ?? "") === (selector.actorId ?? "") && (r.projectId ?? "") === (selector.projectId ?? "")
|
|
220242
|
+
);
|
|
220243
|
+
return hit?.createdBy;
|
|
220244
|
+
}
|
|
220150
220245
|
async revealVariable(key, actorId) {
|
|
220151
220246
|
const ct = await this.opts.store.getVariableCiphertext(key, actorId);
|
|
220152
220247
|
if (ct === null) return null;
|
|
@@ -221932,6 +222027,17 @@ function actorsDomain(opts) {
|
|
|
221932
222027
|
);
|
|
221933
222028
|
}
|
|
221934
222029
|
};
|
|
222030
|
+
const requireSecretOwner = async (req, service, key, selector = {}) => {
|
|
222031
|
+
const owner = await service.variableCreatedBy(key, selector);
|
|
222032
|
+
if (!owner || owner === req.auth.actor) return;
|
|
222033
|
+
const manager = opts.isCompanyManager ? await opts.isCompanyManager(req.auth.companyId ?? "", req.auth.actor).catch(() => false) : false;
|
|
222034
|
+
if (manager) return;
|
|
222035
|
+
throw new ApiError(
|
|
222036
|
+
403,
|
|
222037
|
+
"SECRET_NOT_OWNED",
|
|
222038
|
+
"\u8FD9\u4EFD\u5BC6\u94A5\u7531\u522B\u4EBA\u521B\u5EFA\uFF0C\u53EA\u6709\u521B\u5EFA\u8005\u672C\u4EBA\u6216\u7EC4\u7EC7\u7BA1\u7406\u8005\u53EF\u4EE5\u4FEE\u6539 / \u5220\u9664"
|
|
222039
|
+
);
|
|
222040
|
+
};
|
|
221935
222041
|
const auditVariableChange = async (req, kind, variableKey, targetScope) => {
|
|
221936
222042
|
await opts.credentialAudit?.({
|
|
221937
222043
|
kind,
|
|
@@ -221986,6 +222092,7 @@ function actorsDomain(opts) {
|
|
|
221986
222092
|
);
|
|
221987
222093
|
}
|
|
221988
222094
|
if (b2.value === void 0 || b2.value === "") return { status: 200, body: { ok: true } };
|
|
222095
|
+
await requireSecretOwner(req, service, b2.key, scope === "project" ? { projectId: b2.projectId } : {});
|
|
221989
222096
|
await service.putVariable({
|
|
221990
222097
|
key: b2.key,
|
|
221991
222098
|
value: b2.value,
|
|
@@ -222013,6 +222120,7 @@ function actorsDomain(opts) {
|
|
|
222013
222120
|
requireVariableManager(req);
|
|
222014
222121
|
const { service } = await resolveCtx(req.auth.companyId);
|
|
222015
222122
|
const projectId2 = req.query.get("projectId") ?? void 0;
|
|
222123
|
+
await requireSecretOwner(req, service, req.params.key, projectId2 ? { projectId: projectId2 } : {});
|
|
222016
222124
|
await service.deleteVariable(
|
|
222017
222125
|
req.params.key,
|
|
222018
222126
|
void 0,
|
|
@@ -222060,6 +222168,7 @@ function actorsDomain(opts) {
|
|
|
222060
222168
|
);
|
|
222061
222169
|
}
|
|
222062
222170
|
}
|
|
222171
|
+
await requireSecretOwner(req, service, b2.key, { actorId });
|
|
222063
222172
|
await service.putVariable({
|
|
222064
222173
|
key: b2.key,
|
|
222065
222174
|
value: b2.value,
|
|
@@ -222080,6 +222189,7 @@ function actorsDomain(opts) {
|
|
|
222080
222189
|
router.delete("/api/actors/:id/variables/:key", async (req) => {
|
|
222081
222190
|
requireVariableManager(req);
|
|
222082
222191
|
const { service } = await resolveCtx(req.auth.companyId);
|
|
222192
|
+
await requireSecretOwner(req, service, req.params.key, { actorId: req.params.id });
|
|
222083
222193
|
await service.deleteVariable(req.params.key, req.params.id);
|
|
222084
222194
|
await auditVariableChange(req, "credential_delete", req.params.key, `personal:${req.params.id}`);
|
|
222085
222195
|
return { status: 200, body: { ok: true } };
|
|
@@ -222888,7 +222998,7 @@ function createActorsDomain(opts) {
|
|
|
222888
222998
|
return {
|
|
222889
222999
|
service: defaultCtx.service,
|
|
222890
223000
|
resolveCtx,
|
|
222891
|
-
register: actorsDomain({ resolveCtx, ...opts.trace ? { trace: opts.trace } : {}, ...opts.listBuiltinSkills ? { listBuiltinSkills: opts.listBuiltinSkills } : {}, ...opts.getBuiltinSkills ? { getBuiltinSkills: opts.getBuiltinSkills } : {}, ...opts.getConnectorSkills ? { getConnectorSkills: opts.getConnectorSkills } : {}, ...opts.refreshConnectorSkills ? { refreshConnectorSkills: opts.refreshConnectorSkills } : {}, ...opts.memory ? { memory: opts.memory } : {}, ...opts.resolveDispatchScope ? { resolveDispatchScope: opts.resolveDispatchScope } : {}, ...opts.projectExists ? { projectExists: opts.projectExists } : {}, ...opts.credentialAudit ? { credentialAudit: opts.credentialAudit } : {}, ...opts.readCredentialAudit ? { readCredentialAudit: opts.readCredentialAudit } : {}, ...opts.credentialRevealLimits ? { credentialRevealLimits: opts.credentialRevealLimits } : {}, ...opts.skillMarket ? { skillMarket: opts.skillMarket } : {}, ...opts.resolveNodeTenancy ? { resolveNodeTenancy: opts.resolveNodeTenancy } : {} })
|
|
223001
|
+
register: actorsDomain({ resolveCtx, ...opts.trace ? { trace: opts.trace } : {}, ...opts.listBuiltinSkills ? { listBuiltinSkills: opts.listBuiltinSkills } : {}, ...opts.getBuiltinSkills ? { getBuiltinSkills: opts.getBuiltinSkills } : {}, ...opts.getConnectorSkills ? { getConnectorSkills: opts.getConnectorSkills } : {}, ...opts.refreshConnectorSkills ? { refreshConnectorSkills: opts.refreshConnectorSkills } : {}, ...opts.memory ? { memory: opts.memory } : {}, ...opts.resolveDispatchScope ? { resolveDispatchScope: opts.resolveDispatchScope } : {}, ...opts.projectExists ? { projectExists: opts.projectExists } : {}, ...opts.isCompanyManager ? { isCompanyManager: opts.isCompanyManager } : {}, ...opts.credentialAudit ? { credentialAudit: opts.credentialAudit } : {}, ...opts.readCredentialAudit ? { readCredentialAudit: opts.readCredentialAudit } : {}, ...opts.credentialRevealLimits ? { credentialRevealLimits: opts.credentialRevealLimits } : {}, ...opts.skillMarket ? { skillMarket: opts.skillMarket } : {}, ...opts.resolveNodeTenancy ? { resolveNodeTenancy: opts.resolveNodeTenancy } : {} })
|
|
222892
223002
|
};
|
|
222893
223003
|
}
|
|
222894
223004
|
var import_node_crypto49;
|
|
@@ -223245,6 +223355,7 @@ ${errs.join("\n")}`);
|
|
|
223245
223355
|
id: item.id,
|
|
223246
223356
|
artifact_id: item.artifactId,
|
|
223247
223357
|
project_id: item.projectId,
|
|
223358
|
+
revision_id: item.revisionId,
|
|
223248
223359
|
type: item.type,
|
|
223249
223360
|
name: item.name,
|
|
223250
223361
|
file_path: item.filePath,
|
|
@@ -223920,6 +224031,15 @@ var init_routes5 = __esm({
|
|
|
223920
224031
|
}
|
|
223921
224032
|
});
|
|
223922
224033
|
|
|
224034
|
+
// ../server/src/domains/collab/activity-notice.ts
|
|
224035
|
+
var NOTICE_TITLE;
|
|
224036
|
+
var init_activity_notice = __esm({
|
|
224037
|
+
"../server/src/domains/collab/activity-notice.ts"() {
|
|
224038
|
+
"use strict";
|
|
224039
|
+
NOTICE_TITLE = "\u53D8\u66F4\u901A\u77E5";
|
|
224040
|
+
}
|
|
224041
|
+
});
|
|
224042
|
+
|
|
223923
224043
|
// ../server/src/domains/collab/review-activity.ts
|
|
223924
224044
|
function reviewCardId(targetWorkId, reviewerActorId) {
|
|
223925
224045
|
return `${REVIEW_CARD_PREFIX}${encodeURIComponent(targetWorkId)}:${encodeURIComponent(reviewerActorId)}`;
|
|
@@ -224068,35 +224188,18 @@ var init_review_activity = __esm({
|
|
|
224068
224188
|
|
|
224069
224189
|
// ../server/src/domains/collab/review-requirement-activity.ts
|
|
224070
224190
|
function reviewRequirementActivity(snap, events, ref2) {
|
|
224071
|
-
const previous3 = /* @__PURE__ */ new Map();
|
|
224072
224191
|
const titles = new Map(snap.nodes.map((n) => [n.id, n.title]));
|
|
224073
224192
|
const cards = [];
|
|
224074
224193
|
const label = (r) => `${ref2(r.reviewerActorId).name || "\u672A\u547D\u540D\u5BA1\u6838\u4EBA"}${r.source === "closure" ? "\uFF08\u7ED3\u6848\u5BA1\u6838\uFF09" : ""}`;
|
|
224075
|
-
const names = (rows) => [...new Set(rows.map(label))].join("\u3001")
|
|
224194
|
+
const names = (rows) => [...new Set(rows.map(label))].join("\u3001");
|
|
224195
|
+
const submitter = (id) => ref2(id).name?.trim() || "\u672A\u547D\u540D\u6210\u5458";
|
|
224196
|
+
const sentence = (id, nodeTitle, rows) => rows.length ? `${submitter(id)} \u66F4\u6539\u300A${nodeTitle}\u300B\u8282\u70B9\u7684\u5BA1\u6838\u4EBA\u4E3A ${names(rows)}\u3002` : `${submitter(id)} \u6E05\u7A7A\u4E86\u300A${nodeTitle}\u300B\u8282\u70B9\u7684\u5BA1\u6838\u4EBA\u3002`;
|
|
224076
224197
|
for (const rec of [...events].sort((a, b2) => a.seq - b2.seq)) {
|
|
224077
224198
|
if (rec.status === "pending" || rec.status === "failed") continue;
|
|
224078
224199
|
const e = rec.event;
|
|
224079
|
-
if (e.kind === "plan.changed") {
|
|
224080
|
-
for (const node2 of e.addNodes ?? []) previous3.set(node2.id, node2.reviewers ?? []);
|
|
224081
|
-
for (const id of e.removeNodes ?? []) previous3.delete(id);
|
|
224082
|
-
continue;
|
|
224083
|
-
}
|
|
224084
|
-
if (e.kind === "plan.add_node") {
|
|
224085
|
-
previous3.set(e.nodeId, e.reviewers ?? []);
|
|
224086
|
-
continue;
|
|
224087
|
-
}
|
|
224088
224200
|
if (e.kind !== "plan.update_review_requirements") continue;
|
|
224089
|
-
|
|
224090
|
-
const after = e.reviewers;
|
|
224091
|
-
const beforeKeys = new Set(before?.map(requirementKey));
|
|
224092
|
-
const afterKeys = new Set(after.map(requirementKey));
|
|
224093
|
-
const added = before ? after.filter((r) => !beforeKeys.has(requirementKey(r))) : [];
|
|
224094
|
-
const removed = before?.filter((r) => !afterKeys.has(requirementKey(r))) ?? [];
|
|
224201
|
+
if (!isAuthoredChange(rec)) continue;
|
|
224095
224202
|
const nodeTitle = titles.get(e.nodeId) || "\u5DF2\u79FB\u9664\u7684\u8282\u70B9";
|
|
224096
|
-
if (!isAuthoredChange(rec)) {
|
|
224097
|
-
previous3.set(e.nodeId, after);
|
|
224098
|
-
continue;
|
|
224099
|
-
}
|
|
224100
224203
|
cards.push({
|
|
224101
224204
|
id: `wo:${rec.seq}`,
|
|
224102
224205
|
seq: rec.seq,
|
|
@@ -224107,24 +224210,19 @@ function reviewRequirementActivity(snap, events, ref2) {
|
|
|
224107
224210
|
executor: ref2(rec.actorId),
|
|
224108
224211
|
...rec.handActorId ? { handActor: ref2(rec.handActorId) } : {},
|
|
224109
224212
|
phase: "done",
|
|
224110
|
-
|
|
224111
|
-
|
|
224112
|
-
...added.length ? [`\u65B0\u589E\uFF1A${names(added)}`] : [],
|
|
224113
|
-
...removed.length ? [`\u79FB\u9664\uFF1A${names(removed)}`] : [],
|
|
224114
|
-
`\u66F4\u65B0\u540E\uFF1A${names(after)}`
|
|
224115
|
-
].join("\n"),
|
|
224213
|
+
noticeTitle: NOTICE_TITLE,
|
|
224214
|
+
status: sentence(rec.actorId, nodeTitle, e.reviewers),
|
|
224116
224215
|
artifacts: [],
|
|
224117
224216
|
actions: []
|
|
224118
224217
|
});
|
|
224119
|
-
previous3.set(e.nodeId, after);
|
|
224120
224218
|
}
|
|
224121
224219
|
return cards;
|
|
224122
224220
|
}
|
|
224123
|
-
var
|
|
224221
|
+
var isAuthoredChange;
|
|
224124
224222
|
var init_review_requirement_activity = __esm({
|
|
224125
224223
|
"../server/src/domains/collab/review-requirement-activity.ts"() {
|
|
224126
224224
|
"use strict";
|
|
224127
|
-
|
|
224225
|
+
init_activity_notice();
|
|
224128
224226
|
isAuthoredChange = (rec) => rec.origin !== "apply" && rec.origin !== "effect" && !rec.actorId.startsWith("actor:system:");
|
|
224129
224227
|
}
|
|
224130
224228
|
});
|
|
@@ -224220,7 +224318,6 @@ function buildWorkorderActivity(input) {
|
|
|
224220
224318
|
return tail || nodeId;
|
|
224221
224319
|
};
|
|
224222
224320
|
const cardOfWork = /* @__PURE__ */ new Map();
|
|
224223
|
-
const latestCardOfNode = /* @__PURE__ */ new Map();
|
|
224224
224321
|
const executionCards = [];
|
|
224225
224322
|
const offTimeline = (w2) => isDispatchLayerFailure(w2.outcomeDetail) || w2.outcome === "completed" && statusOf2(w2) !== "success" && !artifactsByWork.get(w2.id)?.length;
|
|
224226
224323
|
const statusOf2 = (w2) => w2.status ?? (w2.outcome === "failed" ? "failed" : w2.outcome === "completed" ? "success" : "running");
|
|
@@ -224249,35 +224346,45 @@ function buildWorkorderActivity(input) {
|
|
|
224249
224346
|
works.sort((a, b2) => a.createdAt.localeCompare(b2.createdAt) || a.id.localeCompare(b2.id));
|
|
224250
224347
|
const interrupts = [...interruptsOfNode.get(nodeId) ?? []].sort();
|
|
224251
224348
|
const node2 = nodeById.get(nodeId);
|
|
224349
|
+
const nodeCancelledAt = node2?.cancelledAt ?? null;
|
|
224252
224350
|
const name = nodeName(nodeId);
|
|
224253
|
-
let
|
|
224351
|
+
let fold2 = null;
|
|
224254
224352
|
for (const [idx, w2] of works.entries()) {
|
|
224255
|
-
|
|
224256
|
-
|
|
224257
|
-
|
|
224258
|
-
|
|
224259
|
-
|
|
224260
|
-
|
|
224261
|
-
|
|
224262
|
-
|
|
224263
|
-
|
|
224264
|
-
|
|
224265
|
-
|
|
224266
|
-
|
|
224267
|
-
|
|
224268
|
-
|
|
224269
|
-
|
|
224270
|
-
|
|
224271
|
-
executionCards.push(card2);
|
|
224272
|
-
latestCardOfNode.set(nodeId, card2);
|
|
224353
|
+
const next = works[idx + 1];
|
|
224354
|
+
const artifacts = artifactsByWork.get(w2.id) ?? [];
|
|
224355
|
+
const st = statusOf2(w2);
|
|
224356
|
+
const interrupt = interrupts.find((t) => t >= w2.createdAt && (!next || t < next.createdAt));
|
|
224357
|
+
const removedHere = !!nodeCancelledAt && nodeCancelledAt >= w2.createdAt && (!next || nodeCancelledAt < next.createdAt);
|
|
224358
|
+
const reassigned = !!next && next.assigneeActorId !== w2.assigneeActorId;
|
|
224359
|
+
const killedByPlan = !!(w2.deadAt || w2.cancelledAt) && !!next && (reassigned || (next.nodeVersion ?? 0) > (w2.nodeVersion ?? 0));
|
|
224360
|
+
const failedRound = !w2.conclusion && !interrupt && !removedHere && !killedByPlan && (!!w2.deadAt || !!w2.cancelledAt || st === "failed" || st === "retry");
|
|
224361
|
+
if (failedRound && fold2) {
|
|
224362
|
+
fold2.traceWorkIds.push(w2.id);
|
|
224363
|
+
cardOfWork.set(w2.id, fold2);
|
|
224364
|
+
fold2.updatedAt = lastStampOf(w2);
|
|
224365
|
+
fold2.stuckReason = w2.outcomeDetail?.reason === "timeout" ? "timeout" : "failed";
|
|
224366
|
+
fold2.foldedRounds = (fold2.foldedRounds ?? 1) + 1;
|
|
224367
|
+
fold2.status = ACTIVITY_COPY.foldedFailures(nameOf(fold2.executorId), name, fold2.foldedRounds);
|
|
224368
|
+
continue;
|
|
224273
224369
|
}
|
|
224274
|
-
|
|
224370
|
+
fold2 = null;
|
|
224371
|
+
const card2 = {
|
|
224372
|
+
/* 卡 id = 这一轮的 workId(连败折叠时取第一轮,React key 与滚动锚点因此稳定)。 */
|
|
224373
|
+
id: `work:${w2.id}`,
|
|
224374
|
+
traceWorkIds: [w2.id],
|
|
224375
|
+
seq: 0,
|
|
224376
|
+
// 事件循环里按这一轮 work.create 的 seq 填
|
|
224377
|
+
at: w2.createdAt,
|
|
224378
|
+
updatedAt: lastStampOf(w2),
|
|
224379
|
+
nodeId,
|
|
224380
|
+
executorId: w2.assigneeActorId,
|
|
224381
|
+
phase: "running",
|
|
224382
|
+
status: ACTIVITY_COPY.working(name),
|
|
224383
|
+
artifacts,
|
|
224384
|
+
actions: []
|
|
224385
|
+
};
|
|
224386
|
+
executionCards.push(card2);
|
|
224275
224387
|
cardOfWork.set(w2.id, card2);
|
|
224276
|
-
card2.executorId = w2.assigneeActorId;
|
|
224277
|
-
card2.updatedAt = lastStampOf(w2);
|
|
224278
|
-
card2.artifacts = artifactsByWork.get(w2.id) ?? card2.artifacts;
|
|
224279
|
-
delete card2.openWorkId;
|
|
224280
|
-
delete card2.stuckReason;
|
|
224281
224388
|
if (w2.conclusion) {
|
|
224282
224389
|
card2.phase = "done";
|
|
224283
224390
|
if (rootBriefNodeIds.has(nodeId)) {
|
|
@@ -224293,32 +224400,21 @@ function buildWorkorderActivity(input) {
|
|
|
224293
224400
|
card2.status = awaits ? ACTIVITY_COPY.doneAwaitingReview(name) : ACTIVITY_COPY.done(name);
|
|
224294
224401
|
}
|
|
224295
224402
|
}
|
|
224296
|
-
|
|
224297
|
-
continue;
|
|
224298
|
-
}
|
|
224299
|
-
const next = works[idx + 1];
|
|
224300
|
-
const interrupt = interrupts.find((t) => t >= w2.createdAt && (!next || t < next.createdAt));
|
|
224301
|
-
if (interrupt) {
|
|
224403
|
+
} else if (interrupt) {
|
|
224302
224404
|
card2.phase = "stuck";
|
|
224303
224405
|
card2.status = ACTIVITY_COPY.interruptedByIssue(name);
|
|
224304
224406
|
if (interrupt > card2.updatedAt) card2.updatedAt = interrupt;
|
|
224305
|
-
|
|
224306
|
-
continue;
|
|
224307
|
-
}
|
|
224308
|
-
const stuckKind = w2.outcomeDetail?.reason === "timeout" ? "timeout" : "failed";
|
|
224309
|
-
const st = statusOf2(w2);
|
|
224310
|
-
if (w2.deadAt || w2.cancelledAt) {
|
|
224407
|
+
} else if (removedHere || killedByPlan) {
|
|
224311
224408
|
card2.phase = "stuck";
|
|
224312
|
-
card2.
|
|
224313
|
-
card2.
|
|
224314
|
-
|
|
224409
|
+
card2.status = removedHere ? ACTIVITY_COPY.interruptedByNodeRemoved(name) : reassigned ? ACTIVITY_COPY.interruptedByReassign(name, nameOf(next.assigneeActorId)) : ACTIVITY_COPY.interruptedByPlanChange(name);
|
|
224410
|
+
if (!removedHere && !reassigned) card2.planChangeUnnamed = true;
|
|
224411
|
+
if (removedHere && nodeCancelledAt > card2.updatedAt) card2.updatedAt = nodeCancelledAt;
|
|
224412
|
+
} else if (failedRound) {
|
|
224413
|
+
const stuckKind = w2.outcomeDetail?.reason === "timeout" ? "timeout" : "failed";
|
|
224315
224414
|
card2.phase = "stuck";
|
|
224316
224415
|
card2.stuckReason = stuckKind;
|
|
224317
|
-
card2.status = stuckKind === "timeout" ? ACTIVITY_COPY.timedOut(nameOf(card2.executorId)) : ACTIVITY_COPY.failed(nameOf(card2.executorId));
|
|
224318
|
-
|
|
224319
|
-
card2.phase = "stuck";
|
|
224320
|
-
card2.stuckReason = stuckKind;
|
|
224321
|
-
card2.status = ACTIVITY_COPY.awaitingRedispatch(nameOf(card2.executorId), name);
|
|
224416
|
+
card2.status = w2.deadAt || w2.cancelledAt ? ACTIVITY_COPY.killed(name) : st === "retry" ? ACTIVITY_COPY.awaitingRedispatch(nameOf(card2.executorId), name) : stuckKind === "timeout" ? ACTIVITY_COPY.timedOut(nameOf(card2.executorId)) : ACTIVITY_COPY.failed(nameOf(card2.executorId));
|
|
224417
|
+
fold2 = card2;
|
|
224322
224418
|
} else if (w2.endedAt) {
|
|
224323
224419
|
card2.phase = "running";
|
|
224324
224420
|
card2.status = ACTIVITY_COPY.awaitingConclude(name);
|
|
@@ -224339,17 +224435,28 @@ function buildWorkorderActivity(input) {
|
|
|
224339
224435
|
if (rec.handActorId) d.handActorId = rec.handActorId;
|
|
224340
224436
|
return d;
|
|
224341
224437
|
};
|
|
224342
|
-
const
|
|
224438
|
+
const cardOpenAt = (nodeId, at) => {
|
|
224439
|
+
const list2 = worksOfNode.get(nodeId);
|
|
224440
|
+
if (!list2) return void 0;
|
|
224441
|
+
let hit;
|
|
224442
|
+
for (const w2 of list2) {
|
|
224443
|
+
if (w2.createdAt > at) break;
|
|
224444
|
+
hit = w2;
|
|
224445
|
+
}
|
|
224446
|
+
return hit ? cardOfWork.get(hit.id) : void 0;
|
|
224447
|
+
};
|
|
224448
|
+
const woCard = (rec, status, opts = {}) => {
|
|
224343
224449
|
standalone.push({
|
|
224344
224450
|
id: `wo:${rec.seq}`,
|
|
224345
224451
|
seq: rec.seq,
|
|
224346
224452
|
at: rec.createdAt,
|
|
224347
224453
|
updatedAt: rec.createdAt,
|
|
224348
|
-
executorId,
|
|
224454
|
+
executorId: rec.actorId,
|
|
224349
224455
|
...rec.handActorId ? { handActorId: rec.handActorId } : {},
|
|
224350
|
-
phase,
|
|
224456
|
+
phase: "done",
|
|
224457
|
+
...opts.notice ? { noticeTitle: NOTICE_TITLE } : {},
|
|
224351
224458
|
status,
|
|
224352
|
-
...detail ? { detail } : {},
|
|
224459
|
+
...opts.detail ? { detail: opts.detail } : {},
|
|
224353
224460
|
artifacts: [],
|
|
224354
224461
|
actions: []
|
|
224355
224462
|
});
|
|
@@ -224362,20 +224469,29 @@ function buildWorkorderActivity(input) {
|
|
|
224362
224469
|
case "plan.changed": {
|
|
224363
224470
|
break;
|
|
224364
224471
|
}
|
|
224472
|
+
/* 暂停 / 恢复是**人对这单做的事**,不是某一轮执行——走「变更通知」那套卡面(帧 `2874:1189`)。
|
|
224473
|
+
此前它们沿用执行卡的标题模板,卡面第一行被拼成「{谁} 已完成」(`ACTIVITY_PHASE_LABEL.done`),
|
|
224474
|
+
第二行才是「{谁}已暂停任务。」——标题和正文说的不是同一件事,而「已完成」在这一拍根本
|
|
224475
|
+
没有所指(暂停不是完成)。 */
|
|
224365
224476
|
case "workorder.paused":
|
|
224366
|
-
woCard(rec,
|
|
224477
|
+
woCard(rec, ACTIVITY_COPY.paused(nameOf(rec.actorId)), { notice: true, detail: str4(ev.reason) });
|
|
224367
224478
|
break;
|
|
224368
224479
|
case "workorder.resumed":
|
|
224369
|
-
woCard(rec,
|
|
224480
|
+
woCard(rec, ACTIVITY_COPY.resumedWorkorder(nameOf(rec.actorId)), { notice: true });
|
|
224370
224481
|
break;
|
|
224371
224482
|
case "workorder.meta_changed": {
|
|
224372
224483
|
const patch = ev.patch ?? {};
|
|
224373
224484
|
if (!("goal" in patch) && !("description" in patch)) break;
|
|
224374
|
-
woCard(rec,
|
|
224485
|
+
woCard(rec, ACTIVITY_COPY.goalChanged(nameOf(rec.actorId), managerName()));
|
|
224375
224486
|
break;
|
|
224376
224487
|
}
|
|
224377
224488
|
case "plan.update_spec": {
|
|
224378
|
-
|
|
224489
|
+
const nodeId = String(ev.nodeId ?? "");
|
|
224490
|
+
const d = cardOpenAt(nodeId, rec.createdAt);
|
|
224491
|
+
if (d?.planChangeUnnamed) {
|
|
224492
|
+
d.status = ACTIVITY_COPY.interruptedBySpecChange(nameOf(rec.actorId), nodeName(nodeId));
|
|
224493
|
+
delete d.planChangeUnnamed;
|
|
224494
|
+
}
|
|
224379
224495
|
break;
|
|
224380
224496
|
}
|
|
224381
224497
|
/* ── 执行(work)──────────────────────────────────────────────
|
|
@@ -224655,10 +224771,10 @@ function buildWorkorderActivity(input) {
|
|
|
224655
224771
|
case "plan.node_retry": {
|
|
224656
224772
|
const nodeId = String(ev.nodeId ?? "");
|
|
224657
224773
|
const by = str4(ev.by) ?? rec.actorId;
|
|
224658
|
-
const target =
|
|
224659
|
-
if (!target || target.phase !== "stuck") break;
|
|
224774
|
+
const target = cardOpenAt(nodeId, rec.createdAt);
|
|
224775
|
+
if (!target || target.phase !== "stuck" || !target.stuckReason) break;
|
|
224660
224776
|
touch(target, rec);
|
|
224661
|
-
target.status = target.stuckReason === "timeout" ? ACTIVITY_COPY.retriedAfterTimeout(nameOf(target.executorId), nameOf(by)) : ACTIVITY_COPY.retriedAfterFailure(nameOf(target.executorId), nameOf(by));
|
|
224777
|
+
target.status = target.foldedRounds && target.foldedRounds > 1 ? ACTIVITY_COPY.foldedFailuresRetried(nameOf(target.executorId), nodeName(nodeId), target.foldedRounds, nameOf(by)) : target.stuckReason === "timeout" ? ACTIVITY_COPY.retriedAfterTimeout(nameOf(target.executorId), nameOf(by)) : ACTIVITY_COPY.retriedAfterFailure(nameOf(target.executorId), nameOf(by));
|
|
224662
224778
|
break;
|
|
224663
224779
|
}
|
|
224664
224780
|
default:
|
|
@@ -224822,6 +224938,7 @@ function buildWorkorderActivity(input) {
|
|
|
224822
224938
|
...d.reviewWorkId ? { reviewWorkId: d.reviewWorkId, reviewers: d.reviewerIds?.map(ref2) } : {},
|
|
224823
224939
|
...d.handActorId ? { handActor: ref2(d.handActorId) } : {},
|
|
224824
224940
|
phase: d.phase,
|
|
224941
|
+
...d.noticeTitle ? { noticeTitle: d.noticeTitle } : {},
|
|
224825
224942
|
status: d.status,
|
|
224826
224943
|
...d.detail ? { detail: d.detail } : {},
|
|
224827
224944
|
...d.reviewNotes?.length ? { reviewNotes: d.reviewNotes } : {},
|
|
@@ -224843,13 +224960,13 @@ var init_activity2 = __esm({
|
|
|
224843
224960
|
"use strict";
|
|
224844
224961
|
init_src();
|
|
224845
224962
|
init_src4();
|
|
224963
|
+
init_activity_notice();
|
|
224846
224964
|
init_review_activity();
|
|
224847
224965
|
init_review_requirement_activity();
|
|
224848
224966
|
init_workorder_manager();
|
|
224849
224967
|
init_escalation_recipient();
|
|
224850
224968
|
ACTIVITY_EVENT_KINDS = [
|
|
224851
224969
|
"plan.changed",
|
|
224852
|
-
"plan.add_node",
|
|
224853
224970
|
"plan.update_review_requirements",
|
|
224854
224971
|
"plan.update_spec",
|
|
224855
224972
|
"plan.node_retry",
|
|
@@ -224923,7 +225040,39 @@ var init_activity2 = __esm({
|
|
|
224923
225040
|
*
|
|
224924
225041
|
* 不重复缺口卡那句「需要 @{协调者} 协助解决」:该找谁由缺口卡自己说,执行卡只说这一轮到此为止。
|
|
224925
225042
|
*/
|
|
225043
|
+
/**
|
|
225044
|
+
* **连败折叠**:同一节点上连续没跑成的 N 轮折成一张。不折叠的话 `ws:wo-6d588f8e` 的《隔离边界实现》
|
|
225045
|
+
* 会在时间线上连出 12 张,其中 9 张都是「执行失败。」——真正要给人看的是「这个节点一直没跑成」
|
|
225046
|
+
* 这一件事,逐轮的原因在轨迹抽屉里(`traceWorkIds` 收齐全部 N 轮,一轮不少)。
|
|
225047
|
+
*/
|
|
225048
|
+
foldedFailures: (executor, node2, n) => `${executor} \u8FDE\u7EED ${n} \u6B21\u6CA1\u8DD1\u6210\u300A${node2}\u300B\u3002`,
|
|
225049
|
+
/** 折叠卡上补「谁按的重试」——不把「连续 N 次」换成单轮那句。 */
|
|
225050
|
+
foldedFailuresRetried: (executor, node2, n, by) => `${executor} \u8FDE\u7EED ${n} \u6B21\u6CA1\u8DD1\u6210\u300A${node2}\u300B\uFF0C${by}\u5DF2\u4ECB\u5165\u91CD\u8BD5\u3002`,
|
|
224926
225051
|
interruptedByIssue: (node2) => `\u300A${node2}\u300B\u672C\u8F6E\u6267\u884C\u56E0\u5361\u70B9\u4E2D\u65AD\u3002`,
|
|
225052
|
+
/**
|
|
225053
|
+
* 这一轮**被计划变更打断**(P15)。与卡点中断同级:计划一动,这一轮就不再是「还在跑」,
|
|
225054
|
+
* 而是被引擎当场顶掉了——`plan.delete_node` 直接 `killLatestWork`;`plan.update_spec`(spec 真变)
|
|
225055
|
+
* 与 `plan.assign_actor`(换人)把 `node.version` 加一,scan 随即重派,`work.create` 自带
|
|
225056
|
+
* supersede 把在跑的这一轮翻成 dead。
|
|
225057
|
+
*
|
|
225058
|
+
* 此前这两条都不结束卡:删节点那档只落到 {@link killed}「本轮执行已终止」,不说是计划调整;
|
|
225059
|
+
* 改 spec / 改派那档**根本没有结局**——新一轮直接改写同一张卡,「中断」在页面上不存在,
|
|
225060
|
+
* 卡上署名还会换成新人(与 §2.1「一张卡只讲一个人的事」相抵)。
|
|
225061
|
+
*/
|
|
225062
|
+
interruptedByPlanChange: (node2) => `\u300A${node2}\u300B\u672C\u8F6E\u6267\u884C\u56E0\u8BA1\u5212\u8C03\u6574\u4E2D\u65AD\u3002`,
|
|
225063
|
+
/**
|
|
225064
|
+
* 计划变更里最常见的那一种:**有人改了任务说明**,在跑的这一轮因此被顶掉。
|
|
225065
|
+
*
|
|
225066
|
+
* 这句取代了原来那张独立的任务级卡「已确认任务说明更新,正在判断该修改的影响。」——两张卡讲的
|
|
225067
|
+
* 本来就是同一件事:计划动了、这一轮做不下去了。旧卡还有两个毛病:`phase` 写死 `running`,
|
|
225068
|
+
* 没有任何一拍能让它收口(`ws:wo-217a0909` 12:13 那两张就是这么永久停在「进行中」的);文案是
|
|
225069
|
+
* 一句没有主语也没有宾语的固定串,两次内容不同的修改渲染成两行一模一样的字。
|
|
225070
|
+
*/
|
|
225071
|
+
interruptedBySpecChange: (by, node2) => `${by} \u66F4\u65B0\u4E86\u4EFB\u52A1\u8BF4\u660E\uFF0C\u300A${node2}\u300B\u672C\u8F6E\u6267\u884C\u5230\u6B64\u4E2D\u65AD\u3002`,
|
|
225072
|
+
/** 换人那一档点破接手的人——卡到此为止,下一轮是新执行人的新卡。 */
|
|
225073
|
+
interruptedByReassign: (node2, next) => `\u300A${node2}\u300B\u672C\u8F6E\u6267\u884C\u56E0\u8BA1\u5212\u8C03\u6574\u4E2D\u65AD\uFF0C\u5DF2\u6539\u7531 ${next} \u63A5\u624B\u3002`,
|
|
225074
|
+
/** 节点被移出计划:这一轮再没有下文,也不会有新卡。 */
|
|
225075
|
+
interruptedByNodeRemoved: (node2) => `\u300A${node2}\u300B\u5DF2\u4ECE\u8BA1\u5212\u4E2D\u79FB\u9664\uFF0C\u672C\u8F6E\u6267\u884C\u4E2D\u65AD\u3002`,
|
|
224927
225076
|
/**
|
|
224928
225077
|
* 引擎判卷 success、`endedAt` 也写了,却始终没有 `work.conclude`:**交了稿没收口**。
|
|
224929
225078
|
* 如实说这一档,不冒充「正在处理」——后者把一轮早就结束的执行画成还在跑。
|
|
@@ -224960,7 +225109,6 @@ var init_activity2 = __esm({
|
|
|
224960
225109
|
paused: (actor) => `${actor}\u5DF2\u6682\u505C\u4EFB\u52A1\u3002`,
|
|
224961
225110
|
resumedWorkorder: (actor) => `${actor}\u5DF2\u6062\u590D\u4EFB\u52A1\u3002`,
|
|
224962
225111
|
goalChanged: (actor, manager) => `${actor}\u4FEE\u6539\u4E86\u4EFB\u52A1\u76EE\u6807\uFF0C\u9700\u8981 ${manager}\u91CD\u65B0\u8BC4\u4F30\u3002`,
|
|
224963
|
-
specConfirmed: "\u5DF2\u786E\u8BA4\u4EFB\u52A1\u8BF4\u660E\u66F4\u65B0\uFF0C\u6B63\u5728\u5224\u65AD\u8BE5\u4FEE\u6539\u7684\u5F71\u54CD\u3002",
|
|
224964
225112
|
handleEscalation: (target) => `${target} \u7684\u6267\u884C\u5F02\u5E38\u6301\u7EED\u672A\u89E3\u51B3\u3002`,
|
|
224965
225113
|
handledEscalation: (target) => `\u5DF2\u5904\u7406 ${target} \u7684\u6267\u884C\u5F02\u5E38\u3002`,
|
|
224966
225114
|
/**
|
|
@@ -225969,13 +226117,7 @@ function buildWaitingInbox(reviews, works, policy, nowIso, me, resolveActor) {
|
|
|
225969
226117
|
const isSelf = r.actorId === me;
|
|
225970
226118
|
const actorLabel = displayName(resolveActor, r.actorId);
|
|
225971
226119
|
if (r.status === "dead" || r.status === "exhausted") {
|
|
225972
|
-
out.push(isSelf ? card(kind, r, "urgent", start, `\u4F60\u7684${KIND_LABEL[kind]}\u5DF2\u4E2D\u6B62\uFF0C\u9700\u8981\u4F60\u4ECB\u5165\u91CD\u5F00`) : card(
|
|
225973
|
-
kind,
|
|
225974
|
-
r,
|
|
225975
|
-
"urgent",
|
|
225976
|
-
start,
|
|
225977
|
-
`\u8FD9\u4E00\u6B65\u7684${KIND_LABEL[kind]}\u5DF2\u4E2D\u6B62\uFF08${actorLabel} \u8FDE\u7EED\u5931\u8D25\u8D85\u9650\uFF09\u2014\u2014\u672C\u8F6E${KIND_LABEL[kind]}\u6536\u4E0D\u9F50\u3001\u8282\u70B9\u505C\u5728\u8FD9\u91CC\uFF0C\u9700\u8981\u4F60\u91CD\u5F00\u8FD9\u4E00\u8F6E`
|
|
225978
|
-
));
|
|
226120
|
+
out.push(isSelf ? card(kind, r, "urgent", start, `\u4F60\u7684${KIND_LABEL[kind]}\u5DF2\u4E2D\u6B62\uFF0C\u9700\u8981\u4F60\u4ECB\u5165\u91CD\u5F00`) : card(kind, r, "urgent", start, kind === "review-waiting" ? `\u8FD9\u4E00\u6B65\u7684\u8BC4\u5BA1\u5DF2\u4E2D\u6B62\uFF08${actorLabel} \u8FDE\u7EED\u5931\u8D25\u8D85\u9650\uFF09\u2014\u2014\u672C\u8F6E\u8BC4\u5BA1\u6536\u4E0D\u9F50\u3001\u8282\u70B9\u505C\u5728\u8FD9\u91CC\uFF0C\u9700\u8981\u4F60\u91CD\u5F00\u8FD9\u4E00\u8F6E` : `\u8FD9\u4E00\u6B65\u7684\u5904\u7406\u5DF2\u4E2D\u6B62\uFF08${actorLabel} \u8FDE\u7EED\u5931\u8D25\u8D85\u9650\uFF09\u2014\u2014\u8282\u70B9\u505C\u5728\u8FD9\u91CC\uFF0C\u9700\u8981\u6709\u4EBA\u91CD\u5F00`));
|
|
225979
226121
|
continue;
|
|
225980
226122
|
}
|
|
225981
226123
|
if (r.status !== "running") continue;
|
|
@@ -226027,14 +226169,20 @@ function collectWaitingRows(snapshots2, me, policy = CODE_DEFAULT_POLICY) {
|
|
|
226027
226169
|
const requirementHeads = new Map(snap.requirements.map((q2) => [reqKey(q2), q2.latestReviewId]));
|
|
226028
226170
|
const requirementHead = (r) => requirementHeads.get(reqKey(r));
|
|
226029
226171
|
const nodesWithRequirements = new Set(snap.requirements.map((q2) => q2.nodeId));
|
|
226030
|
-
const
|
|
226172
|
+
const briefNode = snap.nodes.find((n) => n.type === "brief");
|
|
226173
|
+
const managerOfWo = briefNode?.fields?.["manager"];
|
|
226031
226174
|
const isManager = typeof managerOfWo === "string" && managerOfWo === me;
|
|
226175
|
+
const launcher = briefNode?.assigneeActorId ?? null;
|
|
226176
|
+
const openIssueNodes = new Set(
|
|
226177
|
+
snap.issues.filter((i) => i.resolvedAt === null && i.aboutNodeId !== null).map((i) => i.aboutNodeId)
|
|
226178
|
+
);
|
|
226179
|
+
const nodeHasOpenIssue = (nodeId) => openIssueNodes.has(nodeId);
|
|
226032
226180
|
const latestReview = /* @__PURE__ */ new Map();
|
|
226033
226181
|
for (const r of snap.reviews) {
|
|
226034
226182
|
if (cancelled.has(r.nodeId)) continue;
|
|
226035
226183
|
if (acceptedWork.get(r.nodeId) === r.targetWorkId) continue;
|
|
226036
226184
|
const mine = r.reviewerActorId === me;
|
|
226037
|
-
const oversees = nodeOwner.get(r.nodeId) === me || isManager;
|
|
226185
|
+
const oversees = nodeOwner.get(r.nodeId) === me || isManager || launcher === me;
|
|
226038
226186
|
if (!mine && !oversees) continue;
|
|
226039
226187
|
latestBy(latestReview, r);
|
|
226040
226188
|
}
|
|
@@ -226053,10 +226201,12 @@ function collectWaitingRows(snapshots2, me, policy = CODE_DEFAULT_POLICY) {
|
|
|
226053
226201
|
const humanPending = !stopped && classify(r.reviewerActorId) === "human";
|
|
226054
226202
|
if (!mine && !stopped && !humanPending) continue;
|
|
226055
226203
|
const audience = [r.reviewerActorId];
|
|
226056
|
-
const
|
|
226057
|
-
if (
|
|
226058
|
-
|
|
226059
|
-
|
|
226204
|
+
const addBystanders = humanPending || stopped && !nodeHasOpenIssue(r.nodeId);
|
|
226205
|
+
if (addBystanders) {
|
|
226206
|
+
for (const candidate of [launcher, managerOfWo, nodeOwner.get(r.nodeId)]) {
|
|
226207
|
+
if (typeof candidate !== "string" || classify(candidate) !== "human") continue;
|
|
226208
|
+
if (!audience.includes(candidate)) audience.push(candidate);
|
|
226209
|
+
}
|
|
226060
226210
|
}
|
|
226061
226211
|
reviews.push({
|
|
226062
226212
|
id: r.id,
|
|
@@ -226073,17 +226223,25 @@ function collectWaitingRows(snapshots2, me, policy = CODE_DEFAULT_POLICY) {
|
|
|
226073
226223
|
const latestMain = /* @__PURE__ */ new Map();
|
|
226074
226224
|
const latestReply = /* @__PURE__ */ new Map();
|
|
226075
226225
|
for (const w2 of snap.works) {
|
|
226076
|
-
if (
|
|
226226
|
+
if (cancelled.has(w2.nodeId)) continue;
|
|
226077
226227
|
latestBy(isReplyWork(w2) ? latestReply : latestMain, w2);
|
|
226078
226228
|
}
|
|
226079
226229
|
for (const w2 of [...latestMain.values(), ...latestReply.values()]) {
|
|
226080
226230
|
const st = workState(w2, hasOutput(w2, outputCountOf(w2.id)));
|
|
226081
226231
|
if (st !== "running" && st !== "dead") continue;
|
|
226232
|
+
const audience = [w2.assigneeActorId];
|
|
226233
|
+
if (st === "dead" && !nodeHasOpenIssue(w2.nodeId)) {
|
|
226234
|
+
for (const candidate of [launcher, managerOfWo, nodeOwner.get(w2.nodeId)]) {
|
|
226235
|
+
if (typeof candidate !== "string" || classify(candidate) !== "human") continue;
|
|
226236
|
+
if (!audience.includes(candidate)) audience.push(candidate);
|
|
226237
|
+
}
|
|
226238
|
+
}
|
|
226082
226239
|
works.push({
|
|
226083
226240
|
id: w2.id,
|
|
226084
226241
|
workorderId: snap.workorder.id,
|
|
226085
226242
|
artifactId: w2.nodeId,
|
|
226086
226243
|
actorId: w2.assigneeActorId,
|
|
226244
|
+
notifyActorIds: audience,
|
|
226087
226245
|
...nodeTitle.get(w2.nodeId) ? { title: nodeTitle.get(w2.nodeId) } : {},
|
|
226088
226246
|
createdAt: w2.createdAt,
|
|
226089
226247
|
startedAt: w2.startedAt,
|
|
@@ -259067,6 +259225,16 @@ async function startServe(opts) {
|
|
|
259067
259225
|
// ADR 0125:写 scope=project 变量时校验 projectId 真实存在(getProject 直查,含临时项目)。
|
|
259068
259226
|
// projectStateStore 在下方 ~行 1023 才赋值——本箭头只在请求时跑,那时已就绪(同 resolveDispatchScope 惰性模式)。
|
|
259069
259227
|
projectExists: async (id) => await projectStateStore.getProject(id) !== null,
|
|
259228
|
+
/* 子 PRD《密钥管理》§1 的权限判据:密钥的写 / 删要认「创建者本人或组织管理者」。
|
|
259229
|
+
owner 与 admin 都算组织管理者(与组织页 `orgIdentityOf` 同一口径)。
|
|
259230
|
+
`controlPlaneStore` 在下方才赋值——本箭头只在请求时跑,那时已就绪(同 projectExists 的惰性模式)。
|
|
259231
|
+
成员表按 accountId 主键,但行上带 actorId,密钥那边手里只有 actorId,所以按 actorId 找。 */
|
|
259232
|
+
isCompanyManager: async (companyId, actorId) => {
|
|
259233
|
+
if (!companyId || !actorId) return false;
|
|
259234
|
+
const members = await controlPlaneStore.listMembers(companyId).catch(() => []);
|
|
259235
|
+
const me = members.find((m2) => m2.actorId === actorId);
|
|
259236
|
+
return me?.role === "owner" || me?.role === "admin";
|
|
259237
|
+
},
|
|
259070
259238
|
// CO-302 数据面隔离(actors 域试点):按当前公司取其引擎,用各自 registry 服务该域请求。
|
|
259071
259239
|
// engineRouter 在下方(行 ~411)以 const 声明;此箭头只在请求时调用,那时已初始化,闭包引用合法
|
|
259072
259240
|
// (TDZ 只在构造时访问才报错,这里不访问)。默认公司命中现有单实例(registry===registryStore),
|
|
@@ -271790,7 +271958,7 @@ function shimScript() {
|
|
|
271790
271958
|
}
|
|
271791
271959
|
|
|
271792
271960
|
// src/index.ts
|
|
271793
|
-
var PKG_VERSION = true ? "2.2.
|
|
271961
|
+
var PKG_VERSION = true ? "2.2.17" : "dev";
|
|
271794
271962
|
var LOCAL_BIN = localBin();
|
|
271795
271963
|
var NPM_PREFIX = npmPrefix();
|
|
271796
271964
|
var INSTANCE = DEFAULT_INSTANCE;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oasis_test_v2",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.17",
|
|
4
4
|
"description": "Oasis node daemon + CLI — background daemon, auto-start, full server CLI",
|
|
5
5
|
"bin": {
|
|
6
6
|
"oasis": "./dist/index.js"
|
|
@@ -26,6 +26,6 @@
|
|
|
26
26
|
"node": ">=20"
|
|
27
27
|
},
|
|
28
28
|
"oasisRelease": {
|
|
29
|
-
"sourceHead": "
|
|
29
|
+
"sourceHead": "1c93b971ada0fb88e327a81413365e770998710a"
|
|
30
30
|
}
|
|
31
31
|
}
|