@tea-agent/loop-agent 0.42.0-next.5 → 0.42.0-next.7
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/AGENTS.md +1 -1
- package/CHANGELOG.md +22 -0
- package/dist/build-stamp.json +3 -3
- package/dist/executors/dag-pi-executor.js +75 -13
- package/dist/task/source-prepare/fragment-inventory.js +49 -16
- package/dist/task/source-prepare/prepare.js +37 -20
- package/dist/worker/console/chat/model-resolver.js +7 -4
- package/dist/worker/console/chat/pi-runtime.js +40 -1
- package/dist/worker/console/chat/routes.js +25 -24
- package/dist/worker/console/chat/sdd-data-alignment.js +6 -3
- package/dist/worker/console/pi-readiness.js +3 -2
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-BJpsGgAv.js → abnfDiagram-N423BO3Z-BQBHX31n.js} +1 -1
- package/dist/worker/console/static/assets/{arc-DjMFPsEd.js → arc-MSJ199eR.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DqdZlr1O.js → architectureDiagram-T3A2C74G-DiRq2qMX.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-BmZnfFEO.js → blockDiagram-VBNYF7ZC-rlaxFOvi.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-3cKberpL.js → c4Diagram-5PPSVZJV-BSpi9ump.js} +1 -1
- package/dist/worker/console/static/assets/channel-BdTaMnYP.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-q-0yjQ1h.js → chunk-2GRJ4B5K-BN8VNeh5.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DnONBaDW.js → chunk-2Q5K7J3B-CDa1OVRA.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-v3a6PCOS.js → chunk-5RXB4S5H-zxMQ5b8E.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-D1kLrlU_.js → chunk-5VM5RSS4-XTqNlzH4.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BPelzihi.js → chunk-6Q2QTUOP-DpZQfFTq.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-DauNE69H.js → chunk-GF5L2VYU-DPv743zu.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-RIg5_bFJ.js → chunk-JWPE2WC7-CFsCFoF4.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BuILuoxs.js → chunk-KBJHAD2P-CV__aJTt.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-VHORV5oW.js → chunk-RYQCIY6F-BshKlNir.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-BJYr4xHC.js → chunk-XXDRQBXY-B-A3ErBP.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Cid7CSRK.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Cid7CSRK.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BS6nuqQn.js → cose-bilkent-JH36ORCC-25xj8928.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BBhF0NAT.js → cynefin-VYW2F7L2-DFmv38cl.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-D1135Ifg.js → cynefinDiagram-MW4NZA55-hReuWDQP.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-vAOYPUWJ.js → dagre-VZM6K2ZE-C9X3YPUI.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-DuaSPkfa.js → diagram-7IWD3JNH-CAVmqjg9.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-mMvXvGU6.js → diagram-B4RE2ZJO-DYNlLsIm.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-CDIEJtlL.js → diagram-LBJQPF4R-BP5mlbfb.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-Bi_6JvCW.js → diagram-Q27KOJAE-KWOHK5ZM.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-DAqj57yN.js → diagram-UB23O5K3-D25Ro5y-.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DfWaxNEo.js → ebnfDiagram-BXEA7PRR-kLaahtbh.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-Bt7QqkNG.js → erDiagram-JOGREHBK-1tgKuLez.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CRRhQwT2.js → flowDiagram-UKHOOZJN-DARr-Cvn.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-DAV1O5B6.js → ganttDiagram-PKOTCBZU-BgwjBv_V.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-0X2Qo4Lp.js → gitGraphDiagram-DS77QQ5N-DneIlYSI.js} +1 -1
- package/dist/worker/console/static/assets/{index-CVdddQsA.js → index-Dkv0ezuE.js} +75 -77
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-C5W5eILC.js → infoDiagram-6WML65LV-DA7ATqY6.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-5SfqU-Tb.js → ishikawaDiagram-WSZJBQD7-DnOS-E1Z.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CKDRcaKf.js → journeyDiagram-NVQOT4AX-C8JnwJ_s.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Cc7_TuSb.js → kanban-definition-27J2QSJJ-Cw0_OjEC.js} +1 -1
- package/dist/worker/console/static/assets/{linear-BcQYYmeT.js → linear-mrxq3fyn.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-CwLZPtQo.js → mermaid.core-Bagus6zF.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-D29M025z.js → mindmap-definition-FAOFIHXS-Bf95bLuW.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-CV-ZAWYe.js → pegDiagram-VL7TDLO6-BvtUW6w2.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-C6iPS_o5.js → pieDiagram-7S7Q4E2Y-CbWtT-Ry.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-BFCD1sTg.js → quadrantDiagram-CIZ2JOQS-ctnxxIdD.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-CztNj4O9.js → railroadDiagram-AXF67PYL-BZ7Z3X0-.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BWIELSNd.js → requirementDiagram-LRYGKXZP-BMX1xnIy.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-CPtxrjJQ.js → sankeyDiagram-W5VNT64P-MShOQznz.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BEWVSFsu.js → sequenceDiagram-SI44F4Z6-CTGOazaB.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-DyhzoX_J.js → sizeCapture-X5ZJPWSS-zGbfM4nV.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-xLox0nu7.js → stateDiagram-OKZ733FA-Brf8Wwzr.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-CIkoculx.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CLC_ow9A.js → swimlanes-SLNWSIFB-CvNw3ZqF.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-pJ-V5EdT.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DmJZ4uyY.js → timeline-definition-Z64GVDOM-z2LyZpDt.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DD7GGi9Z.js → vennDiagram-T6HMQDX7-Bc_coprH.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-Bi8PQoGt.js → wardleyDiagram-T6FBY63Y-M1NSmWq-.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-uKdAY8Dp.js → xychartDiagram-ELKLHX3M-Ct0rjGXW.js} +1 -1
- package/dist/worker/console/static/index.html +1 -1
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +5 -5
- package/dist/worker/console/static-src/operator-chat/input-history.js +8 -6
- package/dist/worker/console/static-src/shell/console-update-reload.js +36 -9
- package/dist/worker/console/static-src/shell/workspace-route.js +11 -0
- package/dist/worker/observe/static/dag-helpers.js +4 -2
- package/dist/worker/observe/static/inspect-workspace.js +23 -0
- package/dist/worker/observe/static/kpi.js +2 -2
- package/dist/worker/observe/static/operator-chrome.d.ts +10 -2
- package/dist/worker/observe/static/operator-chrome.js +37 -23
- package/dist/worker/observe/static/relations.js +2 -2
- package/dist/worker/observe/static/router.d.ts +12 -1
- package/dist/worker/observe/static/router.js +48 -6
- package/dist/worker/observe/static/run-processing.js +4 -2
- package/dist/worker/observe/static/shell-chrome.js +2 -2
- package/dist/worker/observe/static/state.js +5 -0
- package/dist/worker/observe/static/styles.css +95 -0
- package/dist/worker/observe/static/views/dag-inspector.js +14 -7
- package/dist/worker/observe/static/views/dag.js +10 -2
- package/dist/worker/observe/static/views/dags.js +3 -1
- package/dist/worker/observe/static/views/dashboard.js +14 -6
- package/dist/worker/observe/static/views/pool.js +7 -2
- package/dist/worker/observe/static/views/run.js +12 -3
- package/dist/worker/observe/static/views/session-timeline.js +69 -10
- package/dist/worker/observe/static/views/task.js +7 -2
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +58 -15
- package/dist/workflows/dag/backend-test-plan-protocol.js +106 -0
- package/dist/workflows/dag/backend-test-scenario-param.js +16 -5
- package/dist/workflows/dag/backend-test-writer-completeness.js +42 -6
- package/dist/workflows/dag/frontend-shadow-dual-write.js +33 -6
- package/dist/workflows/dag/init-hybrid.js +193 -32
- package/dist/workflows/dag/node-execution.js +1 -1
- package/dist/workflows/dag/prompt.js +36 -2
- package/dist/workflows/dag/types.js +1 -1
- package/docs/templates/backend-test-dag.json +5 -5
- package/package.json +3 -2
- package/dist/worker/console/static/assets/channel-DWYwciiv.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-D6jOf_l4.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-D6jOf_l4.js +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DS5ZRvJG.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Dvpxn-bf.js +0 -8
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* mountOperatorChrome 仅在浏览器环境调用。
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import { withWorkspaceQuery } from "./router.js";
|
|
15
|
+
|
|
14
16
|
export const OPERATE_TABS = Object.freeze([
|
|
15
17
|
{ id: "tasks", label: "任务", href: "/#/operate/tasks" },
|
|
16
18
|
{ id: "recovery", label: "恢复", href: "/#/operate/recovery" },
|
|
@@ -52,16 +54,7 @@ export function parseInspectWorkspaceId(hashValue) {
|
|
|
52
54
|
return id || null;
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
export
|
|
56
|
-
const raw = String(href ?? "");
|
|
57
|
-
if (!workspaceId) return raw;
|
|
58
|
-
const qIndex = raw.indexOf("?");
|
|
59
|
-
if (qIndex < 0) return `${raw}?workspaceId=${encodeURIComponent(workspaceId)}`;
|
|
60
|
-
const params = new URLSearchParams(raw.slice(qIndex + 1));
|
|
61
|
-
if (params.get("workspaceId") === workspaceId) return raw;
|
|
62
|
-
params.set("workspaceId", workspaceId);
|
|
63
|
-
return `${raw.slice(0, qIndex)}?${params.toString()}`;
|
|
64
|
-
}
|
|
57
|
+
export { withWorkspaceQuery };
|
|
65
58
|
|
|
66
59
|
function splitHash(hashValue) {
|
|
67
60
|
const raw = String(hashValue ?? "").replace(/^#/, "");
|
|
@@ -129,13 +122,17 @@ export function parseInspectContext(hashValue) {
|
|
|
129
122
|
if (from !== "recovery") return null;
|
|
130
123
|
const returnParams = new URLSearchParams();
|
|
131
124
|
if (objectView.view === "dag") returnParams.set("dagRunId", objectView.id);
|
|
125
|
+
const workspaceId = parseInspectWorkspaceId(hashValue);
|
|
132
126
|
const suffix = returnParams.toString();
|
|
133
127
|
return {
|
|
134
128
|
view: objectView.view,
|
|
135
129
|
id: objectView.id,
|
|
136
130
|
from,
|
|
137
131
|
kindLabel: CONTEXT_KIND_LABELS[objectView.view] ?? objectView.view,
|
|
138
|
-
returnHref:
|
|
132
|
+
returnHref: withWorkspaceQuery(
|
|
133
|
+
`/#/operate/recovery${suffix ? `?${suffix}` : ""}`,
|
|
134
|
+
workspaceId,
|
|
135
|
+
),
|
|
139
136
|
returnLabel: "返回 恢复",
|
|
140
137
|
};
|
|
141
138
|
}
|
|
@@ -143,21 +140,31 @@ export function parseInspectContext(hashValue) {
|
|
|
143
140
|
/** 从 Inspect 切回操作侧:持有 DAG 选中时不丢对象(换动作不换对象)。 */
|
|
144
141
|
export function resolveOperateHrefFromInspectHash(hashValue) {
|
|
145
142
|
const objectView = parseInspectObjectView(hashValue);
|
|
143
|
+
const workspaceId = parseInspectWorkspaceId(hashValue);
|
|
146
144
|
if (objectView?.view === "dag") {
|
|
147
|
-
return
|
|
145
|
+
return withWorkspaceQuery(
|
|
146
|
+
`/#/operate/recovery?dagRunId=${encodeURIComponent(objectView.id)}`,
|
|
147
|
+
workspaceId,
|
|
148
|
+
);
|
|
148
149
|
}
|
|
149
|
-
return "/#/operate/tasks";
|
|
150
|
+
return withWorkspaceQuery("/#/operate/tasks", workspaceId);
|
|
150
151
|
}
|
|
151
152
|
|
|
152
153
|
/** Inspect DAG 详情 → Operate Recovery 协作中止深链(只改导航,不发 mutation)。 */
|
|
153
|
-
export function buildOperateInterruptHref(dagRunId) {
|
|
154
|
-
return
|
|
154
|
+
export function buildOperateInterruptHref(dagRunId, options = {}) {
|
|
155
|
+
return withWorkspaceQuery(
|
|
156
|
+
`/#/operate/recovery?dagRunId=${encodeURIComponent(dagRunId)}&intent=interrupt`,
|
|
157
|
+
options.workspaceId,
|
|
158
|
+
);
|
|
155
159
|
}
|
|
156
160
|
|
|
157
161
|
/** 操作侧 → Inspect 的 canonical 深链(可携带 from 以激活上下文 chip)。 */
|
|
158
162
|
export function buildInspectDagHref(dagRunId, options = {}) {
|
|
159
163
|
const base = `/#/inspect/dag/${encodeURIComponent(dagRunId)}`;
|
|
160
|
-
return
|
|
164
|
+
return withWorkspaceQuery(
|
|
165
|
+
options.from === "recovery" ? `${base}?from=recovery` : base,
|
|
166
|
+
options.workspaceId,
|
|
167
|
+
);
|
|
161
168
|
}
|
|
162
169
|
|
|
163
170
|
/** 清除跨模式来源标记,但保留对象页上的其它查询参数与当前历史位置。 */
|
|
@@ -218,14 +225,19 @@ export function buildOperatorChromeModel(config) {
|
|
|
218
225
|
: []
|
|
219
226
|
).map((tab) => ({
|
|
220
227
|
...tab,
|
|
228
|
+
href: withWorkspaceQuery(
|
|
229
|
+
tab.href,
|
|
230
|
+
config.workspaceSwitcher?.activeId ?? config.workspaceId,
|
|
231
|
+
),
|
|
221
232
|
active:
|
|
222
|
-
tab.href ===
|
|
233
|
+
tab.href.split("?")[0] ===
|
|
223
234
|
(String(config.activeTabHref ?? "").split("?")[0] || null),
|
|
224
235
|
}));
|
|
225
236
|
const state = config.readinessState ?? "unknown";
|
|
226
237
|
const operateHref = config.operateHref ?? "/#/operate/tasks";
|
|
227
238
|
const inspectHref = config.inspectHref ?? "/#/inspect";
|
|
228
|
-
const activeWorkspaceId =
|
|
239
|
+
const activeWorkspaceId =
|
|
240
|
+
config.workspaceSwitcher?.activeId ?? config.workspaceId ?? null;
|
|
229
241
|
return {
|
|
230
242
|
mode,
|
|
231
243
|
homeHref: withWorkspaceQuery(
|
|
@@ -245,13 +257,13 @@ export function buildOperatorChromeModel(config) {
|
|
|
245
257
|
{
|
|
246
258
|
id: "operate",
|
|
247
259
|
label: "操作",
|
|
248
|
-
href: operateHref,
|
|
260
|
+
href: withWorkspaceQuery(operateHref, activeWorkspaceId),
|
|
249
261
|
active: mode === "operate",
|
|
250
262
|
},
|
|
251
263
|
{
|
|
252
264
|
id: "inspect",
|
|
253
265
|
label: "观测",
|
|
254
|
-
href: inspectHref,
|
|
266
|
+
href: withWorkspaceQuery(inspectHref, activeWorkspaceId),
|
|
255
267
|
active: mode === "inspect",
|
|
256
268
|
},
|
|
257
269
|
],
|
|
@@ -261,9 +273,10 @@ export function buildOperatorChromeModel(config) {
|
|
|
261
273
|
pi: {
|
|
262
274
|
state,
|
|
263
275
|
label: READINESS_LABELS[state] ?? state,
|
|
264
|
-
href:
|
|
265
|
-
config.readinessHref ??
|
|
266
|
-
|
|
276
|
+
href: withWorkspaceQuery(
|
|
277
|
+
config.readinessHref ?? "/#/operate/models",
|
|
278
|
+
activeWorkspaceId,
|
|
279
|
+
),
|
|
267
280
|
title: config.readinessTitle ?? "",
|
|
268
281
|
},
|
|
269
282
|
workspaceTabsVisible: tabs.length > 0,
|
|
@@ -424,6 +437,7 @@ export function mountOperatorChrome(root, config = {}) {
|
|
|
424
437
|
const base = { ...currentConfig };
|
|
425
438
|
if (base.liveLocation) {
|
|
426
439
|
const hash = window.location.hash;
|
|
440
|
+
base.workspaceId = parseInspectWorkspaceId(hash);
|
|
427
441
|
base.activeTabHref = activeInspectTabHref(hash);
|
|
428
442
|
base.operateHref = resolveOperateHrefFromInspectHash(hash);
|
|
429
443
|
base.context = parseInspectContext(hash);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Object relation navigation (Observe UI R5). */
|
|
2
2
|
import { el } from "./dom.js";
|
|
3
|
-
import { navigate } from "./router.js";
|
|
3
|
+
import { inspectPathToCanonicalHref, navigate } from "./router.js";
|
|
4
4
|
|
|
5
5
|
export function objectExistsInSnapshot(kind, id, snapshot, featureId) {
|
|
6
6
|
if (!id) return false;
|
|
@@ -48,7 +48,7 @@ export function objectRouteLink(kind, id, snapshot, featureId) {
|
|
|
48
48
|
if (!exists) return el("span", "muted", id);
|
|
49
49
|
const path = objectRoutePath(kind, id, featureId);
|
|
50
50
|
const a = el("a", "object-link", id);
|
|
51
|
-
a.href =
|
|
51
|
+
a.href = inspectPathToCanonicalHref(path);
|
|
52
52
|
a.addEventListener("click", (e) => {
|
|
53
53
|
e.preventDefault();
|
|
54
54
|
e.stopPropagation();
|
|
@@ -18,7 +18,18 @@ export function parseRoute(): InspectHashRoute;
|
|
|
18
18
|
export function setNavigateHandler(
|
|
19
19
|
handler: ((href: string) => void) | null,
|
|
20
20
|
): void;
|
|
21
|
-
export function
|
|
21
|
+
export function workspaceIdFromLocation(locationLike?: {
|
|
22
|
+
hash?: string;
|
|
23
|
+
search?: string;
|
|
24
|
+
} | null): string | null;
|
|
25
|
+
export function withWorkspaceQuery(
|
|
26
|
+
href: string,
|
|
27
|
+
workspaceId?: string | null,
|
|
28
|
+
): string;
|
|
29
|
+
export function inspectPathToCanonicalHref(
|
|
30
|
+
path: string,
|
|
31
|
+
workspaceId?: string | null,
|
|
32
|
+
): string;
|
|
22
33
|
export function navigate(path: string): void;
|
|
23
34
|
|
|
24
35
|
export const DAG_HISTORY_DEFAULT_PAGE: number;
|
|
@@ -70,15 +70,57 @@ export function setNavigateHandler(handler) {
|
|
|
70
70
|
navigateHandler = typeof handler === "function" ? handler : null;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
/** Read the selected workspace from either Console's canonical hash or the
|
|
74
|
+
* standalone Inspect document query. The latter remains a compatibility
|
|
75
|
+
* entrypoint, so it must not manufacture a scope when none was supplied. */
|
|
76
|
+
export function workspaceIdFromLocation(locationLike = typeof location !== "undefined" ? location : null) {
|
|
77
|
+
if (!locationLike) return null;
|
|
78
|
+
const hash = String(locationLike.hash ?? "").replace(/^#/, "");
|
|
79
|
+
const hashQuery = hash.includes("?") ? hash.slice(hash.indexOf("?") + 1) : "";
|
|
80
|
+
const fromHash = new URLSearchParams(hashQuery).get("workspaceId")?.trim();
|
|
81
|
+
if (fromHash) return fromHash;
|
|
82
|
+
return new URLSearchParams(String(locationLike.search ?? ""))
|
|
83
|
+
.get("workspaceId")
|
|
84
|
+
?.trim() || null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Add the current workspace to a Console hash href without changing the
|
|
88
|
+
* destination's own query. This is deliberately usable for Inspect, Operate,
|
|
89
|
+
* and Chat; Console canonicalization still strips scope from Chat itself. */
|
|
90
|
+
export function withWorkspaceQuery(href, workspaceId = workspaceIdFromLocation()) {
|
|
91
|
+
const raw = String(href ?? "").trim();
|
|
92
|
+
const id = workspaceId?.trim();
|
|
93
|
+
if (!raw || !id) return raw;
|
|
94
|
+
const normalized = raw.startsWith("#") ? `/${raw}` : raw;
|
|
95
|
+
const qIndex = normalized.indexOf("?");
|
|
96
|
+
const base = qIndex >= 0 ? normalized.slice(0, qIndex) : normalized;
|
|
97
|
+
const query = new URLSearchParams(qIndex >= 0 ? normalized.slice(qIndex + 1) : "");
|
|
98
|
+
query.set("workspaceId", id);
|
|
99
|
+
return `${base}?${query.toString()}`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function inspectPathToCanonicalHref(path, workspaceId = workspaceIdFromLocation()) {
|
|
74
103
|
const raw = String(path ?? "");
|
|
75
|
-
if (raw.startsWith("/#/inspect")) return raw;
|
|
76
|
-
if (raw.startsWith("#/inspect")) return `/${raw}
|
|
104
|
+
if (raw.startsWith("/#/inspect")) return withWorkspaceQuery(raw, workspaceId);
|
|
105
|
+
if (raw.startsWith("#/inspect")) return withWorkspaceQuery(`/${raw}`, workspaceId);
|
|
106
|
+
// Inspect owns only its own paths. Passing an Operate/Chat deep link through
|
|
107
|
+
// the old converter produced /#/inspect/#/operate/... and was the direct
|
|
108
|
+
// cause of the live-DAG interrupt link landing on the wrong page.
|
|
109
|
+
if (raw.startsWith("/#/operate") || raw.startsWith("/#/chat")) {
|
|
110
|
+
return withWorkspaceQuery(raw, workspaceId);
|
|
111
|
+
}
|
|
112
|
+
if (raw.startsWith("#/operate") || raw.startsWith("#/chat")) {
|
|
113
|
+
return withWorkspaceQuery(`/${raw}`, workspaceId);
|
|
114
|
+
}
|
|
77
115
|
const stripped = raw.replace(/^#/, "");
|
|
78
116
|
const withSlash = stripped.startsWith("/") ? stripped : `/${stripped}`;
|
|
79
|
-
if (withSlash === "/inspect" || withSlash === "/")
|
|
80
|
-
|
|
81
|
-
|
|
117
|
+
if (withSlash === "/inspect" || withSlash === "/") {
|
|
118
|
+
return withWorkspaceQuery("/#/inspect", workspaceId);
|
|
119
|
+
}
|
|
120
|
+
if (withSlash.startsWith("/inspect/")) {
|
|
121
|
+
return withWorkspaceQuery(`/#${withSlash}`, workspaceId);
|
|
122
|
+
}
|
|
123
|
+
return withWorkspaceQuery(`/#/inspect${withSlash}`, workspaceId);
|
|
82
124
|
}
|
|
83
125
|
|
|
84
126
|
export function navigate(path) {
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
ARTIFACT_KEYS,
|
|
7
7
|
} from "./constants.js";
|
|
8
8
|
import { badge } from "./format.js";
|
|
9
|
-
import { navigate } from "./router.js";
|
|
9
|
+
import { inspectPathToCanonicalHref, navigate } from "./router.js";
|
|
10
10
|
|
|
11
11
|
export function stepDisplayLabel(label) {
|
|
12
12
|
return STEP_LABELS[label] ?? label;
|
|
@@ -127,7 +127,9 @@ export function taskRunHistoryRow(run) {
|
|
|
127
127
|
let runLink = run.workerRunId;
|
|
128
128
|
if (run.workerRunId) {
|
|
129
129
|
const a = document.createElement("a");
|
|
130
|
-
a.href =
|
|
130
|
+
a.href = inspectPathToCanonicalHref(
|
|
131
|
+
`/run/${encodeURIComponent(run.workerRunId)}`,
|
|
132
|
+
);
|
|
131
133
|
a.textContent = run.workerRunId;
|
|
132
134
|
a.addEventListener("click", (e) => {
|
|
133
135
|
e.preventDefault();
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { UI_TEXT } from "./constants.js";
|
|
3
3
|
import { clearNode, el } from "./dom.js";
|
|
4
4
|
import { formatAgo } from "./format.js";
|
|
5
|
-
import { navigate } from "./router.js";
|
|
5
|
+
import { inspectPathToCanonicalHref, navigate } from "./router.js";
|
|
6
6
|
import { buildObjectRelations, renderObjectRelationBar } from "./relations.js";
|
|
7
7
|
import { closeDagInspector } from "./views/dag-inspector.js";
|
|
8
8
|
|
|
@@ -23,7 +23,7 @@ export function setBreadcrumb(parts) {
|
|
|
23
23
|
if (i > 0) nav.appendChild(document.createTextNode(" / "));
|
|
24
24
|
if (part.href) {
|
|
25
25
|
const a = document.createElement("a");
|
|
26
|
-
a.href = part.href;
|
|
26
|
+
a.href = inspectPathToCanonicalHref(part.href);
|
|
27
27
|
a.textContent = part.label;
|
|
28
28
|
a.addEventListener("click", (e) => {
|
|
29
29
|
e.preventDefault();
|
|
@@ -105,6 +105,11 @@ export const uiState = {
|
|
|
105
105
|
dagGraphViewportState: null,
|
|
106
106
|
dagTrajectoryViewportState: null,
|
|
107
107
|
dagTimelineViewportState: null,
|
|
108
|
+
/**
|
|
109
|
+
* Explicit「执行记录」auto-scroll preference for the selected node.
|
|
110
|
+
* Defaults on for every newly selected node and survives polling/tab switches.
|
|
111
|
+
*/
|
|
112
|
+
dagTimelineAutoScrollEnabled: true,
|
|
108
113
|
dagNodeOutputViewportState: null,
|
|
109
114
|
dagNodeInputViewportState: null,
|
|
110
115
|
/** Spec-evidence inspector content scroll (single active region snapshot). */
|
|
@@ -1968,6 +1968,101 @@ body.is-resizing-dag-graph {
|
|
|
1968
1968
|
gap: 8px;
|
|
1969
1969
|
margin-bottom: var(--space-2);
|
|
1970
1970
|
}
|
|
1971
|
+
.process-timeline-autoscroll-dock {
|
|
1972
|
+
position: sticky;
|
|
1973
|
+
top: calc(100% - 42px);
|
|
1974
|
+
z-index: 4;
|
|
1975
|
+
display: flex;
|
|
1976
|
+
justify-content: flex-end;
|
|
1977
|
+
height: 0;
|
|
1978
|
+
margin: 0 2px;
|
|
1979
|
+
pointer-events: none;
|
|
1980
|
+
}
|
|
1981
|
+
.process-timeline-autoscroll {
|
|
1982
|
+
position: relative;
|
|
1983
|
+
pointer-events: auto;
|
|
1984
|
+
display: inline-flex;
|
|
1985
|
+
align-items: center;
|
|
1986
|
+
justify-content: center;
|
|
1987
|
+
gap: 7px;
|
|
1988
|
+
width: 36px;
|
|
1989
|
+
height: 36px;
|
|
1990
|
+
padding: 0;
|
|
1991
|
+
border: 1px solid var(--hairline-strong);
|
|
1992
|
+
border-radius: 50%;
|
|
1993
|
+
background: color-mix(in srgb, var(--surface) 94%, var(--canvas));
|
|
1994
|
+
color: var(--body);
|
|
1995
|
+
box-shadow: 0 8px 24px rgb(38 37 30 / 14%);
|
|
1996
|
+
font-size: 14px;
|
|
1997
|
+
cursor: pointer;
|
|
1998
|
+
backdrop-filter: blur(10px);
|
|
1999
|
+
}
|
|
2000
|
+
.process-timeline-autoscroll::after {
|
|
2001
|
+
position: absolute;
|
|
2002
|
+
right: 0;
|
|
2003
|
+
bottom: calc(100% + 8px);
|
|
2004
|
+
padding: 6px 8px;
|
|
2005
|
+
border: 1px solid var(--hairline-strong);
|
|
2006
|
+
border-radius: var(--radius-sm);
|
|
2007
|
+
background: var(--ink);
|
|
2008
|
+
color: #fff;
|
|
2009
|
+
box-shadow: 0 6px 16px rgb(38 37 30 / 14%);
|
|
2010
|
+
content: attr(data-tooltip);
|
|
2011
|
+
font-size: 11px;
|
|
2012
|
+
font-weight: 650;
|
|
2013
|
+
line-height: 1;
|
|
2014
|
+
white-space: nowrap;
|
|
2015
|
+
opacity: 0;
|
|
2016
|
+
transform: translateY(3px);
|
|
2017
|
+
pointer-events: none;
|
|
2018
|
+
transition: opacity 120ms ease, transform 120ms ease;
|
|
2019
|
+
}
|
|
2020
|
+
.process-timeline-autoscroll:hover::after,
|
|
2021
|
+
.process-timeline-autoscroll:focus-visible::after {
|
|
2022
|
+
opacity: 1;
|
|
2023
|
+
transform: translateY(0);
|
|
2024
|
+
}
|
|
2025
|
+
.process-timeline-autoscroll:hover,
|
|
2026
|
+
.process-timeline-autoscroll:focus-visible {
|
|
2027
|
+
border-color: var(--orange);
|
|
2028
|
+
color: var(--orange-active);
|
|
2029
|
+
outline: none;
|
|
2030
|
+
box-shadow: 0 8px 24px rgb(38 37 30 / 16%), 0 0 0 3px var(--orange-soft);
|
|
2031
|
+
}
|
|
2032
|
+
.process-timeline-autoscroll.is-active {
|
|
2033
|
+
border-color: var(--ink);
|
|
2034
|
+
background: var(--ink);
|
|
2035
|
+
color: #fff;
|
|
2036
|
+
}
|
|
2037
|
+
.process-timeline-autoscroll.is-active:hover,
|
|
2038
|
+
.process-timeline-autoscroll.is-active:focus-visible {
|
|
2039
|
+
border-color: var(--orange);
|
|
2040
|
+
background: var(--ink);
|
|
2041
|
+
color: #fff;
|
|
2042
|
+
}
|
|
2043
|
+
.process-timeline-autoscroll-signal {
|
|
2044
|
+
width: 5px;
|
|
2045
|
+
height: 5px;
|
|
2046
|
+
border-radius: 50%;
|
|
2047
|
+
background: var(--muted-soft);
|
|
2048
|
+
}
|
|
2049
|
+
.process-timeline-autoscroll.is-active .process-timeline-autoscroll-signal {
|
|
2050
|
+
background: var(--orange);
|
|
2051
|
+
box-shadow: 0 0 0 4px rgb(245 78 0 / 16%);
|
|
2052
|
+
animation: process-timeline-live-pulse 1.8s ease-out infinite;
|
|
2053
|
+
}
|
|
2054
|
+
@keyframes process-timeline-live-pulse {
|
|
2055
|
+
0%, 55%, 100% { box-shadow: 0 0 0 4px rgb(245 78 0 / 16%); }
|
|
2056
|
+
75% { box-shadow: 0 0 0 7px rgb(245 78 0 / 0%); }
|
|
2057
|
+
}
|
|
2058
|
+
@media (prefers-reduced-motion: reduce) {
|
|
2059
|
+
.process-timeline-autoscroll.is-active .process-timeline-autoscroll-signal {
|
|
2060
|
+
animation: none;
|
|
2061
|
+
}
|
|
2062
|
+
.process-timeline-autoscroll::after {
|
|
2063
|
+
transition: none;
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
1971
2066
|
.process-timeline-filter-group {
|
|
1972
2067
|
display: inline-flex;
|
|
1973
2068
|
flex-wrap: wrap;
|
|
@@ -421,6 +421,10 @@ function renderDagInspectorForCurrent() {
|
|
|
421
421
|
function resetInspectorNodeSelection(dagRunId, nodeId) {
|
|
422
422
|
uiState.selectedDagNodeId = nodeId;
|
|
423
423
|
uiState.dagInspectorOpen = true;
|
|
424
|
+
// Each node opens in live-follow mode by default. The explicit floating
|
|
425
|
+
// control can pause it without losing the reader's current position.
|
|
426
|
+
uiState.dagTimelineAutoScrollEnabled = true;
|
|
427
|
+
uiState.dagTimelineViewportState = null;
|
|
424
428
|
// Reset the per-attempt selection so an old node's attempt cannot leak
|
|
425
429
|
// into the newly selected node (always defaults back to the latest).
|
|
426
430
|
uiState.dagInspectorAttempt = null;
|
|
@@ -594,18 +598,13 @@ export function createDagInspectorResizeHandle(panel) {
|
|
|
594
598
|
export function captureDagTimelineViewportState(panel) {
|
|
595
599
|
const scrollEl = panel.querySelector(".process-timeline-scroll");
|
|
596
600
|
if (scrollEl?.dataset.dagRunId && scrollEl.dataset.nodeId) {
|
|
597
|
-
const followLatest = computeFollowLatest(
|
|
598
|
-
scrollEl.scrollTop,
|
|
599
|
-
scrollEl.scrollHeight,
|
|
600
|
-
scrollEl.clientHeight,
|
|
601
|
-
);
|
|
602
601
|
uiState.dagTimelineViewportState = updateDagTimelineViewportState(
|
|
603
602
|
uiState.dagTimelineViewportState,
|
|
604
603
|
scrollEl.dataset.dagRunId,
|
|
605
604
|
scrollEl.dataset.nodeId,
|
|
606
605
|
scrollEl.scrollLeft,
|
|
607
606
|
scrollEl.scrollTop,
|
|
608
|
-
|
|
607
|
+
uiState.dagTimelineAutoScrollEnabled !== false,
|
|
609
608
|
);
|
|
610
609
|
}
|
|
611
610
|
}
|
|
@@ -2111,7 +2110,7 @@ function buildArtifactRevisionSection(dagRunId, node) {
|
|
|
2111
2110
|
? "打开新运行"
|
|
2112
2111
|
: "打开失败的新运行";
|
|
2113
2112
|
open.addEventListener("click", () => {
|
|
2114
|
-
|
|
2113
|
+
navigate(`/dag/${encodeURIComponent(childRunId)}`);
|
|
2115
2114
|
});
|
|
2116
2115
|
planHost.appendChild(open);
|
|
2117
2116
|
}
|
|
@@ -2919,6 +2918,11 @@ function restoreInspectorContentScroll(content, dagRunId, nodeId, tab) {
|
|
|
2919
2918
|
) {
|
|
2920
2919
|
return;
|
|
2921
2920
|
}
|
|
2921
|
+
if (tab === "timeline" && uiState.dagTimelineAutoScrollEnabled !== false) {
|
|
2922
|
+
content.scrollLeft = uiState.dagTimelineViewportState?.scrollLeft ?? 0;
|
|
2923
|
+
content.scrollTop = content.scrollHeight;
|
|
2924
|
+
return;
|
|
2925
|
+
}
|
|
2922
2926
|
if (tab === "output") {
|
|
2923
2927
|
const saved = uiState.dagNodeOutputViewportState;
|
|
2924
2928
|
if (saved?.regionKey === regionKey && saved.followLatest) {
|
|
@@ -3673,6 +3677,9 @@ function fillDagInspectorContent(content, dagRunId, node) {
|
|
|
3673
3677
|
const attemptBuilt = buildAttemptBar(dagRunId, node);
|
|
3674
3678
|
if (attemptBuilt?.bar) content.appendChild(attemptBuilt.bar);
|
|
3675
3679
|
renderExecutorAwareTimeline(content, dagRunId, node);
|
|
3680
|
+
// Initial content is rendered before it is attached. Restore again on the
|
|
3681
|
+
// connected animation frame so live-follow sees the final scroll height.
|
|
3682
|
+
restoreInspectorContentScroll(content, dagRunId, node.nodeId, "timeline");
|
|
3676
3683
|
} else if (activeTab === "input") {
|
|
3677
3684
|
void renderDagNodeInput(content, dagRunId, node);
|
|
3678
3685
|
} else {
|
|
@@ -35,7 +35,12 @@ import {
|
|
|
35
35
|
parseMarkdownBlocks,
|
|
36
36
|
markdownLinkHref,
|
|
37
37
|
} from "../format.js";
|
|
38
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
parseRoute,
|
|
40
|
+
navigate,
|
|
41
|
+
buildHashPath,
|
|
42
|
+
withWorkspaceQuery,
|
|
43
|
+
} from "../router.js";
|
|
39
44
|
import {
|
|
40
45
|
fetchJson,
|
|
41
46
|
artifactUrl,
|
|
@@ -248,6 +253,7 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
248
253
|
uiState.dagInspectorAttempt = null;
|
|
249
254
|
uiState.dagGraphViewportState = null;
|
|
250
255
|
uiState.dagTimelineViewportState = null;
|
|
256
|
+
uiState.dagTimelineAutoScrollEnabled = true;
|
|
251
257
|
}
|
|
252
258
|
// AC-001:同会话内重新进入同一 dagRunId 保持当前图视图模式(不切回逻辑拓扑)。
|
|
253
259
|
uiState.dagGraphViewMode = dagGraphViewModes.get(dagRunId) ?? "topology";
|
|
@@ -364,7 +370,9 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
364
370
|
"page-action-btn",
|
|
365
371
|
"在操作台中止此运行 →",
|
|
366
372
|
);
|
|
367
|
-
interruptLink.href =
|
|
373
|
+
interruptLink.href = withWorkspaceQuery(
|
|
374
|
+
`/#/operate/recovery?dagRunId=${encodeURIComponent(dag.dagRunId)}&intent=interrupt`,
|
|
375
|
+
);
|
|
368
376
|
interruptLink.setAttribute("data-testid", "inspect-dag-interrupt-link");
|
|
369
377
|
interruptLink.title = "只跳转到操作台 Recovery,不会在观测页执行中止";
|
|
370
378
|
primaryEntries.push(["协作中止", interruptLink]);
|
|
@@ -327,7 +327,9 @@ function buildRow(run) {
|
|
|
327
327
|
|
|
328
328
|
const nameTd = document.createElement("td");
|
|
329
329
|
const nameLink = el("a", "dags-name-link", run.title || run.dagRunId);
|
|
330
|
-
nameLink.href =
|
|
330
|
+
nameLink.href = inspectPathToCanonicalHref(
|
|
331
|
+
`/dag/${encodeURIComponent(run.dagRunId)}`,
|
|
332
|
+
);
|
|
331
333
|
nameLink.addEventListener("click", (event) => {
|
|
332
334
|
event.preventDefault();
|
|
333
335
|
event.stopPropagation();
|
|
@@ -35,7 +35,13 @@ import {
|
|
|
35
35
|
parseMarkdownBlocks,
|
|
36
36
|
markdownLinkHref,
|
|
37
37
|
} from "../format.js";
|
|
38
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
parseRoute,
|
|
40
|
+
navigate,
|
|
41
|
+
buildHashPath,
|
|
42
|
+
inspectPathToCanonicalHref,
|
|
43
|
+
withWorkspaceQuery,
|
|
44
|
+
} from "../router.js";
|
|
39
45
|
import { fetchJson, artifactUrl, parseArtifactPreviewResponse } from "../api.js";
|
|
40
46
|
import {
|
|
41
47
|
objectRouteLink,
|
|
@@ -118,14 +124,14 @@ async function renderNightStrip() {
|
|
|
118
124
|
|
|
119
125
|
const actions = el("div", "night-strip-actions");
|
|
120
126
|
const detail = el("a", "page-action-btn", "夜间详情");
|
|
121
|
-
detail.href = "
|
|
127
|
+
detail.href = inspectPathToCanonicalHref("/night");
|
|
122
128
|
detail.addEventListener("click", (e) => {
|
|
123
129
|
e.preventDefault();
|
|
124
130
|
navigate("/night");
|
|
125
131
|
});
|
|
126
132
|
actions.appendChild(detail);
|
|
127
133
|
const manage = el("a", "page-action-btn night-strip-manage", "晨审处置 →");
|
|
128
|
-
manage.href = "/#/operate/night";
|
|
134
|
+
manage.href = withWorkspaceQuery("/#/operate/night");
|
|
129
135
|
manage.title = "夜间计划的管理与晨审处置在操作台完成(Inspect 保持只读)";
|
|
130
136
|
actions.appendChild(manage);
|
|
131
137
|
strip.appendChild(actions);
|
|
@@ -528,7 +534,7 @@ export async function renderDashboard(scrollTo) {
|
|
|
528
534
|
workerBlock.appendChild(el("div", "risk-total", String(block.total)));
|
|
529
535
|
workerBlock.appendChild(el("p", "risk-summary", block.summary));
|
|
530
536
|
const link = el("a", "risk-link", "查看异常 Task →");
|
|
531
|
-
link.href = "
|
|
537
|
+
link.href = inspectPathToCanonicalHref("/failures");
|
|
532
538
|
link.addEventListener("click", (event) => {
|
|
533
539
|
event.preventDefault();
|
|
534
540
|
navigate("/failures");
|
|
@@ -556,7 +562,7 @@ export async function renderDashboard(scrollTo) {
|
|
|
556
562
|
const head = el("div", "section-heading-row");
|
|
557
563
|
head.appendChild(el("h3", "section-heading", UI_TEXT.recentDags));
|
|
558
564
|
const allLink = el("a", "section-heading-action", UI_TEXT.viewAllDags);
|
|
559
|
-
allLink.href = "
|
|
565
|
+
allLink.href = inspectPathToCanonicalHref("/dags?page=1&pageSize=20");
|
|
560
566
|
allLink.setAttribute("aria-label", "查看全部 DAG 运行历史");
|
|
561
567
|
allLink.addEventListener("click", (event) => {
|
|
562
568
|
event.preventDefault();
|
|
@@ -686,7 +692,9 @@ function syncTableRow(row, cells) {
|
|
|
686
692
|
function buildDagTimelineItem(dag) {
|
|
687
693
|
const progress = dagProgress(dag);
|
|
688
694
|
const item = el("a", "dag-run-timeline-item");
|
|
689
|
-
item.href =
|
|
695
|
+
item.href = inspectPathToCanonicalHref(
|
|
696
|
+
`/dag/${encodeURIComponent(dag.dagRunId)}`,
|
|
697
|
+
);
|
|
690
698
|
item.title = dag.title || dag.dagRunId;
|
|
691
699
|
item.addEventListener("click", (event) => {
|
|
692
700
|
event.preventDefault();
|
|
@@ -8,7 +8,12 @@ import {
|
|
|
8
8
|
livenessBadge,
|
|
9
9
|
truncateText,
|
|
10
10
|
} from "../format.js";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
parseRoute,
|
|
13
|
+
navigate,
|
|
14
|
+
buildHashPath,
|
|
15
|
+
inspectPathToCanonicalHref,
|
|
16
|
+
} from "../router.js";
|
|
12
17
|
import { fetchJson } from "../api.js";
|
|
13
18
|
import { objectRouteLink } from "../relations.js";
|
|
14
19
|
import {
|
|
@@ -118,7 +123,7 @@ function taskPath(task) {
|
|
|
118
123
|
|
|
119
124
|
function linkTo(path, label, className) {
|
|
120
125
|
const a = el("a", className || "object-link", label);
|
|
121
|
-
a.href =
|
|
126
|
+
a.href = inspectPathToCanonicalHref(path);
|
|
122
127
|
a.addEventListener("click", (e) => {
|
|
123
128
|
e.preventDefault();
|
|
124
129
|
e.stopPropagation();
|
|
@@ -35,7 +35,12 @@ import {
|
|
|
35
35
|
parseMarkdownBlocks,
|
|
36
36
|
markdownLinkHref,
|
|
37
37
|
} from "../format.js";
|
|
38
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
parseRoute,
|
|
40
|
+
navigate,
|
|
41
|
+
buildHashPath,
|
|
42
|
+
inspectPathToCanonicalHref,
|
|
43
|
+
} from "../router.js";
|
|
39
44
|
import {
|
|
40
45
|
fetchJson,
|
|
41
46
|
artifactUrl,
|
|
@@ -176,7 +181,9 @@ export function renderRunDagProgress(task, snapshot) {
|
|
|
176
181
|
"详情",
|
|
177
182
|
(() => {
|
|
178
183
|
const a = el("a", "link", "查看 DAG 详情 →");
|
|
179
|
-
a.href =
|
|
184
|
+
a.href = inspectPathToCanonicalHref(
|
|
185
|
+
`/dag/${encodeURIComponent(dag.dagRunId)}`,
|
|
186
|
+
);
|
|
180
187
|
a.addEventListener("click", (e) => {
|
|
181
188
|
e.preventDefault();
|
|
182
189
|
navigate(`/dag/${encodeURIComponent(dag.dagRunId)}`);
|
|
@@ -295,7 +302,9 @@ export function renderRunPanels(task, events, snapshot) {
|
|
|
295
302
|
li.appendChild(marker);
|
|
296
303
|
if (step.label === "run-dag" && dag?.dagRunId) {
|
|
297
304
|
const link = document.createElement("a");
|
|
298
|
-
link.href =
|
|
305
|
+
link.href = inspectPathToCanonicalHref(
|
|
306
|
+
`/dag/${encodeURIComponent(dag.dagRunId)}`,
|
|
307
|
+
);
|
|
299
308
|
link.className = "step-link";
|
|
300
309
|
link.textContent = "DAG →";
|
|
301
310
|
link.addEventListener("click", (e) => {
|