dsh-pentester 1.0.0 → 2.2.0
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/README.md +22 -5
- package/docker/kali/TOOL_PROMPT.md +7 -8
- package/lib/build-id.json +1 -0
- package/lib/client.js +3705 -48
- package/lib/client.js.map +4 -4
- package/lib/index.d.ts +0 -12
- package/lib/index.js +2076 -430
- package/lib/index.js.map +1 -1
- package/package.json +13 -13
- package/presets/pentester/agent.cordis.yml +71 -44
- package/presets/pentester/run-state.mjs +99 -90
package/lib/client.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
window.__ModuleLoader__.load({ id: 'dsh-pentester', factory: (require) => { var module = { exports: {} }; var exports = module.exports;
|
|
2
2
|
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
3
4
|
var __defProp = Object.defineProperty;
|
|
4
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
9
|
var __export = (target, all) => {
|
|
8
10
|
for (var name in all)
|
|
@@ -16,15 +18,29 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
18
|
}
|
|
17
19
|
return to;
|
|
18
20
|
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
19
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
30
|
|
|
21
31
|
// src/ui/client/plugin.tsx
|
|
22
32
|
var plugin_exports = {};
|
|
23
33
|
__export(plugin_exports, {
|
|
24
34
|
apply: () => apply,
|
|
25
|
-
|
|
35
|
+
getBuildMismatch: () => getBuildMismatch,
|
|
36
|
+
getPentesterEndpointState: () => getPentesterEndpointState2,
|
|
37
|
+
getRemoteMountError: () => getRemoteMountError,
|
|
38
|
+
inject: () => inject,
|
|
39
|
+
isRemoteMounted: () => isRemoteMounted,
|
|
40
|
+
setPentesterEndpointState: () => setPentesterEndpointState
|
|
26
41
|
});
|
|
27
42
|
module.exports = __toCommonJS(plugin_exports);
|
|
43
|
+
var import_react19 = __toESM(require("react"), 1);
|
|
28
44
|
|
|
29
45
|
// node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
30
46
|
var external_exports = {};
|
|
@@ -14591,6 +14607,147 @@ var PENTESTER_REMOTE = {
|
|
|
14591
14607
|
descriptors: [PENTESTER_DOCKER_INVOCATION]
|
|
14592
14608
|
};
|
|
14593
14609
|
|
|
14610
|
+
// src/ui-view/invocations.ts
|
|
14611
|
+
var pentesterViewInputSchema = external_exports.discriminatedUnion("kind", [
|
|
14612
|
+
external_exports.object({ kind: external_exports.literal("health") }),
|
|
14613
|
+
external_exports.object({ kind: external_exports.literal("snapshot"), sessionId: external_exports.string() }),
|
|
14614
|
+
external_exports.object({ kind: external_exports.literal("listOutput"), sessionId: external_exports.string(), targetId: external_exports.string().optional() }),
|
|
14615
|
+
external_exports.object({ kind: external_exports.literal("readOutput"), sessionId: external_exports.string(), targetId: external_exports.string().optional(), path: external_exports.string() }),
|
|
14616
|
+
external_exports.object({ kind: external_exports.literal("readFinding"), sessionId: external_exports.string(), detailFile: external_exports.string() })
|
|
14617
|
+
]);
|
|
14618
|
+
var snapshotResultSchema = external_exports.object({
|
|
14619
|
+
run: external_exports.union([external_exports.object({
|
|
14620
|
+
id: external_exports.string(),
|
|
14621
|
+
status: external_exports.enum(["active", "completed"]),
|
|
14622
|
+
currentStage: external_exports.string().nullable(),
|
|
14623
|
+
targets: external_exports.array(external_exports.string()),
|
|
14624
|
+
createdAt: external_exports.string()
|
|
14625
|
+
}), external_exports.null()]),
|
|
14626
|
+
stats: external_exports.object({
|
|
14627
|
+
assets: external_exports.number(),
|
|
14628
|
+
findings: external_exports.number(),
|
|
14629
|
+
delegations: external_exports.number()
|
|
14630
|
+
}),
|
|
14631
|
+
stages: external_exports.array(external_exports.object({
|
|
14632
|
+
id: external_exports.string(),
|
|
14633
|
+
name: external_exports.string(),
|
|
14634
|
+
shortName: external_exports.string(),
|
|
14635
|
+
status: external_exports.string(),
|
|
14636
|
+
delegationCount: external_exports.number(),
|
|
14637
|
+
activeDelegationCount: external_exports.number()
|
|
14638
|
+
})),
|
|
14639
|
+
delegations: external_exports.array(external_exports.object({
|
|
14640
|
+
id: external_exports.string(),
|
|
14641
|
+
stageId: external_exports.string(),
|
|
14642
|
+
agentId: external_exports.string(),
|
|
14643
|
+
sessionId: external_exports.string().optional(),
|
|
14644
|
+
objective: external_exports.string(),
|
|
14645
|
+
status: external_exports.string(),
|
|
14646
|
+
createdAt: external_exports.string(),
|
|
14647
|
+
finishedAt: external_exports.string().optional()
|
|
14648
|
+
})),
|
|
14649
|
+
findings: external_exports.array(external_exports.object({
|
|
14650
|
+
id: external_exports.string(),
|
|
14651
|
+
title: external_exports.string(),
|
|
14652
|
+
severity: external_exports.string(),
|
|
14653
|
+
status: external_exports.string().optional(),
|
|
14654
|
+
sourceStage: external_exports.string(),
|
|
14655
|
+
sourceDelegationId: external_exports.string(),
|
|
14656
|
+
detailFile: external_exports.string(),
|
|
14657
|
+
affectedAsset: external_exports.string().optional(),
|
|
14658
|
+
description: external_exports.string().optional(),
|
|
14659
|
+
updatedAt: external_exports.string()
|
|
14660
|
+
})),
|
|
14661
|
+
trace: external_exports.object({
|
|
14662
|
+
nodes: external_exports.array(external_exports.object({
|
|
14663
|
+
id: external_exports.string(),
|
|
14664
|
+
kind: external_exports.string(),
|
|
14665
|
+
label: external_exports.string(),
|
|
14666
|
+
status: external_exports.string().optional(),
|
|
14667
|
+
sublabel: external_exports.string().optional(),
|
|
14668
|
+
timestamp: external_exports.string().optional()
|
|
14669
|
+
})),
|
|
14670
|
+
edges: external_exports.array(external_exports.object({
|
|
14671
|
+
id: external_exports.string(),
|
|
14672
|
+
source: external_exports.string(),
|
|
14673
|
+
target: external_exports.string(),
|
|
14674
|
+
kind: external_exports.string(),
|
|
14675
|
+
label: external_exports.string().optional()
|
|
14676
|
+
}))
|
|
14677
|
+
}),
|
|
14678
|
+
targets: external_exports.array(external_exports.object({
|
|
14679
|
+
id: external_exports.string(),
|
|
14680
|
+
target: external_exports.string(),
|
|
14681
|
+
canonicalTarget: external_exports.string(),
|
|
14682
|
+
rootSessionId: external_exports.string().nullable(),
|
|
14683
|
+
status: external_exports.string(),
|
|
14684
|
+
currentStage: external_exports.string().nullable(),
|
|
14685
|
+
createdAt: external_exports.string()
|
|
14686
|
+
}))
|
|
14687
|
+
});
|
|
14688
|
+
var unboundResultSchema = external_exports.object({
|
|
14689
|
+
kind: external_exports.literal("unbound"),
|
|
14690
|
+
targets: external_exports.array(external_exports.object({
|
|
14691
|
+
id: external_exports.string(),
|
|
14692
|
+
target: external_exports.string(),
|
|
14693
|
+
canonicalTarget: external_exports.string(),
|
|
14694
|
+
rootSessionId: external_exports.string().nullable(),
|
|
14695
|
+
status: external_exports.string(),
|
|
14696
|
+
currentStage: external_exports.string().nullable(),
|
|
14697
|
+
createdAt: external_exports.string()
|
|
14698
|
+
}))
|
|
14699
|
+
});
|
|
14700
|
+
var outputFileResultSchema = external_exports.object({
|
|
14701
|
+
path: external_exports.string(),
|
|
14702
|
+
name: external_exports.string(),
|
|
14703
|
+
size: external_exports.number(),
|
|
14704
|
+
mtime: external_exports.string(),
|
|
14705
|
+
mime: external_exports.enum(["text", "markdown", "json", "image", "binary"]),
|
|
14706
|
+
content: external_exports.string().optional()
|
|
14707
|
+
});
|
|
14708
|
+
var outputTreeResultSchema = external_exports.object({
|
|
14709
|
+
targetId: external_exports.string(),
|
|
14710
|
+
target: external_exports.string(),
|
|
14711
|
+
current: external_exports.boolean(),
|
|
14712
|
+
children: external_exports.array(external_exports.unknown())
|
|
14713
|
+
});
|
|
14714
|
+
var PENTESTER_VIEW_INVOCATION = {
|
|
14715
|
+
id: "dsh-pentester#pentesterView/command",
|
|
14716
|
+
service: "pentesterView",
|
|
14717
|
+
namespace: "pentesterView",
|
|
14718
|
+
method: "command",
|
|
14719
|
+
invocation: { kind: "direct" },
|
|
14720
|
+
cancellation: {
|
|
14721
|
+
parameter: "signal"
|
|
14722
|
+
},
|
|
14723
|
+
parameters: [
|
|
14724
|
+
{
|
|
14725
|
+
name: "input",
|
|
14726
|
+
wire: "input",
|
|
14727
|
+
source: "json",
|
|
14728
|
+
codec: {
|
|
14729
|
+
mode: "strict",
|
|
14730
|
+
typeSymbol: "dsh-pentester#PentesterViewInput",
|
|
14731
|
+
schema: pentesterViewInputSchema
|
|
14732
|
+
}
|
|
14733
|
+
}
|
|
14734
|
+
],
|
|
14735
|
+
result: {
|
|
14736
|
+
mode: "strict",
|
|
14737
|
+
typeSymbol: "dsh-pentester#PentesterViewResult",
|
|
14738
|
+
schema: external_exports.object({
|
|
14739
|
+
ok: external_exports.boolean(),
|
|
14740
|
+
value: external_exports.union([snapshotResultSchema, outputTreeResultSchema, outputFileResultSchema, unboundResultSchema, external_exports.object({ ready: external_exports.literal(true), buildId: external_exports.string() })]).optional(),
|
|
14741
|
+
error: external_exports.string().optional(),
|
|
14742
|
+
code: external_exports.string().optional()
|
|
14743
|
+
})
|
|
14744
|
+
}
|
|
14745
|
+
};
|
|
14746
|
+
var PENTESTER_VIEW_REMOTE = {
|
|
14747
|
+
package: "dsh-pentester",
|
|
14748
|
+
descriptors: [PENTESTER_VIEW_INVOCATION]
|
|
14749
|
+
};
|
|
14750
|
+
|
|
14594
14751
|
// src/ui/client/remote.ts
|
|
14595
14752
|
function dockerCommand(face, input2) {
|
|
14596
14753
|
if (face === void 0 || typeof face.command !== "function") {
|
|
@@ -14605,6 +14762,86 @@ function dockerCommand(face, input2) {
|
|
|
14605
14762
|
return { ok: false, error: inner.error, code: inner.code };
|
|
14606
14763
|
});
|
|
14607
14764
|
}
|
|
14765
|
+
var pentesterEndpointState = "unknown";
|
|
14766
|
+
function getPentesterEndpointState() {
|
|
14767
|
+
return pentesterEndpointState;
|
|
14768
|
+
}
|
|
14769
|
+
function resetPentesterEndpointState() {
|
|
14770
|
+
if (pentesterEndpointState === "missing") {
|
|
14771
|
+
pentesterEndpointState = "unknown";
|
|
14772
|
+
}
|
|
14773
|
+
}
|
|
14774
|
+
function pentesterViewCommand(face, input2, timeoutMs = 3e4) {
|
|
14775
|
+
if (face === void 0 || typeof face.command !== "function") {
|
|
14776
|
+
return Promise.resolve({ ok: false, error: "rpc_unavailable", code: "rpc_unavailable" });
|
|
14777
|
+
}
|
|
14778
|
+
if (pentesterEndpointState === "missing") {
|
|
14779
|
+
return Promise.resolve({
|
|
14780
|
+
ok: false,
|
|
14781
|
+
error: "Pentester RPC was never registered by the running Host. Check Host build and plugin activation.",
|
|
14782
|
+
code: "rpc_endpoint_missing"
|
|
14783
|
+
});
|
|
14784
|
+
}
|
|
14785
|
+
const controller = new AbortController();
|
|
14786
|
+
const signal = AbortSignal.any([
|
|
14787
|
+
controller.signal,
|
|
14788
|
+
AbortSignal.timeout(timeoutMs)
|
|
14789
|
+
]);
|
|
14790
|
+
const promise2 = face.command(input2, signal).then((result) => {
|
|
14791
|
+
pentesterEndpointState = "healthy";
|
|
14792
|
+
if (!result.ok) {
|
|
14793
|
+
const code = result.error.code;
|
|
14794
|
+
if (code === "transport" || result.error.message?.includes("404")) {
|
|
14795
|
+
pentesterEndpointState = "missing";
|
|
14796
|
+
return {
|
|
14797
|
+
ok: false,
|
|
14798
|
+
error: "Pentester RPC was never registered by the running Host.",
|
|
14799
|
+
code: "rpc_endpoint_missing"
|
|
14800
|
+
};
|
|
14801
|
+
}
|
|
14802
|
+
return { ok: false, error: result.error.message, code: result.error.code };
|
|
14803
|
+
}
|
|
14804
|
+
const inner = result.value;
|
|
14805
|
+
if (inner.ok) return { ok: true, value: inner.value };
|
|
14806
|
+
return { ok: false, error: inner.error, code: inner.code };
|
|
14807
|
+
}).catch((err) => {
|
|
14808
|
+
return {
|
|
14809
|
+
ok: false,
|
|
14810
|
+
error: err instanceof Error ? err.message : "network error",
|
|
14811
|
+
code: "network_error"
|
|
14812
|
+
};
|
|
14813
|
+
});
|
|
14814
|
+
return new Promise((resolve, reject) => {
|
|
14815
|
+
const onAbort = () => {
|
|
14816
|
+
if (signal.aborted) {
|
|
14817
|
+
if (signal.reason instanceof DOMException && signal.reason.name === "TimeoutError") {
|
|
14818
|
+
resolve({
|
|
14819
|
+
ok: false,
|
|
14820
|
+
error: `Pentester RPC timed out after ${timeoutMs}ms.`,
|
|
14821
|
+
code: "timeout"
|
|
14822
|
+
});
|
|
14823
|
+
} else {
|
|
14824
|
+
resolve({
|
|
14825
|
+
ok: false,
|
|
14826
|
+
error: "Pentester RPC request was aborted.",
|
|
14827
|
+
code: "aborted"
|
|
14828
|
+
});
|
|
14829
|
+
}
|
|
14830
|
+
}
|
|
14831
|
+
};
|
|
14832
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
14833
|
+
promise2.then(
|
|
14834
|
+
(value) => {
|
|
14835
|
+
signal.removeEventListener("abort", onAbort);
|
|
14836
|
+
resolve(value);
|
|
14837
|
+
},
|
|
14838
|
+
(error51) => {
|
|
14839
|
+
signal.removeEventListener("abort", onAbort);
|
|
14840
|
+
reject(error51);
|
|
14841
|
+
}
|
|
14842
|
+
);
|
|
14843
|
+
});
|
|
14844
|
+
}
|
|
14608
14845
|
|
|
14609
14846
|
// src/ui/client/pentester-config-section.tsx
|
|
14610
14847
|
var import_react3 = require("react");
|
|
@@ -15160,61 +15397,3481 @@ function isPentesterRootSession(snapshot, id) {
|
|
|
15160
15397
|
}
|
|
15161
15398
|
|
|
15162
15399
|
// src/ui/client/pentester-view.tsx
|
|
15163
|
-
var
|
|
15164
|
-
|
|
15165
|
-
|
|
15400
|
+
var import_react18 = __toESM(require("react"), 1);
|
|
15401
|
+
|
|
15402
|
+
// src/ui/client/pentester/navigation.ts
|
|
15403
|
+
var PENTESTER_TABS = [
|
|
15404
|
+
{ id: "overview", localeKey: "view.overview" },
|
|
15405
|
+
{ id: "stages", localeKey: "view.stages" },
|
|
15406
|
+
{ id: "trace", localeKey: "view.trace" },
|
|
15407
|
+
{ id: "findings", localeKey: "view.findings" },
|
|
15408
|
+
{ id: "output", localeKey: "view.output" }
|
|
15409
|
+
];
|
|
15410
|
+
|
|
15411
|
+
// src/ui/client/pentester/PentesterTabBar.tsx
|
|
15412
|
+
var import_react4 = __toESM(require("react"), 1);
|
|
15413
|
+
function PentesterTabBar(props) {
|
|
15414
|
+
const { items, active, onSelect, variant = "primary" } = props;
|
|
15415
|
+
const gap = variant === "compact" ? 28 : 36;
|
|
15416
|
+
return import_react4.default.createElement(
|
|
15417
|
+
"div",
|
|
15418
|
+
{
|
|
15419
|
+
role: "tablist",
|
|
15420
|
+
style: {
|
|
15421
|
+
display: "flex",
|
|
15422
|
+
gap: `${gap}px`,
|
|
15423
|
+
paddingLeft: "8px",
|
|
15424
|
+
flexShrink: 0
|
|
15425
|
+
}
|
|
15426
|
+
},
|
|
15427
|
+
...items.map(
|
|
15428
|
+
(item) => import_react4.default.createElement(
|
|
15429
|
+
"button",
|
|
15430
|
+
{
|
|
15431
|
+
key: item.id,
|
|
15432
|
+
role: "tab",
|
|
15433
|
+
"aria-selected": item.id === active,
|
|
15434
|
+
type: "button",
|
|
15435
|
+
onClick: () => onSelect(item.id),
|
|
15436
|
+
style: {
|
|
15437
|
+
position: "relative",
|
|
15438
|
+
padding: "0 0 11px",
|
|
15439
|
+
border: "none",
|
|
15440
|
+
background: "transparent",
|
|
15441
|
+
fontSize: "13px",
|
|
15442
|
+
lineHeight: "16px",
|
|
15443
|
+
fontWeight: 500,
|
|
15444
|
+
color: item.id === active ? "var(--dsw-alias-state-business-primary)" : "var(--dsw-alias-label-tertiary)",
|
|
15445
|
+
cursor: "pointer",
|
|
15446
|
+
whiteSpace: "nowrap"
|
|
15447
|
+
}
|
|
15448
|
+
},
|
|
15449
|
+
item.label,
|
|
15450
|
+
// Active underline pseudo-element rendered as a real element
|
|
15451
|
+
// because inline styles can't do ::after.
|
|
15452
|
+
item.id === active && import_react4.default.createElement("span", {
|
|
15453
|
+
"aria-hidden": true,
|
|
15454
|
+
style: {
|
|
15455
|
+
position: "absolute",
|
|
15456
|
+
right: 0,
|
|
15457
|
+
bottom: "1px",
|
|
15458
|
+
left: 0,
|
|
15459
|
+
height: "2px",
|
|
15460
|
+
borderRadius: "2px",
|
|
15461
|
+
background: "var(--dsw-alias-state-business-primary)"
|
|
15462
|
+
}
|
|
15463
|
+
})
|
|
15464
|
+
)
|
|
15465
|
+
)
|
|
15466
|
+
);
|
|
15166
15467
|
}
|
|
15167
15468
|
|
|
15168
|
-
// src/ui/client/
|
|
15169
|
-
var
|
|
15170
|
-
|
|
15171
|
-
|
|
15172
|
-
|
|
15173
|
-
|
|
15174
|
-
|
|
15175
|
-
|
|
15176
|
-
|
|
15177
|
-
|
|
15178
|
-
|
|
15469
|
+
// src/ui/client/pentester/OverviewView.tsx
|
|
15470
|
+
var import_react8 = __toESM(require("react"), 1);
|
|
15471
|
+
|
|
15472
|
+
// src/ui/client/pentester/MetricStrip.tsx
|
|
15473
|
+
var import_react5 = __toESM(require("react"), 1);
|
|
15474
|
+
function MetricStrip(props) {
|
|
15475
|
+
const { items } = props;
|
|
15476
|
+
return import_react5.default.createElement(
|
|
15477
|
+
"div",
|
|
15478
|
+
{
|
|
15479
|
+
style: {
|
|
15480
|
+
display: "flex",
|
|
15481
|
+
alignItems: "center",
|
|
15482
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
15483
|
+
borderRadius: "8px",
|
|
15484
|
+
background: "var(--dsw-alias-bg-base)",
|
|
15485
|
+
overflow: "hidden"
|
|
15179
15486
|
}
|
|
15180
|
-
|
|
15181
|
-
|
|
15182
|
-
|
|
15183
|
-
|
|
15184
|
-
|
|
15185
|
-
|
|
15186
|
-
|
|
15187
|
-
|
|
15188
|
-
|
|
15189
|
-
|
|
15190
|
-
|
|
15191
|
-
|
|
15192
|
-
|
|
15193
|
-
|
|
15194
|
-
|
|
15195
|
-
|
|
15196
|
-
|
|
15197
|
-
|
|
15198
|
-
|
|
15487
|
+
},
|
|
15488
|
+
...items.map(
|
|
15489
|
+
(item, i) => import_react5.default.createElement(
|
|
15490
|
+
import_react5.default.Fragment,
|
|
15491
|
+
{ key: item.label },
|
|
15492
|
+
i > 0 && import_react5.default.createElement("div", {
|
|
15493
|
+
"aria-hidden": true,
|
|
15494
|
+
style: {
|
|
15495
|
+
width: "1px",
|
|
15496
|
+
height: "32px",
|
|
15497
|
+
background: "var(--dsw-alias-border-l2)",
|
|
15498
|
+
flexShrink: 0
|
|
15499
|
+
}
|
|
15500
|
+
}),
|
|
15501
|
+
import_react5.default.createElement(
|
|
15502
|
+
"div",
|
|
15503
|
+
{
|
|
15504
|
+
style: {
|
|
15505
|
+
flex: 1,
|
|
15506
|
+
display: "flex",
|
|
15507
|
+
flexDirection: "column",
|
|
15508
|
+
alignItems: "center",
|
|
15509
|
+
padding: "12px 16px",
|
|
15510
|
+
minWidth: "80px"
|
|
15511
|
+
}
|
|
15512
|
+
},
|
|
15513
|
+
import_react5.default.createElement("div", {
|
|
15514
|
+
style: {
|
|
15515
|
+
fontSize: "20px",
|
|
15516
|
+
fontWeight: 600,
|
|
15517
|
+
color: "var(--dsw-alias-label-primary)",
|
|
15518
|
+
lineHeight: "1.3"
|
|
15519
|
+
}
|
|
15520
|
+
}, item.value),
|
|
15521
|
+
import_react5.default.createElement("div", {
|
|
15522
|
+
style: {
|
|
15523
|
+
fontSize: "11px",
|
|
15524
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
15525
|
+
marginTop: "2px"
|
|
15526
|
+
}
|
|
15527
|
+
}, item.label)
|
|
15528
|
+
)
|
|
15529
|
+
)
|
|
15530
|
+
)
|
|
15531
|
+
);
|
|
15532
|
+
}
|
|
15533
|
+
|
|
15534
|
+
// src/ui/client/pentester/StageRail.tsx
|
|
15535
|
+
var import_react6 = __toESM(require("react"), 1);
|
|
15536
|
+
|
|
15537
|
+
// src/ui/client/pentester/status.ts
|
|
15538
|
+
function stageStatusToUi(status) {
|
|
15539
|
+
switch (status) {
|
|
15540
|
+
case "completed":
|
|
15541
|
+
return "completed";
|
|
15542
|
+
case "active":
|
|
15543
|
+
return "active";
|
|
15544
|
+
case "reviewing":
|
|
15545
|
+
return "reviewing";
|
|
15546
|
+
default:
|
|
15547
|
+
return "pending";
|
|
15548
|
+
}
|
|
15549
|
+
}
|
|
15550
|
+
function uiStatusSymbol(ui) {
|
|
15551
|
+
switch (ui) {
|
|
15552
|
+
case "completed":
|
|
15553
|
+
return "\u2713";
|
|
15554
|
+
case "active":
|
|
15555
|
+
return "\u25CF";
|
|
15556
|
+
case "reviewing":
|
|
15557
|
+
return "\u25D0";
|
|
15558
|
+
case "pending":
|
|
15559
|
+
return "\u25CB";
|
|
15560
|
+
case "blocked":
|
|
15561
|
+
return "!";
|
|
15562
|
+
case "failed":
|
|
15563
|
+
return "\xD7";
|
|
15564
|
+
}
|
|
15565
|
+
}
|
|
15566
|
+
function uiStatusColor(ui) {
|
|
15567
|
+
switch (ui) {
|
|
15568
|
+
case "completed":
|
|
15569
|
+
return "var(--dsw-status-done, #22c55e)";
|
|
15570
|
+
case "active":
|
|
15571
|
+
return "var(--dsw-status-ongoing, #3b82f6)";
|
|
15572
|
+
case "reviewing":
|
|
15573
|
+
return "var(--dsw-status-ongoing, #f59e0b)";
|
|
15574
|
+
case "pending":
|
|
15575
|
+
return "var(--dsw-fg-muted, #6b7280)";
|
|
15576
|
+
case "blocked":
|
|
15577
|
+
return "var(--dsw-status-warning, #f59e0b)";
|
|
15578
|
+
case "failed":
|
|
15579
|
+
return "var(--dsw-status-error, #ef4444)";
|
|
15580
|
+
}
|
|
15581
|
+
}
|
|
15582
|
+
|
|
15583
|
+
// src/ui/client/pentester/StageRail.tsx
|
|
15584
|
+
function StageRail(props) {
|
|
15585
|
+
const { stages, currentStage, onStageClick } = props;
|
|
15586
|
+
return import_react6.default.createElement(
|
|
15587
|
+
"div",
|
|
15588
|
+
{
|
|
15589
|
+
style: {
|
|
15590
|
+
display: "flex",
|
|
15591
|
+
alignItems: "center",
|
|
15592
|
+
gap: "0px",
|
|
15593
|
+
fontSize: "13px",
|
|
15594
|
+
lineHeight: "20px",
|
|
15595
|
+
overflowX: "auto",
|
|
15596
|
+
paddingBottom: "4px"
|
|
15199
15597
|
},
|
|
15200
|
-
|
|
15598
|
+
"aria-label": "PTES stages timeline"
|
|
15599
|
+
},
|
|
15600
|
+
...stages.map((stage, i) => {
|
|
15601
|
+
const ui = stageStatusToUi(stage.status);
|
|
15602
|
+
const isCurrent = stage.id === currentStage;
|
|
15603
|
+
const symbol2 = uiStatusSymbol(ui);
|
|
15604
|
+
const color = uiStatusColor(ui);
|
|
15605
|
+
return import_react6.default.createElement(
|
|
15606
|
+
import_react6.default.Fragment,
|
|
15607
|
+
{ key: stage.id },
|
|
15608
|
+
// Connector line between stages
|
|
15609
|
+
i > 0 && import_react6.default.createElement("span", {
|
|
15610
|
+
"aria-hidden": true,
|
|
15611
|
+
style: {
|
|
15612
|
+
width: "24px",
|
|
15613
|
+
height: "1px",
|
|
15614
|
+
background: "var(--dsw-alias-border-l2)",
|
|
15615
|
+
flexShrink: 0,
|
|
15616
|
+
margin: "0 0px"
|
|
15617
|
+
}
|
|
15618
|
+
}),
|
|
15619
|
+
import_react6.default.createElement(
|
|
15620
|
+
"button",
|
|
15621
|
+
{
|
|
15622
|
+
type: "button",
|
|
15623
|
+
title: `${stage.name} (${stage.status})`,
|
|
15624
|
+
onClick: onStageClick !== void 0 ? () => onStageClick(stage.id) : void 0,
|
|
15625
|
+
style: {
|
|
15626
|
+
display: "inline-flex",
|
|
15627
|
+
alignItems: "center",
|
|
15628
|
+
gap: "4px",
|
|
15629
|
+
padding: "2px 4px",
|
|
15630
|
+
border: "none",
|
|
15631
|
+
borderRadius: "4px",
|
|
15632
|
+
background: isCurrent ? "var(--dsw-alias-interactive-bg-hover)" : "transparent",
|
|
15633
|
+
color: isCurrent ? color : "var(--dsw-alias-label-secondary)",
|
|
15634
|
+
fontWeight: isCurrent ? 600 : 400,
|
|
15635
|
+
cursor: onStageClick !== void 0 ? "pointer" : "default",
|
|
15636
|
+
fontSize: "13px",
|
|
15637
|
+
lineHeight: "20px",
|
|
15638
|
+
whiteSpace: "nowrap",
|
|
15639
|
+
fontFamily: "inherit"
|
|
15640
|
+
}
|
|
15641
|
+
},
|
|
15642
|
+
import_react6.default.createElement("span", {
|
|
15643
|
+
style: { fontSize: "14px", fontWeight: isCurrent ? 700 : 400 }
|
|
15644
|
+
}, symbol2),
|
|
15645
|
+
import_react6.default.createElement("span", null, stage.shortName)
|
|
15646
|
+
)
|
|
15647
|
+
);
|
|
15648
|
+
})
|
|
15649
|
+
);
|
|
15650
|
+
}
|
|
15651
|
+
|
|
15652
|
+
// src/ui/client/pentester/DelegationActivityRow.tsx
|
|
15653
|
+
var import_react7 = __toESM(require("react"), 1);
|
|
15654
|
+
var import_dsh_client_ui_primitives2 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
15655
|
+
function DelegationActivityRow(props) {
|
|
15656
|
+
const { delegation, stageShortName, onOpenSession, compact = false, liveActivity } = props;
|
|
15657
|
+
const hasSession = delegation.sessionId !== void 0;
|
|
15658
|
+
const dotState = (() => {
|
|
15659
|
+
if (delegation.status === "failed") return "error";
|
|
15660
|
+
if (delegation.status === "closed") return "done";
|
|
15661
|
+
if (liveActivity === "running") return "ongoing";
|
|
15662
|
+
if (liveActivity === "inactive") return "done";
|
|
15663
|
+
if (delegation.status === "starting") return "ongoing";
|
|
15664
|
+
return "done";
|
|
15665
|
+
})();
|
|
15666
|
+
return import_react7.default.createElement(
|
|
15667
|
+
"div",
|
|
15668
|
+
{
|
|
15669
|
+
role: hasSession ? "button" : void 0,
|
|
15670
|
+
tabIndex: hasSession ? 0 : void 0,
|
|
15671
|
+
"aria-label": hasSession ? `Open session ${delegation.id}` : void 0,
|
|
15672
|
+
title: delegation.objective,
|
|
15673
|
+
onClick: hasSession ? () => {
|
|
15674
|
+
if (delegation.sessionId !== void 0) onOpenSession(delegation.sessionId);
|
|
15675
|
+
} : void 0,
|
|
15676
|
+
onKeyDown: hasSession ? (e) => {
|
|
15677
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
15678
|
+
e.preventDefault();
|
|
15679
|
+
if (delegation.sessionId !== void 0) onOpenSession(delegation.sessionId);
|
|
15680
|
+
}
|
|
15681
|
+
} : void 0,
|
|
15682
|
+
style: {
|
|
15683
|
+
display: "flex",
|
|
15684
|
+
alignItems: "center",
|
|
15685
|
+
gap: "8px",
|
|
15686
|
+
padding: compact ? "4px 8px" : "8px 12px",
|
|
15687
|
+
borderRadius: "6px",
|
|
15688
|
+
background: "var(--dsw-alias-bg-base)",
|
|
15689
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
15690
|
+
cursor: hasSession ? "pointer" : "default",
|
|
15691
|
+
fontSize: "13px",
|
|
15692
|
+
lineHeight: "20px"
|
|
15693
|
+
}
|
|
15694
|
+
},
|
|
15695
|
+
import_react7.default.createElement(import_dsh_client_ui_primitives2.StateDot, { state: dotState, size: 10 }),
|
|
15696
|
+
import_react7.default.createElement("span", {
|
|
15697
|
+
style: {
|
|
15698
|
+
fontWeight: 600,
|
|
15699
|
+
color: "var(--dsw-alias-label-primary)",
|
|
15700
|
+
flexShrink: 0,
|
|
15701
|
+
minWidth: compact ? "50px" : "60px"
|
|
15702
|
+
}
|
|
15703
|
+
}, `${delegation.id} \xB7 ${delegation.agentId}`),
|
|
15704
|
+
!compact && import_react7.default.createElement("span", {
|
|
15705
|
+
style: {
|
|
15706
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
15707
|
+
flex: 1,
|
|
15708
|
+
overflow: "hidden",
|
|
15709
|
+
textOverflow: "ellipsis",
|
|
15710
|
+
whiteSpace: "nowrap",
|
|
15711
|
+
minWidth: 0
|
|
15712
|
+
}
|
|
15713
|
+
}, delegation.objective),
|
|
15714
|
+
!compact && stageShortName !== void 0 && import_react7.default.createElement("span", {
|
|
15715
|
+
style: {
|
|
15716
|
+
fontSize: "11px",
|
|
15717
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
15718
|
+
flexShrink: 0
|
|
15719
|
+
}
|
|
15720
|
+
}, stageShortName)
|
|
15721
|
+
);
|
|
15722
|
+
}
|
|
15723
|
+
|
|
15724
|
+
// src/ui/client/pentester/OverviewView.tsx
|
|
15725
|
+
var CARD_SURFACE = {
|
|
15726
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
15727
|
+
borderRadius: "8px",
|
|
15728
|
+
background: "var(--dsw-alias-bg-base)",
|
|
15729
|
+
padding: "16px",
|
|
15730
|
+
marginBottom: "16px"
|
|
15731
|
+
};
|
|
15732
|
+
function OverviewView(props) {
|
|
15733
|
+
const { snapshot, liveActivity, onNavigate, onOpenSession } = props;
|
|
15734
|
+
if (snapshot?.run === null || snapshot === null) {
|
|
15735
|
+
return import_react8.default.createElement("div", {
|
|
15736
|
+
style: { padding: "24px", color: "var(--dsw-alias-label-tertiary)", fontSize: "14px" }
|
|
15737
|
+
}, "No run data available.");
|
|
15738
|
+
}
|
|
15739
|
+
const run = snapshot.run;
|
|
15740
|
+
const stages = snapshot.stages;
|
|
15741
|
+
const currentStage = stages.find((s) => s.id === run.currentStage);
|
|
15742
|
+
const currentIndex = stages.findIndex((s) => s.id === run.currentStage);
|
|
15743
|
+
const allDelegations = snapshot.delegations;
|
|
15744
|
+
const currentWork = allDelegations.filter((d) => {
|
|
15745
|
+
const activity = liveActivity.get(d.id);
|
|
15746
|
+
return activity === "running" || d.status === "starting";
|
|
15747
|
+
});
|
|
15748
|
+
return import_react8.default.createElement(
|
|
15749
|
+
"div",
|
|
15750
|
+
{ style: { maxWidth: "900px" } },
|
|
15751
|
+
// ── Target / Status / Current Stage card ──────────────────────────
|
|
15752
|
+
import_react8.default.createElement(
|
|
15753
|
+
"div",
|
|
15754
|
+
{ style: CARD_SURFACE },
|
|
15755
|
+
import_react8.default.createElement(
|
|
15756
|
+
"div",
|
|
15757
|
+
{
|
|
15758
|
+
style: {
|
|
15759
|
+
display: "flex",
|
|
15760
|
+
alignItems: "center",
|
|
15761
|
+
justifyContent: "space-between",
|
|
15762
|
+
marginBottom: "12px"
|
|
15763
|
+
}
|
|
15764
|
+
},
|
|
15765
|
+
import_react8.default.createElement("div", {
|
|
15766
|
+
style: { fontSize: "16px", fontWeight: 600, color: "var(--dsw-alias-label-primary)" }
|
|
15767
|
+
}, run.targets[0] ?? "No target"),
|
|
15768
|
+
import_react8.default.createElement("span", {
|
|
15769
|
+
style: {
|
|
15770
|
+
fontSize: "12px",
|
|
15771
|
+
padding: "2px 10px",
|
|
15772
|
+
borderRadius: "9999px",
|
|
15773
|
+
background: "var(--dsw-alias-interactive-bg-hover)",
|
|
15774
|
+
color: run.status === "active" ? "var(--dsw-alias-state-business-primary)" : "var(--dsw-alias-label-secondary)",
|
|
15775
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
15776
|
+
fontWeight: 500
|
|
15777
|
+
}
|
|
15778
|
+
}, run.status === "active" ? "Running" : "Completed")
|
|
15779
|
+
),
|
|
15780
|
+
currentStage !== void 0 && import_react8.default.createElement(
|
|
15781
|
+
"div",
|
|
15782
|
+
{
|
|
15783
|
+
style: {
|
|
15784
|
+
display: "flex",
|
|
15785
|
+
alignItems: "baseline",
|
|
15786
|
+
gap: "8px",
|
|
15787
|
+
marginBottom: "12px"
|
|
15788
|
+
}
|
|
15789
|
+
},
|
|
15790
|
+
import_react8.default.createElement("span", {
|
|
15791
|
+
style: { fontSize: "12px", color: "var(--dsw-alias-label-tertiary)" }
|
|
15792
|
+
}, "Current Stage"),
|
|
15793
|
+
import_react8.default.createElement("span", {
|
|
15794
|
+
style: { fontSize: "14px", fontWeight: 600, color: "var(--dsw-alias-label-primary)" }
|
|
15795
|
+
}, currentStage.name),
|
|
15796
|
+
import_react8.default.createElement("span", {
|
|
15797
|
+
style: { fontSize: "12px", color: "var(--dsw-alias-label-tertiary)" }
|
|
15798
|
+
}, `Stage ${currentIndex + 1} / ${stages.length}`)
|
|
15799
|
+
),
|
|
15800
|
+
import_react8.default.createElement(StageRail, {
|
|
15801
|
+
stages,
|
|
15802
|
+
currentStage: run.currentStage,
|
|
15803
|
+
onStageClick: (stageId) => onNavigate("stages", { stage: stageId })
|
|
15804
|
+
})
|
|
15805
|
+
),
|
|
15806
|
+
// ── Metrics strip (total delegations, not active) ─────────────────
|
|
15807
|
+
import_react8.default.createElement(MetricStrip, {
|
|
15808
|
+
items: [
|
|
15809
|
+
{ label: "Assets", value: String(snapshot.stats.assets) },
|
|
15810
|
+
{ label: "Findings", value: String(snapshot.stats.findings) },
|
|
15811
|
+
{ label: "Delegations", value: String(snapshot.stats.delegations) }
|
|
15812
|
+
]
|
|
15813
|
+
}),
|
|
15814
|
+
import_react8.default.createElement("div", { style: { height: "16px" } }),
|
|
15815
|
+
// ── Current Work (only running/starting) ──────────────────────────
|
|
15816
|
+
currentWork.length > 0 && import_react8.default.createElement(
|
|
15817
|
+
import_react8.default.Fragment,
|
|
15818
|
+
null,
|
|
15819
|
+
import_react8.default.createElement("div", {
|
|
15820
|
+
style: {
|
|
15821
|
+
fontSize: "13px",
|
|
15822
|
+
fontWeight: 600,
|
|
15823
|
+
color: "var(--dsw-alias-label-primary)",
|
|
15824
|
+
marginBottom: "8px"
|
|
15825
|
+
}
|
|
15826
|
+
}, "Current Work"),
|
|
15827
|
+
import_react8.default.createElement(
|
|
15828
|
+
"div",
|
|
15829
|
+
{
|
|
15830
|
+
style: { display: "flex", flexDirection: "column", gap: "4px" }
|
|
15831
|
+
},
|
|
15832
|
+
...currentWork.map((d) => {
|
|
15833
|
+
const stage = stages.find((s) => s.id === d.stageId);
|
|
15834
|
+
return import_react8.default.createElement(DelegationActivityRow, {
|
|
15835
|
+
key: d.id,
|
|
15836
|
+
delegation: d,
|
|
15837
|
+
stageShortName: stage?.shortName,
|
|
15838
|
+
onOpenSession,
|
|
15839
|
+
liveActivity: liveActivity.get(d.id)
|
|
15840
|
+
});
|
|
15841
|
+
})
|
|
15842
|
+
)
|
|
15201
15843
|
)
|
|
15202
15844
|
);
|
|
15203
|
-
|
|
15204
|
-
|
|
15205
|
-
|
|
15206
|
-
|
|
15207
|
-
|
|
15208
|
-
|
|
15209
|
-
|
|
15210
|
-
|
|
15845
|
+
}
|
|
15846
|
+
|
|
15847
|
+
// src/ui/client/pentester/StagesView.tsx
|
|
15848
|
+
var import_react9 = __toESM(require("react"), 1);
|
|
15849
|
+
var STAGE_LABEL_WIDTH = 260;
|
|
15850
|
+
var MAX_WIDTH = 1100;
|
|
15851
|
+
var COMPACT_THRESHOLD = 720;
|
|
15852
|
+
var STATUS_TEXT = {
|
|
15853
|
+
completed: "Completed",
|
|
15854
|
+
active: "Active",
|
|
15855
|
+
reviewing: "Pending approval",
|
|
15856
|
+
pending: "Pending",
|
|
15857
|
+
blocked: "Blocked",
|
|
15858
|
+
failed: "Failed"
|
|
15859
|
+
};
|
|
15860
|
+
function StagesView(props) {
|
|
15861
|
+
const { snapshot, liveActivity, onOpenSession, focusStage } = props;
|
|
15862
|
+
if (snapshot === null || snapshot.run === null) {
|
|
15863
|
+
return import_react9.default.createElement("div", {
|
|
15864
|
+
style: { padding: "24px", color: "var(--dsw-alias-label-tertiary)", fontSize: "14px" }
|
|
15865
|
+
}, "No run data available.");
|
|
15866
|
+
}
|
|
15867
|
+
const stages = snapshot.stages;
|
|
15868
|
+
const allDelegations = snapshot.delegations;
|
|
15869
|
+
const delegationsByStage = /* @__PURE__ */ new Map();
|
|
15870
|
+
for (const d of allDelegations) {
|
|
15871
|
+
const existing = delegationsByStage.get(d.stageId) ?? [];
|
|
15872
|
+
delegationsByStage.set(d.stageId, [...existing, d]);
|
|
15873
|
+
}
|
|
15874
|
+
const containerRef = (0, import_react9.useRef)(null);
|
|
15875
|
+
const [compact, setCompact] = (0, import_react9.useState)(false);
|
|
15876
|
+
(0, import_react9.useEffect)(() => {
|
|
15877
|
+
const el = containerRef.current;
|
|
15878
|
+
if (el === null) return;
|
|
15879
|
+
const check2 = () => setCompact(el.clientHeight < COMPACT_THRESHOLD);
|
|
15880
|
+
check2();
|
|
15881
|
+
const obs = new ResizeObserver(check2);
|
|
15882
|
+
obs.observe(el);
|
|
15883
|
+
return () => obs.disconnect();
|
|
15884
|
+
}, []);
|
|
15885
|
+
const focusRef = (0, import_react9.useRef)(null);
|
|
15886
|
+
(0, import_react9.useEffect)(() => {
|
|
15887
|
+
if (focusStage !== void 0 && focusRef.current !== null) {
|
|
15888
|
+
focusRef.current.scrollIntoView({ behavior: "auto", block: "center" });
|
|
15889
|
+
}
|
|
15890
|
+
}, [focusStage]);
|
|
15891
|
+
const cardMb = compact ? 6 : 8;
|
|
15892
|
+
const emptyMinH = compact ? 64 : 72;
|
|
15893
|
+
const leftPad = compact ? 12 : 16;
|
|
15894
|
+
const leftPadH = compact ? 10 : 16;
|
|
15895
|
+
const rightPad = compact ? 8 : 12;
|
|
15896
|
+
const rightPadH = compact ? 10 : 14;
|
|
15897
|
+
const titleGap = compact ? 8 : 10;
|
|
15898
|
+
const titleSize = compact ? 13 : 14;
|
|
15899
|
+
const markerSize = compact ? 14 : 16;
|
|
15900
|
+
const statusMargin = compact ? 24 : 28;
|
|
15901
|
+
const delGap = compact ? 4 : 6;
|
|
15902
|
+
const legendGap = compact ? 14 : 18;
|
|
15903
|
+
const topPad = compact ? 4 : 8;
|
|
15904
|
+
const bottomPad = compact ? 12 : 16;
|
|
15905
|
+
return import_react9.default.createElement(
|
|
15906
|
+
"div",
|
|
15907
|
+
{
|
|
15908
|
+
ref: containerRef,
|
|
15909
|
+
style: {
|
|
15910
|
+
overflow: "visible",
|
|
15911
|
+
paddingBottom: `${bottomPad}px`,
|
|
15912
|
+
display: "flex",
|
|
15913
|
+
flexDirection: "column",
|
|
15914
|
+
alignItems: "center",
|
|
15915
|
+
minHeight: 0
|
|
15916
|
+
}
|
|
15917
|
+
},
|
|
15918
|
+
import_react9.default.createElement(
|
|
15919
|
+
"div",
|
|
15920
|
+
{
|
|
15921
|
+
style: {
|
|
15922
|
+
width: "100%",
|
|
15923
|
+
maxWidth: `${MAX_WIDTH}px`,
|
|
15924
|
+
position: "relative",
|
|
15925
|
+
paddingTop: `${topPad}px`
|
|
15926
|
+
}
|
|
15927
|
+
},
|
|
15928
|
+
// ── Continuous vertical backbone ──────────────────────────────────
|
|
15929
|
+
import_react9.default.createElement("div", {
|
|
15930
|
+
"aria-hidden": true,
|
|
15931
|
+
style: {
|
|
15932
|
+
position: "absolute",
|
|
15933
|
+
left: `${STAGE_LABEL_WIDTH + 10}px`,
|
|
15934
|
+
top: `${compact ? 16 : 24}px`,
|
|
15935
|
+
bottom: "12px",
|
|
15936
|
+
width: "1px",
|
|
15937
|
+
background: "var(--dsw-alias-border-l2)",
|
|
15938
|
+
zIndex: 0
|
|
15939
|
+
}
|
|
15940
|
+
}),
|
|
15941
|
+
...stages.map((stage, i) => {
|
|
15942
|
+
const ui = stageStatusToUi(stage.status);
|
|
15943
|
+
const isCurrent = stage.id === snapshot.run.currentStage;
|
|
15944
|
+
const isFocus = stage.id === focusStage;
|
|
15945
|
+
const color = uiStatusColor(ui);
|
|
15946
|
+
const symbol2 = uiStatusSymbol(ui);
|
|
15947
|
+
const stageDelegations = delegationsByStage.get(stage.id) ?? [];
|
|
15948
|
+
const statusText = STATUS_TEXT[ui] ?? ui;
|
|
15949
|
+
return import_react9.default.createElement(
|
|
15950
|
+
"div",
|
|
15951
|
+
{
|
|
15952
|
+
key: stage.id,
|
|
15953
|
+
ref: isFocus ? focusRef : void 0,
|
|
15954
|
+
style: {
|
|
15955
|
+
display: "grid",
|
|
15956
|
+
gridTemplateColumns: `${STAGE_LABEL_WIDTH}px minmax(0, 1fr)`,
|
|
15957
|
+
minHeight: stageDelegations.length > 0 ? "auto" : `${emptyMinH}px`,
|
|
15958
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
15959
|
+
borderRadius: "8px",
|
|
15960
|
+
background: "var(--dsw-alias-bg-base)",
|
|
15961
|
+
marginBottom: `${cardMb}px`,
|
|
15962
|
+
position: "relative",
|
|
15963
|
+
zIndex: 1,
|
|
15964
|
+
overflow: "hidden"
|
|
15965
|
+
}
|
|
15966
|
+
},
|
|
15967
|
+
// ── Left column: marker + stage info ─────────────────────────
|
|
15968
|
+
import_react9.default.createElement(
|
|
15969
|
+
"div",
|
|
15211
15970
|
{
|
|
15212
|
-
|
|
15213
|
-
|
|
15214
|
-
|
|
15215
|
-
|
|
15971
|
+
style: {
|
|
15972
|
+
display: "flex",
|
|
15973
|
+
flexDirection: "column",
|
|
15974
|
+
justifyContent: "center",
|
|
15975
|
+
padding: `${leftPadH}px ${leftPad}px`,
|
|
15976
|
+
gap: `${compact ? 2 : 4}px`,
|
|
15977
|
+
background: "var(--dsw-alias-bg-base)",
|
|
15978
|
+
position: "relative",
|
|
15979
|
+
zIndex: 1
|
|
15980
|
+
}
|
|
15216
15981
|
},
|
|
15217
|
-
|
|
15982
|
+
import_react9.default.createElement(
|
|
15983
|
+
"div",
|
|
15984
|
+
{
|
|
15985
|
+
style: {
|
|
15986
|
+
display: "flex",
|
|
15987
|
+
alignItems: "center",
|
|
15988
|
+
gap: `${titleGap}px`
|
|
15989
|
+
}
|
|
15990
|
+
},
|
|
15991
|
+
import_react9.default.createElement("span", {
|
|
15992
|
+
style: {
|
|
15993
|
+
fontSize: `${markerSize}px`,
|
|
15994
|
+
color,
|
|
15995
|
+
fontWeight: isCurrent ? 700 : 400,
|
|
15996
|
+
lineHeight: "1",
|
|
15997
|
+
flexShrink: 0,
|
|
15998
|
+
width: `${compact ? 16 : 18}px`,
|
|
15999
|
+
textAlign: "center",
|
|
16000
|
+
background: "var(--dsw-alias-bg-base)",
|
|
16001
|
+
position: "relative",
|
|
16002
|
+
zIndex: 2
|
|
16003
|
+
}
|
|
16004
|
+
}, symbol2),
|
|
16005
|
+
import_react9.default.createElement("span", {
|
|
16006
|
+
style: {
|
|
16007
|
+
fontSize: `${titleSize}px`,
|
|
16008
|
+
fontWeight: isCurrent ? 600 : 500,
|
|
16009
|
+
color: "var(--dsw-alias-label-primary)",
|
|
16010
|
+
lineHeight: "20px"
|
|
16011
|
+
}
|
|
16012
|
+
}, stage.name)
|
|
16013
|
+
),
|
|
16014
|
+
import_react9.default.createElement("span", {
|
|
16015
|
+
style: {
|
|
16016
|
+
fontSize: "12px",
|
|
16017
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
16018
|
+
marginLeft: `${statusMargin}px`,
|
|
16019
|
+
lineHeight: "16px"
|
|
16020
|
+
}
|
|
16021
|
+
}, statusText)
|
|
16022
|
+
),
|
|
16023
|
+
// ── Right column: delegations ─────────────────────────────────
|
|
16024
|
+
import_react9.default.createElement(
|
|
16025
|
+
"div",
|
|
16026
|
+
{
|
|
16027
|
+
style: {
|
|
16028
|
+
padding: `${rightPadH}px ${rightPad}px`,
|
|
16029
|
+
display: "flex",
|
|
16030
|
+
flexDirection: "column",
|
|
16031
|
+
justifyContent: "center",
|
|
16032
|
+
gap: `${delGap}px`
|
|
16033
|
+
}
|
|
16034
|
+
},
|
|
16035
|
+
stageDelegations.length > 0 && import_react9.default.createElement("div", {
|
|
16036
|
+
style: {
|
|
16037
|
+
fontSize: "11px",
|
|
16038
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
16039
|
+
marginBottom: "1px"
|
|
16040
|
+
}
|
|
16041
|
+
}, `${stageDelegations.length} agent${stageDelegations.length !== 1 ? "s" : ""}`),
|
|
16042
|
+
...stageDelegations.map(
|
|
16043
|
+
(d) => import_react9.default.createElement(
|
|
16044
|
+
"div",
|
|
16045
|
+
{
|
|
16046
|
+
key: d.id,
|
|
16047
|
+
style: {
|
|
16048
|
+
maxWidth: "min(100%, 480px)"
|
|
16049
|
+
}
|
|
16050
|
+
},
|
|
16051
|
+
import_react9.default.createElement(DelegationActivityRow, {
|
|
16052
|
+
delegation: d,
|
|
16053
|
+
onOpenSession,
|
|
16054
|
+
compact: true,
|
|
16055
|
+
liveActivity: liveActivity.get(d.id)
|
|
16056
|
+
})
|
|
16057
|
+
)
|
|
16058
|
+
),
|
|
16059
|
+
stageDelegations.length === 0 && import_react9.default.createElement("span", {
|
|
16060
|
+
style: {
|
|
16061
|
+
fontSize: "12px",
|
|
16062
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
16063
|
+
fontStyle: "italic"
|
|
16064
|
+
}
|
|
16065
|
+
}, "No delegations")
|
|
16066
|
+
)
|
|
16067
|
+
);
|
|
16068
|
+
}),
|
|
16069
|
+
// ── Legend ─────────────────────────────────────────────────────────
|
|
16070
|
+
import_react9.default.createElement(
|
|
16071
|
+
"div",
|
|
16072
|
+
{
|
|
16073
|
+
style: {
|
|
16074
|
+
display: "flex",
|
|
16075
|
+
gap: `${legendGap}px`,
|
|
16076
|
+
padding: `${compact ? 6 : 10}px 0 ${compact ? 2 : 4}px 20px`,
|
|
16077
|
+
fontSize: "11px",
|
|
16078
|
+
color: "var(--dsw-alias-label-tertiary)"
|
|
16079
|
+
}
|
|
16080
|
+
},
|
|
16081
|
+
import_react9.default.createElement(LegendItem, { symbol: "\u2713", label: "Completed" }),
|
|
16082
|
+
import_react9.default.createElement(LegendItem, { symbol: "\u25CF", label: "Active" }),
|
|
16083
|
+
import_react9.default.createElement(LegendItem, { symbol: "\u25D0", label: "Pending approval" }),
|
|
16084
|
+
import_react9.default.createElement(LegendItem, { symbol: "\u25CB", label: "Pending" }),
|
|
16085
|
+
import_react9.default.createElement(LegendItem, { symbol: "!", label: "Blocked" }),
|
|
16086
|
+
import_react9.default.createElement(LegendItem, { symbol: "\xD7", label: "Failed" })
|
|
16087
|
+
)
|
|
16088
|
+
)
|
|
16089
|
+
);
|
|
16090
|
+
}
|
|
16091
|
+
function LegendItem(props) {
|
|
16092
|
+
return import_react9.default.createElement(
|
|
16093
|
+
"div",
|
|
16094
|
+
{
|
|
16095
|
+
style: { display: "flex", alignItems: "center", gap: "4px" }
|
|
16096
|
+
},
|
|
16097
|
+
import_react9.default.createElement("span", { style: { width: "14px", textAlign: "center" } }, props.symbol),
|
|
16098
|
+
import_react9.default.createElement("span", null, props.label)
|
|
16099
|
+
);
|
|
16100
|
+
}
|
|
16101
|
+
|
|
16102
|
+
// src/ui/client/pentester/FindingsView.tsx
|
|
16103
|
+
var import_react10 = __toESM(require("react"), 1);
|
|
16104
|
+
var import_dsh_client_ui_primitives3 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
16105
|
+
var SEVERITY_ORDER = ["critical", "high", "medium", "low", "info"];
|
|
16106
|
+
var SEVERITY_LABELS = {
|
|
16107
|
+
critical: "Critical",
|
|
16108
|
+
high: "High",
|
|
16109
|
+
medium: "Medium",
|
|
16110
|
+
low: "Low",
|
|
16111
|
+
info: "Info"
|
|
16112
|
+
};
|
|
16113
|
+
var SEVERITY_COLORS = {
|
|
16114
|
+
critical: "#ef4444",
|
|
16115
|
+
high: "#f97316",
|
|
16116
|
+
medium: "#eab308",
|
|
16117
|
+
low: "#3b82f6",
|
|
16118
|
+
info: "#6b7280"
|
|
16119
|
+
};
|
|
16120
|
+
function ExpandableSection(props) {
|
|
16121
|
+
const [open, setOpen] = (0, import_react10.useState)(props.defaultOpen);
|
|
16122
|
+
return import_react10.default.createElement(
|
|
16123
|
+
"div",
|
|
16124
|
+
{ style: { marginBottom: "4px" } },
|
|
16125
|
+
import_react10.default.createElement(
|
|
16126
|
+
"div",
|
|
16127
|
+
{
|
|
16128
|
+
onClick: () => setOpen(!open),
|
|
16129
|
+
style: {
|
|
16130
|
+
display: "flex",
|
|
16131
|
+
alignItems: "center",
|
|
16132
|
+
gap: "6px",
|
|
16133
|
+
padding: "6px 8px",
|
|
16134
|
+
cursor: "pointer",
|
|
16135
|
+
borderRadius: "4px",
|
|
16136
|
+
fontSize: "13px",
|
|
16137
|
+
fontWeight: 600,
|
|
16138
|
+
color: "var(--dsw-fg)",
|
|
16139
|
+
userSelect: "none"
|
|
16140
|
+
}
|
|
16141
|
+
},
|
|
16142
|
+
import_react10.default.createElement("span", {
|
|
16143
|
+
style: { fontSize: "12px", transition: "transform 0.15s", transform: open ? "rotate(90deg)" : "rotate(0deg)" }
|
|
16144
|
+
}, "\u25B6"),
|
|
16145
|
+
import_react10.default.createElement("span", null, props.title)
|
|
16146
|
+
),
|
|
16147
|
+
open && import_react10.default.createElement("div", { style: { paddingLeft: "20px" } }, props.children)
|
|
16148
|
+
);
|
|
16149
|
+
}
|
|
16150
|
+
function FindingsView(props) {
|
|
16151
|
+
const { snapshot, command, sessionId } = props;
|
|
16152
|
+
const [selectedFinding, setSelectedFinding] = (0, import_react10.useState)(null);
|
|
16153
|
+
const [detailContent, setDetailContent] = (0, import_react10.useState)(null);
|
|
16154
|
+
const [detailLoading, setDetailLoading] = (0, import_react10.useState)(false);
|
|
16155
|
+
const findings = snapshot?.findings ?? [];
|
|
16156
|
+
const grouped = (0, import_react10.useMemo)(() => {
|
|
16157
|
+
const groups = /* @__PURE__ */ new Map();
|
|
16158
|
+
for (const severity of SEVERITY_ORDER) {
|
|
16159
|
+
groups.set(severity, []);
|
|
16160
|
+
}
|
|
16161
|
+
for (const f of findings) {
|
|
16162
|
+
const list = groups.get(f.severity);
|
|
16163
|
+
if (list !== void 0) list.push(f);
|
|
16164
|
+
}
|
|
16165
|
+
return groups;
|
|
16166
|
+
}, [findings]);
|
|
16167
|
+
const openDetail = (0, import_react10.useCallback)(async (finding) => {
|
|
16168
|
+
setSelectedFinding(finding);
|
|
16169
|
+
setDetailContent(null);
|
|
16170
|
+
setDetailLoading(true);
|
|
16171
|
+
try {
|
|
16172
|
+
const result = await command({
|
|
16173
|
+
kind: "readFinding",
|
|
16174
|
+
detailFile: finding.detailFile,
|
|
16175
|
+
sessionId
|
|
16176
|
+
});
|
|
16177
|
+
if (result.ok) {
|
|
16178
|
+
const fileResult = result.value;
|
|
16179
|
+
setDetailContent(fileResult.content ?? "");
|
|
16180
|
+
} else {
|
|
16181
|
+
setDetailContent("*Unable to load finding detail.*");
|
|
16182
|
+
}
|
|
16183
|
+
} catch {
|
|
16184
|
+
setDetailContent("*Error loading finding detail.*");
|
|
16185
|
+
} finally {
|
|
16186
|
+
setDetailLoading(false);
|
|
16187
|
+
}
|
|
16188
|
+
}, [command, sessionId]);
|
|
16189
|
+
const closeDetail = (0, import_react10.useCallback)(() => {
|
|
16190
|
+
setSelectedFinding(null);
|
|
16191
|
+
setDetailContent(null);
|
|
16192
|
+
}, []);
|
|
16193
|
+
if (findings.length === 0) {
|
|
16194
|
+
return import_react10.default.createElement("div", {
|
|
16195
|
+
style: { padding: "24px", color: "var(--dsw-fg-muted)", fontSize: "14px" }
|
|
16196
|
+
}, "No findings yet");
|
|
16197
|
+
}
|
|
16198
|
+
const severitySections = SEVERITY_ORDER.map((severity) => {
|
|
16199
|
+
const items = grouped.get(severity) ?? [];
|
|
16200
|
+
if (items.length === 0) return null;
|
|
16201
|
+
const findingRows = items.map(
|
|
16202
|
+
(finding) => import_react10.default.createElement(
|
|
16203
|
+
"div",
|
|
16204
|
+
{
|
|
16205
|
+
key: finding.id,
|
|
16206
|
+
style: {
|
|
16207
|
+
display: "flex",
|
|
16208
|
+
alignItems: "center",
|
|
16209
|
+
gap: "8px",
|
|
16210
|
+
padding: "6px 12px",
|
|
16211
|
+
cursor: "pointer",
|
|
16212
|
+
borderRadius: "4px",
|
|
16213
|
+
fontSize: "13px",
|
|
16214
|
+
color: "var(--dsw-fg)",
|
|
16215
|
+
borderLeft: `3px solid ${SEVERITY_COLORS[finding.severity]}`,
|
|
16216
|
+
marginBottom: "2px"
|
|
16217
|
+
},
|
|
16218
|
+
onClick: () => openDetail(finding)
|
|
16219
|
+
},
|
|
16220
|
+
import_react10.default.createElement("span", {
|
|
16221
|
+
style: {
|
|
16222
|
+
width: "8px",
|
|
16223
|
+
height: "8px",
|
|
16224
|
+
borderRadius: "50%",
|
|
16225
|
+
background: SEVERITY_COLORS[finding.severity],
|
|
16226
|
+
flexShrink: 0
|
|
16227
|
+
}
|
|
16228
|
+
}),
|
|
16229
|
+
import_react10.default.createElement("span", { style: { fontWeight: 500 } }, finding.title),
|
|
16230
|
+
finding.affectedAsset !== void 0 && import_react10.default.createElement("span", {
|
|
16231
|
+
style: { fontSize: "11px", color: "var(--dsw-fg-muted)", marginLeft: "auto" }
|
|
16232
|
+
}, finding.affectedAsset)
|
|
16233
|
+
)
|
|
16234
|
+
);
|
|
16235
|
+
return import_react10.default.createElement(ExpandableSection, {
|
|
16236
|
+
key: severity,
|
|
16237
|
+
title: `${SEVERITY_LABELS[severity]} \xB7 ${items.length}`,
|
|
16238
|
+
defaultOpen: severity === "critical" || severity === "high",
|
|
16239
|
+
children: findingRows
|
|
16240
|
+
});
|
|
16241
|
+
});
|
|
16242
|
+
const modalContent = selectedFinding !== null ? import_react10.default.createElement(
|
|
16243
|
+
import_dsh_client_ui_primitives3.Modal,
|
|
16244
|
+
{
|
|
16245
|
+
open: true,
|
|
16246
|
+
onClose: closeDetail,
|
|
16247
|
+
title: selectedFinding.title,
|
|
16248
|
+
contentClassName: "pentester-finding-detail"
|
|
16249
|
+
},
|
|
16250
|
+
import_react10.default.createElement(
|
|
16251
|
+
"div",
|
|
16252
|
+
{ style: { fontSize: "13px", lineHeight: "1.6" } },
|
|
16253
|
+
import_react10.default.createElement("div", {
|
|
16254
|
+
style: {
|
|
16255
|
+
display: "inline-block",
|
|
16256
|
+
padding: "2px 10px",
|
|
16257
|
+
borderRadius: "10px",
|
|
16258
|
+
background: SEVERITY_COLORS[selectedFinding.severity],
|
|
16259
|
+
color: "#fff",
|
|
16260
|
+
fontSize: "12px",
|
|
16261
|
+
fontWeight: 600,
|
|
16262
|
+
marginBottom: "12px"
|
|
16263
|
+
}
|
|
16264
|
+
}, SEVERITY_LABELS[selectedFinding.severity]),
|
|
16265
|
+
selectedFinding.affectedAsset !== void 0 && import_react10.default.createElement("div", {
|
|
16266
|
+
style: { color: "var(--dsw-fg-muted)", marginBottom: "4px" }
|
|
16267
|
+
}, "Affected: ", import_react10.default.createElement("code", null, selectedFinding.affectedAsset)),
|
|
16268
|
+
import_react10.default.createElement("div", {
|
|
16269
|
+
style: { color: "var(--dsw-fg-muted)", marginBottom: "12px", fontSize: "12px" }
|
|
16270
|
+
}, `Source: ${selectedFinding.sourceStage} \xB7 ${selectedFinding.sourceDelegationId}`),
|
|
16271
|
+
detailLoading && import_react10.default.createElement("div", {
|
|
16272
|
+
style: { color: "var(--dsw-fg-muted)", padding: "16px 0" }
|
|
16273
|
+
}, "Loading..."),
|
|
16274
|
+
detailContent !== null && import_react10.default.createElement(import_dsh_client_ui_primitives3.MarkdownText, { text: detailContent })
|
|
16275
|
+
)
|
|
16276
|
+
) : null;
|
|
16277
|
+
return import_react10.default.createElement(
|
|
16278
|
+
"div",
|
|
16279
|
+
{ style: { maxWidth: "640px" } },
|
|
16280
|
+
...severitySections,
|
|
16281
|
+
modalContent
|
|
16282
|
+
);
|
|
16283
|
+
}
|
|
16284
|
+
|
|
16285
|
+
// src/ui/client/pentester/TraceView.tsx
|
|
16286
|
+
var import_react15 = __toESM(require("react"), 1);
|
|
16287
|
+
|
|
16288
|
+
// src/ui/client/pentester/trace/TraceTimeline.tsx
|
|
16289
|
+
var import_react14 = __toESM(require("react"), 1);
|
|
16290
|
+
|
|
16291
|
+
// src/ui/client/pentester/trace/types.ts
|
|
16292
|
+
var STAGE_NODE_H = 72;
|
|
16293
|
+
var START_NODE_H = 48;
|
|
16294
|
+
var AGENT_NODE_W = 195;
|
|
16295
|
+
var AGENT_NODE_H = 68;
|
|
16296
|
+
var DELIVERY_NODE_W = 110;
|
|
16297
|
+
var DELIVERY_NODE_H = 62;
|
|
16298
|
+
var STAGE_TO_AGENT_GAP = 60;
|
|
16299
|
+
var AGENT_TO_DELIVERY_GAP = 50;
|
|
16300
|
+
var DELIVERY_ROW_GAP = 18;
|
|
16301
|
+
var STAGE_SECTION_PAD = 32;
|
|
16302
|
+
var TOP_OFFSET = 20;
|
|
16303
|
+
var MIN_SCALE = 0.5;
|
|
16304
|
+
var MAX_SCALE = 1.75;
|
|
16305
|
+
var DEFAULT_SCALE = 1;
|
|
16306
|
+
var AGENT_OFFSET = 280;
|
|
16307
|
+
var MAX_DELIVERY_PER_ROW = 3;
|
|
16308
|
+
var DELIVERY_H_GAP = 20;
|
|
16309
|
+
var TIME_PANEL_W = 245;
|
|
16310
|
+
var GRAPH_PADDING = 32;
|
|
16311
|
+
var TIMELINE_DOT_W = 16;
|
|
16312
|
+
var TIMELINE_TIME_W = 66;
|
|
16313
|
+
var TIMELINE_ROW_H = 44;
|
|
16314
|
+
var MIN_BAND_HEIGHT = 44;
|
|
16315
|
+
var BAND_GAP = 8;
|
|
16316
|
+
var COMPOSER_BOTTOM_PAD = 200;
|
|
16317
|
+
function formatTime(iso) {
|
|
16318
|
+
try {
|
|
16319
|
+
const d = new Date(iso);
|
|
16320
|
+
if (isNaN(d.getTime())) return "--";
|
|
16321
|
+
const hh = String(d.getHours()).padStart(2, "0");
|
|
16322
|
+
const mm = String(d.getMinutes()).padStart(2, "0");
|
|
16323
|
+
const ss = String(d.getSeconds()).padStart(2, "0");
|
|
16324
|
+
return `${hh}:${mm}:${ss}`;
|
|
16325
|
+
} catch {
|
|
16326
|
+
return "--";
|
|
16327
|
+
}
|
|
16328
|
+
}
|
|
16329
|
+
|
|
16330
|
+
// src/ui/client/pentester/trace/aggregation.ts
|
|
16331
|
+
var KIND_ORDER = ["result", "evidence", "artifact", "raw_output", "finding", "asset"];
|
|
16332
|
+
function aggregateDeliveries(snapshot) {
|
|
16333
|
+
const byDelegation = /* @__PURE__ */ new Map();
|
|
16334
|
+
const ensure = (delId) => {
|
|
16335
|
+
let m = byDelegation.get(delId);
|
|
16336
|
+
if (m === void 0) {
|
|
16337
|
+
m = /* @__PURE__ */ new Map();
|
|
16338
|
+
byDelegation.set(delId, m);
|
|
16339
|
+
}
|
|
16340
|
+
return m;
|
|
16341
|
+
};
|
|
16342
|
+
for (const f of snapshot.findings) {
|
|
16343
|
+
if (f.sourceDelegationId === "") continue;
|
|
16344
|
+
const delId = f.sourceDelegationId;
|
|
16345
|
+
const m = ensure(delId);
|
|
16346
|
+
let g = m.get("finding");
|
|
16347
|
+
if (g === void 0) {
|
|
16348
|
+
g = { names: [], navPath: f.id };
|
|
16349
|
+
m.set("finding", g);
|
|
16350
|
+
}
|
|
16351
|
+
g.names.push(f.title);
|
|
16352
|
+
}
|
|
16353
|
+
const result = /* @__PURE__ */ new Map();
|
|
16354
|
+
for (const [delId, kindMap] of byDelegation) {
|
|
16355
|
+
const groups = [];
|
|
16356
|
+
for (const kind of KIND_ORDER) {
|
|
16357
|
+
const g = kindMap.get(kind);
|
|
16358
|
+
if (g === void 0 || g.names.length === 0) continue;
|
|
16359
|
+
groups.push({
|
|
16360
|
+
kind,
|
|
16361
|
+
label: deliveryLabel(kind, g.names.length),
|
|
16362
|
+
count: g.names.length,
|
|
16363
|
+
navPath: g.navPath,
|
|
16364
|
+
navTab: kind === "finding" ? "findings" : "output",
|
|
16365
|
+
sampleNames: g.names.slice(0, 5)
|
|
16366
|
+
});
|
|
16367
|
+
}
|
|
16368
|
+
if (groups.length > 0) result.set(delId, groups);
|
|
16369
|
+
}
|
|
16370
|
+
return result;
|
|
16371
|
+
}
|
|
16372
|
+
function deliveryLabel(kind, count) {
|
|
16373
|
+
if (count === 1) {
|
|
16374
|
+
switch (kind) {
|
|
16375
|
+
case "result":
|
|
16376
|
+
return "Result";
|
|
16377
|
+
case "evidence":
|
|
16378
|
+
return "Evidence";
|
|
16379
|
+
case "artifact":
|
|
16380
|
+
return "Artifact";
|
|
16381
|
+
case "raw_output":
|
|
16382
|
+
return "Raw output";
|
|
16383
|
+
case "finding":
|
|
16384
|
+
return "Finding";
|
|
16385
|
+
case "asset":
|
|
16386
|
+
return "Asset";
|
|
16387
|
+
}
|
|
16388
|
+
}
|
|
16389
|
+
switch (kind) {
|
|
16390
|
+
case "result":
|
|
16391
|
+
return "Results";
|
|
16392
|
+
case "evidence":
|
|
16393
|
+
return "Evidence";
|
|
16394
|
+
case "artifact":
|
|
16395
|
+
return "Artifacts";
|
|
16396
|
+
case "raw_output":
|
|
16397
|
+
return "Raw outputs";
|
|
16398
|
+
case "finding":
|
|
16399
|
+
return "Findings";
|
|
16400
|
+
case "asset":
|
|
16401
|
+
return "Assets";
|
|
16402
|
+
}
|
|
16403
|
+
}
|
|
16404
|
+
|
|
16405
|
+
// src/ui/client/pentester/trace/layout.ts
|
|
16406
|
+
var PTES_STAGE_IDS = [
|
|
16407
|
+
"pre-engagement",
|
|
16408
|
+
"intelligence-gathering",
|
|
16409
|
+
"threat-modeling",
|
|
16410
|
+
"vulnerability-analysis",
|
|
16411
|
+
"exploitation",
|
|
16412
|
+
"post-exploitation",
|
|
16413
|
+
"reporting"
|
|
16414
|
+
];
|
|
16415
|
+
var STAGE_SHORT = {
|
|
16416
|
+
"pre-engagement": "Pre",
|
|
16417
|
+
"intelligence-gathering": "Intel",
|
|
16418
|
+
"threat-modeling": "Threat",
|
|
16419
|
+
"vulnerability-analysis": "Vuln",
|
|
16420
|
+
"exploitation": "Exploit",
|
|
16421
|
+
"post-exploitation": "Post",
|
|
16422
|
+
"reporting": "Report"
|
|
16423
|
+
};
|
|
16424
|
+
var STAGE_FULL = {
|
|
16425
|
+
"pre-engagement": "Pre-engagement",
|
|
16426
|
+
"intelligence-gathering": "Intelligence Gathering",
|
|
16427
|
+
"threat-modeling": "Threat Modeling",
|
|
16428
|
+
"vulnerability-analysis": "Vulnerability Analysis",
|
|
16429
|
+
"exploitation": "Exploitation",
|
|
16430
|
+
"post-exploitation": "Post Exploitation",
|
|
16431
|
+
"reporting": "Reporting"
|
|
16432
|
+
};
|
|
16433
|
+
var MIN_WORLD_HALF = 600;
|
|
16434
|
+
function layoutDeliveryRows(groups, agentX) {
|
|
16435
|
+
if (groups.length === 0) return { rows: [], rowCount: 0 };
|
|
16436
|
+
const rows = [];
|
|
16437
|
+
for (let i = 0; i < groups.length; i += MAX_DELIVERY_PER_ROW) {
|
|
16438
|
+
const rowGroup = groups.slice(i, i + MAX_DELIVERY_PER_ROW);
|
|
16439
|
+
const totalW = rowGroup.length * DELIVERY_NODE_W + (rowGroup.length - 1) * DELIVERY_H_GAP;
|
|
16440
|
+
const startX = agentX - totalW / 2 + DELIVERY_NODE_W / 2;
|
|
16441
|
+
const row3 = [];
|
|
16442
|
+
for (let gi = 0; gi < rowGroup.length; gi++) {
|
|
16443
|
+
row3.push(startX + gi * (DELIVERY_NODE_W + DELIVERY_H_GAP));
|
|
16444
|
+
}
|
|
16445
|
+
rows.push(row3);
|
|
16446
|
+
}
|
|
16447
|
+
return { rows, rowCount: rows.length };
|
|
16448
|
+
}
|
|
16449
|
+
function buildTraceLayout(snapshot) {
|
|
16450
|
+
const run = snapshot.run;
|
|
16451
|
+
const stageViews = snapshot.stages;
|
|
16452
|
+
const delegations = snapshot.delegations;
|
|
16453
|
+
const deliveryMap = aggregateDeliveries(snapshot);
|
|
16454
|
+
const sections = [];
|
|
16455
|
+
const timeBands = [];
|
|
16456
|
+
let cursorY = TOP_OFFSET;
|
|
16457
|
+
const START_GAP = 20;
|
|
16458
|
+
sections.push({
|
|
16459
|
+
stageId: "start",
|
|
16460
|
+
label: "Start",
|
|
16461
|
+
sublabel: "Pentest begins",
|
|
16462
|
+
status: "completed",
|
|
16463
|
+
timestamp: run.createdAt,
|
|
16464
|
+
y: cursorY,
|
|
16465
|
+
height: START_NODE_H + START_GAP,
|
|
16466
|
+
agentBranches: [],
|
|
16467
|
+
isFuture: false
|
|
16468
|
+
});
|
|
16469
|
+
timeBands.push({
|
|
16470
|
+
id: "band:start",
|
|
16471
|
+
timestamp: run.createdAt,
|
|
16472
|
+
worldY: cursorY,
|
|
16473
|
+
sectionIndex: 0,
|
|
16474
|
+
timelineItems: [{
|
|
16475
|
+
timeLabel: formatTime(run.createdAt),
|
|
16476
|
+
title: "Start",
|
|
16477
|
+
subtitle: "Assessment started",
|
|
16478
|
+
kind: "stage",
|
|
16479
|
+
dotColor: "var(--dsw-status-done, #22c55e)"
|
|
16480
|
+
}]
|
|
16481
|
+
});
|
|
16482
|
+
cursorY += START_NODE_H + START_GAP;
|
|
16483
|
+
for (let sectionIndex = 0; sectionIndex < PTES_STAGE_IDS.length; sectionIndex++) {
|
|
16484
|
+
const stageId = PTES_STAGE_IDS[sectionIndex];
|
|
16485
|
+
const stageView = stageViews.find((s) => s.id === stageId);
|
|
16486
|
+
const stageName = STAGE_SHORT[stageId] ?? stageId;
|
|
16487
|
+
const stageFullName = STAGE_FULL[stageId] ?? stageId;
|
|
16488
|
+
const stageStatus = stageView?.status ?? "pending";
|
|
16489
|
+
const isCurrent = run.currentStage === stageId;
|
|
16490
|
+
const traceNodes = snapshot.trace.nodes;
|
|
16491
|
+
const stageNode = traceNodes.find((n) => n.id === `stage:${stageId}`);
|
|
16492
|
+
const stageTimestamp = stageNode?.timestamp;
|
|
16493
|
+
const isFuture = stageTimestamp === void 0 && !isCurrent && stageStatus === "pending";
|
|
16494
|
+
const stageDelegations = delegations.filter((d) => d.stageId === stageId);
|
|
16495
|
+
const agentBranches = [];
|
|
16496
|
+
const timelineItems = [];
|
|
16497
|
+
if (stageDelegations.length > 0) {
|
|
16498
|
+
const agentY = cursorY + STAGE_NODE_H + STAGE_TO_AGENT_GAP;
|
|
16499
|
+
for (let i = 0; i < stageDelegations.length; i++) {
|
|
16500
|
+
const d = stageDelegations[i];
|
|
16501
|
+
const dir = i % 2 === 0 ? -1 : 1;
|
|
16502
|
+
const pairIndex = Math.floor(i / 2);
|
|
16503
|
+
const offset = dir * (AGENT_OFFSET + pairIndex * 60);
|
|
16504
|
+
const agentX = offset;
|
|
16505
|
+
const groups = deliveryMap.get(d.id) ?? [];
|
|
16506
|
+
const { rows: deliveryRows, rowCount: deliveryRowCount } = layoutDeliveryRows(groups, agentX);
|
|
16507
|
+
const deliveryY = agentY + AGENT_NODE_H + AGENT_TO_DELIVERY_GAP;
|
|
16508
|
+
agentBranches.push({
|
|
16509
|
+
delegationId: d.id,
|
|
16510
|
+
agentId: d.agentId,
|
|
16511
|
+
label: `${d.id} \xB7 ${d.agentId}`,
|
|
16512
|
+
status: d.status,
|
|
16513
|
+
sessionId: d.sessionId,
|
|
16514
|
+
timestamp: d.createdAt,
|
|
16515
|
+
x: agentX,
|
|
16516
|
+
y: agentY,
|
|
16517
|
+
deliveryGroups: groups,
|
|
16518
|
+
deliveryRows,
|
|
16519
|
+
deliveryY: groups.length > 0 ? deliveryY : agentY + AGENT_NODE_H,
|
|
16520
|
+
deliveryRowCount
|
|
16521
|
+
});
|
|
16522
|
+
}
|
|
16523
|
+
}
|
|
16524
|
+
let sectionBottom = cursorY + STAGE_NODE_H;
|
|
16525
|
+
if (agentBranches.length > 0) {
|
|
16526
|
+
const maxDelBottom = Math.max(...agentBranches.map((a) => {
|
|
16527
|
+
if (a.deliveryGroups.length > 0) {
|
|
16528
|
+
return a.deliveryY + a.deliveryRowCount * (DELIVERY_NODE_H + DELIVERY_ROW_GAP) - DELIVERY_ROW_GAP;
|
|
16529
|
+
}
|
|
16530
|
+
return a.y + AGENT_NODE_H;
|
|
16531
|
+
}));
|
|
16532
|
+
sectionBottom = Math.max(sectionBottom, maxDelBottom);
|
|
16533
|
+
}
|
|
16534
|
+
sectionBottom += STAGE_SECTION_PAD;
|
|
16535
|
+
const contentHeight = sectionBottom - cursorY;
|
|
16536
|
+
if (isFuture) continue;
|
|
16537
|
+
const sectionIdx = sections.length;
|
|
16538
|
+
sections.push({
|
|
16539
|
+
stageId,
|
|
16540
|
+
label: stageName,
|
|
16541
|
+
sublabel: stageFullName,
|
|
16542
|
+
status: stageStatus,
|
|
16543
|
+
timestamp: stageTimestamp,
|
|
16544
|
+
y: cursorY,
|
|
16545
|
+
height: contentHeight,
|
|
16546
|
+
agentBranches,
|
|
16547
|
+
isFuture: false
|
|
16548
|
+
});
|
|
16549
|
+
if (stageTimestamp !== void 0) {
|
|
16550
|
+
timelineItems.push({
|
|
16551
|
+
timeLabel: formatTime(stageTimestamp),
|
|
16552
|
+
title: stageName,
|
|
16553
|
+
subtitle: stageStatus === "completed" ? "Stage completed" : "Stage started",
|
|
16554
|
+
kind: "stage",
|
|
16555
|
+
dotColor: stageStatus === "completed" ? "var(--dsw-status-done)" : stageStatus === "active" ? "var(--dsw-alias-state-business-primary)" : void 0
|
|
16556
|
+
});
|
|
16557
|
+
}
|
|
16558
|
+
const agentTimestamps = /* @__PURE__ */ new Map();
|
|
16559
|
+
for (const a of agentBranches) {
|
|
16560
|
+
if (a.timestamp !== void 0) {
|
|
16561
|
+
const key = a.timestamp;
|
|
16562
|
+
let list = agentTimestamps.get(key);
|
|
16563
|
+
if (list === void 0) {
|
|
16564
|
+
list = [];
|
|
16565
|
+
agentTimestamps.set(key, list);
|
|
16566
|
+
}
|
|
16567
|
+
list.push(a);
|
|
16568
|
+
}
|
|
16569
|
+
}
|
|
16570
|
+
for (const [ts, agents] of agentTimestamps) {
|
|
16571
|
+
if (agents.length === 1) {
|
|
16572
|
+
const a = agents[0];
|
|
16573
|
+
timelineItems.push({
|
|
16574
|
+
timeLabel: formatTime(ts),
|
|
16575
|
+
title: a.label,
|
|
16576
|
+
subtitle: a.status === "starting" ? "Agent starting" : a.status === "active" ? "Agent active" : "Agent started",
|
|
16577
|
+
kind: "delegation"
|
|
16578
|
+
});
|
|
16579
|
+
} else {
|
|
16580
|
+
for (const a of agents) {
|
|
16581
|
+
timelineItems.push({
|
|
16582
|
+
timeLabel: formatTime(ts),
|
|
16583
|
+
title: a.label,
|
|
16584
|
+
subtitle: a.status === "starting" ? "Agent starting" : "Agent started",
|
|
16585
|
+
kind: "delegation"
|
|
16586
|
+
});
|
|
16587
|
+
}
|
|
16588
|
+
}
|
|
16589
|
+
}
|
|
16590
|
+
timeBands.push({
|
|
16591
|
+
id: `band:${stageId}`,
|
|
16592
|
+
timestamp: stageTimestamp,
|
|
16593
|
+
worldY: cursorY,
|
|
16594
|
+
sectionIndex: sectionIdx,
|
|
16595
|
+
timelineItems
|
|
16596
|
+
});
|
|
16597
|
+
cursorY = sectionBottom;
|
|
16598
|
+
}
|
|
16599
|
+
let maxExtent = MIN_WORLD_HALF;
|
|
16600
|
+
for (const s of sections) {
|
|
16601
|
+
for (const a of s.agentBranches) {
|
|
16602
|
+
const absX = Math.abs(a.x);
|
|
16603
|
+
if (absX + AGENT_NODE_W / 2 > maxExtent) maxExtent = absX + AGENT_NODE_W / 2;
|
|
16604
|
+
for (const row3 of a.deliveryRows) {
|
|
16605
|
+
for (const dx of row3) {
|
|
16606
|
+
if (Math.abs(dx) + DELIVERY_NODE_W / 2 > maxExtent) maxExtent = Math.abs(dx) + DELIVERY_NODE_W / 2;
|
|
16607
|
+
}
|
|
16608
|
+
}
|
|
16609
|
+
}
|
|
16610
|
+
}
|
|
16611
|
+
maxExtent += 80;
|
|
16612
|
+
const worldHalfWidth = maxExtent;
|
|
16613
|
+
const worldHeight = cursorY + 80;
|
|
16614
|
+
return {
|
|
16615
|
+
sections,
|
|
16616
|
+
timeBands,
|
|
16617
|
+
worldHalfWidth,
|
|
16618
|
+
worldHeight,
|
|
16619
|
+
spineX: worldHalfWidth
|
|
16620
|
+
};
|
|
16621
|
+
}
|
|
16622
|
+
|
|
16623
|
+
// src/ui/client/pentester/trace/scaledLayout.ts
|
|
16624
|
+
function computeTimelineHeight(items) {
|
|
16625
|
+
if (items.length === 0) return 0;
|
|
16626
|
+
return 18 + items.length * TIMELINE_ROW_H + 8;
|
|
16627
|
+
}
|
|
16628
|
+
function computeTopologyHeight(section, scale) {
|
|
16629
|
+
if (section.stageId === "start") {
|
|
16630
|
+
return (START_NODE_H + 20) * scale;
|
|
16631
|
+
}
|
|
16632
|
+
let h = STAGE_NODE_H;
|
|
16633
|
+
if (section.agentBranches.length > 0) {
|
|
16634
|
+
h += STAGE_TO_AGENT_GAP + AGENT_NODE_H;
|
|
16635
|
+
const maxDelRows = Math.max(...section.agentBranches.map((a) => a.deliveryRowCount));
|
|
16636
|
+
if (maxDelRows > 0) {
|
|
16637
|
+
h += AGENT_TO_DELIVERY_GAP + maxDelRows * (DELIVERY_NODE_H + DELIVERY_ROW_GAP) - DELIVERY_ROW_GAP;
|
|
16638
|
+
}
|
|
16639
|
+
}
|
|
16640
|
+
h += STAGE_SECTION_PAD;
|
|
16641
|
+
return h * scale;
|
|
16642
|
+
}
|
|
16643
|
+
function adjustSectionY(section, deltaY) {
|
|
16644
|
+
const adjustAgentY = (a) => ({
|
|
16645
|
+
...a,
|
|
16646
|
+
y: a.y + deltaY,
|
|
16647
|
+
deliveryY: a.deliveryY + deltaY
|
|
16648
|
+
});
|
|
16649
|
+
return {
|
|
16650
|
+
...section,
|
|
16651
|
+
y: section.y + deltaY,
|
|
16652
|
+
agentBranches: section.agentBranches.map(adjustAgentY)
|
|
16653
|
+
};
|
|
16654
|
+
}
|
|
16655
|
+
function buildScaledTraceLayout(baseLayout, scale) {
|
|
16656
|
+
const bands = [];
|
|
16657
|
+
let visualY = 20;
|
|
16658
|
+
for (let i = 0; i < baseLayout.timeBands.length; i++) {
|
|
16659
|
+
const band = baseLayout.timeBands[i];
|
|
16660
|
+
const section = baseLayout.sections[band.sectionIndex];
|
|
16661
|
+
const timelineH = computeTimelineHeight(band.timelineItems);
|
|
16662
|
+
const topologyH = computeTopologyHeight(section, scale);
|
|
16663
|
+
const visualH = Math.max(timelineH, topologyH, MIN_BAND_HEIGHT);
|
|
16664
|
+
const adjustedWorldY = visualY / scale;
|
|
16665
|
+
const deltaY = adjustedWorldY - section.y;
|
|
16666
|
+
bands.push({
|
|
16667
|
+
bandId: band.id,
|
|
16668
|
+
visualY,
|
|
16669
|
+
visualHeight: visualH,
|
|
16670
|
+
sectionIndex: band.sectionIndex,
|
|
16671
|
+
adjustedWorldY,
|
|
16672
|
+
timelineItems: band.timelineItems
|
|
16673
|
+
});
|
|
16674
|
+
visualY += visualH + BAND_GAP;
|
|
16675
|
+
}
|
|
16676
|
+
const adjustedSections = baseLayout.sections.map((section, idx) => {
|
|
16677
|
+
const band = bands.find((b) => b.sectionIndex === idx);
|
|
16678
|
+
if (band === void 0) return section;
|
|
16679
|
+
const deltaY = band.adjustedWorldY - section.y;
|
|
16680
|
+
return deltaY === 0 ? section : adjustSectionY(section, deltaY);
|
|
16681
|
+
});
|
|
16682
|
+
const totalHeight = visualY + COMPOSER_BOTTOM_PAD;
|
|
16683
|
+
return {
|
|
16684
|
+
bands,
|
|
16685
|
+
totalHeight,
|
|
16686
|
+
worldHalfWidth: baseLayout.worldHalfWidth,
|
|
16687
|
+
spineX: baseLayout.spineX,
|
|
16688
|
+
sections: adjustedSections
|
|
16689
|
+
};
|
|
16690
|
+
}
|
|
16691
|
+
|
|
16692
|
+
// src/ui/client/pentester/trace/TraceNode.tsx
|
|
16693
|
+
var import_react11 = __toESM(require("react"), 1);
|
|
16694
|
+
var import_dsh_client_ui_primitives4 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
16695
|
+
var CARD = {
|
|
16696
|
+
position: "absolute",
|
|
16697
|
+
borderRadius: "7px",
|
|
16698
|
+
background: "var(--dsw-alias-bg-base)",
|
|
16699
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
16700
|
+
boxShadow: "0 1px 3px rgba(0,0,0,0.04)",
|
|
16701
|
+
boxSizing: "border-box",
|
|
16702
|
+
fontSize: "12px",
|
|
16703
|
+
lineHeight: "1.4",
|
|
16704
|
+
cursor: "pointer",
|
|
16705
|
+
pointerEvents: "auto"
|
|
16706
|
+
};
|
|
16707
|
+
var StartNode = (0, import_react11.forwardRef)(
|
|
16708
|
+
function StartNode2(props, ref) {
|
|
16709
|
+
const { section, x } = props;
|
|
16710
|
+
return import_react11.default.createElement(
|
|
16711
|
+
"div",
|
|
16712
|
+
{
|
|
16713
|
+
ref,
|
|
16714
|
+
"data-node-id": `stage:${section.stageId}`,
|
|
16715
|
+
"data-node-kind": "stage",
|
|
16716
|
+
style: {
|
|
16717
|
+
position: "absolute",
|
|
16718
|
+
left: `${x - 70}px`,
|
|
16719
|
+
top: `${section.y}px`,
|
|
16720
|
+
width: "140px",
|
|
16721
|
+
minHeight: "48px",
|
|
16722
|
+
padding: "10px 16px",
|
|
16723
|
+
borderRadius: "7px",
|
|
16724
|
+
background: "var(--dsw-alias-bg-base)",
|
|
16725
|
+
border: "1.5px solid var(--dsw-status-done, #22c55e)",
|
|
16726
|
+
boxSizing: "border-box",
|
|
16727
|
+
display: "flex",
|
|
16728
|
+
alignItems: "center",
|
|
16729
|
+
justifyContent: "center",
|
|
16730
|
+
gap: "8px",
|
|
16731
|
+
fontSize: "13px",
|
|
16732
|
+
fontWeight: 600,
|
|
16733
|
+
color: "var(--dsw-alias-label-primary)",
|
|
16734
|
+
lineHeight: "20px",
|
|
16735
|
+
pointerEvents: "auto"
|
|
16736
|
+
}
|
|
16737
|
+
},
|
|
16738
|
+
import_react11.default.createElement("div", {
|
|
16739
|
+
style: {
|
|
16740
|
+
width: "6px",
|
|
16741
|
+
height: "6px",
|
|
16742
|
+
borderRadius: "50%",
|
|
16743
|
+
background: "var(--dsw-status-done, #22c55e)",
|
|
16744
|
+
flexShrink: 0
|
|
16745
|
+
}
|
|
16746
|
+
}),
|
|
16747
|
+
section.label
|
|
16748
|
+
);
|
|
16749
|
+
}
|
|
16750
|
+
);
|
|
16751
|
+
var StageNode = (0, import_react11.forwardRef)(
|
|
16752
|
+
function StageNode2(props, ref) {
|
|
16753
|
+
const { section, x, onClick } = props;
|
|
16754
|
+
const status = section.status;
|
|
16755
|
+
const borderColor = status === "completed" ? "var(--dsw-status-done, #22c55e)" : status === "active" ? "var(--dsw-alias-state-business-primary)" : "var(--dsw-alias-border-l2)";
|
|
16756
|
+
const dotState = status === "completed" ? "done" : status === "active" ? "ongoing" : "ongoing";
|
|
16757
|
+
return import_react11.default.createElement(
|
|
16758
|
+
"div",
|
|
16759
|
+
{
|
|
16760
|
+
ref,
|
|
16761
|
+
"data-node-id": `stage:${section.stageId}`,
|
|
16762
|
+
"data-node-kind": "stage",
|
|
16763
|
+
role: "button",
|
|
16764
|
+
tabIndex: 0,
|
|
16765
|
+
onClick,
|
|
16766
|
+
onKeyDown: (e) => {
|
|
16767
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
16768
|
+
e.preventDefault();
|
|
16769
|
+
onClick?.();
|
|
16770
|
+
}
|
|
16771
|
+
},
|
|
16772
|
+
style: {
|
|
16773
|
+
...CARD,
|
|
16774
|
+
left: `${x - 115}px`,
|
|
16775
|
+
top: `${section.y}px`,
|
|
16776
|
+
width: "230px",
|
|
16777
|
+
minHeight: "72px",
|
|
16778
|
+
padding: "14px 16px",
|
|
16779
|
+
border: `1.5px solid ${borderColor}`,
|
|
16780
|
+
display: "flex",
|
|
16781
|
+
alignItems: "center",
|
|
16782
|
+
gap: "10px"
|
|
16783
|
+
}
|
|
16784
|
+
},
|
|
16785
|
+
import_react11.default.createElement(import_dsh_client_ui_primitives4.StateDot, { state: dotState, size: 8 }),
|
|
16786
|
+
import_react11.default.createElement(
|
|
16787
|
+
"div",
|
|
16788
|
+
{ style: { minWidth: 0 } },
|
|
16789
|
+
import_react11.default.createElement("div", {
|
|
16790
|
+
style: { fontWeight: 600, color: "var(--dsw-alias-label-primary)", fontSize: "14px", lineHeight: "20px" }
|
|
16791
|
+
}, section.label),
|
|
16792
|
+
import_react11.default.createElement("div", {
|
|
16793
|
+
style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary)", marginTop: "2px" }
|
|
16794
|
+
}, section.sublabel)
|
|
16795
|
+
)
|
|
16796
|
+
);
|
|
16797
|
+
}
|
|
16798
|
+
);
|
|
16799
|
+
var AgentNode = (0, import_react11.forwardRef)(
|
|
16800
|
+
function AgentNode2(props, ref) {
|
|
16801
|
+
const { branch, x, liveActivity, onClick } = props;
|
|
16802
|
+
const dotState = branch.status === "failed" ? "error" : branch.status === "closed" ? "done" : liveActivity === "running" ? "ongoing" : liveActivity === "inactive" ? "done" : branch.status === "starting" ? "ongoing" : "done";
|
|
16803
|
+
const borderColor = dotState === "error" ? "var(--dsw-status-error, #ef4444)" : dotState === "ongoing" ? "var(--dsw-alias-state-business-primary)" : "var(--dsw-alias-border-l2)";
|
|
16804
|
+
return import_react11.default.createElement(
|
|
16805
|
+
"div",
|
|
16806
|
+
{
|
|
16807
|
+
ref,
|
|
16808
|
+
"data-node-id": `delegation:${branch.delegationId}`,
|
|
16809
|
+
"data-node-kind": "agent",
|
|
16810
|
+
role: "button",
|
|
16811
|
+
tabIndex: 0,
|
|
16812
|
+
onClick,
|
|
16813
|
+
onKeyDown: (e) => {
|
|
16814
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
16815
|
+
e.preventDefault();
|
|
16816
|
+
onClick?.();
|
|
16817
|
+
}
|
|
16818
|
+
},
|
|
16819
|
+
style: {
|
|
16820
|
+
...CARD,
|
|
16821
|
+
left: `${x - 97}px`,
|
|
16822
|
+
top: `${branch.y}px`,
|
|
16823
|
+
width: "195px",
|
|
16824
|
+
minHeight: "68px",
|
|
16825
|
+
padding: "12px 14px",
|
|
16826
|
+
border: `1px solid ${borderColor}`,
|
|
16827
|
+
display: "flex",
|
|
16828
|
+
alignItems: "center",
|
|
16829
|
+
gap: "8px"
|
|
16830
|
+
}
|
|
16831
|
+
},
|
|
16832
|
+
import_react11.default.createElement(import_dsh_client_ui_primitives4.StateDot, { state: dotState, size: 8 }),
|
|
16833
|
+
import_react11.default.createElement(
|
|
16834
|
+
"div",
|
|
16835
|
+
{ style: { minWidth: 0 } },
|
|
16836
|
+
import_react11.default.createElement("div", {
|
|
16837
|
+
style: { fontWeight: 600, color: "var(--dsw-alias-label-primary)", fontSize: "13px", lineHeight: "18px" }
|
|
16838
|
+
}, branch.label),
|
|
16839
|
+
import_react11.default.createElement("div", {
|
|
16840
|
+
style: { fontSize: "11px", color: "var(--dsw-alias-label-tertiary)", marginTop: "2px" }
|
|
16841
|
+
}, branch.status === "starting" ? "Agent starting" : branch.status === "active" ? "Agent active" : "Agent completed")
|
|
16842
|
+
)
|
|
16843
|
+
);
|
|
16844
|
+
}
|
|
16845
|
+
);
|
|
16846
|
+
var DELIVERY_ICON_COLORS = {
|
|
16847
|
+
result: "var(--dsw-status-done, #22c55e)",
|
|
16848
|
+
evidence: "#8b5cf6",
|
|
16849
|
+
artifact: "#f59e0b",
|
|
16850
|
+
raw_output: "var(--dsw-alias-label-tertiary)",
|
|
16851
|
+
finding: "var(--dsw-status-error, #ef4444)",
|
|
16852
|
+
asset: "#3b82f6"
|
|
16853
|
+
};
|
|
16854
|
+
var DeliveryNode = (0, import_react11.forwardRef)(
|
|
16855
|
+
function DeliveryNode2(props, ref) {
|
|
16856
|
+
const { group, x, y, onClick } = props;
|
|
16857
|
+
const iconColor = DELIVERY_ICON_COLORS[group.kind] ?? "var(--dsw-alias-label-tertiary)";
|
|
16858
|
+
return import_react11.default.createElement(
|
|
16859
|
+
"div",
|
|
16860
|
+
{
|
|
16861
|
+
ref,
|
|
16862
|
+
"data-node-id": `delivery:${group.kind}`,
|
|
16863
|
+
"data-node-kind": "delivery",
|
|
16864
|
+
role: "button",
|
|
16865
|
+
tabIndex: 0,
|
|
16866
|
+
onClick,
|
|
16867
|
+
onKeyDown: (e) => {
|
|
16868
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
16869
|
+
e.preventDefault();
|
|
16870
|
+
onClick?.();
|
|
16871
|
+
}
|
|
16872
|
+
},
|
|
16873
|
+
title: group.sampleNames.slice(0, 5).join("\n") + (group.count > 5 ? `
|
|
16874
|
+
+${group.count - 5} more` : ""),
|
|
16875
|
+
style: {
|
|
16876
|
+
...CARD,
|
|
16877
|
+
left: `${x - 55}px`,
|
|
16878
|
+
top: `${y}px`,
|
|
16879
|
+
width: "110px",
|
|
16880
|
+
minHeight: "62px",
|
|
16881
|
+
padding: "8px 10px",
|
|
16882
|
+
display: "flex",
|
|
16883
|
+
flexDirection: "column",
|
|
16884
|
+
alignItems: "center",
|
|
16885
|
+
justifyContent: "center",
|
|
16886
|
+
gap: "4px",
|
|
16887
|
+
textAlign: "center"
|
|
16888
|
+
}
|
|
16889
|
+
},
|
|
16890
|
+
import_react11.default.createElement("div", {
|
|
16891
|
+
style: {
|
|
16892
|
+
width: "8px",
|
|
16893
|
+
height: "8px",
|
|
16894
|
+
borderRadius: "4px",
|
|
16895
|
+
background: iconColor,
|
|
16896
|
+
flexShrink: 0
|
|
16897
|
+
}
|
|
16898
|
+
}),
|
|
16899
|
+
import_react11.default.createElement("div", {
|
|
16900
|
+
style: { fontWeight: 600, color: "var(--dsw-alias-label-primary)", fontSize: "12px", lineHeight: "16px" }
|
|
16901
|
+
}, group.label),
|
|
16902
|
+
import_react11.default.createElement("div", {
|
|
16903
|
+
style: { fontSize: "10px", color: "var(--dsw-alias-label-tertiary)" }
|
|
16904
|
+
}, `${group.count} ${group.count === 1 ? "file" : "files"}`)
|
|
16905
|
+
);
|
|
16906
|
+
}
|
|
16907
|
+
);
|
|
16908
|
+
|
|
16909
|
+
// src/ui/client/pentester/trace/TraceConnectors.tsx
|
|
16910
|
+
var import_react12 = __toESM(require("react"), 1);
|
|
16911
|
+
function roundedOrthogonal(sx, sy, tx, ty, r = 6) {
|
|
16912
|
+
if (Math.abs(sx - tx) < 2) {
|
|
16913
|
+
return `M ${sx} ${sy} L ${tx} ${ty}`;
|
|
16914
|
+
}
|
|
16915
|
+
const midY = (sy + ty) / 2;
|
|
16916
|
+
const dir = tx > sx ? 1 : -1;
|
|
16917
|
+
return [
|
|
16918
|
+
`M ${sx} ${sy}`,
|
|
16919
|
+
`L ${sx} ${midY - r}`,
|
|
16920
|
+
`A ${r} ${r} 0 0 ${dir > 0 ? 1 : 0} ${sx + dir * r} ${midY}`,
|
|
16921
|
+
`L ${tx - dir * r} ${midY}`,
|
|
16922
|
+
`A ${r} ${r} 0 0 ${dir > 0 ? 1 : 0} ${tx} ${midY + r}`,
|
|
16923
|
+
`L ${tx} ${ty}`
|
|
16924
|
+
].join(" ");
|
|
16925
|
+
}
|
|
16926
|
+
function branchJunction(cx, sy, targets) {
|
|
16927
|
+
if (targets.length === 0) return [];
|
|
16928
|
+
const trunkY = sy + 18;
|
|
16929
|
+
return targets.map(
|
|
16930
|
+
(t) => `M ${cx} ${sy} L ${cx} ${trunkY} L ${t.tx} ${trunkY} L ${t.tx} ${t.ty}`
|
|
16931
|
+
);
|
|
16932
|
+
}
|
|
16933
|
+
function TraceConnectors(props) {
|
|
16934
|
+
const { layout } = props;
|
|
16935
|
+
const paths = (0, import_react12.useMemo)(() => {
|
|
16936
|
+
const result = [];
|
|
16937
|
+
const cx = layout.spineX;
|
|
16938
|
+
const solid = { stroke: "var(--dsw-alias-border-l2)", strokeWidth: 1.25 };
|
|
16939
|
+
const dashed = { stroke: "var(--dsw-alias-border-l1)", strokeWidth: 1, strokeDasharray: "4 3" };
|
|
16940
|
+
for (let i = 0; i < layout.sections.length - 1; i++) {
|
|
16941
|
+
const s = layout.sections[i];
|
|
16942
|
+
const next = layout.sections[i + 1];
|
|
16943
|
+
const y1 = s.y + STAGE_NODE_H;
|
|
16944
|
+
const y2 = next.y;
|
|
16945
|
+
if (y2 > y1) {
|
|
16946
|
+
result.push({
|
|
16947
|
+
d: `M ${cx} ${y1} L ${cx} ${y2}`,
|
|
16948
|
+
...solid
|
|
16949
|
+
});
|
|
16950
|
+
}
|
|
16951
|
+
}
|
|
16952
|
+
for (const s of layout.sections) {
|
|
16953
|
+
const sy = s.y + STAGE_NODE_H;
|
|
16954
|
+
const targets = s.agentBranches.map((a) => ({
|
|
16955
|
+
tx: cx + a.x,
|
|
16956
|
+
ty: a.y
|
|
16957
|
+
}));
|
|
16958
|
+
if (targets.length === 1) {
|
|
16959
|
+
result.push({
|
|
16960
|
+
d: roundedOrthogonal(cx, sy, targets[0].tx, targets[0].ty),
|
|
16961
|
+
...dashed
|
|
16962
|
+
});
|
|
16963
|
+
} else if (targets.length > 1) {
|
|
16964
|
+
const branches = branchJunction(cx, sy, targets);
|
|
16965
|
+
for (const b of branches) {
|
|
16966
|
+
result.push({ d: b, ...dashed });
|
|
16967
|
+
}
|
|
16968
|
+
}
|
|
16969
|
+
if (targets.length > 0) {
|
|
16970
|
+
const trunkY = sy + 18;
|
|
16971
|
+
result.push({
|
|
16972
|
+
d: `M ${cx - 3} ${trunkY} A 3 3 0 1 1 ${cx + 3} ${trunkY} A 3 3 0 1 1 ${cx - 3} ${trunkY}`,
|
|
16973
|
+
stroke: "var(--dsw-alias-border-l2)",
|
|
16974
|
+
strokeWidth: 1
|
|
16975
|
+
});
|
|
16976
|
+
}
|
|
16977
|
+
for (const a of s.agentBranches) {
|
|
16978
|
+
if (a.deliveryGroups.length === 0) continue;
|
|
16979
|
+
const ax = cx + a.x;
|
|
16980
|
+
const ay = a.y + AGENT_NODE_H;
|
|
16981
|
+
const firstDY = a.deliveryY;
|
|
16982
|
+
result.push({
|
|
16983
|
+
d: `M ${ax} ${ay} L ${ax} ${firstDY}`,
|
|
16984
|
+
...dashed
|
|
16985
|
+
});
|
|
16986
|
+
for (let ri = 0; ri < a.deliveryRows.length; ri++) {
|
|
16987
|
+
const row3 = a.deliveryRows[ri];
|
|
16988
|
+
const rowY = a.deliveryY + ri * (DELIVERY_NODE_H + DELIVERY_ROW_GAP);
|
|
16989
|
+
const trunkY = rowY - 10;
|
|
16990
|
+
if (row3.length > 1) {
|
|
16991
|
+
const minDX = Math.min(...row3.map((dx) => cx + dx));
|
|
16992
|
+
const maxDX = Math.max(...row3.map((dx) => cx + dx));
|
|
16993
|
+
result.push({
|
|
16994
|
+
d: `M ${minDX} ${trunkY} L ${maxDX} ${trunkY}`,
|
|
16995
|
+
...dashed
|
|
16996
|
+
});
|
|
16997
|
+
for (const dx of row3) {
|
|
16998
|
+
result.push({
|
|
16999
|
+
d: `M ${cx + dx} ${trunkY} L ${cx + dx} ${rowY}`,
|
|
17000
|
+
...dashed
|
|
17001
|
+
});
|
|
17002
|
+
}
|
|
17003
|
+
}
|
|
17004
|
+
if (ri < a.deliveryRows.length - 1) {
|
|
17005
|
+
const nextRowY = a.deliveryY + (ri + 1) * (DELIVERY_NODE_H + DELIVERY_ROW_GAP);
|
|
17006
|
+
result.push({
|
|
17007
|
+
d: `M ${ax} ${rowY + DELIVERY_NODE_H} L ${ax} ${nextRowY}`,
|
|
17008
|
+
...dashed
|
|
17009
|
+
});
|
|
17010
|
+
}
|
|
17011
|
+
}
|
|
17012
|
+
}
|
|
17013
|
+
}
|
|
17014
|
+
return result;
|
|
17015
|
+
}, [layout]);
|
|
17016
|
+
return import_react12.default.createElement(
|
|
17017
|
+
"svg",
|
|
17018
|
+
{
|
|
17019
|
+
width: layout.worldHalfWidth * 2,
|
|
17020
|
+
height: layout.worldHeight,
|
|
17021
|
+
style: {
|
|
17022
|
+
position: "absolute",
|
|
17023
|
+
inset: 0,
|
|
17024
|
+
overflow: "visible",
|
|
17025
|
+
pointerEvents: "none"
|
|
17026
|
+
}
|
|
17027
|
+
},
|
|
17028
|
+
...paths.map(
|
|
17029
|
+
(p, i) => import_react12.default.createElement("path", {
|
|
17030
|
+
key: i,
|
|
17031
|
+
d: p.d,
|
|
17032
|
+
fill: "none",
|
|
17033
|
+
stroke: p.stroke,
|
|
17034
|
+
strokeWidth: p.strokeWidth,
|
|
17035
|
+
strokeDasharray: p.strokeDasharray,
|
|
17036
|
+
strokeLinecap: "round",
|
|
17037
|
+
strokeLinejoin: "round"
|
|
17038
|
+
})
|
|
17039
|
+
)
|
|
17040
|
+
);
|
|
17041
|
+
}
|
|
17042
|
+
|
|
17043
|
+
// src/ui/client/pentester/trace/TraceControls.tsx
|
|
17044
|
+
var import_react13 = __toESM(require("react"), 1);
|
|
17045
|
+
var BTN = {
|
|
17046
|
+
height: "32px",
|
|
17047
|
+
display: "flex",
|
|
17048
|
+
alignItems: "center",
|
|
17049
|
+
justifyContent: "center",
|
|
17050
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
17051
|
+
borderRadius: "6px",
|
|
17052
|
+
background: "var(--dsw-alias-bg-base)",
|
|
17053
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
17054
|
+
cursor: "pointer",
|
|
17055
|
+
fontSize: "13px",
|
|
17056
|
+
fontFamily: "inherit",
|
|
17057
|
+
padding: "0 10px",
|
|
17058
|
+
lineHeight: "1"
|
|
17059
|
+
};
|
|
17060
|
+
function TraceControls(props) {
|
|
17061
|
+
const { scale, onZoomIn, onZoomOut, onReset, onFit } = props;
|
|
17062
|
+
const pct = Math.round(scale * 100);
|
|
17063
|
+
return import_react13.default.createElement(
|
|
17064
|
+
"div",
|
|
17065
|
+
{
|
|
17066
|
+
style: { display: "flex", alignItems: "center", gap: "4px" }
|
|
17067
|
+
},
|
|
17068
|
+
import_react13.default.createElement("button", {
|
|
17069
|
+
type: "button",
|
|
17070
|
+
"aria-label": "Zoom out",
|
|
17071
|
+
title: "Zoom out",
|
|
17072
|
+
onClick: onZoomOut,
|
|
17073
|
+
style: { ...BTN, width: "32px", padding: 0 }
|
|
17074
|
+
}, "\u2212"),
|
|
17075
|
+
import_react13.default.createElement("button", {
|
|
17076
|
+
type: "button",
|
|
17077
|
+
"aria-label": "Reset zoom",
|
|
17078
|
+
title: "Reset to 100%",
|
|
17079
|
+
onClick: onReset,
|
|
17080
|
+
style: { ...BTN, fontWeight: 500, fontSize: "11px", color: "var(--dsw-alias-label-primary)" }
|
|
17081
|
+
}, `${pct}%`),
|
|
17082
|
+
import_react13.default.createElement("button", {
|
|
17083
|
+
type: "button",
|
|
17084
|
+
"aria-label": "Zoom in",
|
|
17085
|
+
title: "Zoom in",
|
|
17086
|
+
onClick: onZoomIn,
|
|
17087
|
+
style: { ...BTN, width: "32px", padding: 0 }
|
|
17088
|
+
}, "+"),
|
|
17089
|
+
import_react13.default.createElement("button", {
|
|
17090
|
+
type: "button",
|
|
17091
|
+
"aria-label": "Fit to width",
|
|
17092
|
+
title: "Fit topology width",
|
|
17093
|
+
onClick: onFit,
|
|
17094
|
+
style: { ...BTN, fontWeight: 500, fontSize: "11px" }
|
|
17095
|
+
}, "Fit")
|
|
17096
|
+
);
|
|
17097
|
+
}
|
|
17098
|
+
|
|
17099
|
+
// src/ui/client/pentester/trace/TraceTimeline.tsx
|
|
17100
|
+
function getScrollOwner() {
|
|
17101
|
+
return document.scrollingElement ?? document.documentElement;
|
|
17102
|
+
}
|
|
17103
|
+
function checkSpineInvariant(graphEl, scale) {
|
|
17104
|
+
if (graphEl === null) return;
|
|
17105
|
+
const startEl = graphEl.querySelector('[data-node-id="stage:start"]');
|
|
17106
|
+
if (startEl === null) return;
|
|
17107
|
+
const graphRect = graphEl.getBoundingClientRect();
|
|
17108
|
+
const startRect = startEl.getBoundingClientRect();
|
|
17109
|
+
const startCenter = startRect.left + startRect.width / 2;
|
|
17110
|
+
const graphCenter = graphRect.left + graphRect.width / 2;
|
|
17111
|
+
const error51 = Math.abs(startCenter - graphCenter);
|
|
17112
|
+
if (error51 > 2) {
|
|
17113
|
+
console.warn(`[Trace] Spine invariant violated: error=${error51.toFixed(1)}px at scale=${scale.toFixed(2)}`);
|
|
17114
|
+
}
|
|
17115
|
+
}
|
|
17116
|
+
function TraceTimeline(props) {
|
|
17117
|
+
const { snapshot, liveActivity, onNavigate, onOpenSession } = props;
|
|
17118
|
+
const baseLayout = (0, import_react14.useMemo)(() => buildTraceLayout(snapshot), [snapshot]);
|
|
17119
|
+
const [scale, setScale] = (0, import_react14.useState)(DEFAULT_SCALE);
|
|
17120
|
+
const graphRef = (0, import_react14.useRef)(null);
|
|
17121
|
+
const graphVpRef = (0, import_react14.useRef)(null);
|
|
17122
|
+
const autoFitDoneRef = (0, import_react14.useRef)(false);
|
|
17123
|
+
const scaledLayout = (0, import_react14.useMemo)(
|
|
17124
|
+
() => buildScaledTraceLayout(baseLayout, scale),
|
|
17125
|
+
[baseLayout, scale]
|
|
17126
|
+
);
|
|
17127
|
+
const adjustedSections = scaledLayout.sections;
|
|
17128
|
+
const scaledBands = scaledLayout.bands;
|
|
17129
|
+
const worldHalfWidth = scaledLayout.worldHalfWidth;
|
|
17130
|
+
const spineX = scaledLayout.spineX;
|
|
17131
|
+
const worldW = worldHalfWidth * 2;
|
|
17132
|
+
const worldH = scaledLayout.totalHeight;
|
|
17133
|
+
const doAutoFit = (0, import_react14.useCallback)(() => {
|
|
17134
|
+
const vp = graphVpRef.current;
|
|
17135
|
+
if (vp === null) return DEFAULT_SCALE;
|
|
17136
|
+
const availableHalf = vp.clientWidth / 2 - GRAPH_PADDING;
|
|
17137
|
+
const requiredHalf = baseLayout.worldHalfWidth;
|
|
17138
|
+
const fitScale = availableHalf / requiredHalf;
|
|
17139
|
+
return Math.min(DEFAULT_SCALE, Math.max(MIN_SCALE, fitScale));
|
|
17140
|
+
}, [baseLayout.worldHalfWidth]);
|
|
17141
|
+
(0, import_react14.useEffect)(() => {
|
|
17142
|
+
if (autoFitDoneRef.current) return;
|
|
17143
|
+
const fitScale = doAutoFit();
|
|
17144
|
+
if (fitScale !== DEFAULT_SCALE) setScale(fitScale);
|
|
17145
|
+
autoFitDoneRef.current = true;
|
|
17146
|
+
}, [doAutoFit]);
|
|
17147
|
+
const zoomTo = (0, import_react14.useCallback)((newScale) => {
|
|
17148
|
+
const clamped = Math.min(MAX_SCALE, Math.max(MIN_SCALE, newScale));
|
|
17149
|
+
const owner = getScrollOwner();
|
|
17150
|
+
const oldScale = scale;
|
|
17151
|
+
const viewH = owner.clientHeight;
|
|
17152
|
+
const scrollTop = owner.scrollTop;
|
|
17153
|
+
const worldCenterY = (scrollTop + viewH / 2) / oldScale;
|
|
17154
|
+
const nextScrollTop = worldCenterY * clamped - viewH / 2;
|
|
17155
|
+
setScale(clamped);
|
|
17156
|
+
requestAnimationFrame(() => {
|
|
17157
|
+
owner.scrollTop = Math.max(0, nextScrollTop);
|
|
17158
|
+
});
|
|
17159
|
+
}, [scale]);
|
|
17160
|
+
const zoomIn = (0, import_react14.useCallback)(() => zoomTo(scale * 1.15), [zoomTo, scale]);
|
|
17161
|
+
const zoomOut = (0, import_react14.useCallback)(() => zoomTo(scale / 1.15), [zoomTo, scale]);
|
|
17162
|
+
const resetZoom = (0, import_react14.useCallback)(() => zoomTo(DEFAULT_SCALE), [zoomTo]);
|
|
17163
|
+
const fitWidth = (0, import_react14.useCallback)(() => zoomTo(doAutoFit()), [zoomTo, doAutoFit]);
|
|
17164
|
+
const handleWheel = (0, import_react14.useCallback)((e) => {
|
|
17165
|
+
if (!e.ctrlKey && !e.metaKey) return;
|
|
17166
|
+
e.preventDefault();
|
|
17167
|
+
const factor = e.deltaY < 0 ? 1.12 : 1 / 1.12;
|
|
17168
|
+
zoomTo(scale * factor);
|
|
17169
|
+
}, [zoomTo, scale]);
|
|
17170
|
+
(0, import_react14.useEffect)(() => {
|
|
17171
|
+
checkSpineInvariant(graphRef.current, scale);
|
|
17172
|
+
}, [scale, adjustedSections]);
|
|
17173
|
+
const handleStageClick = (0, import_react14.useCallback)((stageId) => {
|
|
17174
|
+
onNavigate("stages", { stage: stageId });
|
|
17175
|
+
}, [onNavigate]);
|
|
17176
|
+
const handleAgentClick = (0, import_react14.useCallback)((sessionId) => {
|
|
17177
|
+
if (sessionId !== void 0) onOpenSession(sessionId);
|
|
17178
|
+
}, [onOpenSession]);
|
|
17179
|
+
const handleDeliveryClick = (0, import_react14.useCallback)((group) => {
|
|
17180
|
+
if (group.navTab === "findings") {
|
|
17181
|
+
onNavigate("findings", { finding: group.navPath });
|
|
17182
|
+
} else {
|
|
17183
|
+
onNavigate("output", { output: group.navPath });
|
|
17184
|
+
}
|
|
17185
|
+
}, [onNavigate]);
|
|
17186
|
+
const gridCols = `${TIME_PANEL_W}px minmax(0, 1fr)`;
|
|
17187
|
+
const timelineEventGrid = `${TIMELINE_DOT_W}px ${TIMELINE_TIME_W}px minmax(0, 1fr)`;
|
|
17188
|
+
return import_react14.default.createElement(
|
|
17189
|
+
"div",
|
|
17190
|
+
{
|
|
17191
|
+
style: {
|
|
17192
|
+
display: "flex",
|
|
17193
|
+
flexDirection: "column",
|
|
17194
|
+
width: "100%",
|
|
17195
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
17196
|
+
borderRadius: "8px",
|
|
17197
|
+
background: "var(--dsw-alias-bg-base)"
|
|
17198
|
+
}
|
|
17199
|
+
},
|
|
17200
|
+
// ── TraceHeader ─────────────────────────────────────────────────────────
|
|
17201
|
+
import_react14.default.createElement(
|
|
17202
|
+
"div",
|
|
17203
|
+
{
|
|
17204
|
+
"data-testid": "trace-header",
|
|
17205
|
+
style: {
|
|
17206
|
+
display: "grid",
|
|
17207
|
+
gridTemplateColumns: gridCols,
|
|
17208
|
+
flexShrink: 0,
|
|
17209
|
+
borderBottom: "1px solid var(--dsw-alias-border-l1)",
|
|
17210
|
+
background: "var(--dsw-alias-bg-base)"
|
|
17211
|
+
}
|
|
17212
|
+
},
|
|
17213
|
+
import_react14.default.createElement(
|
|
17214
|
+
"div",
|
|
17215
|
+
{
|
|
17216
|
+
style: { padding: "12px 18px", borderRight: "1px solid var(--dsw-alias-border-l1)" }
|
|
17217
|
+
},
|
|
17218
|
+
import_react14.default.createElement("div", {
|
|
17219
|
+
style: { fontSize: "13px", fontWeight: 600, color: "var(--dsw-alias-label-primary)" }
|
|
17220
|
+
}, "Timeline")
|
|
17221
|
+
),
|
|
17222
|
+
import_react14.default.createElement(
|
|
17223
|
+
"div",
|
|
17224
|
+
{
|
|
17225
|
+
style: {
|
|
17226
|
+
padding: "12px 18px",
|
|
17227
|
+
display: "flex",
|
|
17228
|
+
alignItems: "center",
|
|
17229
|
+
justifyContent: "space-between"
|
|
17230
|
+
}
|
|
17231
|
+
},
|
|
17232
|
+
import_react14.default.createElement("div", {
|
|
17233
|
+
style: { fontSize: "13px", fontWeight: 600, color: "var(--dsw-alias-label-primary)" }
|
|
17234
|
+
}, "Topology"),
|
|
17235
|
+
import_react14.default.createElement(TraceControls, {
|
|
17236
|
+
scale,
|
|
17237
|
+
onZoomIn: zoomIn,
|
|
17238
|
+
onZoomOut: zoomOut,
|
|
17239
|
+
onReset: resetZoom,
|
|
17240
|
+
onFit: fitWidth
|
|
17241
|
+
})
|
|
17242
|
+
)
|
|
17243
|
+
),
|
|
17244
|
+
// ── Content (no scroll — flows into Harness scroll owner) ───────────────
|
|
17245
|
+
import_react14.default.createElement(
|
|
17246
|
+
"div",
|
|
17247
|
+
{
|
|
17248
|
+
onWheel: handleWheel,
|
|
17249
|
+
style: {
|
|
17250
|
+
display: "grid",
|
|
17251
|
+
gridTemplateColumns: gridCols
|
|
17252
|
+
}
|
|
17253
|
+
},
|
|
17254
|
+
// ── Timeline rail ────────────────────────────────────────────────────
|
|
17255
|
+
import_react14.default.createElement(
|
|
17256
|
+
"div",
|
|
17257
|
+
{
|
|
17258
|
+
style: {
|
|
17259
|
+
position: "relative",
|
|
17260
|
+
borderRight: "1px solid var(--dsw-alias-border-l1)",
|
|
17261
|
+
background: "var(--dsw-alias-bg-base)",
|
|
17262
|
+
height: `${worldH}px`
|
|
17263
|
+
}
|
|
17264
|
+
},
|
|
17265
|
+
// Vertical timeline spine line
|
|
17266
|
+
import_react14.default.createElement("div", {
|
|
17267
|
+
"aria-hidden": true,
|
|
17268
|
+
style: {
|
|
17269
|
+
position: "absolute",
|
|
17270
|
+
left: "22px",
|
|
17271
|
+
top: "8px",
|
|
17272
|
+
bottom: "8px",
|
|
17273
|
+
width: "1px",
|
|
17274
|
+
background: "var(--dsw-alias-border-l2)",
|
|
17275
|
+
zIndex: 0
|
|
17276
|
+
}
|
|
17277
|
+
}),
|
|
17278
|
+
...scaledBands.map((band) => {
|
|
17279
|
+
const isStart = band.bandId === "band:start";
|
|
17280
|
+
if (band.timelineItems.length === 0) return null;
|
|
17281
|
+
return import_react14.default.createElement(
|
|
17282
|
+
"div",
|
|
17283
|
+
{
|
|
17284
|
+
key: band.bandId,
|
|
17285
|
+
style: {
|
|
17286
|
+
position: "absolute",
|
|
17287
|
+
top: `${band.visualY}px`,
|
|
17288
|
+
left: "0px",
|
|
17289
|
+
right: "4px",
|
|
17290
|
+
display: "flex",
|
|
17291
|
+
flexDirection: "column",
|
|
17292
|
+
gap: "4px",
|
|
17293
|
+
zIndex: 1
|
|
17294
|
+
}
|
|
17295
|
+
},
|
|
17296
|
+
// Start band
|
|
17297
|
+
isStart && band.timelineItems.map(
|
|
17298
|
+
(item, ii) => import_react14.default.createElement(
|
|
17299
|
+
"div",
|
|
17300
|
+
{
|
|
17301
|
+
key: ii,
|
|
17302
|
+
style: {
|
|
17303
|
+
display: "grid",
|
|
17304
|
+
gridTemplateColumns: timelineEventGrid,
|
|
17305
|
+
alignItems: "flex-start",
|
|
17306
|
+
gap: "6px",
|
|
17307
|
+
minHeight: `${TIMELINE_ROW_H}px`
|
|
17308
|
+
}
|
|
17309
|
+
},
|
|
17310
|
+
import_react14.default.createElement(
|
|
17311
|
+
"div",
|
|
17312
|
+
{
|
|
17313
|
+
style: { display: "flex", justifyContent: "center", paddingTop: "4px" }
|
|
17314
|
+
},
|
|
17315
|
+
import_react14.default.createElement("div", {
|
|
17316
|
+
style: {
|
|
17317
|
+
width: "6px",
|
|
17318
|
+
height: "6px",
|
|
17319
|
+
borderRadius: "50%",
|
|
17320
|
+
background: item.dotColor ?? "var(--dsw-status-done, #22c55e)"
|
|
17321
|
+
}
|
|
17322
|
+
})
|
|
17323
|
+
),
|
|
17324
|
+
import_react14.default.createElement("span", {
|
|
17325
|
+
style: {
|
|
17326
|
+
fontSize: "11px",
|
|
17327
|
+
fontFamily: "monospace",
|
|
17328
|
+
color: "var(--dsw-alias-label-caption, var(--dsw-alias-label-tertiary))",
|
|
17329
|
+
lineHeight: "18px",
|
|
17330
|
+
paddingTop: "2px"
|
|
17331
|
+
}
|
|
17332
|
+
}, item.timeLabel),
|
|
17333
|
+
import_react14.default.createElement(
|
|
17334
|
+
"div",
|
|
17335
|
+
{ style: { minWidth: 0, paddingTop: "2px" } },
|
|
17336
|
+
import_react14.default.createElement("div", {
|
|
17337
|
+
style: { fontSize: "13px", fontWeight: 500, color: "var(--dsw-alias-label-primary)", lineHeight: "18px" }
|
|
17338
|
+
}, item.title),
|
|
17339
|
+
item.subtitle !== "" && import_react14.default.createElement("div", {
|
|
17340
|
+
style: { fontSize: "11px", color: "var(--dsw-alias-label-tertiary)", lineHeight: "16px" }
|
|
17341
|
+
}, item.subtitle)
|
|
17342
|
+
)
|
|
17343
|
+
)
|
|
17344
|
+
),
|
|
17345
|
+
// Regular stage/agent items
|
|
17346
|
+
!isStart && band.timelineItems.map(
|
|
17347
|
+
(item, ii) => import_react14.default.createElement(
|
|
17348
|
+
"div",
|
|
17349
|
+
{
|
|
17350
|
+
key: ii,
|
|
17351
|
+
style: {
|
|
17352
|
+
display: "grid",
|
|
17353
|
+
gridTemplateColumns: timelineEventGrid,
|
|
17354
|
+
alignItems: "flex-start",
|
|
17355
|
+
gap: "6px",
|
|
17356
|
+
minHeight: `${TIMELINE_ROW_H}px`
|
|
17357
|
+
}
|
|
17358
|
+
},
|
|
17359
|
+
import_react14.default.createElement(
|
|
17360
|
+
"div",
|
|
17361
|
+
{
|
|
17362
|
+
style: { display: "flex", justifyContent: "center", paddingTop: "4px" }
|
|
17363
|
+
},
|
|
17364
|
+
import_react14.default.createElement("div", {
|
|
17365
|
+
style: {
|
|
17366
|
+
width: "8px",
|
|
17367
|
+
height: "8px",
|
|
17368
|
+
borderRadius: "50%",
|
|
17369
|
+
background: item.dotColor ?? "var(--dsw-alias-label-tertiary)"
|
|
17370
|
+
}
|
|
17371
|
+
})
|
|
17372
|
+
),
|
|
17373
|
+
import_react14.default.createElement("span", {
|
|
17374
|
+
style: {
|
|
17375
|
+
fontSize: "11px",
|
|
17376
|
+
fontFamily: "monospace",
|
|
17377
|
+
color: "var(--dsw-alias-label-caption, var(--dsw-alias-label-tertiary))",
|
|
17378
|
+
lineHeight: "18px",
|
|
17379
|
+
paddingTop: "2px"
|
|
17380
|
+
}
|
|
17381
|
+
}, item.timeLabel),
|
|
17382
|
+
import_react14.default.createElement(
|
|
17383
|
+
"div",
|
|
17384
|
+
{ style: { minWidth: 0, paddingTop: "2px" } },
|
|
17385
|
+
import_react14.default.createElement("div", {
|
|
17386
|
+
style: { fontSize: "13px", fontWeight: 500, color: "var(--dsw-alias-label-primary)", lineHeight: "18px" }
|
|
17387
|
+
}, item.title),
|
|
17388
|
+
item.subtitle !== "" && import_react14.default.createElement("div", {
|
|
17389
|
+
style: { fontSize: "11px", color: "var(--dsw-alias-label-tertiary)", lineHeight: "16px" }
|
|
17390
|
+
}, item.subtitle)
|
|
17391
|
+
)
|
|
17392
|
+
)
|
|
17393
|
+
)
|
|
17394
|
+
);
|
|
17395
|
+
})
|
|
17396
|
+
),
|
|
17397
|
+
// ── Graph viewport ────────────────────────────────────────────────────
|
|
17398
|
+
import_react14.default.createElement(
|
|
17399
|
+
"div",
|
|
17400
|
+
{
|
|
17401
|
+
ref: graphVpRef,
|
|
17402
|
+
style: {
|
|
17403
|
+
position: "relative",
|
|
17404
|
+
background: "var(--dsw-alias-bg-base)",
|
|
17405
|
+
overflow: "hidden",
|
|
17406
|
+
height: `${worldH}px`
|
|
17407
|
+
}
|
|
17408
|
+
},
|
|
17409
|
+
// SpineAnchor: CSS left:50%, always at viewport center
|
|
17410
|
+
import_react14.default.createElement(
|
|
17411
|
+
"div",
|
|
17412
|
+
{
|
|
17413
|
+
style: {
|
|
17414
|
+
position: "absolute",
|
|
17415
|
+
left: "50%",
|
|
17416
|
+
top: "0px",
|
|
17417
|
+
width: "0px",
|
|
17418
|
+
height: `${worldH}px`,
|
|
17419
|
+
pointerEvents: "none"
|
|
17420
|
+
}
|
|
17421
|
+
},
|
|
17422
|
+
// TopologyWorld: symmetric around spine
|
|
17423
|
+
import_react14.default.createElement(
|
|
17424
|
+
"div",
|
|
17425
|
+
{
|
|
17426
|
+
ref: graphRef,
|
|
17427
|
+
style: {
|
|
17428
|
+
position: "absolute",
|
|
17429
|
+
left: `-${worldHalfWidth}px`,
|
|
17430
|
+
top: "0px",
|
|
17431
|
+
width: `${worldW}px`,
|
|
17432
|
+
height: `${worldH}px`,
|
|
17433
|
+
transform: `scale(${scale})`,
|
|
17434
|
+
transformOrigin: `${spineX}px 0px`
|
|
17435
|
+
}
|
|
17436
|
+
},
|
|
17437
|
+
import_react14.default.createElement(TraceConnectors, {
|
|
17438
|
+
layout: { ...baseLayout, sections: adjustedSections, worldHeight: worldH, spineX },
|
|
17439
|
+
rects: /* @__PURE__ */ new Map()
|
|
17440
|
+
}),
|
|
17441
|
+
import_react14.default.createElement(
|
|
17442
|
+
"div",
|
|
17443
|
+
{
|
|
17444
|
+
style: { position: "absolute", inset: 0, zIndex: 1 }
|
|
17445
|
+
},
|
|
17446
|
+
...adjustedSections.filter((s) => s.stageId === "start").map(
|
|
17447
|
+
(section) => import_react14.default.createElement(StartNode, {
|
|
17448
|
+
key: "stage:start",
|
|
17449
|
+
section,
|
|
17450
|
+
x: spineX
|
|
17451
|
+
})
|
|
17452
|
+
),
|
|
17453
|
+
...adjustedSections.filter((s) => s.stageId !== "start").map(
|
|
17454
|
+
(section) => import_react14.default.createElement(StageNode, {
|
|
17455
|
+
key: `stage:${section.stageId}`,
|
|
17456
|
+
section,
|
|
17457
|
+
x: spineX,
|
|
17458
|
+
onClick: () => handleStageClick(section.stageId)
|
|
17459
|
+
})
|
|
17460
|
+
),
|
|
17461
|
+
...adjustedSections.flatMap(
|
|
17462
|
+
(section) => section.agentBranches.map(
|
|
17463
|
+
(branch) => import_react14.default.createElement(AgentNode, {
|
|
17464
|
+
key: `agent:${branch.delegationId}`,
|
|
17465
|
+
branch,
|
|
17466
|
+
x: spineX + branch.x,
|
|
17467
|
+
liveActivity: liveActivity.get(branch.delegationId),
|
|
17468
|
+
onClick: () => handleAgentClick(branch.sessionId)
|
|
17469
|
+
})
|
|
17470
|
+
)
|
|
17471
|
+
),
|
|
17472
|
+
...adjustedSections.flatMap(
|
|
17473
|
+
(section) => section.agentBranches.flatMap(
|
|
17474
|
+
(branch) => branch.deliveryRows.flatMap(
|
|
17475
|
+
(row3, ri) => row3.map((dx, gi) => {
|
|
17476
|
+
const group = branch.deliveryGroups[ri * 3 + gi];
|
|
17477
|
+
if (group === void 0) return null;
|
|
17478
|
+
return import_react14.default.createElement(DeliveryNode, {
|
|
17479
|
+
key: `delivery:${branch.delegationId}:${group.kind}`,
|
|
17480
|
+
group,
|
|
17481
|
+
x: spineX + dx,
|
|
17482
|
+
y: branch.deliveryY + ri * (DELIVERY_NODE_H + DELIVERY_ROW_GAP),
|
|
17483
|
+
onClick: () => handleDeliveryClick(group)
|
|
17484
|
+
});
|
|
17485
|
+
})
|
|
17486
|
+
).filter(Boolean)
|
|
17487
|
+
)
|
|
17488
|
+
)
|
|
17489
|
+
)
|
|
17490
|
+
)
|
|
17491
|
+
)
|
|
17492
|
+
)
|
|
17493
|
+
)
|
|
17494
|
+
);
|
|
17495
|
+
}
|
|
17496
|
+
|
|
17497
|
+
// src/ui/client/pentester/TraceView.tsx
|
|
17498
|
+
function TraceView(props) {
|
|
17499
|
+
const { snapshot, liveActivity, onNavigate, onOpenSession, activeTargetId } = props;
|
|
17500
|
+
if (snapshot === null || snapshot.run === null) {
|
|
17501
|
+
return import_react15.default.createElement("div", {
|
|
17502
|
+
style: { padding: "24px", color: "var(--dsw-alias-label-tertiary)", fontSize: "14px" }
|
|
17503
|
+
}, "No execution trace yet");
|
|
17504
|
+
}
|
|
17505
|
+
return import_react15.default.createElement(TraceTimeline, {
|
|
17506
|
+
snapshot,
|
|
17507
|
+
liveActivity,
|
|
17508
|
+
onNavigate,
|
|
17509
|
+
onOpenSession,
|
|
17510
|
+
activeTargetId
|
|
17511
|
+
});
|
|
17512
|
+
}
|
|
17513
|
+
|
|
17514
|
+
// src/ui/client/pentester/OutputView.tsx
|
|
17515
|
+
var import_react17 = __toESM(require("react"), 1);
|
|
17516
|
+
var import_dsh_client_ui_primitives6 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
17517
|
+
|
|
17518
|
+
// src/ui/client/pentester/FolderTreeRow.tsx
|
|
17519
|
+
var import_react16 = __toESM(require("react"), 1);
|
|
17520
|
+
var import_dsh_client_ui_primitives5 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
17521
|
+
function FolderTreeRow(props) {
|
|
17522
|
+
const { name, depth, defaultOpen = true, children, containsSelected = false } = props;
|
|
17523
|
+
const [open, setOpen] = (0, import_react16.useState)(defaultOpen);
|
|
17524
|
+
const hasChildren = children !== void 0 && import_react16.default.Children.count(children) > 0;
|
|
17525
|
+
const folderColor = containsSelected ? "var(--dsw-alias-state-business-primary)" : "var(--dsw-alias-label-secondary)";
|
|
17526
|
+
return import_react16.default.createElement(
|
|
17527
|
+
"div",
|
|
17528
|
+
null,
|
|
17529
|
+
import_react16.default.createElement(
|
|
17530
|
+
"div",
|
|
17531
|
+
{
|
|
17532
|
+
role: "button",
|
|
17533
|
+
tabIndex: 0,
|
|
17534
|
+
"aria-expanded": open,
|
|
17535
|
+
onClick: () => setOpen(!open),
|
|
17536
|
+
onKeyDown: (e) => {
|
|
17537
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
17538
|
+
e.preventDefault();
|
|
17539
|
+
setOpen(!open);
|
|
17540
|
+
}
|
|
17541
|
+
},
|
|
17542
|
+
style: {
|
|
17543
|
+
display: "flex",
|
|
17544
|
+
alignItems: "center",
|
|
17545
|
+
gap: "4px",
|
|
17546
|
+
marginLeft: `${depth * 16}px`,
|
|
17547
|
+
padding: "3px 6px",
|
|
17548
|
+
borderRadius: "4px",
|
|
17549
|
+
cursor: "pointer",
|
|
17550
|
+
fontSize: "13px",
|
|
17551
|
+
lineHeight: "20px",
|
|
17552
|
+
color: "var(--dsw-alias-label-primary)",
|
|
17553
|
+
userSelect: "none",
|
|
17554
|
+
height: "28px",
|
|
17555
|
+
boxSizing: "border-box"
|
|
17556
|
+
},
|
|
17557
|
+
onMouseEnter: (e) => {
|
|
17558
|
+
e.currentTarget.style.background = "var(--dsw-alias-interactive-bg-hover)";
|
|
17559
|
+
},
|
|
17560
|
+
onMouseLeave: (e) => {
|
|
17561
|
+
e.currentTarget.style.background = "transparent";
|
|
17562
|
+
}
|
|
17563
|
+
},
|
|
17564
|
+
// Chevron
|
|
17565
|
+
hasChildren ? import_react16.default.createElement(
|
|
17566
|
+
"span",
|
|
17567
|
+
{ style: { flexShrink: 0, color: "var(--dsw-alias-label-tertiary)" } },
|
|
17568
|
+
import_react16.default.createElement(open ? import_dsh_client_ui_primitives5.IconChevronDownOutline14 : import_dsh_client_ui_primitives5.IconChevronRightOutline14, { size: 14 })
|
|
17569
|
+
) : import_react16.default.createElement("span", { style: { width: "14px", flexShrink: 0 } }),
|
|
17570
|
+
// Folder icon — color reflects selected ancestry
|
|
17571
|
+
import_react16.default.createElement(
|
|
17572
|
+
"span",
|
|
17573
|
+
{ style: { flexShrink: 0, color: folderColor } },
|
|
17574
|
+
import_react16.default.createElement(open ? import_dsh_client_ui_primitives5.IconFolderOpen16 : import_dsh_client_ui_primitives5.IconFolderClose16, { size: 16 })
|
|
17575
|
+
),
|
|
17576
|
+
// Name
|
|
17577
|
+
import_react16.default.createElement("span", {
|
|
17578
|
+
style: {
|
|
17579
|
+
overflow: "hidden",
|
|
17580
|
+
textOverflow: "ellipsis",
|
|
17581
|
+
whiteSpace: "nowrap"
|
|
17582
|
+
}
|
|
17583
|
+
}, name)
|
|
17584
|
+
),
|
|
17585
|
+
open && hasChildren && import_react16.default.createElement("div", null, children)
|
|
17586
|
+
);
|
|
17587
|
+
}
|
|
17588
|
+
function FileRow(props) {
|
|
17589
|
+
const { name, depth, size, isSelected = false, onClick } = props;
|
|
17590
|
+
return import_react16.default.createElement(
|
|
17591
|
+
"div",
|
|
17592
|
+
{
|
|
17593
|
+
role: "button",
|
|
17594
|
+
tabIndex: 0,
|
|
17595
|
+
onClick,
|
|
17596
|
+
onKeyDown: (e) => {
|
|
17597
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
17598
|
+
e.preventDefault();
|
|
17599
|
+
onClick();
|
|
17600
|
+
}
|
|
17601
|
+
},
|
|
17602
|
+
style: {
|
|
17603
|
+
display: "flex",
|
|
17604
|
+
alignItems: "center",
|
|
17605
|
+
gap: "6px",
|
|
17606
|
+
marginLeft: `${depth * 16 + 16}px`,
|
|
17607
|
+
padding: "3px 8px",
|
|
17608
|
+
borderRadius: "4px",
|
|
17609
|
+
cursor: "pointer",
|
|
17610
|
+
background: isSelected ? "var(--dsw-alias-interactive-bg-hover)" : "transparent",
|
|
17611
|
+
fontSize: "13px",
|
|
17612
|
+
lineHeight: "20px",
|
|
17613
|
+
color: "var(--dsw-alias-label-primary)",
|
|
17614
|
+
height: "28px",
|
|
17615
|
+
boxSizing: "border-box"
|
|
17616
|
+
},
|
|
17617
|
+
onMouseEnter: (e) => {
|
|
17618
|
+
if (!isSelected) e.currentTarget.style.background = "var(--dsw-alias-interactive-bg-hover)";
|
|
17619
|
+
},
|
|
17620
|
+
onMouseLeave: (e) => {
|
|
17621
|
+
if (!isSelected) e.currentTarget.style.background = "transparent";
|
|
17622
|
+
}
|
|
17623
|
+
},
|
|
17624
|
+
// File icon placeholder (Harness doesn't have a specific file icon; use chevron space)
|
|
17625
|
+
import_react16.default.createElement("span", {
|
|
17626
|
+
style: { width: "14px", flexShrink: 0 }
|
|
17627
|
+
}),
|
|
17628
|
+
import_react16.default.createElement("span", {
|
|
17629
|
+
style: {
|
|
17630
|
+
overflow: "hidden",
|
|
17631
|
+
textOverflow: "ellipsis",
|
|
17632
|
+
whiteSpace: "nowrap",
|
|
17633
|
+
flex: 1,
|
|
17634
|
+
minWidth: 0
|
|
17635
|
+
}
|
|
17636
|
+
}, name),
|
|
17637
|
+
size !== void 0 && import_react16.default.createElement("span", {
|
|
17638
|
+
style: {
|
|
17639
|
+
fontSize: "11px",
|
|
17640
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
17641
|
+
flexShrink: 0
|
|
17642
|
+
}
|
|
17643
|
+
}, size)
|
|
17644
|
+
);
|
|
17645
|
+
}
|
|
17646
|
+
|
|
17647
|
+
// src/ui/client/pentester/OutputView.tsx
|
|
17648
|
+
var OUTPUT_FILTER_TABS = [
|
|
17649
|
+
{ id: "all", label: "All" },
|
|
17650
|
+
{ id: "report", label: "Reports" },
|
|
17651
|
+
{ id: "evidence", label: "Evidence" },
|
|
17652
|
+
{ id: "artifact", label: "Artifacts" }
|
|
17653
|
+
];
|
|
17654
|
+
function TargetIcon(props) {
|
|
17655
|
+
const s = props.size ?? 16;
|
|
17656
|
+
return import_react17.default.createElement(
|
|
17657
|
+
"svg",
|
|
17658
|
+
{
|
|
17659
|
+
width: s,
|
|
17660
|
+
height: s,
|
|
17661
|
+
viewBox: "0 0 16 16",
|
|
17662
|
+
fill: "none",
|
|
17663
|
+
stroke: "currentColor",
|
|
17664
|
+
strokeWidth: "1.5",
|
|
17665
|
+
strokeLinecap: "round",
|
|
17666
|
+
strokeLinejoin: "round",
|
|
17667
|
+
"aria-hidden": true,
|
|
17668
|
+
style: { flexShrink: 0 }
|
|
17669
|
+
},
|
|
17670
|
+
import_react17.default.createElement("circle", { cx: "8", cy: "8", r: "6.5" }),
|
|
17671
|
+
import_react17.default.createElement("circle", { cx: "8", cy: "8", r: "3.5" }),
|
|
17672
|
+
import_react17.default.createElement("circle", { cx: "8", cy: "8", r: "1", fill: "currentColor", stroke: "none" }),
|
|
17673
|
+
import_react17.default.createElement("line", { x1: "8", y1: "1", x2: "8", y2: "3" }),
|
|
17674
|
+
import_react17.default.createElement("line", { x1: "8", y1: "13", x2: "8", y2: "15" }),
|
|
17675
|
+
import_react17.default.createElement("line", { x1: "1", y1: "8", x2: "3", y2: "8" }),
|
|
17676
|
+
import_react17.default.createElement("line", { x1: "13", y1: "8", x2: "15", y2: "8" })
|
|
17677
|
+
);
|
|
17678
|
+
}
|
|
17679
|
+
function entryContainsSelected(entry, selectedPath) {
|
|
17680
|
+
if (selectedPath === null) return false;
|
|
17681
|
+
if (entry.kind === "file") return entry.path === selectedPath;
|
|
17682
|
+
if (entry.children === void 0) return false;
|
|
17683
|
+
return entry.children.some((child) => entryContainsSelected(child, selectedPath));
|
|
17684
|
+
}
|
|
17685
|
+
function TargetRow(props) {
|
|
17686
|
+
const { targetId, target, current, defaultOpen, containsSelected, children } = props;
|
|
17687
|
+
const [open, setOpen] = (0, import_react17.useState)(defaultOpen);
|
|
17688
|
+
const iconColor = containsSelected ? "var(--dsw-alias-state-business-primary)" : current ? "var(--dsw-alias-state-business-primary)" : "var(--dsw-alias-label-secondary)";
|
|
17689
|
+
return import_react17.default.createElement(
|
|
17690
|
+
"div",
|
|
17691
|
+
null,
|
|
17692
|
+
import_react17.default.createElement(
|
|
17693
|
+
"div",
|
|
17694
|
+
{
|
|
17695
|
+
role: "button",
|
|
17696
|
+
tabIndex: 0,
|
|
17697
|
+
"aria-expanded": open,
|
|
17698
|
+
"aria-label": `Target ${target}`,
|
|
17699
|
+
onClick: () => setOpen(!open),
|
|
17700
|
+
onKeyDown: (e) => {
|
|
17701
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
17702
|
+
e.preventDefault();
|
|
17703
|
+
setOpen(!open);
|
|
17704
|
+
}
|
|
17705
|
+
},
|
|
17706
|
+
style: {
|
|
17707
|
+
display: "flex",
|
|
17708
|
+
alignItems: "center",
|
|
17709
|
+
gap: "6px",
|
|
17710
|
+
padding: "5px 8px",
|
|
17711
|
+
borderRadius: "4px",
|
|
17712
|
+
cursor: "pointer",
|
|
17713
|
+
fontSize: "13px",
|
|
17714
|
+
lineHeight: "20px",
|
|
17715
|
+
color: current ? "var(--dsw-alias-label-primary)" : "var(--dsw-alias-label-secondary)",
|
|
17716
|
+
fontWeight: current ? 500 : 400,
|
|
17717
|
+
userSelect: "none",
|
|
17718
|
+
height: "30px",
|
|
17719
|
+
boxSizing: "border-box"
|
|
17720
|
+
},
|
|
17721
|
+
onMouseEnter: (e) => {
|
|
17722
|
+
e.currentTarget.style.background = "var(--dsw-alias-interactive-bg-hover)";
|
|
17723
|
+
},
|
|
17724
|
+
onMouseLeave: (e) => {
|
|
17725
|
+
e.currentTarget.style.background = "transparent";
|
|
17726
|
+
}
|
|
17727
|
+
},
|
|
17728
|
+
import_react17.default.createElement("span", {
|
|
17729
|
+
style: {
|
|
17730
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
17731
|
+
flexShrink: 0,
|
|
17732
|
+
display: "flex",
|
|
17733
|
+
alignItems: "center",
|
|
17734
|
+
fontSize: "10px",
|
|
17735
|
+
width: "12px"
|
|
17736
|
+
}
|
|
17737
|
+
}, open ? "\u25BE" : "\u25B8"),
|
|
17738
|
+
import_react17.default.createElement("span", {
|
|
17739
|
+
style: {
|
|
17740
|
+
color: iconColor,
|
|
17741
|
+
flexShrink: 0,
|
|
17742
|
+
display: "flex",
|
|
17743
|
+
alignItems: "center"
|
|
17744
|
+
}
|
|
17745
|
+
}, import_react17.default.createElement(TargetIcon, { size: 16 })),
|
|
17746
|
+
import_react17.default.createElement("span", {
|
|
17747
|
+
style: {
|
|
17748
|
+
overflow: "hidden",
|
|
17749
|
+
textOverflow: "ellipsis",
|
|
17750
|
+
whiteSpace: "nowrap"
|
|
17751
|
+
}
|
|
17752
|
+
}, target),
|
|
17753
|
+
current && import_react17.default.createElement("span", {
|
|
17754
|
+
style: {
|
|
17755
|
+
fontSize: "10px",
|
|
17756
|
+
color: "var(--dsw-alias-state-business-primary)",
|
|
17757
|
+
background: "rgba(59, 130, 246, 0.1)",
|
|
17758
|
+
padding: "0 5px",
|
|
17759
|
+
borderRadius: "3px",
|
|
17760
|
+
lineHeight: "16px",
|
|
17761
|
+
flexShrink: 0,
|
|
17762
|
+
fontWeight: 500
|
|
17763
|
+
}
|
|
17764
|
+
}, "active")
|
|
17765
|
+
),
|
|
17766
|
+
open && import_react17.default.createElement("div", { style: { marginLeft: "20px" } }, children)
|
|
17767
|
+
);
|
|
17768
|
+
}
|
|
17769
|
+
function OutputView(props) {
|
|
17770
|
+
const { snapshot, command, sessionId } = props;
|
|
17771
|
+
const [filter, setFilter] = (0, import_react17.useState)("all");
|
|
17772
|
+
const [preview, setPreview] = (0, import_react17.useState)(null);
|
|
17773
|
+
const [previewContent, setPreviewContent] = (0, import_react17.useState)(null);
|
|
17774
|
+
const [previewLoading, setPreviewLoading] = (0, import_react17.useState)(false);
|
|
17775
|
+
const [outputTree, setOutputTree] = (0, import_react17.useState)(null);
|
|
17776
|
+
const [outputLoading, setOutputLoading] = (0, import_react17.useState)(false);
|
|
17777
|
+
const [outputError, setOutputError] = (0, import_react17.useState)(null);
|
|
17778
|
+
(0, import_react17.useEffect)(() => {
|
|
17779
|
+
if (snapshot === null) return;
|
|
17780
|
+
let cancelled = false;
|
|
17781
|
+
setOutputLoading(true);
|
|
17782
|
+
setOutputError(null);
|
|
17783
|
+
command({ kind: "listOutput", sessionId }).then((result) => {
|
|
17784
|
+
if (cancelled) return;
|
|
17785
|
+
setOutputLoading(false);
|
|
17786
|
+
if (result.ok && result.value !== null && typeof result.value === "object" && "children" in result.value) {
|
|
17787
|
+
setOutputTree(result.value);
|
|
17788
|
+
setOutputError(null);
|
|
17789
|
+
} else {
|
|
17790
|
+
setOutputError(result.ok === false ? result.error ?? "Failed to load output tree" : "Invalid response");
|
|
17791
|
+
}
|
|
17792
|
+
}).catch((err) => {
|
|
17793
|
+
if (cancelled) return;
|
|
17794
|
+
setOutputLoading(false);
|
|
17795
|
+
setOutputError(err instanceof Error ? err.message : "Failed to load output tree");
|
|
17796
|
+
});
|
|
17797
|
+
return () => {
|
|
17798
|
+
cancelled = true;
|
|
17799
|
+
};
|
|
17800
|
+
}, [command, sessionId, snapshot?.run?.id]);
|
|
17801
|
+
const targetTrees = outputTree !== null ? [outputTree] : [];
|
|
17802
|
+
const selectedPath = preview?.entry.path ?? null;
|
|
17803
|
+
const selectedTargetId = preview?.targetId ?? null;
|
|
17804
|
+
const filteredTargets = (0, import_react17.useMemo)(() => {
|
|
17805
|
+
const base = filter === "all" ? targetTrees : (() => {
|
|
17806
|
+
const categoryMap = {
|
|
17807
|
+
all: [],
|
|
17808
|
+
report: ["report"],
|
|
17809
|
+
evidence: ["evidence"],
|
|
17810
|
+
artifact: ["artifact", "finding", "stage"]
|
|
17811
|
+
};
|
|
17812
|
+
const allowed = new Set(categoryMap[filter]);
|
|
17813
|
+
return targetTrees.map((t) => ({
|
|
17814
|
+
...t,
|
|
17815
|
+
children: filterEntries(t.children, allowed)
|
|
17816
|
+
})).filter((t) => t.children.length > 0);
|
|
17817
|
+
})();
|
|
17818
|
+
return base.map((t) => ({
|
|
17819
|
+
...t,
|
|
17820
|
+
containsSelected: selectedTargetId === t.targetId && t.children.some(
|
|
17821
|
+
(child) => entryContainsSelected(child, selectedPath)
|
|
17822
|
+
)
|
|
17823
|
+
}));
|
|
17824
|
+
}, [targetTrees, filter, selectedPath, selectedTargetId]);
|
|
17825
|
+
const openPreview = (0, import_react17.useCallback)(async (entry, targetId) => {
|
|
17826
|
+
if (entry.kind === "directory") return;
|
|
17827
|
+
setPreview({ entry, targetId });
|
|
17828
|
+
setPreviewContent(null);
|
|
17829
|
+
setPreviewLoading(true);
|
|
17830
|
+
try {
|
|
17831
|
+
const result = await command({
|
|
17832
|
+
kind: "readOutput",
|
|
17833
|
+
path: entry.path,
|
|
17834
|
+
sessionId,
|
|
17835
|
+
targetId: targetId !== "" ? targetId : void 0
|
|
17836
|
+
});
|
|
17837
|
+
if (result.ok) {
|
|
17838
|
+
setPreviewContent(result.value);
|
|
17839
|
+
} else {
|
|
17840
|
+
setPreviewContent({ path: entry.path, name: entry.name, size: 0, mtime: "", mime: "text", content: "Unable to load." });
|
|
17841
|
+
}
|
|
17842
|
+
} catch {
|
|
17843
|
+
setPreviewContent({ path: entry.path, name: entry.name, size: 0, mtime: "", mime: "text", content: "Error loading." });
|
|
17844
|
+
} finally {
|
|
17845
|
+
setPreviewLoading(false);
|
|
17846
|
+
}
|
|
17847
|
+
}, [command, sessionId]);
|
|
17848
|
+
const closePreview = (0, import_react17.useCallback)(() => {
|
|
17849
|
+
setPreview(null);
|
|
17850
|
+
setPreviewContent(null);
|
|
17851
|
+
}, []);
|
|
17852
|
+
const initialLoading = outputTree === null && outputLoading;
|
|
17853
|
+
if (initialLoading) {
|
|
17854
|
+
return import_react17.default.createElement("div", {
|
|
17855
|
+
style: { padding: "24px", color: "var(--dsw-alias-label-tertiary)", fontSize: "14px" }
|
|
17856
|
+
}, "Loading output tree...");
|
|
17857
|
+
}
|
|
17858
|
+
if (outputError !== null) {
|
|
17859
|
+
return import_react17.default.createElement(
|
|
17860
|
+
"div",
|
|
17861
|
+
{ style: { padding: "24px" } },
|
|
17862
|
+
import_react17.default.createElement("div", {
|
|
17863
|
+
style: { color: "var(--dsw-status-error)", marginBottom: "12px", fontSize: "14px" }
|
|
17864
|
+
}, "Unable to load output tree"),
|
|
17865
|
+
import_react17.default.createElement("div", {
|
|
17866
|
+
style: { color: "var(--dsw-alias-label-tertiary)", fontSize: "12px", marginBottom: "12px" }
|
|
17867
|
+
}, outputError),
|
|
17868
|
+
import_react17.default.createElement("button", {
|
|
17869
|
+
onClick: () => {
|
|
17870
|
+
setOutputError(null);
|
|
17871
|
+
setOutputTree(null);
|
|
17872
|
+
setOutputLoading(true);
|
|
17873
|
+
command({ kind: "listOutput", sessionId }).then((result) => {
|
|
17874
|
+
setOutputLoading(false);
|
|
17875
|
+
if (result.ok && result.value !== null && typeof result.value === "object" && "children" in result.value) {
|
|
17876
|
+
setOutputTree(result.value);
|
|
17877
|
+
} else {
|
|
17878
|
+
setOutputError(result.ok === false ? result.error ?? "Failed to load output tree" : "Invalid response");
|
|
17879
|
+
}
|
|
17880
|
+
}).catch((err) => {
|
|
17881
|
+
setOutputLoading(false);
|
|
17882
|
+
setOutputError(err instanceof Error ? err.message : "Failed to load output tree");
|
|
17883
|
+
});
|
|
17884
|
+
},
|
|
17885
|
+
style: {
|
|
17886
|
+
padding: "6px 16px",
|
|
17887
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
17888
|
+
borderRadius: "6px",
|
|
17889
|
+
background: "var(--dsw-alias-interactive-bg-hover)",
|
|
17890
|
+
color: "var(--dsw-alias-label-primary)",
|
|
17891
|
+
cursor: "pointer",
|
|
17892
|
+
fontSize: "13px"
|
|
17893
|
+
}
|
|
17894
|
+
}, "Retry Output")
|
|
17895
|
+
);
|
|
17896
|
+
}
|
|
17897
|
+
if (snapshot === null) {
|
|
17898
|
+
return import_react17.default.createElement("div", {
|
|
17899
|
+
style: { padding: "24px", color: "var(--dsw-alias-label-tertiary)", fontSize: "14px" }
|
|
17900
|
+
}, "No output data");
|
|
17901
|
+
}
|
|
17902
|
+
if (targetTrees.length === 0) {
|
|
17903
|
+
return import_react17.default.createElement("div", {
|
|
17904
|
+
style: { padding: "24px", color: "var(--dsw-alias-label-tertiary)", fontSize: "14px" }
|
|
17905
|
+
}, "No output files");
|
|
17906
|
+
}
|
|
17907
|
+
return import_react17.default.createElement(
|
|
17908
|
+
"div",
|
|
17909
|
+
{
|
|
17910
|
+
style: { display: "flex", gap: "16px", minHeight: "400px", overflow: "visible" }
|
|
17911
|
+
},
|
|
17912
|
+
// Left: file tree
|
|
17913
|
+
import_react17.default.createElement(
|
|
17914
|
+
"div",
|
|
17915
|
+
{
|
|
17916
|
+
style: {
|
|
17917
|
+
flex: "0 0 320px",
|
|
17918
|
+
overflow: "auto",
|
|
17919
|
+
borderRight: "1px solid var(--dsw-alias-border-l2)",
|
|
17920
|
+
paddingRight: "12px",
|
|
17921
|
+
display: "flex",
|
|
17922
|
+
flexDirection: "column"
|
|
17923
|
+
}
|
|
17924
|
+
},
|
|
17925
|
+
import_react17.default.createElement(
|
|
17926
|
+
"div",
|
|
17927
|
+
{ style: { marginBottom: "12px" } },
|
|
17928
|
+
import_react17.default.createElement(PentesterTabBar, {
|
|
17929
|
+
items: OUTPUT_FILTER_TABS,
|
|
17930
|
+
active: filter,
|
|
17931
|
+
onSelect: (id) => setFilter(id),
|
|
17932
|
+
variant: "compact"
|
|
17933
|
+
})
|
|
17934
|
+
),
|
|
17935
|
+
// ── Tree: "Targets" section label + target rows at root depth ─────
|
|
17936
|
+
import_react17.default.createElement(
|
|
17937
|
+
"div",
|
|
17938
|
+
{ style: { flex: 1 } },
|
|
17939
|
+
import_react17.default.createElement("div", {
|
|
17940
|
+
style: {
|
|
17941
|
+
fontSize: "11px",
|
|
17942
|
+
fontWeight: 600,
|
|
17943
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
17944
|
+
textTransform: "uppercase",
|
|
17945
|
+
letterSpacing: "0.5px",
|
|
17946
|
+
padding: "4px 8px",
|
|
17947
|
+
marginBottom: "4px"
|
|
17948
|
+
}
|
|
17949
|
+
}, "Targets"),
|
|
17950
|
+
...filteredTargets.map(
|
|
17951
|
+
(t) => import_react17.default.createElement(TargetRow, {
|
|
17952
|
+
key: t.targetId,
|
|
17953
|
+
targetId: t.targetId,
|
|
17954
|
+
target: t.target,
|
|
17955
|
+
current: t.current,
|
|
17956
|
+
defaultOpen: t.current,
|
|
17957
|
+
containsSelected: t.containsSelected === true,
|
|
17958
|
+
children: t.children.length > 0 ? t.children.map(
|
|
17959
|
+
(entry) => renderOutputTree(entry, 0, t.targetId, openPreview, preview, selectedPath, selectedTargetId)
|
|
17960
|
+
) : void 0
|
|
17961
|
+
})
|
|
17962
|
+
)
|
|
17963
|
+
)
|
|
17964
|
+
),
|
|
17965
|
+
// Right: preview frame
|
|
17966
|
+
import_react17.default.createElement(
|
|
17967
|
+
"div",
|
|
17968
|
+
{
|
|
17969
|
+
style: {
|
|
17970
|
+
flex: 1,
|
|
17971
|
+
minWidth: 0,
|
|
17972
|
+
display: "flex",
|
|
17973
|
+
flexDirection: "column",
|
|
17974
|
+
overflow: "hidden",
|
|
17975
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
17976
|
+
borderRadius: "8px",
|
|
17977
|
+
background: "var(--dsw-alias-bg-base)",
|
|
17978
|
+
minHeight: "320px",
|
|
17979
|
+
maxHeight: "clamp(320px, 65vh, 680px)"
|
|
17980
|
+
}
|
|
17981
|
+
},
|
|
17982
|
+
preview === null ? import_react17.default.createElement("div", {
|
|
17983
|
+
style: {
|
|
17984
|
+
flex: 1,
|
|
17985
|
+
display: "flex",
|
|
17986
|
+
alignItems: "center",
|
|
17987
|
+
justifyContent: "center",
|
|
17988
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
17989
|
+
fontSize: "13px"
|
|
17990
|
+
}
|
|
17991
|
+
}, "Select a file to preview") : import_react17.default.createElement(
|
|
17992
|
+
import_react17.default.Fragment,
|
|
17993
|
+
null,
|
|
17994
|
+
import_react17.default.createElement(
|
|
17995
|
+
"div",
|
|
17996
|
+
{
|
|
17997
|
+
style: {
|
|
17998
|
+
flex: "none",
|
|
17999
|
+
display: "flex",
|
|
18000
|
+
alignItems: "center",
|
|
18001
|
+
justifyContent: "space-between",
|
|
18002
|
+
padding: "8px 12px",
|
|
18003
|
+
borderBottom: "1px solid var(--dsw-alias-border-l2)"
|
|
18004
|
+
}
|
|
18005
|
+
},
|
|
18006
|
+
import_react17.default.createElement("div", {
|
|
18007
|
+
style: { fontSize: "13px", fontWeight: 600, color: "var(--dsw-alias-label-primary)" }
|
|
18008
|
+
}, preview.entry.name),
|
|
18009
|
+
import_react17.default.createElement("button", {
|
|
18010
|
+
onClick: closePreview,
|
|
18011
|
+
style: {
|
|
18012
|
+
background: "none",
|
|
18013
|
+
border: "none",
|
|
18014
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
18015
|
+
cursor: "pointer",
|
|
18016
|
+
fontSize: "18px",
|
|
18017
|
+
padding: "0 4px"
|
|
18018
|
+
},
|
|
18019
|
+
"aria-label": "Close preview"
|
|
18020
|
+
}, "\xD7")
|
|
18021
|
+
),
|
|
18022
|
+
import_react17.default.createElement(
|
|
18023
|
+
"div",
|
|
18024
|
+
{
|
|
18025
|
+
style: { flex: 1, minHeight: 0, overflow: "auto", padding: "12px" }
|
|
18026
|
+
},
|
|
18027
|
+
previewLoading && import_react17.default.createElement("div", {
|
|
18028
|
+
style: { color: "var(--dsw-alias-label-tertiary)", padding: "16px 0" }
|
|
18029
|
+
}, "Loading..."),
|
|
18030
|
+
previewContent !== null && renderPreview(previewContent)
|
|
18031
|
+
)
|
|
18032
|
+
)
|
|
18033
|
+
)
|
|
18034
|
+
);
|
|
18035
|
+
}
|
|
18036
|
+
function renderOutputTree(entry, depth, targetId, onSelect, selected, selectedPath, selectedTargetId) {
|
|
18037
|
+
if (entry.kind === "directory" && entry.children !== void 0 && entry.children.length > 0) {
|
|
18038
|
+
const isSelectedTarget = selectedTargetId === targetId;
|
|
18039
|
+
const containsSelected = isSelectedTarget && entryContainsSelected(entry, selectedPath);
|
|
18040
|
+
return import_react17.default.createElement(FolderTreeRow, {
|
|
18041
|
+
key: `${targetId}:${entry.path}`,
|
|
18042
|
+
name: entry.name,
|
|
18043
|
+
depth,
|
|
18044
|
+
defaultOpen: depth < 2,
|
|
18045
|
+
containsSelected,
|
|
18046
|
+
children: entry.children.map(
|
|
18047
|
+
(child) => renderOutputTree(child, depth + 1, targetId, onSelect, selected, selectedPath, selectedTargetId)
|
|
18048
|
+
)
|
|
18049
|
+
});
|
|
18050
|
+
}
|
|
18051
|
+
const isSelected = selected?.entry.path === entry.path && selected?.targetId === targetId;
|
|
18052
|
+
return import_react17.default.createElement(FileRow, {
|
|
18053
|
+
key: `${targetId}:${entry.path}`,
|
|
18054
|
+
name: entry.name,
|
|
18055
|
+
depth,
|
|
18056
|
+
size: entry.size !== void 0 ? formatSize(entry.size) : void 0,
|
|
18057
|
+
isSelected,
|
|
18058
|
+
onClick: () => onSelect(entry, targetId)
|
|
18059
|
+
});
|
|
18060
|
+
}
|
|
18061
|
+
function renderPreview(result) {
|
|
18062
|
+
if (result.mime === "binary") {
|
|
18063
|
+
return import_react17.default.createElement(
|
|
18064
|
+
"div",
|
|
18065
|
+
{
|
|
18066
|
+
style: { color: "var(--dsw-alias-label-tertiary)", fontSize: "13px", padding: "16px" }
|
|
18067
|
+
},
|
|
18068
|
+
import_react17.default.createElement("div", { style: { marginBottom: "8px" } }, "Preview unavailable"),
|
|
18069
|
+
import_react17.default.createElement("div", { style: { fontSize: "12px" } }, `Binary file \xB7 ${formatSize(result.size)}`),
|
|
18070
|
+
import_react17.default.createElement("div", { style: { fontSize: "12px" } }, result.path)
|
|
18071
|
+
);
|
|
18072
|
+
}
|
|
18073
|
+
if (result.content === void 0) {
|
|
18074
|
+
return import_react17.default.createElement("div", {
|
|
18075
|
+
style: { color: "var(--dsw-alias-label-tertiary)", fontSize: "13px" }
|
|
18076
|
+
}, "No content available");
|
|
18077
|
+
}
|
|
18078
|
+
if (result.mime === "markdown") {
|
|
18079
|
+
return import_react17.default.createElement(import_dsh_client_ui_primitives6.MarkdownText, { text: result.content });
|
|
18080
|
+
}
|
|
18081
|
+
if (result.mime === "json") {
|
|
18082
|
+
try {
|
|
18083
|
+
const parsed = JSON.parse(result.content);
|
|
18084
|
+
return import_react17.default.createElement(import_dsh_client_ui_primitives6.JsonTree, { data: parsed, label: result.name });
|
|
18085
|
+
} catch {
|
|
18086
|
+
}
|
|
18087
|
+
}
|
|
18088
|
+
const lines = result.content.split("\n");
|
|
18089
|
+
return import_react17.default.createElement(import_dsh_client_ui_primitives6.ReadBlock, {
|
|
18090
|
+
lines: lines.map((text, i) => ({ number: i + 1, text })),
|
|
18091
|
+
totalLines: lines.length,
|
|
18092
|
+
maxLines: 500
|
|
18093
|
+
});
|
|
18094
|
+
}
|
|
18095
|
+
function filterEntries(entries, allowed) {
|
|
18096
|
+
const result = [];
|
|
18097
|
+
for (const entry of entries) {
|
|
18098
|
+
if (entry.kind === "directory" && entry.children !== void 0) {
|
|
18099
|
+
const filtered = filterEntries(entry.children, allowed);
|
|
18100
|
+
if (filtered.length > 0) result.push({ ...entry, children: filtered });
|
|
18101
|
+
} else if (allowed.has(entry.category)) {
|
|
18102
|
+
result.push(entry);
|
|
18103
|
+
}
|
|
18104
|
+
}
|
|
18105
|
+
return result;
|
|
18106
|
+
}
|
|
18107
|
+
function formatSize(bytes) {
|
|
18108
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
18109
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
18110
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
18111
|
+
}
|
|
18112
|
+
|
|
18113
|
+
// src/ui/client/pentester-view.tsx
|
|
18114
|
+
var CORE_REFRESH_MS = 1e4;
|
|
18115
|
+
var BACKOFF_BASE = 3e3;
|
|
18116
|
+
var BACKOFF_MAX = 3e4;
|
|
18117
|
+
function computeActivitySignature(sessionList, rootSessionId, delegationSessionIds) {
|
|
18118
|
+
const subagents = sessionList.subagentsByParent?.[rootSessionId];
|
|
18119
|
+
let running = 0;
|
|
18120
|
+
if (subagents !== void 0) {
|
|
18121
|
+
for (const entry of subagents.entries ?? []) {
|
|
18122
|
+
if (delegationSessionIds.has(entry.sessionId) && entry.activity === "running") {
|
|
18123
|
+
running++;
|
|
18124
|
+
}
|
|
18125
|
+
}
|
|
18126
|
+
}
|
|
18127
|
+
for (const sid of delegationSessionIds) {
|
|
18128
|
+
const s = sessionList.byId[sid];
|
|
18129
|
+
if (s !== void 0 && s.running) running++;
|
|
18130
|
+
}
|
|
18131
|
+
return { ids: [...delegationSessionIds].sort().join(","), running };
|
|
18132
|
+
}
|
|
18133
|
+
function resolveDelegationPresentationState(delegation, sessionList, rootSessionId) {
|
|
18134
|
+
if (delegation.status === "failed") return "inactive";
|
|
18135
|
+
if (delegation.status === "closed") return "inactive";
|
|
18136
|
+
const subagents = sessionList.subagentsByParent?.[rootSessionId];
|
|
18137
|
+
if (subagents !== void 0 && delegation.sessionId !== void 0) {
|
|
18138
|
+
const entry = subagents.entries?.find(
|
|
18139
|
+
(e) => e.sessionId === delegation.sessionId
|
|
18140
|
+
);
|
|
18141
|
+
if (entry !== void 0) {
|
|
18142
|
+
if (entry.activity === "running") return "running";
|
|
18143
|
+
if (entry.activity === "inactive") return "inactive";
|
|
18144
|
+
}
|
|
18145
|
+
}
|
|
18146
|
+
if (delegation.sessionId !== void 0) {
|
|
18147
|
+
const s = sessionList.byId[delegation.sessionId];
|
|
18148
|
+
if (s !== void 0 && s.running) return "running";
|
|
18149
|
+
}
|
|
18150
|
+
if (delegation.status === "starting") return "running";
|
|
18151
|
+
return "unknown";
|
|
18152
|
+
}
|
|
18153
|
+
function useDelegationActivity(sessions, rootSessionId, delegations) {
|
|
18154
|
+
const delegationSessionIds = (0, import_react18.useMemo)(
|
|
18155
|
+
() => new Set(delegations.filter((d) => d.sessionId !== void 0).map((d) => d.sessionId)),
|
|
18156
|
+
[delegations]
|
|
18157
|
+
);
|
|
18158
|
+
const prevSigRef = (0, import_react18.useRef)("");
|
|
18159
|
+
const cachedMapRef = (0, import_react18.useRef)(/* @__PURE__ */ new Map());
|
|
18160
|
+
const getSnapshot = (0, import_react18.useCallback)(() => {
|
|
18161
|
+
const raw = sessions?.list.getSnapshot();
|
|
18162
|
+
if (raw === void 0) return cachedMapRef.current;
|
|
18163
|
+
const sig = computeActivitySignature(raw, rootSessionId, delegationSessionIds);
|
|
18164
|
+
const sigKey = `${sig.ids}|${sig.running}`;
|
|
18165
|
+
if (sigKey === prevSigRef.current) return cachedMapRef.current;
|
|
18166
|
+
prevSigRef.current = sigKey;
|
|
18167
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
18168
|
+
for (const d of delegations) {
|
|
18169
|
+
map2.set(d.id, resolveDelegationPresentationState(d, raw, rootSessionId));
|
|
18170
|
+
}
|
|
18171
|
+
cachedMapRef.current = map2;
|
|
18172
|
+
return map2;
|
|
18173
|
+
}, [sessions, rootSessionId, delegationSessionIds, delegations]);
|
|
18174
|
+
return (0, import_react18.useSyncExternalStore)(
|
|
18175
|
+
sessions?.list.subscribe ?? (() => () => {
|
|
18176
|
+
}),
|
|
18177
|
+
getSnapshot
|
|
18178
|
+
);
|
|
18179
|
+
}
|
|
18180
|
+
var PERF_PREFIX = "[PENTERSTER_PERF]";
|
|
18181
|
+
var SWITCH_PREFIX = "[SESSION_SWITCH_PERF]";
|
|
18182
|
+
var RPC_PREFIX = "[PENTESTER_RPC]";
|
|
18183
|
+
var isPerf = typeof window !== "undefined" && window.__DSH_PENTESTER_PERF === true;
|
|
18184
|
+
function PentesterView(props) {
|
|
18185
|
+
const { sessionId, reflect, sessions, useSession, sessionSwitchTimes: sessionSwitchTimes2, sessionPerf } = props;
|
|
18186
|
+
const mountTime = (0, import_react18.useRef)(performance.now());
|
|
18187
|
+
const switchTime = sessionSwitchTimes2?.get(sessionId);
|
|
18188
|
+
if (isPerf) {
|
|
18189
|
+
console.log(`${PERF_PREFIX} mount=0ms`);
|
|
18190
|
+
if (switchTime !== void 0) {
|
|
18191
|
+
console.log(`${SWITCH_PREFIX} mount-delay=${(mountTime.current - switchTime).toFixed(0)}ms (current\u2192pentester-mount)`);
|
|
18192
|
+
}
|
|
18193
|
+
}
|
|
18194
|
+
const openState = useSession?.((s) => s.openState);
|
|
18195
|
+
const prevOpenState = (0, import_react18.useRef)(void 0);
|
|
18196
|
+
(0, import_react18.useEffect)(() => {
|
|
18197
|
+
if (!sessionPerf || openState === void 0) return;
|
|
18198
|
+
if (openState !== prevOpenState.current) {
|
|
18199
|
+
const elapsed = (performance.now() - mountTime.current).toFixed(0);
|
|
18200
|
+
console.log(
|
|
18201
|
+
`%c${SWITCH_PREFIX}%c openState %c${String(prevOpenState.current)}%c \u2192 %c${openState}%c +${elapsed}ms`,
|
|
18202
|
+
"color:#f59e0b;font-weight:bold",
|
|
18203
|
+
"",
|
|
18204
|
+
"color:#6b7280",
|
|
18205
|
+
"",
|
|
18206
|
+
"color:#10b981;font-weight:bold",
|
|
18207
|
+
""
|
|
18208
|
+
);
|
|
18209
|
+
if (openState === "open" && switchTime !== void 0) {
|
|
18210
|
+
console.log(
|
|
18211
|
+
`%c${SWITCH_PREFIX}%c session-open %c+${(performance.now() - switchTime).toFixed(0)}ms%c (current\u2192open)`,
|
|
18212
|
+
"color:#f59e0b;font-weight:bold",
|
|
18213
|
+
"",
|
|
18214
|
+
"color:#10b981;font-weight:bold",
|
|
18215
|
+
"",
|
|
18216
|
+
""
|
|
18217
|
+
);
|
|
18218
|
+
}
|
|
18219
|
+
prevOpenState.current = openState;
|
|
18220
|
+
}
|
|
18221
|
+
}, [openState, sessionPerf, switchTime]);
|
|
18222
|
+
const [tab, setTab] = (0, import_react18.useState)("overview");
|
|
18223
|
+
const [dataState, setDataState] = (0, import_react18.useState)({ kind: "initial" });
|
|
18224
|
+
const [requestState, setRequestState] = (0, import_react18.useState)("idle");
|
|
18225
|
+
const [navFocus, setNavFocus] = (0, import_react18.useState)({});
|
|
18226
|
+
const [activeTargetId, setActiveTargetId] = (0, import_react18.useState)(null);
|
|
18227
|
+
const requestSeqRef = (0, import_react18.useRef)(0);
|
|
18228
|
+
const pendingRef = (0, import_react18.useRef)(null);
|
|
18229
|
+
const mountedRef = (0, import_react18.useRef)(true);
|
|
18230
|
+
const firstLoadDone = (0, import_react18.useRef)(false);
|
|
18231
|
+
const pollingPausedRef = (0, import_react18.useRef)(false);
|
|
18232
|
+
const backoffRef = (0, import_react18.useRef)(0);
|
|
18233
|
+
const timerRef = (0, import_react18.useRef)(null);
|
|
18234
|
+
const snapshotRef = (0, import_react18.useRef)(null);
|
|
18235
|
+
const pentesterCommand = (0, import_react18.useCallback)((input2, timeoutMs) => {
|
|
18236
|
+
const currentFace = reflect?.get("remote.pentesterView");
|
|
18237
|
+
return pentesterViewCommand(currentFace, input2, timeoutMs);
|
|
18238
|
+
}, [reflect]);
|
|
18239
|
+
const snapshot = dataState.kind === "bound" ? dataState.snapshot : null;
|
|
18240
|
+
const delegations = snapshot?.delegations ?? [];
|
|
18241
|
+
const liveActivity = useDelegationActivity(sessions, sessionId, delegations);
|
|
18242
|
+
const loadSnapshot = (0, import_react18.useCallback)(async (reason) => {
|
|
18243
|
+
if (reason !== "retry" && reason !== "refresh" && getPentesterEndpointState() === "missing") {
|
|
18244
|
+
if (isPerf) console.log(`${RPC_PREFIX} circuit-open endpoint=missing reason=${reason}`);
|
|
18245
|
+
return;
|
|
18246
|
+
}
|
|
18247
|
+
const mismatch = getBuildMismatch();
|
|
18248
|
+
if (mismatch !== null) {
|
|
18249
|
+
pollingPausedRef.current = true;
|
|
18250
|
+
setDataState({
|
|
18251
|
+
kind: "error",
|
|
18252
|
+
error: `Pentester Host/Client build mismatch
|
|
18253
|
+
Host: ${mismatch.host}
|
|
18254
|
+
Client: ${mismatch.client}`,
|
|
18255
|
+
code: "build_mismatch"
|
|
18256
|
+
});
|
|
18257
|
+
setRequestState("idle");
|
|
18258
|
+
return;
|
|
18259
|
+
}
|
|
18260
|
+
if (pendingRef.current !== null) {
|
|
18261
|
+
if (isPerf) console.log(`${RPC_PREFIX} busy reason=${reason} existing=${pendingRef.current.reason}`);
|
|
18262
|
+
return;
|
|
18263
|
+
}
|
|
18264
|
+
if (reason === "retry") {
|
|
18265
|
+
resetPentesterEndpointState();
|
|
18266
|
+
}
|
|
18267
|
+
const id = ++requestSeqRef.current;
|
|
18268
|
+
const pending = { id, sessionId, reason };
|
|
18269
|
+
pendingRef.current = pending;
|
|
18270
|
+
if (reason === "startup") {
|
|
18271
|
+
setRequestState("pending");
|
|
18272
|
+
} else if (reason === "retry") {
|
|
18273
|
+
setRequestState("retrying");
|
|
18274
|
+
}
|
|
18275
|
+
const tStart = performance.now();
|
|
18276
|
+
if (isPerf) {
|
|
18277
|
+
console.log(`${RPC_PREFIX} id=${id} kind=snapshot reason=${reason} start`);
|
|
18278
|
+
if (switchTime !== void 0) {
|
|
18279
|
+
console.log(`${SWITCH_PREFIX} pentesterView-request +${(tStart - switchTime).toFixed(0)}ms`);
|
|
18280
|
+
}
|
|
18281
|
+
}
|
|
18282
|
+
try {
|
|
18283
|
+
const result = await pentesterCommand({ kind: "snapshot", sessionId }, 3e4);
|
|
18284
|
+
if (pendingRef.current?.id !== id) {
|
|
18285
|
+
if (isPerf) console.log(`${RPC_PREFIX} id=${id} stale (current=${pendingRef.current?.id})`);
|
|
18286
|
+
return;
|
|
18287
|
+
}
|
|
18288
|
+
if (!mountedRef.current) return;
|
|
18289
|
+
const tResp = performance.now();
|
|
18290
|
+
if (isPerf) {
|
|
18291
|
+
console.log(`${RPC_PREFIX} id=${id} success duration=${(tResp - tStart).toFixed(0)}ms`);
|
|
18292
|
+
if (switchTime !== void 0) {
|
|
18293
|
+
console.log(`${SWITCH_PREFIX} pentesterView-response +${(tResp - switchTime).toFixed(0)}ms duration=${(tResp - tStart).toFixed(0)}ms`);
|
|
18294
|
+
}
|
|
18295
|
+
}
|
|
18296
|
+
if (result.ok) {
|
|
18297
|
+
backoffRef.current = 0;
|
|
18298
|
+
pollingPausedRef.current = false;
|
|
18299
|
+
const value = result.value;
|
|
18300
|
+
if (value !== null && typeof value === "object" && "kind" in value && value.kind === "unbound") {
|
|
18301
|
+
const unbound = value;
|
|
18302
|
+
setDataState({ kind: "unbound", targets: unbound.targets });
|
|
18303
|
+
snapshotRef.current = null;
|
|
18304
|
+
} else {
|
|
18305
|
+
const snap = value;
|
|
18306
|
+
snapshotRef.current = snap;
|
|
18307
|
+
setDataState({ kind: "bound", snapshot: snap });
|
|
18308
|
+
setActiveTargetId((prev) => {
|
|
18309
|
+
if (prev !== null) return prev;
|
|
18310
|
+
const targets = snap.targets;
|
|
18311
|
+
if (targets.length > 0) return targets[0].id;
|
|
18312
|
+
return null;
|
|
18313
|
+
});
|
|
18314
|
+
}
|
|
18315
|
+
setRequestState("idle");
|
|
18316
|
+
} else {
|
|
18317
|
+
if (result.code === "rpc_endpoint_missing") {
|
|
18318
|
+
pollingPausedRef.current = true;
|
|
18319
|
+
backoffRef.current = 0;
|
|
18320
|
+
if (isPerf) console.log(`${RPC_PREFIX} id=${id} fatal=${result.code} polling-paused`);
|
|
18321
|
+
} else {
|
|
18322
|
+
backoffRef.current = backoffRef.current === 0 ? BACKOFF_BASE : Math.min(backoffRef.current * 2, BACKOFF_MAX);
|
|
18323
|
+
}
|
|
18324
|
+
if (snapshotRef.current !== null) {
|
|
18325
|
+
setDataState({ kind: "error", error: result.error ?? "Unknown error", code: result.code });
|
|
18326
|
+
setRequestState("idle");
|
|
18327
|
+
} else {
|
|
18328
|
+
setDataState({ kind: "error", error: result.error ?? "Unknown error", code: result.code });
|
|
18329
|
+
setRequestState("idle");
|
|
18330
|
+
}
|
|
18331
|
+
}
|
|
18332
|
+
} catch (err) {
|
|
18333
|
+
if (pendingRef.current?.id !== id) {
|
|
18334
|
+
if (isPerf) console.log(`${RPC_PREFIX} id=${id} stale-error (current=${pendingRef.current?.id})`);
|
|
18335
|
+
return;
|
|
18336
|
+
}
|
|
18337
|
+
if (!mountedRef.current) return;
|
|
18338
|
+
const errMsg2 = err instanceof Error ? err.message : String(err);
|
|
18339
|
+
if (isPerf) console.log(`${RPC_PREFIX} id=${id} error=${errMsg2}`);
|
|
18340
|
+
if (errMsg2.includes("404") || errMsg2.includes("transport")) {
|
|
18341
|
+
pollingPausedRef.current = true;
|
|
18342
|
+
backoffRef.current = 0;
|
|
18343
|
+
} else {
|
|
18344
|
+
backoffRef.current = backoffRef.current === 0 ? BACKOFF_BASE : Math.min(backoffRef.current * 2, BACKOFF_MAX);
|
|
18345
|
+
}
|
|
18346
|
+
if (snapshotRef.current !== null) {
|
|
18347
|
+
setDataState({ kind: "error", error: errMsg2 });
|
|
18348
|
+
setRequestState("idle");
|
|
18349
|
+
} else {
|
|
18350
|
+
setDataState({ kind: "error", error: errMsg2 });
|
|
18351
|
+
setRequestState("idle");
|
|
18352
|
+
}
|
|
18353
|
+
} finally {
|
|
18354
|
+
if (pendingRef.current?.id === id) {
|
|
18355
|
+
pendingRef.current = null;
|
|
18356
|
+
firstLoadDone.current = true;
|
|
18357
|
+
}
|
|
18358
|
+
}
|
|
18359
|
+
}, [pentesterCommand, sessionId, switchTime]);
|
|
18360
|
+
(0, import_react18.useEffect)(() => {
|
|
18361
|
+
mountedRef.current = true;
|
|
18362
|
+
pollingPausedRef.current = false;
|
|
18363
|
+
backoffRef.current = 0;
|
|
18364
|
+
loadSnapshot("startup");
|
|
18365
|
+
const scheduleNext = () => {
|
|
18366
|
+
if (!mountedRef.current) return;
|
|
18367
|
+
if (pollingPausedRef.current) {
|
|
18368
|
+
if (isPerf) console.log(`${RPC_PREFIX} polling-paused endpoint-missing`);
|
|
18369
|
+
return;
|
|
18370
|
+
}
|
|
18371
|
+
const delay = backoffRef.current > 0 ? Math.min(backoffRef.current, BACKOFF_MAX) : CORE_REFRESH_MS;
|
|
18372
|
+
timerRef.current = setTimeout(async () => {
|
|
18373
|
+
if (!mountedRef.current) return;
|
|
18374
|
+
if (document.visibilityState !== "visible") {
|
|
18375
|
+
scheduleNext();
|
|
18376
|
+
return;
|
|
18377
|
+
}
|
|
18378
|
+
await loadSnapshot("poll");
|
|
18379
|
+
scheduleNext();
|
|
18380
|
+
}, delay);
|
|
18381
|
+
};
|
|
18382
|
+
scheduleNext();
|
|
18383
|
+
return () => {
|
|
18384
|
+
mountedRef.current = false;
|
|
18385
|
+
if (timerRef.current !== null) clearTimeout(timerRef.current);
|
|
18386
|
+
};
|
|
18387
|
+
}, [loadSnapshot]);
|
|
18388
|
+
(0, import_react18.useEffect)(() => {
|
|
18389
|
+
const onVisible = () => {
|
|
18390
|
+
if (document.visibilityState === "visible" && mountedRef.current && firstLoadDone.current && !pollingPausedRef.current) {
|
|
18391
|
+
loadSnapshot("visible");
|
|
18392
|
+
}
|
|
18393
|
+
};
|
|
18394
|
+
document.addEventListener("visibilitychange", onVisible);
|
|
18395
|
+
return () => document.removeEventListener("visibilitychange", onVisible);
|
|
18396
|
+
}, [loadSnapshot]);
|
|
18397
|
+
const navigate = (0, import_react18.useCallback)((targetTab, focus) => {
|
|
18398
|
+
setTab(targetTab);
|
|
18399
|
+
if (focus !== void 0) setNavFocus(focus);
|
|
18400
|
+
}, []);
|
|
18401
|
+
const openSession = (0, import_react18.useCallback)((id) => {
|
|
18402
|
+
sessions?.open(id);
|
|
18403
|
+
}, [sessions]);
|
|
18404
|
+
const isInitialLoading = dataState.kind === "initial" && requestState === "pending";
|
|
18405
|
+
const isRetrying = requestState === "retrying";
|
|
18406
|
+
const errorMsg = dataState.kind === "error" ? dataState.error : null;
|
|
18407
|
+
const errorCode = dataState.kind === "error" ? dataState.code : null;
|
|
18408
|
+
const hasSnapshot = snapshot !== null;
|
|
18409
|
+
return import_react18.default.createElement(
|
|
18410
|
+
"div",
|
|
18411
|
+
{
|
|
18412
|
+
"data-testid": "pentester-view",
|
|
18413
|
+
"data-pentester-view": "",
|
|
18414
|
+
style: {
|
|
18415
|
+
display: "flex",
|
|
18416
|
+
flexDirection: "column",
|
|
18417
|
+
width: "100%",
|
|
18418
|
+
minHeight: "100%",
|
|
18419
|
+
overflow: "visible"
|
|
18420
|
+
}
|
|
18421
|
+
},
|
|
18422
|
+
// ── Layer 1: TabsBar (sticky) ────────────────────────────────────────
|
|
18423
|
+
import_react18.default.createElement(
|
|
18424
|
+
"div",
|
|
18425
|
+
{
|
|
18426
|
+
"data-testid": "pentester-tabs-bar",
|
|
18427
|
+
style: {
|
|
18428
|
+
flex: "none",
|
|
18429
|
+
position: "sticky",
|
|
18430
|
+
top: 0,
|
|
18431
|
+
zIndex: 29,
|
|
18432
|
+
background: "var(--dsw-alias-bg-base)",
|
|
18433
|
+
borderBottom: "1px solid var(--dsw-alias-border-l2)",
|
|
18434
|
+
padding: "10px 16px 0 16px",
|
|
18435
|
+
minHeight: "44px",
|
|
18436
|
+
boxSizing: "border-box"
|
|
18437
|
+
}
|
|
18438
|
+
},
|
|
18439
|
+
import_react18.default.createElement(PentesterTabBar, {
|
|
18440
|
+
items: PENTESTER_TABS.map((t) => ({
|
|
18441
|
+
id: t.id,
|
|
18442
|
+
label: t.id.charAt(0).toUpperCase() + t.id.slice(1)
|
|
18443
|
+
})),
|
|
18444
|
+
active: tab,
|
|
18445
|
+
onSelect: (id) => setTab(id)
|
|
18446
|
+
})
|
|
18447
|
+
),
|
|
18448
|
+
// ── Refresh error banner ──────────────────────────────────────────────
|
|
18449
|
+
// Stale-while-revalidate: error + last good snapshot → show banner
|
|
18450
|
+
errorMsg !== null && hasSnapshot && import_react18.default.createElement(
|
|
18451
|
+
"div",
|
|
18452
|
+
{
|
|
18453
|
+
style: {
|
|
18454
|
+
flex: "none",
|
|
18455
|
+
padding: "6px 16px",
|
|
18456
|
+
background: "var(--dsw-bg-status-error, rgba(220, 38, 38, 0.1))",
|
|
18457
|
+
color: "var(--dsw-status-error, #dc2626)",
|
|
18458
|
+
fontSize: "12px",
|
|
18459
|
+
display: "flex",
|
|
18460
|
+
justifyContent: "space-between",
|
|
18461
|
+
alignItems: "center"
|
|
18462
|
+
}
|
|
18463
|
+
},
|
|
18464
|
+
import_react18.default.createElement("span", null, errorMsg),
|
|
18465
|
+
import_react18.default.createElement("button", {
|
|
18466
|
+
onClick: () => {
|
|
18467
|
+
if (pendingRef.current === null) {
|
|
18468
|
+
void loadSnapshot("retry");
|
|
18469
|
+
}
|
|
18470
|
+
},
|
|
18471
|
+
disabled: pendingRef.current !== null,
|
|
18472
|
+
style: {
|
|
18473
|
+
padding: "2px 8px",
|
|
18474
|
+
border: "none",
|
|
18475
|
+
borderRadius: "4px",
|
|
18476
|
+
background: "transparent",
|
|
18477
|
+
color: "var(--dsw-status-error, #dc2626)",
|
|
18478
|
+
cursor: pendingRef.current !== null ? "not-allowed" : "pointer",
|
|
18479
|
+
fontSize: "12px",
|
|
18480
|
+
textDecoration: "underline",
|
|
18481
|
+
opacity: pendingRef.current !== null ? 0.5 : 1
|
|
18482
|
+
}
|
|
18483
|
+
}, isRetrying ? "Retrying\u2026" : "Retry")
|
|
18484
|
+
),
|
|
18485
|
+
// ── Body: flex 1, no scroll ──────────────────────────────────────────
|
|
18486
|
+
import_react18.default.createElement(
|
|
18487
|
+
"div",
|
|
18488
|
+
{
|
|
18489
|
+
style: {
|
|
18490
|
+
flex: 1,
|
|
18491
|
+
minHeight: 0,
|
|
18492
|
+
overflow: "visible",
|
|
18493
|
+
padding: "16px"
|
|
18494
|
+
}
|
|
18495
|
+
},
|
|
18496
|
+
// Initial loading: shell is already visible, show skeleton
|
|
18497
|
+
isInitialLoading && import_react18.default.createElement("div", {
|
|
18498
|
+
style: { padding: "24px", color: "var(--dsw-alias-label-tertiary)", fontSize: "14px" }
|
|
18499
|
+
}, "Loading..."),
|
|
18500
|
+
// Error state (no last good snapshot)
|
|
18501
|
+
dataState.kind === "error" && !hasSnapshot && import_react18.default.createElement(
|
|
18502
|
+
"div",
|
|
18503
|
+
{ style: { padding: "24px" } },
|
|
18504
|
+
import_react18.default.createElement("div", {
|
|
18505
|
+
style: { color: "var(--dsw-status-error)", marginBottom: "12px", fontSize: "14px" }
|
|
18506
|
+
}, errorMsg ?? "Unknown error"),
|
|
18507
|
+
import_react18.default.createElement("button", {
|
|
18508
|
+
onClick: () => {
|
|
18509
|
+
if (pendingRef.current === null) {
|
|
18510
|
+
void loadSnapshot("retry");
|
|
18511
|
+
}
|
|
18512
|
+
},
|
|
18513
|
+
disabled: pendingRef.current !== null,
|
|
18514
|
+
style: {
|
|
18515
|
+
padding: "6px 16px",
|
|
18516
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
18517
|
+
borderRadius: "6px",
|
|
18518
|
+
background: "var(--dsw-alias-interactive-bg-hover)",
|
|
18519
|
+
color: "var(--dsw-alias-label-primary)",
|
|
18520
|
+
cursor: pendingRef.current !== null ? "not-allowed" : "pointer",
|
|
18521
|
+
fontSize: "13px",
|
|
18522
|
+
opacity: pendingRef.current !== null ? 0.5 : 1
|
|
18523
|
+
}
|
|
18524
|
+
}, isRetrying ? "Retrying\u2026" : "Retry")
|
|
18525
|
+
),
|
|
18526
|
+
// Unbound state
|
|
18527
|
+
dataState.kind === "unbound" && import_react18.default.createElement(
|
|
18528
|
+
"div",
|
|
18529
|
+
{ style: { padding: "24px" } },
|
|
18530
|
+
import_react18.default.createElement("div", {
|
|
18531
|
+
style: { fontSize: "14px", color: "var(--dsw-alias-label-tertiary)", marginBottom: "16px" }
|
|
18532
|
+
}, "This session is not bound to a pentest target."),
|
|
18533
|
+
dataState.targets.length > 0 && import_react18.default.createElement(
|
|
18534
|
+
"div",
|
|
18535
|
+
{ style: { marginBottom: "12px" } },
|
|
18536
|
+
import_react18.default.createElement("div", {
|
|
18537
|
+
style: { fontSize: "13px", fontWeight: 600, marginBottom: "8px", color: "var(--dsw-alias-label-primary)" }
|
|
18538
|
+
}, "Existing targets"),
|
|
18539
|
+
...dataState.targets.map(
|
|
18540
|
+
(t) => import_react18.default.createElement(
|
|
18541
|
+
"div",
|
|
18542
|
+
{
|
|
18543
|
+
key: t.id,
|
|
18544
|
+
style: {
|
|
18545
|
+
display: "flex",
|
|
18546
|
+
alignItems: "center",
|
|
18547
|
+
justifyContent: "space-between",
|
|
18548
|
+
padding: "8px 12px",
|
|
18549
|
+
marginBottom: "4px",
|
|
18550
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
18551
|
+
borderRadius: "6px",
|
|
18552
|
+
fontSize: "13px"
|
|
18553
|
+
}
|
|
18554
|
+
},
|
|
18555
|
+
import_react18.default.createElement(
|
|
18556
|
+
"div",
|
|
18557
|
+
null,
|
|
18558
|
+
import_react18.default.createElement("div", { style: { fontWeight: 500 } }, t.target),
|
|
18559
|
+
import_react18.default.createElement(
|
|
18560
|
+
"div",
|
|
18561
|
+
{ style: { color: "var(--dsw-alias-label-tertiary)", fontSize: "12px" } },
|
|
18562
|
+
`${t.currentStage ?? "unknown"} \xB7 ${t.status}`
|
|
18563
|
+
)
|
|
18564
|
+
),
|
|
18565
|
+
t.rootSessionId !== null && import_react18.default.createElement("button", {
|
|
18566
|
+
onClick: () => sessions?.open(t.rootSessionId),
|
|
18567
|
+
style: {
|
|
18568
|
+
padding: "4px 12px",
|
|
18569
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
18570
|
+
borderRadius: "4px",
|
|
18571
|
+
background: "var(--dsw-alias-interactive-bg-hover)",
|
|
18572
|
+
color: "var(--dsw-alias-label-primary)",
|
|
18573
|
+
cursor: "pointer",
|
|
18574
|
+
fontSize: "12px"
|
|
18575
|
+
}
|
|
18576
|
+
}, "Open Session")
|
|
18577
|
+
)
|
|
18578
|
+
)
|
|
18579
|
+
),
|
|
18580
|
+
import_react18.default.createElement("button", {
|
|
18581
|
+
onClick: () => {
|
|
18582
|
+
if (pendingRef.current === null) {
|
|
18583
|
+
void loadSnapshot("refresh");
|
|
18584
|
+
}
|
|
18585
|
+
},
|
|
18586
|
+
disabled: pendingRef.current !== null,
|
|
18587
|
+
style: {
|
|
18588
|
+
padding: "6px 16px",
|
|
18589
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
18590
|
+
borderRadius: "6px",
|
|
18591
|
+
background: "var(--dsw-alias-interactive-bg-hover)",
|
|
18592
|
+
color: "var(--dsw-alias-label-primary)",
|
|
18593
|
+
cursor: pendingRef.current !== null ? "not-allowed" : "pointer",
|
|
18594
|
+
fontSize: "13px",
|
|
18595
|
+
marginTop: "8px",
|
|
18596
|
+
opacity: pendingRef.current !== null ? 0.5 : 1
|
|
18597
|
+
}
|
|
18598
|
+
}, "Refresh")
|
|
18599
|
+
),
|
|
18600
|
+
// Bound content
|
|
18601
|
+
dataState.kind === "bound" && import_react18.default.createElement(
|
|
18602
|
+
import_react18.default.Fragment,
|
|
18603
|
+
null,
|
|
18604
|
+
tab === "overview" && import_react18.default.createElement(OverviewView, {
|
|
18605
|
+
snapshot,
|
|
18606
|
+
liveActivity,
|
|
18607
|
+
onNavigate: navigate,
|
|
18608
|
+
onOpenSession: openSession
|
|
18609
|
+
}),
|
|
18610
|
+
tab === "stages" && import_react18.default.createElement(StagesView, {
|
|
18611
|
+
snapshot,
|
|
18612
|
+
liveActivity,
|
|
18613
|
+
onNavigate: navigate,
|
|
18614
|
+
onOpenSession: openSession,
|
|
18615
|
+
focusStage: navFocus.stage
|
|
18616
|
+
}),
|
|
18617
|
+
tab === "findings" && import_react18.default.createElement(FindingsView, {
|
|
18618
|
+
snapshot,
|
|
18619
|
+
command: pentesterCommand,
|
|
18620
|
+
sessionId,
|
|
18621
|
+
onNavigate: navigate,
|
|
18622
|
+
focusFinding: navFocus.finding
|
|
18623
|
+
}),
|
|
18624
|
+
tab === "trace" && import_react18.default.createElement(TraceView, {
|
|
18625
|
+
snapshot,
|
|
18626
|
+
liveActivity,
|
|
18627
|
+
onNavigate: navigate,
|
|
18628
|
+
onOpenSession: openSession,
|
|
18629
|
+
activeTargetId
|
|
18630
|
+
}),
|
|
18631
|
+
tab === "output" && import_react18.default.createElement(OutputView, {
|
|
18632
|
+
snapshot,
|
|
18633
|
+
command: pentesterCommand,
|
|
18634
|
+
sessionId,
|
|
18635
|
+
focusOutput: navFocus.output
|
|
18636
|
+
})
|
|
18637
|
+
)
|
|
18638
|
+
)
|
|
18639
|
+
);
|
|
18640
|
+
}
|
|
18641
|
+
|
|
18642
|
+
// src/ui/client/pentester/locales.ts
|
|
18643
|
+
var NS = "dsh-pentester-view";
|
|
18644
|
+
var zh = {
|
|
18645
|
+
"view.overview": "\u6982\u89C8",
|
|
18646
|
+
"view.stages": "\u9636\u6BB5",
|
|
18647
|
+
"view.findings": "\u6F0F\u6D1E",
|
|
18648
|
+
"view.trace": "\u8F68\u8FF9",
|
|
18649
|
+
"view.output": "\u8F93\u51FA",
|
|
18650
|
+
"status.completed": "\u5DF2\u5B8C\u6210",
|
|
18651
|
+
"status.active": "\u6D3B\u8DC3",
|
|
18652
|
+
"status.reviewing": "\u5F85\u5BA1\u6279",
|
|
18653
|
+
"status.pending": "\u7B49\u5F85\u4E2D",
|
|
18654
|
+
"status.blocked": "\u963B\u585E",
|
|
18655
|
+
"status.failed": "\u5931\u8D25",
|
|
18656
|
+
"overview.noRun": "\u5C1A\u65E0\u6E17\u900F\u6D4B\u8BD5\u8FD0\u884C\u3002\n\u5728 Chat \u4E2D\u5B8C\u6210 Pre-engagement \u5F00\u59CB\u3002",
|
|
18657
|
+
"overview.stats.assets": "\u8D44\u4EA7",
|
|
18658
|
+
"overview.stats.findings": "\u6F0F\u6D1E",
|
|
18659
|
+
"overview.stats.delegations": "\u59D4\u6258",
|
|
18660
|
+
"overview.currentWork": "\u5F53\u524D\u5DE5\u4F5C",
|
|
18661
|
+
"overview.stage": "\u9636\u6BB5",
|
|
18662
|
+
"stages.title": "PTES \u9636\u6BB5",
|
|
18663
|
+
"stages.noDelegations": "\u6682\u65E0\u59D4\u6258",
|
|
18664
|
+
"findings.empty": "\u6682\u672A\u53D1\u73B0\u6F0F\u6D1E",
|
|
18665
|
+
"findings.severity.critical": "\u4E25\u91CD",
|
|
18666
|
+
"findings.severity.high": "\u9AD8\u5371",
|
|
18667
|
+
"findings.severity.medium": "\u4E2D\u5371",
|
|
18668
|
+
"findings.severity.low": "\u4F4E\u5371",
|
|
18669
|
+
"findings.severity.info": "\u4FE1\u606F",
|
|
18670
|
+
"trace.empty": "\u6682\u65E0\u6267\u884C\u8F68\u8FF9",
|
|
18671
|
+
"output.empty": "\u6682\u65E0\u8F93\u51FA\u6587\u4EF6",
|
|
18672
|
+
"output.filter.all": "\u5168\u90E8",
|
|
18673
|
+
"output.filter.reports": "\u62A5\u544A",
|
|
18674
|
+
"output.filter.evidence": "\u8BC1\u636E",
|
|
18675
|
+
"output.filter.artifacts": "\u4EA7\u7269",
|
|
18676
|
+
"output.preview.unavailable": "\u65E0\u6CD5\u9884\u89C8",
|
|
18677
|
+
"output.preview.tooLarge": "\u6587\u4EF6\u8FC7\u5927",
|
|
18678
|
+
"error.load": "\u65E0\u6CD5\u52A0\u8F7D Pentester \u72B6\u6001",
|
|
18679
|
+
"error.retry": "\u91CD\u8BD5",
|
|
18680
|
+
"loading": "\u52A0\u8F7D\u4E2D..."
|
|
18681
|
+
};
|
|
18682
|
+
var en = {
|
|
18683
|
+
"view.overview": "Overview",
|
|
18684
|
+
"view.stages": "Stages",
|
|
18685
|
+
"view.findings": "Findings",
|
|
18686
|
+
"view.trace": "Trace",
|
|
18687
|
+
"view.output": "Output",
|
|
18688
|
+
"status.completed": "Completed",
|
|
18689
|
+
"status.active": "Active",
|
|
18690
|
+
"status.reviewing": "Pending approval",
|
|
18691
|
+
"status.pending": "Pending",
|
|
18692
|
+
"status.blocked": "Blocked",
|
|
18693
|
+
"status.failed": "Failed",
|
|
18694
|
+
"overview.noRun": "No pentest run yet.\nStart by completing Pre-engagement in Chat.",
|
|
18695
|
+
"overview.stats.assets": "Assets",
|
|
18696
|
+
"overview.stats.findings": "Findings",
|
|
18697
|
+
"overview.stats.delegations": "Delegations",
|
|
18698
|
+
"overview.currentWork": "Current Work",
|
|
18699
|
+
"overview.stage": "Stage",
|
|
18700
|
+
"stages.title": "PTES Stages",
|
|
18701
|
+
"stages.noDelegations": "No delegations",
|
|
18702
|
+
"findings.empty": "No findings yet",
|
|
18703
|
+
"findings.severity.critical": "Critical",
|
|
18704
|
+
"findings.severity.high": "High",
|
|
18705
|
+
"findings.severity.medium": "Medium",
|
|
18706
|
+
"findings.severity.low": "Low",
|
|
18707
|
+
"findings.severity.info": "Info",
|
|
18708
|
+
"trace.empty": "No execution trace yet",
|
|
18709
|
+
"output.empty": "No output files",
|
|
18710
|
+
"output.filter.all": "All",
|
|
18711
|
+
"output.filter.reports": "Reports",
|
|
18712
|
+
"output.filter.evidence": "Evidence",
|
|
18713
|
+
"output.filter.artifacts": "Artifacts",
|
|
18714
|
+
"output.preview.unavailable": "Preview unavailable",
|
|
18715
|
+
"output.preview.tooLarge": "File too large",
|
|
18716
|
+
"error.load": "Unable to load Pentester state",
|
|
18717
|
+
"error.retry": "Retry",
|
|
18718
|
+
"loading": "Loading..."
|
|
18719
|
+
};
|
|
18720
|
+
|
|
18721
|
+
// src/build-id.ts
|
|
18722
|
+
var BUILD_ID = "2.2.0+478786d";
|
|
18723
|
+
|
|
18724
|
+
// src/ui/client/plugin.tsx
|
|
18725
|
+
var SESSION_SWITCH_PERF = "[SESSION_SWITCH_PERF]";
|
|
18726
|
+
var isSessionPerf = typeof window !== "undefined" && window.__DSH_SESSION_PERF === true;
|
|
18727
|
+
var sessionSwitchTimes = /* @__PURE__ */ new Map();
|
|
18728
|
+
var lastCurrentId;
|
|
18729
|
+
var pentesterEndpointState2 = "unknown";
|
|
18730
|
+
function getPentesterEndpointState2() {
|
|
18731
|
+
return pentesterEndpointState2;
|
|
18732
|
+
}
|
|
18733
|
+
function setPentesterEndpointState(state) {
|
|
18734
|
+
pentesterEndpointState2 = state;
|
|
18735
|
+
}
|
|
18736
|
+
var buildMismatch = null;
|
|
18737
|
+
function getBuildMismatch() {
|
|
18738
|
+
return buildMismatch;
|
|
18739
|
+
}
|
|
18740
|
+
var remoteMounted = false;
|
|
18741
|
+
var remoteMountError = null;
|
|
18742
|
+
function isRemoteMounted() {
|
|
18743
|
+
return remoteMounted;
|
|
18744
|
+
}
|
|
18745
|
+
function getRemoteMountError() {
|
|
18746
|
+
return remoteMountError;
|
|
18747
|
+
}
|
|
18748
|
+
var inject = ["slots", "remote", "sessions", "locale"];
|
|
18749
|
+
function apply(ctx) {
|
|
18750
|
+
console.log(`[dsh-pentester] client build=${BUILD_ID}`);
|
|
18751
|
+
ctx.effect(() => ctx.locale.register(NS, { zh, en }), "pentester: locale");
|
|
18752
|
+
ctx.effect(() => {
|
|
18753
|
+
let disposeMount;
|
|
18754
|
+
let disposed = false;
|
|
18755
|
+
void ctx.remote.$mount({
|
|
18756
|
+
package: "dsh-pentester",
|
|
18757
|
+
descriptors: [
|
|
18758
|
+
...PENTESTER_REMOTE.descriptors,
|
|
18759
|
+
...PENTESTER_VIEW_REMOTE.descriptors
|
|
18760
|
+
]
|
|
18761
|
+
}).then((dispose) => {
|
|
18762
|
+
if (disposed) {
|
|
18763
|
+
dispose();
|
|
18764
|
+
return;
|
|
18765
|
+
}
|
|
18766
|
+
disposeMount = dispose;
|
|
18767
|
+
remoteMounted = true;
|
|
18768
|
+
remoteMountError = null;
|
|
18769
|
+
if (pentesterEndpointState2 === "missing") {
|
|
18770
|
+
pentesterEndpointState2 = "unknown";
|
|
18771
|
+
}
|
|
18772
|
+
if (isSessionPerf) {
|
|
18773
|
+
console.log("[dsh-pentester] client remote contribution mounted (pentesterDocker + pentesterView)");
|
|
18774
|
+
}
|
|
18775
|
+
void (async () => {
|
|
18776
|
+
try {
|
|
18777
|
+
const face = ctx.reflect.get("remote.pentesterView");
|
|
18778
|
+
if (face === void 0) return;
|
|
18779
|
+
const result = await face.command({ kind: "health" });
|
|
18780
|
+
if (result.ok && result.value !== null && typeof result.value === "object" && "ok" in result.value) {
|
|
18781
|
+
const inner = result.value.value;
|
|
18782
|
+
if (inner?.ready === true && typeof inner.buildId === "string") {
|
|
18783
|
+
if (inner.buildId === BUILD_ID) {
|
|
18784
|
+
buildMismatch = null;
|
|
18785
|
+
console.log(`[dsh-pentester] health check passed host=${inner.buildId} client=${BUILD_ID}`);
|
|
18786
|
+
} else {
|
|
18787
|
+
buildMismatch = { host: inner.buildId, client: BUILD_ID };
|
|
18788
|
+
console.error(`[dsh-pentester] BUILD MISMATCH host=${inner.buildId} client=${BUILD_ID}`);
|
|
18789
|
+
}
|
|
18790
|
+
}
|
|
18791
|
+
}
|
|
18792
|
+
} catch (error51) {
|
|
18793
|
+
console.warn("[dsh-pentester] build health check unavailable", error51);
|
|
18794
|
+
}
|
|
18795
|
+
})();
|
|
18796
|
+
}).catch((error51) => {
|
|
18797
|
+
remoteMounted = false;
|
|
18798
|
+
remoteMountError = error51 instanceof Error ? error51.message : String(error51);
|
|
18799
|
+
console.error("[dsh-pentester] client remote contribution mount failed:", remoteMountError);
|
|
18800
|
+
});
|
|
18801
|
+
return () => {
|
|
18802
|
+
disposed = true;
|
|
18803
|
+
disposeMount?.();
|
|
18804
|
+
};
|
|
18805
|
+
}, "pentester: remotes");
|
|
18806
|
+
ctx.slots.inject(
|
|
18807
|
+
"settings.section",
|
|
18808
|
+
() => ctx.slots.register(
|
|
18809
|
+
{
|
|
18810
|
+
name: "settings.section",
|
|
18811
|
+
id: "pentester-docker-engine",
|
|
18812
|
+
order: 90,
|
|
18813
|
+
label: () => "Pentester",
|
|
18814
|
+
inject: () => {
|
|
18815
|
+
const face = ctx.reflect.get("remote.pentesterDocker");
|
|
18816
|
+
return { command: (input2) => dockerCommand(face, input2) };
|
|
18817
|
+
}
|
|
18818
|
+
},
|
|
18819
|
+
(props) => import_react19.default.createElement(PentesterConfigSection, { ...props })
|
|
18820
|
+
)
|
|
18821
|
+
);
|
|
18822
|
+
ctx.slots.inject("conversation.view", () => {
|
|
18823
|
+
let disposeEntry;
|
|
18824
|
+
const sync = () => {
|
|
18825
|
+
const snapshot = ctx.sessions.list.getSnapshot();
|
|
18826
|
+
const current = snapshot.current;
|
|
18827
|
+
if (isSessionPerf && current !== lastCurrentId) {
|
|
18828
|
+
const now = performance.now();
|
|
18829
|
+
const prev = lastCurrentId;
|
|
18830
|
+
lastCurrentId = current;
|
|
18831
|
+
if (current !== void 0 && !sessionSwitchTimes.has(current)) {
|
|
18832
|
+
sessionSwitchTimes.set(current, now);
|
|
18833
|
+
}
|
|
18834
|
+
console.log(
|
|
18835
|
+
`%c${SESSION_SWITCH_PERF}%c current-changed %c${String(prev).slice(0, 8)}%c \u2192 %c${String(current).slice(0, 8)}%c +0ms`,
|
|
18836
|
+
"color:#f59e0b;font-weight:bold",
|
|
18837
|
+
"",
|
|
18838
|
+
"color:#6b7280",
|
|
18839
|
+
"",
|
|
18840
|
+
"color:#3b82f6;font-weight:bold",
|
|
18841
|
+
""
|
|
18842
|
+
);
|
|
18843
|
+
}
|
|
18844
|
+
if (current !== void 0 && isPentesterRootSession(snapshot, current)) {
|
|
18845
|
+
if (disposeEntry === void 0) {
|
|
18846
|
+
disposeEntry = ctx.slots.register(
|
|
18847
|
+
{
|
|
18848
|
+
name: "conversation.view",
|
|
18849
|
+
id: "pentester",
|
|
18850
|
+
order: 20,
|
|
18851
|
+
label: "Pentester",
|
|
18852
|
+
inject: (sessionId) => {
|
|
18853
|
+
return {
|
|
18854
|
+
sessionId,
|
|
18855
|
+
reflect: ctx.reflect,
|
|
18856
|
+
sessions: {
|
|
18857
|
+
open: (id) => ctx.sessions.open(id),
|
|
18858
|
+
list: ctx.sessions.list
|
|
18859
|
+
},
|
|
18860
|
+
sessionSwitchTimes,
|
|
18861
|
+
sessionPerf: isSessionPerf
|
|
18862
|
+
};
|
|
18863
|
+
}
|
|
18864
|
+
},
|
|
18865
|
+
(props) => {
|
|
18866
|
+
return import_react19.default.createElement(PentesterView, {
|
|
18867
|
+
sessionId: props.sessionId,
|
|
18868
|
+
reflect: props.reflect,
|
|
18869
|
+
sessions: props.sessions,
|
|
18870
|
+
useSession: props.useSession,
|
|
18871
|
+
sessionSwitchTimes: props.sessionSwitchTimes,
|
|
18872
|
+
sessionPerf: props.sessionPerf
|
|
18873
|
+
});
|
|
18874
|
+
}
|
|
15218
18875
|
);
|
|
15219
18876
|
}
|
|
15220
18877
|
} else {
|