@youweichen/pi-harness 1.0.0 → 1.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/LICENSE +1 -1
- package/README.md +14 -0
- package/dist/server/agent-service.js +87 -7
- package/dist/server/control-socket.js +1 -0
- package/dist/server/document-conversion/extension.js +164 -0
- package/dist/server/document-conversion/python/bridge.py +424 -0
- package/dist/server/document-conversion/python/fixtures.py +201 -0
- package/dist/server/document-conversion/python/native_code.py +149 -0
- package/dist/server/document-conversion/runtime.js +321 -0
- package/dist/server/document-conversion/service.js +340 -0
- package/dist/server/document-conversion/settings.js +63 -0
- package/dist/server/extensions-worker.js +3 -2
- package/dist/server/files-service.js +8 -16
- package/dist/server/index.js +9 -3
- package/dist/server/native-tools.js +4 -0
- package/dist/server/node-agent.js +262 -0
- package/dist/server/node-workbench.js +62 -1
- package/dist/server/okf/extension.js +118 -0
- package/dist/server/okf/markdown-sources.js +58 -0
- package/dist/server/okf/service.js +713 -0
- package/dist/server/okf/storage.js +165 -0
- package/dist/server/okf/types.js +1 -0
- package/dist/server/plan/extension.js +4 -4
- package/dist/server/plan/state.js +11 -1
- package/dist/server/slash-commands.js +6 -0
- package/dist/server/wiki-links.js +24 -0
- package/dist/server/wiki-routes.js +8 -1
- package/dist/server/wiki-service.js +50 -5
- package/package.json +2 -2
- package/web/dist/assets/NodeWorkbench-DM6YTS5e.js +3 -0
- package/web/dist/assets/{TerminalPanel-CWh6vw_N.js → TerminalPanel-aFJylyxg.js} +1 -1
- package/web/dist/assets/{abnfDiagram-VCTEODGH-D5Ti0Kpa.js → abnfDiagram-VCTEODGH-CcM7f7oX.js} +1 -1
- package/web/dist/assets/{arc-DKJilLd6.js → arc-Bv7vS5l5.js} +1 -1
- package/web/dist/assets/{architectureDiagram-5GKGNRK7-Cf0w38g-.js → architectureDiagram-5GKGNRK7-Cl8QcB3w.js} +1 -1
- package/web/dist/assets/{blockDiagram-I7D4REHJ-BOTLWLIL.js → blockDiagram-I7D4REHJ-DarS2-4G.js} +1 -1
- package/web/dist/assets/{c4Diagram-7LVT6UL2-CLCCoJs3.js → c4Diagram-7LVT6UL2-BY0_ttBt.js} +1 -1
- package/web/dist/assets/channel-DJGMgVTh.js +1 -0
- package/web/dist/assets/{chunk-2Q5K7J3B-BHjga2Ge.js → chunk-2Q5K7J3B-Cz-zS7_X.js} +1 -1
- package/web/dist/assets/{chunk-5VM5RSS4-CQJ5VfDZ.js → chunk-5VM5RSS4-B7AyqkmI.js} +1 -1
- package/web/dist/assets/{chunk-F27PBJKO-C67qoPJH.js → chunk-F27PBJKO-B7hVduzL.js} +1 -1
- package/web/dist/assets/{chunk-IMKFNOWR-CgXXGwI8.js → chunk-IMKFNOWR-DNGJqYn5.js} +1 -1
- package/web/dist/assets/{chunk-JWPE2WC7-D0RPyByP.js → chunk-JWPE2WC7-B5iJgXvl.js} +1 -1
- package/web/dist/assets/{chunk-POPQ4Y6H-BeYC9s9x.js → chunk-POPQ4Y6H-BvWCkriR.js} +1 -1
- package/web/dist/assets/{chunk-SVP7TREG-eQPl4UBQ.js → chunk-SVP7TREG-BcdlBuOQ.js} +1 -1
- package/web/dist/assets/{chunk-TICWLB2K-DT73arPU.js → chunk-TICWLB2K-B1igqjQd.js} +1 -1
- package/web/dist/assets/{chunk-XXDRQBXY-B4FwQcP-.js → chunk-XXDRQBXY-DKGDquaw.js} +1 -1
- package/web/dist/assets/classDiagram-ZZMXUADV-D9nxgF0M.js +1 -0
- package/web/dist/assets/classDiagram-v2-VYDZK3BY-D9nxgF0M.js +1 -0
- package/web/dist/assets/{cose-bilkent-JH36ORCC-Bnn5nlQN.js → cose-bilkent-JH36ORCC-BU_9nu88.js} +1 -1
- package/web/dist/assets/{cynefin-OW5HDTMX-CStWX9kE.js → cynefin-OW5HDTMX-DY0BDLjc.js} +1 -1
- package/web/dist/assets/{cynefinDiagram-5FMLGOSQ-BMVHC03f.js → cynefinDiagram-5FMLGOSQ-B-zM25Fk.js} +1 -1
- package/web/dist/assets/{dagre-GXQ25YYZ-ClSaO4ml.js → dagre-GXQ25YYZ-BxooDyBc.js} +1 -1
- package/web/dist/assets/{diagram-S7CK7UJ4-C43LL08C.js → diagram-S7CK7UJ4-CCO-8de9.js} +1 -1
- package/web/dist/assets/{diagram-UQ7AKVKN-BPVn_ySW.js → diagram-UQ7AKVKN-CmGsH525.js} +1 -1
- package/web/dist/assets/{diagram-VSXAHHWV-C34FFhRi.js → diagram-VSXAHHWV-CSQJYo3m.js} +1 -1
- package/web/dist/assets/{diagram-VX7I27RA-BRk4bncf.js → diagram-VX7I27RA-CwuJsF66.js} +1 -1
- package/web/dist/assets/{diagram-Z3DM3KII-O5hYPLEd.js → diagram-Z3DM3KII-BF13T_lj.js} +1 -1
- package/web/dist/assets/{ebnfDiagram-PWID7BFC-DVGEBm99.js → ebnfDiagram-PWID7BFC-ZtoDLro6.js} +1 -1
- package/web/dist/assets/{erDiagram-RLTQ6QDP-M-BLpTEV.js → erDiagram-RLTQ6QDP-BBo-qmNH.js} +1 -1
- package/web/dist/assets/{flowDiagram-HODETNUW-BwdLopEx.js → flowDiagram-HODETNUW-DAq9qdP8.js} +1 -1
- package/web/dist/assets/{ganttDiagram-EL5Y4UJY-DYxx2ppA.js → ganttDiagram-EL5Y4UJY-C53eRlMu.js} +1 -1
- package/web/dist/assets/{gitGraphDiagram-WWUBYQGX-DUU0wMYB.js → gitGraphDiagram-WWUBYQGX-BviBMJxj.js} +1 -1
- package/web/dist/assets/index-Bab44wX8.js +276 -0
- package/web/dist/assets/index-C3vDFSEV.css +10 -0
- package/web/dist/assets/{infoDiagram-27XIBGKW-CFaW5C0C.js → infoDiagram-27XIBGKW-DI95SO3j.js} +1 -1
- package/web/dist/assets/{ishikawaDiagram-5VMMS53U--ENP1MTB.js → ishikawaDiagram-5VMMS53U-Cl8IDXwf.js} +1 -1
- package/web/dist/assets/{journeyDiagram-3NMN7TZE-DgnAzSTj.js → journeyDiagram-3NMN7TZE-Co6aJeTZ.js} +1 -1
- package/web/dist/assets/{kanban-definition-UXKFOSKX-_STZN1vU.js → kanban-definition-UXKFOSKX-CknQQpwW.js} +1 -1
- package/web/dist/assets/{layout-CkUClkXt.js → layout-CFtUmtTW.js} +1 -1
- package/web/dist/assets/{linear-sYHiUOxa.js → linear-mRlGUAKy.js} +1 -1
- package/web/dist/assets/{markdown-vNTCZJ6d.js → markdown-BhJ_LInD.js} +1 -1
- package/web/dist/assets/{mermaid.core-B8fKGWsc.js → mermaid.core-CvQEO3ue.js} +5 -5
- package/web/dist/assets/{mindmap-definition-YA3MSWOX-DP0BYOEx.js → mindmap-definition-YA3MSWOX-DpqhsxUl.js} +1 -1
- package/web/dist/assets/{pegDiagram-XKGWAZYB-DkWTmZre.js → pegDiagram-XKGWAZYB-C9eawB5u.js} +1 -1
- package/web/dist/assets/{pieDiagram-E7YTZNPT-DfZgMV4u.js → pieDiagram-E7YTZNPT-8vaqXVD6.js} +1 -1
- package/web/dist/assets/{quadrantDiagram-AXDQQJYC-Djl4TT7q.js → quadrantDiagram-AXDQQJYC-Diwr8ynm.js} +1 -1
- package/web/dist/assets/{railroadDiagram-O6MQD6OU-DbKF6zXn.js → railroadDiagram-O6MQD6OU-krW50Cgi.js} +1 -1
- package/web/dist/assets/{react-BNj3n7jX.js → react-BddUxV-9.js} +1 -1
- package/web/dist/assets/{requirementDiagram-BXWQKSXE-idola5qb.js → requirementDiagram-BXWQKSXE-BPLI6eMG.js} +1 -1
- package/web/dist/assets/{sankeyDiagram-P5KCCOFB-KA0hQOle.js → sankeyDiagram-P5KCCOFB-Bix_elmH.js} +1 -1
- package/web/dist/assets/{sequenceDiagram-WJ2MYXX4-BXSKvxzP.js → sequenceDiagram-WJ2MYXX4-0m0xaUKi.js} +1 -1
- package/web/dist/assets/{sizeCapture-INFHLROL-C_Ewv3S5.js → sizeCapture-INFHLROL-BtzJ_oOR.js} +1 -1
- package/web/dist/assets/{stateDiagram-D77RDMKH-DIf5X8oo.js → stateDiagram-D77RDMKH-Clq5bN0v.js} +1 -1
- package/web/dist/assets/stateDiagram-v2-MP3YSRHH-Bn1oW4gp.js +1 -0
- package/web/dist/assets/{swimlanes-42K2YHIH-Cx_Rc8M2.js → swimlanes-42K2YHIH-11MDGnYA.js} +1 -1
- package/web/dist/assets/swimlanesDiagram-VR7AAH4N-BW96Edgo.js +8 -0
- package/web/dist/assets/{timeline-definition-24CTP7MA-BRrDhWVz.js → timeline-definition-24CTP7MA-BeQfWP95.js} +1 -1
- package/web/dist/assets/{vennDiagram-4TSXK5OY-Ad6G0YwD.js → vennDiagram-4TSXK5OY-BMYkX0KL.js} +1 -1
- package/web/dist/assets/{wardleyDiagram-VM6X3IG4-X6-ENpSi.js → wardleyDiagram-VM6X3IG4-BV3fTUkU.js} +1 -1
- package/web/dist/assets/{xychartDiagram-S5SC5T6Z-DhegNS6d.js → xychartDiagram-S5SC5T6Z-Ct7ezLnc.js} +1 -1
- package/web/dist/index.html +4 -4
- package/web/dist/assets/NodeWorkbench-C4U1PldE.js +0 -3
- package/web/dist/assets/channel-D8Hd7T4P.js +0 -1
- package/web/dist/assets/classDiagram-ZZMXUADV-B_btgYRB.js +0 -1
- package/web/dist/assets/classDiagram-v2-VYDZK3BY-B_btgYRB.js +0 -1
- package/web/dist/assets/index-CI2KfVSr.css +0 -10
- package/web/dist/assets/index-DQcpggFU.js +0 -275
- package/web/dist/assets/stateDiagram-v2-MP3YSRHH-DYwe_2mB.js +0 -1
- package/web/dist/assets/swimlanesDiagram-VR7AAH4N-52Y8tgRV.js +0 -8
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, isAbsolute, join, parse, relative, resolve, sep } from "node:path";
|
|
4
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
5
|
+
import { withFileMutationQueue } from "@earendil-works/pi-coding-agent";
|
|
6
|
+
import lockfile from "proper-lockfile";
|
|
7
|
+
import { documentDataDir } from "../document-conversion/settings.js";
|
|
8
|
+
const queues = new Map();
|
|
9
|
+
export const digest = (content) => createHash("sha256").update(content).digest("hex");
|
|
10
|
+
export const jsonText = (value) => JSON.stringify(value, null, 2) + "\n";
|
|
11
|
+
export function within(root, path) {
|
|
12
|
+
const part = relative(root, path);
|
|
13
|
+
return part === "" || (!part.startsWith(`..${sep}`) && part !== ".." && !isAbsolute(part));
|
|
14
|
+
}
|
|
15
|
+
/** Reject links at every component, including existing output parents. */
|
|
16
|
+
export function assertPlainPath(path) {
|
|
17
|
+
const absolute = resolve(path);
|
|
18
|
+
let current = parse(absolute).root;
|
|
19
|
+
for (const part of absolute.slice(current.length).split(sep).filter(Boolean)) {
|
|
20
|
+
current = join(current, part);
|
|
21
|
+
try {
|
|
22
|
+
if (lstatSync(current).isSymbolicLink())
|
|
23
|
+
throw new Error(`Symbolic links are not allowed: ${current}`);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
if (error.code !== "ENOENT")
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function workspacePath(cwd) {
|
|
32
|
+
const result = realpathSync(cwd);
|
|
33
|
+
if (!lstatSync(result).isDirectory())
|
|
34
|
+
throw new Error("Workspace must be a directory");
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
export function safePath(root, child) {
|
|
38
|
+
const path = resolve(root, child);
|
|
39
|
+
if (!within(root, path) || path === root)
|
|
40
|
+
throw new Error(`Path escapes its managed root: ${child}`);
|
|
41
|
+
assertPlainPath(path);
|
|
42
|
+
return path;
|
|
43
|
+
}
|
|
44
|
+
export function privateDirectory(cwd) {
|
|
45
|
+
// realpath resolves platform-owned aliases such as macOS /var before checking our subtree.
|
|
46
|
+
const configured = documentDataDir();
|
|
47
|
+
mkdirSync(configured, { recursive: true, mode: 0o700 });
|
|
48
|
+
const root = join(realpathSync(configured), "document-jobs", digest(cwd).slice(0, 24));
|
|
49
|
+
assertPlainPath(root);
|
|
50
|
+
mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
51
|
+
return root;
|
|
52
|
+
}
|
|
53
|
+
export function readJson(path, limit = 16 * 1024 * 1024) {
|
|
54
|
+
assertPlainPath(path);
|
|
55
|
+
const stat = lstatSync(path);
|
|
56
|
+
if (!stat.isFile() || stat.size > limit)
|
|
57
|
+
throw new Error(`Invalid or oversized state file: ${path}`);
|
|
58
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
59
|
+
}
|
|
60
|
+
export function fileHash(path) {
|
|
61
|
+
assertPlainPath(path);
|
|
62
|
+
try {
|
|
63
|
+
const stat = lstatSync(path);
|
|
64
|
+
if (!stat.isFile() || stat.size > 256 * 1024 * 1024)
|
|
65
|
+
throw new Error(`Expected a bounded regular file: ${path}`);
|
|
66
|
+
return digest(readFileSync(path));
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (error.code === "ENOENT")
|
|
70
|
+
return null;
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export function atomicWrite(path, contents) {
|
|
75
|
+
assertPlainPath(path);
|
|
76
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
77
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
78
|
+
try {
|
|
79
|
+
writeFileSync(temporary, contents, { encoding: "utf8", flag: "wx", mode: 0o600 });
|
|
80
|
+
assertPlainPath(path);
|
|
81
|
+
renameSync(temporary, path);
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
rmSync(temporary, { force: true });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/** Manifest is the final write. Replay accepts only the old content or our exact new content. */
|
|
88
|
+
export async function recoverTransaction(cwd) {
|
|
89
|
+
const journal = join(privateDirectory(cwd), "transaction.json");
|
|
90
|
+
if (!existsSync(journal))
|
|
91
|
+
return;
|
|
92
|
+
const transaction = readJson(journal, 64 * 1024 * 1024);
|
|
93
|
+
if (transaction.version !== 1 || transaction.cwd !== cwd || !Array.isArray(transaction.writes))
|
|
94
|
+
throw new Error("Invalid ingestion recovery journal");
|
|
95
|
+
if (!within(cwd, transaction.outputDirectory) || cwd === transaction.outputDirectory)
|
|
96
|
+
throw new Error("Recovery output is outside the workspace");
|
|
97
|
+
for (const item of transaction.writes) {
|
|
98
|
+
const path = safePath(transaction.outputDirectory, item.path);
|
|
99
|
+
await withFileMutationQueue(path, async () => {
|
|
100
|
+
const current = fileHash(path);
|
|
101
|
+
if (current === digest(item.contents))
|
|
102
|
+
return;
|
|
103
|
+
if (current !== item.expectedHash)
|
|
104
|
+
throw new Error(`Recovery preserved an externally modified file: ${item.path}. Restore the expected version or resolve this ingestion transaction before retrying.`);
|
|
105
|
+
atomicWrite(path, item.contents);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
rmSync(journal);
|
|
109
|
+
}
|
|
110
|
+
export async function commitTransaction(cwd, writes, outputDirectory = join(cwd, "knowledge")) {
|
|
111
|
+
if (!writes.length)
|
|
112
|
+
return;
|
|
113
|
+
const journal = join(privateDirectory(cwd), "transaction.json");
|
|
114
|
+
if (existsSync(journal))
|
|
115
|
+
throw new Error("An ingestion transaction already needs recovery");
|
|
116
|
+
const contents = jsonText({ version: 1, cwd, outputDirectory, writes });
|
|
117
|
+
if (Buffer.byteLength(contents) > 64 * 1024 * 1024)
|
|
118
|
+
throw new Error("Ingestion transaction exceeds 64 MiB; publish a smaller batch. Previous state was preserved.");
|
|
119
|
+
atomicWrite(journal, contents);
|
|
120
|
+
await recoverTransaction(cwd);
|
|
121
|
+
}
|
|
122
|
+
function waitWithSignal(task, signal) {
|
|
123
|
+
if (!signal)
|
|
124
|
+
return task;
|
|
125
|
+
if (signal.aborted)
|
|
126
|
+
return Promise.reject(signal.reason ?? new Error("Ingestion cancelled"));
|
|
127
|
+
return new Promise((resolve, reject) => {
|
|
128
|
+
const abort = () => reject(signal.reason ?? new Error("Ingestion cancelled"));
|
|
129
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
130
|
+
task.then(value => { signal.removeEventListener("abort", abort); resolve(value); }, error => { signal.removeEventListener("abort", abort); reject(error); });
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
export async function withKnowledgeLock(inputCwd, action, signal) {
|
|
134
|
+
signal?.throwIfAborted();
|
|
135
|
+
const cwd = workspacePath(inputCwd);
|
|
136
|
+
const prior = queues.get(cwd) ?? Promise.resolve();
|
|
137
|
+
const task = prior.catch(() => undefined).then(async () => {
|
|
138
|
+
signal?.throwIfAborted();
|
|
139
|
+
const folder = privateDirectory(cwd);
|
|
140
|
+
let release;
|
|
141
|
+
for (let attempt = 0; !release; attempt++) {
|
|
142
|
+
signal?.throwIfAborted();
|
|
143
|
+
try {
|
|
144
|
+
release = await lockfile.lock(folder, { realpath: false, stale: 120_000, retries: 0 });
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
if (error.code !== "ELOCKED" || attempt >= 40)
|
|
148
|
+
throw error;
|
|
149
|
+
await delay(100, undefined, { signal });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
signal?.throwIfAborted();
|
|
154
|
+
await recoverTransaction(cwd);
|
|
155
|
+
return await action(cwd);
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
await release();
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
queues.set(cwd, task);
|
|
162
|
+
void task.finally(() => { if (queues.get(cwd) === task)
|
|
163
|
+
queues.delete(cwd); }).catch(() => undefined);
|
|
164
|
+
return waitWithSignal(task, signal);
|
|
165
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -6,7 +6,7 @@ export const planParameters = Type.Object({
|
|
|
6
6
|
planId: Type.Optional(string(128)), expectedRevision: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
7
7
|
title: string(80), status: Type.Union([Type.Literal("active"), Type.Literal("completed"), Type.Literal("cancelled"), Type.Literal("failed")]),
|
|
8
8
|
steps: Type.Array(Type.Object({ id: string(64), title: string(80), detail: Type.Optional(string(500)) }), { minItems: 1, maxItems: 16, description: "Required for BOTH create and update, including completion. Send every step with its id, title and detail; never omit unchanged steps." }),
|
|
9
|
-
currentStepId: Type.Union([string(64), Type.Null()]), completedStepIds: Type.Array(string(64), { maxItems: 16, uniqueItems: true }),
|
|
9
|
+
currentStepId: Type.Union([string(64), Type.Null()], { description: "Current step for active plans; terminal statuses clear it automatically." }), completedStepIds: Type.Array(string(64), { maxItems: 16, uniqueItems: true, description: "Completed step IDs. Explicit status completed completes all submitted steps; cancelled/failed preserve this list." }),
|
|
10
10
|
completionCriteria: string(240), changeSummary: Type.Optional(string(240)),
|
|
11
11
|
});
|
|
12
12
|
export function restorePlanContext(messages, branch) {
|
|
@@ -32,7 +32,7 @@ export function restorePlanContext(messages, branch) {
|
|
|
32
32
|
const failedCall = lastPlanResult?.role === "toolResult" && lastPlanResult.isError;
|
|
33
33
|
if (visibleResult && visibleCall && !failedCall)
|
|
34
34
|
return messages;
|
|
35
|
-
const content = `${failedCall ? "Plan call failed; the saved plan below is unchanged. To correct the call, submit the full state including steps, even if unchanged or marking completion. Use the saved planId and expectedRevision. This reminder does not authorize continuing work.\n" : ""}Historical plan background, not authorization to continue. Judge relevance to the current request yourself. For related implementation,
|
|
35
|
+
const content = `${failedCall ? "Plan call failed; the saved plan below is unchanged. To correct the call, submit the full state including steps, even if unchanged or marking completion. Use the saved planId and expectedRevision. This reminder does not authorize continuing work.\n" : ""}Historical plan background, not authorization to continue. Judge relevance to the current request yourself. For related, already authorized implementation, update this plan with its current identity and revision and continue without requesting separate plan approval. For unrelated multi-step work, create a new plan. Ordinary questions do not require a plan. Follow user/skill instructions and waiting requirements; this tool grants no extra permissions.\n${JSON.stringify({ action: "update", planId: snapshot.planId, expectedRevision: snapshot.revision, ...editableState(snapshot) })}`;
|
|
36
36
|
const reminder = { role: "custom", customType: "pi-harness:plan-background", content, display: false, timestamp: 0 };
|
|
37
37
|
const summary = messages.findLastIndex(message => message.role === "compactionSummary");
|
|
38
38
|
const user = messages.findLastIndex(message => message.role === "user");
|
|
@@ -55,11 +55,11 @@ export function restorePlanContext(messages, branch) {
|
|
|
55
55
|
}
|
|
56
56
|
export const createPlanExtension = () => pi => {
|
|
57
57
|
pi.registerTool({
|
|
58
|
-
name: "plan", label: "Plan", description: "Create or update a complete, versioned implementation plan on this session branch. Every call requires title, status, steps, currentStepId, completedStepIds and completionCriteria, even when completing or only changing status. Updates also require the latest planId and expectedRevision. This is full replacement, not a partial patch. Ended plans require create.",
|
|
58
|
+
name: "plan", label: "Plan", description: "Create or update a complete, versioned implementation plan on this session branch. Every call requires title, status, steps, currentStepId, completedStepIds and completionCriteria, even when completing or only changing status. Updates also require the latest planId and expectedRevision. This is full replacement, not a partial patch. Explicit completed status completes all submitted steps and clears currentStepId; cancelled/failed only clear currentStepId. Ended plans require create.",
|
|
59
59
|
exposure: "model-only", executionMode: "sequential", defaultActive: false,
|
|
60
60
|
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
61
61
|
promptSnippet: "Record and update a multi-step implementation plan.",
|
|
62
|
-
promptGuidelines: ["For multi-step implementation, create or update a relevant plan before execution and update the current step before starting it. Submit the full state each time. Wait for create to return its identity before updating. Follow user and skill instructions, including waiting; a plan does not grant authorization. Ordinary questions need no plan. When work is merged into another step or becomes unnecessary, update or remove the superseded step and explain the change instead of leaving a stale pending item. Never mark unfinished work completed. Mark completion explicitly; never infer it from a stopped run."],
|
|
62
|
+
promptGuidelines: ["For multi-step implementation, create or update a relevant plan before execution and update the current step before starting it. For already authorized work, proceed without requesting separate plan approval. Submit the full state each time. Wait for create to return its identity before updating. Follow user and skill instructions, including waiting; a plan does not grant authorization. Ordinary questions need no plan. When work is merged into another step or becomes unnecessary, update or remove the superseded step and explain the change instead of leaving a stale pending item. Never mark unfinished work completed. Mark completion explicitly; never infer it from a stopped run."],
|
|
63
63
|
parameters: planParameters,
|
|
64
64
|
async execute(_id, args, _signal, _update, ctx) {
|
|
65
65
|
const snapshot = transition(args, latestPlan(ctx.sessionManager.getBranch())?.snapshot);
|
|
@@ -56,6 +56,16 @@ export function changesBetween(before, after) {
|
|
|
56
56
|
changes.push({ kind: "status", status: after.status });
|
|
57
57
|
return changes;
|
|
58
58
|
}
|
|
59
|
+
/** Terminal command intent is atomic; persisted snapshots remain strictly validated. */
|
|
60
|
+
function commandState(value) {
|
|
61
|
+
if (!["completed", "cancelled", "failed"].includes(String(value.status)))
|
|
62
|
+
return editableState(value);
|
|
63
|
+
const state = editableState({ ...value, status: "active", currentStepId: null });
|
|
64
|
+
if (value.currentStepId !== null && (typeof value.currentStepId !== "string" || !state.steps.some(step => step.id === value.currentStepId)))
|
|
65
|
+
throw new Error("currentStepId must be null or an existing step ID");
|
|
66
|
+
return { ...state, status: value.status,
|
|
67
|
+
completedStepIds: value.status === "completed" ? state.steps.map(step => step.id) : state.completedStepIds };
|
|
68
|
+
}
|
|
59
69
|
export function transition(value, current, newId = randomUUID) {
|
|
60
70
|
if (!object(value) || !["create", "update"].includes(String(value.action)))
|
|
61
71
|
throw new Error("action must be create or update");
|
|
@@ -64,7 +74,7 @@ export function transition(value, current, newId = randomUUID) {
|
|
|
64
74
|
if (reason)
|
|
65
75
|
throw new Error(`${reason}. ${!current || current.status !== "active" ? "Use create for a new plan." : "Use update with the current planId and expectedRevision and submit the full corrected plan."}\nCurrent editable state: ${current ? JSON.stringify({ action: current.status === "active" ? "update" : "create", planId: current.planId, expectedRevision: current.revision, ...editableState(current) }) : "none; use create"}`);
|
|
66
76
|
}
|
|
67
|
-
const state =
|
|
77
|
+
const state = commandState(value);
|
|
68
78
|
const changes = changesBetween(value.action === "update" ? current : undefined, state);
|
|
69
79
|
if (value.action === "create" && current?.status === "active")
|
|
70
80
|
changes.unshift({ kind: "replaced", planId: current.planId, revision: current.revision, reason: "Replaced by a newly created plan" });
|
|
@@ -9,6 +9,7 @@ export const NATIVE_COMMANDS = [
|
|
|
9
9
|
{ name: "fork", description: "选择用户消息派生新会话", descriptionEn: "Choose a user message to fork" },
|
|
10
10
|
{ name: "name", description: "重命名当前会话", descriptionEn: "Rename this session", argumentHint: "<名称>", argumentHintEn: "<name>" },
|
|
11
11
|
{ name: "new", description: "新建会话,保留当前会话历史", descriptionEn: "Start a new session, keeping previous history" },
|
|
12
|
+
{ name: "restart", description: "重启当前会话运行环境,保留历史", descriptionEn: "Restart this session runtime, keeping history" },
|
|
12
13
|
{ name: "model", description: "切换模型", descriptionEn: "Switch model", argumentHint: "[名称]", argumentHintEn: "[name]" },
|
|
13
14
|
{ name: "compact", description: "压缩上下文", descriptionEn: "Compact context", argumentHint: "[说明]", argumentHintEn: "[instructions]" },
|
|
14
15
|
{ name: "cwd", description: "切换工作目录", descriptionEn: "Switch workspace", argumentHint: "<路径>", argumentHintEn: "<path>" },
|
|
@@ -111,6 +112,11 @@ export class SlashCommandsService {
|
|
|
111
112
|
case "new":
|
|
112
113
|
await this.host.startNewSession();
|
|
113
114
|
return true;
|
|
115
|
+
case "restart":
|
|
116
|
+
if (!this.host.restartSession)
|
|
117
|
+
throw new Error("Session restart unavailable");
|
|
118
|
+
await this.host.restartSession(context.conversationId);
|
|
119
|
+
return true;
|
|
114
120
|
case "model": {
|
|
115
121
|
if (!args) {
|
|
116
122
|
const current = this.host.getSession().model;
|
|
@@ -98,6 +98,30 @@ export function wikiLinkIndex(paths) {
|
|
|
98
98
|
}
|
|
99
99
|
return { paths: new Set(paths), byName };
|
|
100
100
|
}
|
|
101
|
+
/** Explicit relative file links can address evidence excluded from background indexing.
|
|
102
|
+
* The document/media endpoint still verifies existence and real workspace ownership. */
|
|
103
|
+
export function explicitWikiFilePath(source, target) {
|
|
104
|
+
let raw;
|
|
105
|
+
try {
|
|
106
|
+
raw = decodeURIComponent(target.split("#")[0]).replaceAll("\\", "/");
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (!/^(?:\.\/|\.\.\/)/.test(raw) || /[\0\r\n?:]/.test(raw) || !/\.[a-z0-9]+$/i.test(raw))
|
|
112
|
+
return;
|
|
113
|
+
const parts = source.split("/").slice(0, -1);
|
|
114
|
+
for (const part of raw.split("/")) {
|
|
115
|
+
if (part === "..") {
|
|
116
|
+
if (!parts.length)
|
|
117
|
+
return;
|
|
118
|
+
parts.pop();
|
|
119
|
+
}
|
|
120
|
+
else if (part && part !== ".")
|
|
121
|
+
parts.push(part);
|
|
122
|
+
}
|
|
123
|
+
return parts.join("/") || undefined;
|
|
124
|
+
}
|
|
101
125
|
export function resolveWikiLink(source, target, paths) {
|
|
102
126
|
let raw;
|
|
103
127
|
try {
|
|
@@ -96,6 +96,13 @@ export function installWikiRoutes(app, service, dataDir, originAllowed) {
|
|
|
96
96
|
wiki.write(cwd, path, text, version);
|
|
97
97
|
result = await wiki.documentContent(cwd, path);
|
|
98
98
|
break;
|
|
99
|
+
case "create-file":
|
|
100
|
+
if (service().quiesceInfo().quiesced || session.isStreaming)
|
|
101
|
+
throw new Error("Wait for the current request to finish");
|
|
102
|
+
if (typeof path !== "string" || conversationId !== cs.conversationId)
|
|
103
|
+
throw new Error("Invalid create request");
|
|
104
|
+
result = wiki.createFile(cwd, path);
|
|
105
|
+
break;
|
|
99
106
|
case "restore":
|
|
100
107
|
if (service().quiesceInfo().quiesced || session.isStreaming)
|
|
101
108
|
throw new Error("Wait for the current request to finish");
|
|
@@ -128,7 +135,7 @@ export function installWikiRoutes(app, service, dataDir, originAllowed) {
|
|
|
128
135
|
}
|
|
129
136
|
default: throw new Error("Unknown Wiki action");
|
|
130
137
|
}
|
|
131
|
-
if (cs.switchingWorkspace || cs.cwd !== cwd || (["prompt", "write", "restore"].includes(action) && !valid())) {
|
|
138
|
+
if (cs.switchingWorkspace || cs.cwd !== cwd || (["prompt", "write", "restore", "create-file"].includes(action) && !valid())) {
|
|
132
139
|
res.status(409).json({ error: "Workspace changed" });
|
|
133
140
|
return;
|
|
134
141
|
}
|
|
@@ -11,8 +11,31 @@ const IGNORED = new Set([".git", "node_modules", ".pi-web", ".DS_Store"]);
|
|
|
11
11
|
// Generated dependencies and duplicate checkouts must not consume the main
|
|
12
12
|
// workspace's index or undo budget. Explicit directory/file requests stay usable.
|
|
13
13
|
const TRAVERSAL_IGNORED_DIRS = new Set([".venv", "__pycache__", ".pytest_cache", ".mypy_cache", ".ruff_cache"]);
|
|
14
|
+
function isKnowledgeManifest(contents) {
|
|
15
|
+
try {
|
|
16
|
+
const manifest = JSON.parse(contents.toString());
|
|
17
|
+
return manifest?.kind === "pi-harness-knowledge" && manifest.version === 1 && manifest.evidenceDirectory === "evidence";
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
14
23
|
function skipTraversalDirectory(parent, name) {
|
|
15
|
-
|
|
24
|
+
if (TRAVERSAL_IGNORED_DIRS.has(name) || (name === "worktrees" && basename(parent) === ".claude"))
|
|
25
|
+
return true;
|
|
26
|
+
if (name !== "evidence")
|
|
27
|
+
return false;
|
|
28
|
+
// Only the immutable evidence of a declared knowledge export is excluded.
|
|
29
|
+
// Ordinary user folders named evidence remain searchable and undoable.
|
|
30
|
+
try {
|
|
31
|
+
const marker = join(parent, "manifest.json"), info = lstatSync(marker);
|
|
32
|
+
if (!info.isFile() || info.size > 16 * 1024 * 1024)
|
|
33
|
+
return false;
|
|
34
|
+
return isKnowledgeManifest(readFileSync(marker));
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
16
39
|
}
|
|
17
40
|
const hash = (data) => createHash("sha256").update(data).digest("hex");
|
|
18
41
|
export class WikiConflictError extends Error {
|
|
@@ -45,6 +68,10 @@ export function wikiPath(cwd, path) {
|
|
|
45
68
|
}
|
|
46
69
|
return absolute;
|
|
47
70
|
}
|
|
71
|
+
function isKnowledgeEvidence(cwd, path) {
|
|
72
|
+
const root = realpathSync(cwd), parts = relative(root, wikiPath(cwd, path)).split(sep);
|
|
73
|
+
return parts.some((part, index) => part === "evidence" && skipTraversalDirectory(join(root, ...parts.slice(0, index)), part));
|
|
74
|
+
}
|
|
48
75
|
function kindOf(path, text) {
|
|
49
76
|
if (/\.(md|markdown|txt)$/i.test(path))
|
|
50
77
|
return "document";
|
|
@@ -161,7 +188,7 @@ export class WikiService {
|
|
|
161
188
|
}
|
|
162
189
|
children.sort((a, b) => Number(b.isDirectory()) - Number(a.isDirectory()) || a.name.localeCompare(b.name));
|
|
163
190
|
for (const child of children) {
|
|
164
|
-
if (IGNORED.has(child.name) || child.isDirectory() && skipTraversalDirectory(path, child.name))
|
|
191
|
+
if (IGNORED.has(child.name) || child.isDirectory() && skipTraversalDirectory(wikiPath(cwd, path), child.name))
|
|
165
192
|
continue;
|
|
166
193
|
if (visited >= 20000) {
|
|
167
194
|
limited = true;
|
|
@@ -282,7 +309,7 @@ export class WikiService {
|
|
|
282
309
|
const data = size <= MAX_FILE ? readFileSync(abs) : undefined;
|
|
283
310
|
const text = data && !/\.pdf$/i.test(path) && previewKind(path) !== "image" && looksLikeText(data) ? decodeText(data) : undefined;
|
|
284
311
|
const metadata = wikiMetadata(text ?? "");
|
|
285
|
-
return { entry: { path, name: basename(path), size, modified: info.mtimeMs, kind: kindOf(path, text !== undefined), tags: metadata.tags, title: metadata.title }, text, version: data ? hash(data) : "", editable: text !== undefined && Buffer.from(text).equals(data) };
|
|
312
|
+
return { entry: { path, name: basename(path), size, modified: info.mtimeMs, kind: kindOf(path, text !== undefined), tags: metadata.tags, title: metadata.title }, text, version: data ? hash(data) : "", editable: text !== undefined && Buffer.from(text).equals(data) && !isKnowledgeEvidence(cwd, path) };
|
|
286
313
|
}
|
|
287
314
|
async search(cwd, query) {
|
|
288
315
|
const index = await this.index(cwd), q = query.trim().toLocaleLowerCase().slice(0, 200);
|
|
@@ -355,7 +382,7 @@ export class WikiService {
|
|
|
355
382
|
}
|
|
356
383
|
children.sort((a, b) => Number(b.isDirectory()) - Number(a.isDirectory()) || a.name.localeCompare(b.name));
|
|
357
384
|
for (const child of children) {
|
|
358
|
-
if (IGNORED.has(child.name) || child.isDirectory() && skipTraversalDirectory(path, child.name))
|
|
385
|
+
if (IGNORED.has(child.name) || child.isDirectory() && skipTraversalDirectory(wikiPath(cwd, path), child.name))
|
|
359
386
|
continue;
|
|
360
387
|
if (visited++ >= 20000) {
|
|
361
388
|
skip(path);
|
|
@@ -447,7 +474,22 @@ export class WikiService {
|
|
|
447
474
|
}
|
|
448
475
|
}
|
|
449
476
|
cancel(cwd) { this.active.delete(this.key(cwd)); }
|
|
477
|
+
createFile(cwd, path) {
|
|
478
|
+
if (this.busy(cwd))
|
|
479
|
+
throw new Error("Wait for the current Wiki request to finish");
|
|
480
|
+
// Accept portable relative paths; parent directories must already exist.
|
|
481
|
+
path = path.replaceAll("\\", "/");
|
|
482
|
+
if (!path || path.split("/").some(part => !part || part === "." || part === ".." || /[<>:"|?*\x00-\x1f]/.test(part) || /[. ]$/.test(part) || /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(part)))
|
|
483
|
+
throw new Error("Invalid file name");
|
|
484
|
+
const absolute = wikiPath(cwd, path);
|
|
485
|
+
// Exclusive creation also rejects existing files and symlinks without truncating them.
|
|
486
|
+
writeFileSync(absolute, "", { flag: "wx" });
|
|
487
|
+
this.record(cwd, { files: new Map(), skipped: [] }, { files: new Map([[path, Buffer.alloc(0)]]), skipped: [] }, "user", path);
|
|
488
|
+
return { path };
|
|
489
|
+
}
|
|
450
490
|
write(cwd, path, text, version) {
|
|
491
|
+
if (isKnowledgeEvidence(cwd, path))
|
|
492
|
+
throw new Error("Knowledge evidence snapshots are read-only; edit the source and ingest a new version");
|
|
451
493
|
if (this.busy(cwd))
|
|
452
494
|
throw new Error("Wait for the current Wiki request to finish");
|
|
453
495
|
const abs = wikiPath(cwd, path), data = readFileSync(abs), next = Buffer.from(text);
|
|
@@ -478,7 +520,10 @@ export class WikiService {
|
|
|
478
520
|
if ((current ? hash(current) : null) !== (undo ? blob.after : blob.before))
|
|
479
521
|
throw new Error(`File changed since this request: ${c.path}`);
|
|
480
522
|
const target = undo ? blob.before : blob.after;
|
|
481
|
-
|
|
523
|
+
const next = target ? readFileSync(join(this.folder(cwd), "blobs", target)) : null;
|
|
524
|
+
if (basename(abs) === "manifest.json" && (current && isKnowledgeManifest(current) || next && isKnowledgeManifest(next)))
|
|
525
|
+
throw new Error("Knowledge ingestion contains versioned evidence and cannot be undone as a Wiki request. Use a new ingestion or restore the complete knowledge directory with version control.");
|
|
526
|
+
return { abs, current, next, c };
|
|
482
527
|
});
|
|
483
528
|
const done = [];
|
|
484
529
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@youweichen/pi-harness",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Web chat interface for the pi coding agent, powered by the pi SDK (@earendil-works/pi-coding-agent) — one-command run, Docker/systemd/launchd deployable",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"dev:web": "vite --config web/vite.config.ts",
|
|
52
52
|
"build": "npm run build:web && npm run build:server",
|
|
53
53
|
"build:web": "vite build --config web/vite.config.ts",
|
|
54
|
-
"build:server": "tsc -p tsconfig.server.json",
|
|
54
|
+
"build:server": "tsc -p tsconfig.server.json && node scripts/copy-server-assets.mjs",
|
|
55
55
|
"typecheck": "tsc -p tsconfig.server.json --noEmit && tsc -p web/tsconfig.json --noEmit && tsc -p tsconfig.tests.json --noEmit",
|
|
56
56
|
"test": "node --test tests/check-design-tokens-test.mjs && npm run check:design && vitest run",
|
|
57
57
|
"test:unit": "vitest run",
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{a as r,j as e,M as Pn,b as Dn}from"./markdown-BhJ_LInD.js";import{u as Ee,b as jn,T as fn,r as je}from"./index-Bab44wX8.js";import{D as En,o as $n}from"./xterm-D1D2FVe3.js";/* empty css */import"./react-BddUxV-9.js";const Mn=r.memo(function({text:w}){return e.jsx(Pn,{remarkPlugins:[Dn],components:{a:({href:t,children:s})=>/^https?:\/\//.test(t??"")?e.jsx("a",{href:t,target:"_blank",rel:"noreferrer",children:s}):e.jsx("span",{children:s}),img:({alt:t})=>e.jsx("span",{children:t})},children:w})});function Rn({nodeId:P,defaultDir:w,connected:t,agent:s,request:ne,draft:T,setDraft:$,consumedEditors:J}){var oe;const u=Ee(),[te,se]=r.useState(w),[C,y]=r.useState({}),[b,G]=r.useState(""),[M,q]=r.useState(""),[z,le]=r.useState("followUp"),D=r.useRef(null),W=r.useRef(!0),x=s==null?void 0:s.dialogs[0],A=Object.keys(C).length>0,Q=Object.values(C).some(d=>d.action==="agent_dialog");r.useEffect(()=>{q((x==null?void 0:x.prefill)??"")},[x==null?void 0:x.id]),r.useEffect(()=>{s!=null&&s.editorText&&J[P]!==s.editorText.id&&(J[P]=s.editorText.id,$(s.editorText.text))},[s==null?void 0:s.editorText]),r.useEffect(()=>{W.current&&D.current&&(D.current.scrollTop=D.current.scrollHeight)},[s==null?void 0:s.messages,s==null?void 0:s.streamingMessage,s==null?void 0:s.tools]),r.useEffect(()=>{const d=a=>{var E;const S=a.detail,N=S.requestId?C[S.requestId]:void 0;S.nodeId!==P||!N||(S.event==="failure"&&(G(String(((E=S.data)==null?void 0:E.message)??"")),y(k=>{const R={...k};return delete R[S.requestId],R})),S.event==="result"&&(N.text!==void 0&&$(k=>k===N.text?"":k),y(k=>{const R={...k};return delete R[S.requestId],R})))};return window.addEventListener("pi-node-event",d),()=>window.removeEventListener("pi-node-event",d)},[P,C]),r.useEffect(()=>{t||y({})},[t]);const H=(d,a,S)=>{G("");const N=ne(d,P,a,void 0,s==null?void 0:s.id);N?y(E=>({...E,[N]:{action:d,text:S}})):G(u("nodeAgentOffline"))},ae=()=>{!T.trim()||A||!t||(s==null?void 0:s.phase)!=="ready"||(W.current=!0,H("agent_prompt",{text:T,...s.running?{queue:z}:{}},T))},U=[...(s==null?void 0:s.messages)??[],...s!=null&&s.streamingMessage?[s.streamingMessage]:[]],ce=new Map(U.filter(d=>d.role==="toolResult").map(d=>[d.toolCallId,d])),fe=d=>e.jsxs("article",{className:`node-agent-message ${d.role}`,children:[e.jsx("small",{children:d.role==="user"?u("nodeAgentYou"):"pi"}),d.errorMessage&&e.jsx("p",{role:"alert",className:"node-agent-error",children:d.errorMessage}),d.content.map((a,S)=>{if(a.type==="text"&&"text"in a&&typeof a.text=="string")return e.jsx(Mn,{text:a.text},S);if(a.type==="thinking"&&"thinking"in a&&typeof a.thinking=="string")return e.jsxs("details",{children:[e.jsx("summary",{children:u("nodeAgentThinking")}),e.jsx("pre",{children:a.thinking})]},S);if(a.type==="toolCall"&&"id"in a&&"name"in a){const N=ce.get(String(a.id)),E=s==null?void 0:s.tools.find(k=>k.id===a.id);return e.jsxs("details",{className:"node-agent-tool",children:[e.jsxs("summary",{children:[e.jsx("code",{children:String(a.name)}),e.jsx("span",{children:u(N!=null&&N.isError||E!=null&&E.isError?"error":N||E&&!E.running?"done":s!=null&&s.running?"running":"toolQueued")})]}),e.jsx("pre",{children:String(a.argumentsText??"")}),N==null?void 0:N.content.map((k,R)=>"text"in k&&typeof k.text=="string"?e.jsx("pre",{children:k.text},R):null)]},S)}return a.type==="image"&&"dataUrl"in a&&typeof a.dataUrl=="string"&&a.dataUrl.startsWith("data:image/")?e.jsx("img",{src:a.dataUrl,alt:u("toolResultImage")},S):null})]},d.id);return e.jsxs("section",{className:"node-agent","aria-label":u("nodeAgentTitle"),children:[e.jsxs("header",{children:[e.jsx("strong",{children:u("nodeAgentTitle")}),e.jsx("span",{children:s!=null&&s.model?`${s.model.provider} / ${s.model.name}`:u("nodeAgentRemote")}),(s==null?void 0:s.phase)==="ready"&&e.jsxs(e.Fragment,{children:[e.jsx("button",{disabled:A||s.running||!t,onClick:()=>H("agent_new"),children:u("nodeAgentNew")}),e.jsx("button",{disabled:!t,onClick:()=>H("agent_close"),children:u("nodeAgentClose")})]})]}),(!s||s.phase==="closed")&&e.jsxs("div",{className:"node-agent-setup",children:[e.jsx("p",{children:u("nodeAgentSetup")}),e.jsxs("label",{children:[u("nodeDefaultDir"),e.jsx("input",{value:te,onChange:d=>se(d.target.value)})]}),e.jsx("button",{disabled:!t||A,onClick:()=>H("agent_start",{cwd:te}),children:u(A?"nodeAgentStarting":"nodeAgentStart")}),e.jsx("p",{className:"node-muted",children:u("nodeAgentInstall")}),e.jsx("code",{children:"npm install -g @earendil-works/pi-coding-agent@1.0.4"})]}),(s==null?void 0:s.phase)==="starting"&&e.jsx("p",{role:"status",children:u("nodeAgentStarting")}),!t&&e.jsx("p",{role:"status",children:u("nodeAgentOffline")}),(b||(s==null?void 0:s.error))&&e.jsx("p",{role:"alert",className:"node-agent-error",children:b||(s==null?void 0:s.error)}),(s==null?void 0:s.notice)&&e.jsx("p",{role:"status",className:"node-muted",children:s.notice}),e.jsxs("div",{className:"node-agent-feed",ref:D,onScroll:d=>{const a=d.currentTarget;W.current=a.scrollHeight-a.scrollTop-a.clientHeight<100},children:[U.filter(d=>d.role!=="toolResult"&&(d.errorMessage||d.content.some(a=>a.type==="toolCall"||a.type==="image"||"text"in a&&typeof a.text=="string"&&a.text.trim()||"thinking"in a&&typeof a.thinking=="string"&&a.thinking.trim()))).map(fe),(s==null?void 0:s.running)&&e.jsxs("p",{className:"node-agent-working",role:"status",children:[u("nodeAgentWorking"),s.tools.filter(d=>d.running).map(d=>e.jsx("code",{children:d.name},d.id))]})]}),x&&e.jsxs("form",{className:"node-agent-dialog",onSubmit:d=>{d.preventDefault(),H("agent_dialog",{id:x.id,value:M,confirmed:!0})},children:[e.jsx("strong",{children:x.title}),x.message&&e.jsx("p",{children:x.message}),x.method==="select"?e.jsxs("select",{"aria-label":x.title,required:!0,value:M,onChange:d=>q(d.target.value),children:[e.jsx("option",{value:"",disabled:!0,children:u("nodeAgentSelect")}),(oe=x.options)==null?void 0:oe.map(d=>e.jsx("option",{children:d},d))]}):x.method!=="confirm"&&e.jsx("textarea",{"aria-label":x.title,value:M,onChange:d=>q(d.target.value),rows:x.method==="editor"?6:2}),e.jsx("button",{disabled:Q||!t,type:"submit",children:u("confirm")}),e.jsx("button",{disabled:Q||!t,type:"button",onClick:()=>H("agent_dialog",{id:x.id,cancelled:!0}),children:u("cancel")})]}),(s==null?void 0:s.phase)==="ready"&&e.jsxs("form",{className:"node-agent-input",onSubmit:d=>{d.preventDefault(),ae()},children:[e.jsx("textarea",{"aria-label":u("nodeAgentPrompt"),placeholder:u("nodeAgentPrompt"),value:T,onChange:d=>$(d.target.value),rows:3,onKeyDown:d=>{d.key==="Enter"&&!d.shiftKey&&!d.nativeEvent.isComposing&&(d.preventDefault(),ae())}}),e.jsxs("div",{children:[e.jsxs("select",{"aria-label":u("nodeAgentModel"),value:s.model?JSON.stringify([s.model.provider,s.model.id]):"",disabled:s.running||A||!t,onChange:d=>{const[a,S]=JSON.parse(d.target.value);H("agent_model",{provider:a,modelId:S})},children:[e.jsx("option",{value:"",disabled:!0,children:u("nodeAgentModel")}),s.models.map(d=>e.jsxs("option",{value:JSON.stringify([d.provider,d.id]),children:[d.provider," / ",d.name]},`${d.provider}/${d.id}`))]}),s.running&&e.jsxs(e.Fragment,{children:[e.jsxs("select",{"aria-label":u("nodeAgentQueue"),value:z,onChange:d=>le(d.target.value),children:[e.jsx("option",{value:"followUp",children:u("nodeAgentFollowUp")}),e.jsx("option",{value:"steer",children:u("nodeAgentSteer")})]}),e.jsx("button",{type:"button",disabled:!t,onClick:()=>H("agent_abort"),children:u("stop")})]}),e.jsx("button",{type:"submit",disabled:!T.trim()||A||!t,children:u("nodeAgentSend")})]})]})]})}function Tn({nodeId:P,tab:w,active:t,send:s,initialOutput:ne}){const T=Ee(),$=r.useRef(null),J=r.useRef(w.closed);J.current=w.closed;const[u,te]=r.useState(""),[se,C]=r.useState("");return r.useEffect(()=>{if(!$.current)return;const y=getComputedStyle($.current),b=new En({theme:{...jn(),background:y.getPropertyValue("--node-terminal-bg").trim(),foreground:y.getPropertyValue("--node-terminal-fg").trim()},fontFamily:'"JetBrains Mono", Consolas, monospace',fontSize:13,scrollback:8e3,allowProposedApi:!0}),G=()=>{b.options.theme={...jn(),background:y.getPropertyValue("--node-terminal-bg").trim(),foreground:y.getPropertyValue("--node-terminal-fg").trim()}};window.addEventListener(fn,G);const M=new $n;b.loadAddon(M),b.open($.current),ne&&b.write(ne);const q=(x,A={})=>s({type:"node_request",action:x,requestId:je(),nodeId:P,terminalId:w.id,conversationId:w.conversationId,payload:A}),z=b.onData(x=>{J.current||q(x===""?"interrupt":"terminal_input",{data:x})}),le=b.onSelectionChange(()=>te(b.getSelection().slice(0,32e3))),D=x=>{var Q;const A=x.detail;A.nodeId!==P||A.terminalId!==w.id||A.event==="terminal_output"&&b.write(String(((Q=A.data)==null?void 0:Q.text)??""))};window.addEventListener("pi-node-event",D);const W=new ResizeObserver(()=>{var x;if(!(!((x=$.current)!=null&&x.clientWidth)||!$.current.clientHeight))try{M.fit(),J.current||q("terminal_resize",{cols:b.cols,rows:b.rows})}catch{}});return W.observe($.current),()=>{window.removeEventListener(fn,G),W.disconnect(),z.dispose(),le.dispose(),window.removeEventListener("pi-node-event",D),b.dispose()}},[P,w.id,w.conversationId,s]),e.jsxs("div",{className:"node-terminal-wrap",style:{display:t?"block":"none"},children:[e.jsx("div",{ref:$,className:"node-xterm"}),u&&e.jsxs("div",{className:"node-selection-menu",children:[e.jsx("button",{onClick:()=>void navigator.clipboard.writeText(u).then(()=>C(T("nodeCopied"))).catch(y=>C(String(y))),children:T("nodeCopy")}),se&&e.jsx("span",{role:"status",children:se})]})]})}const qn=()=>({name:"",group:"默认",host:"",port:22,username:"root",auth:"password",defaultDir:"/"});function Un({send:P,active:w}){var Be,Je,Ge,Ve;const t=Ee(),[s,ne]=r.useState([]),[T,$]=r.useState([]),[J,u]=r.useState(!1),[te,se]=r.useState(!1),[C,y]=r.useState(null),[b,G]=r.useState(""),[M,q]=r.useState(!1),[z,le]=r.useState(!1),[D,W]=r.useState("agent"),[x,A]=r.useState({}),Q=r.useRef({}),[H,ae]=r.useState({}),[U,ce]=r.useState("password"),[fe,oe]=r.useState(""),[d,a]=r.useState(null),[S,N]=r.useState(!0),[E,k]=r.useState(!1),[R,ge]=r.useState(!1),re=r.useRef(null),Y=r.useRef(new Set),$e=r.useRef(new Set),[Me,ve]=r.useState(""),[de,gn]=r.useState([]),[Se,vn]=r.useState({}),[Re,Sn]=r.useState(Date.now()),[K,yn]=r.useState({}),[Ie,ue]=r.useState({}),[c,ye]=r.useState(localStorage.getItem("pi-node-selected")??""),[bn,Te]=r.useState({}),[v,B]=r.useState(null),[be,F]=r.useState(""),[qe,he]=r.useState(""),[we,Ae]=r.useState("/"),[Ke,wn]=r.useState({}),[L,ke]=r.useState(null),Z=r.useRef(new Map),Fe=r.useRef(new Map),Le=r.useRef(new Map),me=r.useRef(null),j=r.useCallback((n,i,o,h,I)=>{const ie=je();return P({type:"node_request",requestId:ie,action:n,nodeId:i,terminalId:h,conversationId:I,payload:o})?ie:null},[P]);r.useEffect(()=>{if(!w||!M)return;const n=setInterval(()=>Sn(Date.now()),1e3);return()=>clearInterval(n)},[w,M]);const l=de.find(n=>n.id===c),pe=Ie[c]??[],g=pe.find(n=>n.id===bn[c])??pe.at(-1),V=de.find(n=>n.id===d),X=s.find(n=>n.id===(l==null?void 0:l.sourceId)),kn=((Be=Ke[c])==null?void 0:Be.path)===we?Ke[c].entries:[];r.useEffect(()=>{w&&(j("state"),j("source_detect"))},[w,j]),r.useEffect(()=>{l&&Ae(l.defaultDir)},[c,l==null?void 0:l.defaultDir]),r.useEffect(()=>{const n=i=>{var ie,_e,Pe,Ne,Xe,ze,Qe,Ye,Ze,en,nn,tn,sn,on,rn,dn,ln,an,cn,un,hn,mn,pn,xn;const o=i.detail,h=o.nodeId??"";if(o.event==="state"){const p=((ie=o.data)==null?void 0:ie.nodes)??[];gn(p),ae(m=>{var xe;const f=Object.fromEntries(Object.entries(m).map(([O,_])=>[O,{..._,phase:"closed",running:!1,dialogs:[]}]));for(const O of((xe=o.data)==null?void 0:xe.connections)??[])O.agent&&(f[O.nodeId]=O.agent);for(const O of Object.keys(f))p.some(_=>_.id===O)||delete f[O];return f}),vn(Object.fromEntries((((_e=o.data)==null?void 0:_e.connections)??[]).map(m=>[m.nodeId,m]))),ne(((Pe=o.data)==null?void 0:Pe.sources)??[]),yn(Object.fromEntries((((Ne=o.data)==null?void 0:Ne.connections)??[]).map(m=>[m.nodeId,m.status])));for(const m of((Xe=o.data)==null?void 0:Xe.connections)??[])for(const f of m.terminals)Z.current.set(`${m.nodeId}:${f.id}`,f.output??"");ue(m=>{var xe,O;const f={...m};for(const _ of((xe=o.data)==null?void 0:xe.connections)??[]){const ee=(m[_.nodeId]??[]).filter(De=>!_.terminals.some(_n=>_n.id===De.id)).map(De=>({...De,closed:!0}));f[_.nodeId]=[...ee,..._.terminals]}for(const _ of Object.keys(f))(((O=o.data)==null?void 0:O.connections)??[]).some(ee=>ee.nodeId===_)||(f[_]=f[_].map(ee=>({...ee,closed:!0})));for(const _ of Object.keys(f))p.some(ee=>ee.id===_)||delete f[_];return f});return}if(o.event==="agent_state"&&((ze=o.data)!=null&&ze.agent)){const p=o.data.agent;ae(m=>({...m,[h]:p}));return}if(o.event==="terminal_output"){const p=`${h}:${o.terminalId}`;Z.current.set(p,((Z.current.get(p)??"")+String(((Qe=o.data)==null?void 0:Qe.text)??"")).slice(-65536));return}if(o.event==="trust_required"){if(o.requestId&&$e.current.add(o.requestId),window.confirm(t("nodeTrust",{name:String(((Ye=o.data)==null?void 0:Ye.name)??""),host:String(((Ze=o.data)==null?void 0:Ze.host)??""),fingerprint:String(((en=o.data)==null?void 0:en.fingerprint)??"")}))){const p=re.current;(p==null?void 0:p.nodeId)===h?j("credential_test",h,{...p.payload,fingerprint:(nn=o.data)==null?void 0:nn.fingerprint}):j("trust",h,{fingerprint:(tn=o.data)==null?void 0:tn.fingerprint})}else Y.current.delete(h),re.current=null,ge(!1);return}if(o.event==="failure"&&String(((sn=o.data)==null?void 0:sn.action)??"").startsWith("agent_"))return;if(o.event==="failure"){o.requestId===me.current&&(me.current=null);const p=String(((on=o.data)==null?void 0:on.message)??"");o.requestId&&$e.current.delete(o.requestId)||(he(p),ge(!1),((rn=o.data)==null?void 0:rn.action)==="credential_test"&&(re.current=null),Y.current.delete(h));return}if(o.event==="terminal_busy"){ue(p=>({...p,[h]:(p[h]??[]).map(m=>{var f;return m.id===o.terminalId?{...m,busy:!!((f=o.data)!=null&&f.busy)}:m})}));return}if(o.event==="terminal_exit"){const p=`${h}:${o.terminalId}`;Z.current.set(p,(Z.current.get(p)??"")+`\r
|
|
2
|
+
[SSH 已断开]\r
|
|
3
|
+
`),ue(m=>({...m,[h]:(m[h]??[]).map(f=>f.id===o.terminalId?{...f,closed:!0}:f)}));return}if(o.event!=="result")return;const I=(dn=o.data)==null?void 0:dn.action;if(I==="source_detect"&&($(((ln=o.data)==null?void 0:ln.sources)??[]),se(!0)),I==="source_add"&&(y(null),u(!1)),I==="credential_test"&&(re.current=null,ge(!1),a(null),F(""),ve(t("nodeCredentialsSaved",{n:Number(((an=o.data)==null?void 0:an.count)??1)}))),(I==="connect"||I==="trust"||I==="credential_test")&&Y.current.has(h)&&(Y.current.delete(h),j("terminal_open",h,{},je()),q(!0)),I==="terminal_open"){const p={id:String((cn=o.data)==null?void 0:cn.terminalId),conversationId:String((un=o.data)==null?void 0:un.conversationId),busy:!1};ue(m=>({...m,[h]:(m[h]??[]).some(f=>f.id===p.id)?m[h]:[...m[h]??[],p]})),Te(m=>({...m,[h]:p.id}))}I==="list"&&o.requestId===Fe.current.get(h)&&wn(p=>{var m,f;return{...p,[h]:{path:String(((m=o.data)==null?void 0:m.path)??""),entries:((f=o.data)==null?void 0:f.entries)??[]}}}),I==="read"&&o.requestId===Le.current.get(h)&&ke({nodeId:h,path:String(((hn=o.data)==null?void 0:hn.path)??""),text:String(((mn=o.data)==null?void 0:mn.text)??"")}),I==="write"&&o.requestId===me.current&&(me.current=null,ke(p=>{var m;return(p==null?void 0:p.nodeId)===h&&p.path===((m=o.data)==null?void 0:m.path)?null:p})),I==="save"&&(B(null),F(""),(pn=o.data)!=null&&pn.id&&ye(String(o.data.id))),(I==="import_legacy"||I==="import")&&ve(t("nodeImported",{n:Number(((xn=o.data)==null?void 0:xn.count)??0)}))};return window.addEventListener("pi-node-event",n),()=>window.removeEventListener("pi-node-event",n)},[j,t]);const Ce=r.useMemo(()=>de.filter(n=>`${n.name} ${n.host} ${n.username}`.toLowerCase().includes(b.toLowerCase())),[de,b]),Cn=r.useMemo(()=>[...new Set(Ce.map(n=>n.group))],[Ce]),Oe=n=>{const i=j("list",c,{path:n},g==null?void 0:g.id,g==null?void 0:g.conversationId);i&&Fe.current.set(c,i)},Nn=n=>{const i=j("read",c,{path:n},g==null?void 0:g.id,g==null?void 0:g.conversationId);i&&Le.current.set(c,i)},In=()=>{!L||L.nodeId!==c||(me.current=j("write",L.nodeId,{path:L.path,text:L.text},g==null?void 0:g.id,g==null?void 0:g.conversationId))},He=n=>{var i;if(ye(n.id),he(""),n.auth==="password"&&!n.hasSecret||n.auth==="key"&&(!n.keyPath||(i=n.unsupported)!=null&&i.includes("key"))&&!n.localKeyPath){ce(n.auth==="key"?"key":"password"),oe(n.localKeyPath||n.keyPath||""),a(n.id),F(""),k(!1),Y.current.add(n.id);return}q(!0),u(!1),K[n.id]==="connected"?(Ie[n.id]??[]).some(o=>!o.closed)||j("terminal_open",n.id,{},je()):(Y.current.add(n.id),j("connect",n.id))},Ue=e.jsx("button",{onClick:()=>{B(qn()),F("")},"aria-label":t("nodeAdd"),children:"+"}),We=e.jsxs("label",{className:"node-secondary",children:[t("nodeImportJson"),e.jsx("input",{type:"file",accept:"application/json,.json",hidden:!0,onChange:async n=>{var o;const i=(o=n.target.files)==null?void 0:o[0];if(i){try{const h=JSON.parse(await i.text());j("import",void 0,{nodes:h.nodes})}catch{he(t("nodeInvalidJson"))}n.target.value=""}}})]}),An=e.jsxs("div",{className:"node-source-page",children:[e.jsx("div",{className:"node-eyebrow",children:t("nodeSources")}),e.jsx("h2",{children:t("nodeAdd")}),e.jsx("p",{className:"node-muted",children:t("nodeSourceIntro")}),te&&!T.length&&!s.length&&e.jsx("p",{className:"node-muted",children:t("nodeNoSources")}),[...s,...T.filter(n=>!s.some(i=>i.path===n.path))].map(n=>e.jsxs("section",{className:"node-source-card",children:[e.jsxs("header",{children:[e.jsx("span",{className:"node-source-icon",children:n.kind==="xshell"?"X":"~/"}),e.jsxs("div",{children:[e.jsx("strong",{children:n.kind==="xshell"?"Xshell 8":"SSH config"}),e.jsx("small",{children:t("nodeSyncInfo",{n:n.count??0,g:n.groups??0})})]})]}),e.jsx("code",{children:n.path}),e.jsxs("p",{children:["✓ ",t("nodeSyncFields")]}),e.jsxs("p",{className:"node-muted",children:["— ",t("nodeSyncPassword"),e.jsx("br",{}),"— ",t("nodeSyncUnsupported")]}),n.error&&e.jsx("p",{className:"node-error",role:"alert",children:n.error}),n.lastSync&&e.jsx("small",{children:t("nodeSyncTime",{time:new Date(n.lastSync).toLocaleString()})}),e.jsxs("div",{className:"node-actions",children:[n.id?e.jsxs(e.Fragment,{children:[e.jsx("button",{onClick:()=>j("source_sync",void 0,{id:n.id}),children:t("nodeSyncNow")}),e.jsx("button",{onClick:()=>j("source_toggle",void 0,{id:n.id,enabled:!n.enabled}),children:t(n.enabled?"nodeSyncPause":"nodeSyncResume")})]}):e.jsxs(e.Fragment,{children:[e.jsx("button",{className:"node-primary",onClick:()=>j("source_add",void 0,{kind:n.kind,path:n.path,enabled:!0}),children:t("nodeSyncKeep")}),e.jsx("button",{onClick:()=>j("source_add",void 0,{kind:n.kind,path:n.path,enabled:!1}),children:t("nodeImportOnce")})]}),e.jsx("button",{onClick:()=>y({kind:n.kind??"xshell",path:n.path??""}),children:t("nodeChangePath")})]})]},n.path)),e.jsxs("div",{className:"node-actions",children:[e.jsx("button",{onClick:()=>y({kind:"xshell",path:""}),children:t("nodeAddSource")}),e.jsx("button",{onClick:()=>j("source_detect"),children:t("nodeDetect")}),Ue,We]})]});return e.jsxs("div",{className:`node-workbench ${D==="agent"?"node-agent-open":""} ${M&&l?"node-workspace-open":""}`,children:[qe&&e.jsxs("div",{className:"node-global-message node-error",role:"alert",children:[qe,e.jsx("button",{"aria-label":t("close"),onClick:()=>he(""),children:"×"})]}),Me&&e.jsxs("div",{className:"node-global-message node-notice",role:"status",children:[Me,e.jsx("button",{"aria-label":t("close"),onClick:()=>ve(""),children:"×"})]}),!M&&e.jsxs("aside",{className:"node-sidebar",children:[e.jsxs("header",{children:[e.jsx("strong",{children:t("nodeWorkbench")}),Ue]}),e.jsx("input",{"aria-label":t("nodeSearch"),placeholder:t("nodeSearch"),value:b,onChange:n=>G(n.target.value)}),s.map(n=>e.jsxs("button",{className:"node-source-status",onClick:()=>u(!0),children:[e.jsx("span",{className:`node-dot ${n.error?"connecting":n.enabled?"connected":"offline"}`}),n.kind==="xshell"?"Xshell 8":"SSH config",e.jsx("small",{children:n.error||(n.lastSync?new Date(n.lastSync).toLocaleTimeString():"…")})]},n.id)),e.jsx("div",{className:"node-list",children:Cn.map(n=>e.jsxs("section",{children:[e.jsxs("h3",{children:[n,e.jsx("span",{children:Ce.filter(i=>i.group===n).length})]}),Ce.filter(i=>i.group===n).map(i=>{var o,h;return e.jsxs("div",{className:`node-item ${c===i.id?"selected":""}`,children:[e.jsxs("button",{onClick:()=>{ye(i.id),u(!1)},children:[e.jsx("span",{className:`node-dot ${K[i.id]??"offline"}`}),i.name,e.jsxs("small",{children:[i.username,"@",i.host,":",i.port]})]}),(i.sourceMissing||((o=i.unsupported)==null?void 0:o.length)||i.auth==="password"&&!i.hasSecret)&&e.jsx("span",{className:"node-badge",children:t(i.sourceMissing?"nodeSourceMissing":(h=i.unsupported)!=null&&h.length?"nodeUnsupported":"nodeMissingSecret")}),!i.sourceId&&e.jsx("button",{title:t("nodeEdit"),onClick:()=>{B(i),F("")},children:"⋯"})]},i.id)})]},n))}),e.jsxs("footer",{children:[e.jsx("button",{onClick:()=>u(!0),children:t("nodeSources")}),We,e.jsxs("details",{children:[e.jsx("summary",{children:"⋯"}),e.jsx("button",{onClick:()=>j("import_legacy"),children:t("nodeImportLegacy")}),e.jsx("button",{onClick:()=>{const n=de.map(({hasSecret:h,fingerprint:I,sourceId:ie,sourceKey:_e,sourceMissing:Pe,...Ne})=>Ne),i=URL.createObjectURL(new Blob([JSON.stringify({nodes:n},null,2)],{type:"application/json"})),o=document.createElement("a");o.href=i,o.download="pi-nodes.json",o.click(),setTimeout(()=>URL.revokeObjectURL(i),1e3)},children:t("nodeExport")})]})]})]}),!M&&(J||!l?An:e.jsxs("section",{className:"node-detail",children:[e.jsxs("header",{className:"node-detail-head",children:[e.jsxs("div",{children:[e.jsx("div",{className:"node-eyebrow",children:l.group}),e.jsx("h2",{children:l.name}),e.jsxs("code",{children:[l.username,"@",l.host,":",l.port]})]}),e.jsxs("div",{className:"node-actions",children:[X&&e.jsx("button",{onClick:()=>j("source_reveal",void 0,{id:X.id}),children:t("nodeRevealSource")}),!l.sourceId&&e.jsx("button",{onClick:()=>{B(l),F("")},children:t("nodeEdit")}),e.jsx("button",{className:"node-primary",disabled:!!((Je=l.unsupported)!=null&&Je.some(n=>n!=="key"))||l.sourceMissing||K[l.id]==="connecting",onClick:()=>He(l),children:t("nodeOpenWorkbench")})]})]}),l.sourceMissing&&e.jsx("p",{className:"node-warning",children:t("nodeSourceMissing")}),!!((Ge=l.unsupported)!=null&&Ge.length)&&e.jsxs("p",{className:"node-warning",children:[t("nodeUnsupported"),": ",l.unsupported.join(", ")]}),e.jsxs("div",{className:l.hasSecret?"node-credential-note":"node-warning",children:[e.jsxs("div",{children:[e.jsxs("strong",{children:[t(l.auth==="agent"?"nodeAgentAuth":l.auth==="password"?"nodePassword":"nodePassphrase"),!l.hasSecret&&l.auth==="password"&&` · ${t("nodeMissingSecret")}`]}),e.jsx("p",{children:t("nodeCredentialHint")})]}),e.jsx("button",{onClick:()=>{ce(l.auth==="key"?"key":"password"),oe(l.localKeyPath||l.keyPath||""),a(l.id),F(""),k(!1)},children:t("nodeFillSecret")})]}),e.jsxs("dl",{children:[e.jsx("dt",{children:t("nodeAuth")}),e.jsx("dd",{children:t(l.auth==="key"?"nodeKey":l.auth==="agent"?"nodeAgentAuth":"nodePassword")}),(l.localKeyPath||l.keyPath)&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{children:t("nodeKeyPath")}),e.jsx("dd",{children:e.jsx("code",{children:l.localKeyPath||l.keyPath})})]}),X&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{children:t("nodeSourceLocation")}),e.jsxs("dd",{children:[e.jsxs("code",{children:[X.path,X.kind==="xshell"?`/${l.sourceKey}`:` · ${l.sourceKey}`]}),e.jsx("small",{children:t("nodeSourceReadonly")}),e.jsx("button",{title:t("nodeSourceOpenHint"),onClick:()=>void navigator.clipboard.writeText(X.kind==="xshell"?`${X.path}/${l.sourceKey}`:X.path).then(()=>ve(t("nodeCopied"))).catch(n=>he(String(n))),children:t("nodeCopy")})]})]}),e.jsx("dt",{children:t("nodeLastConnected")}),e.jsx("dd",{children:l.lastConnected?new Date(l.lastConnected).toLocaleString():t("nodeNever")}),e.jsx("dt",{children:t("nodeDefaultDir")}),e.jsx("dd",{children:e.jsx("code",{children:l.defaultDir})})]})]})),M&&l&&e.jsxs("div",{className:"node-connected",children:[e.jsxs("nav",{className:"node-connection-tabs",children:[e.jsx("button",{onClick:()=>q(!1),children:t("nodeBackList")}),de.filter(n=>{var i;return n.id===c||K[n.id]||((i=Ie[n.id])==null?void 0:i.length)}).map(n=>e.jsxs("button",{className:n.id===c?"active":"",onClick:()=>ye(n.id),children:[e.jsx("span",{className:`node-dot ${K[n.id]??"offline"}`}),n.name]},n.id)),e.jsx("button",{onClick:()=>{q(!1),u(!0)},"aria-label":t("nodeAdd"),children:"+"}),((Ve=Se[c])==null?void 0:Ve.connectedAt)&&e.jsx("span",{className:"node-connection-timing",children:t("nodeConnectionTiming",{ms:Se[c].latencyMs??0,duration:`${Math.floor(Math.max(0,Re-Se[c].connectedAt)/6e4)}:${String(Math.floor(Math.max(0,Re-Se[c].connectedAt)/1e3)%60).padStart(2,"0")}`})})]}),e.jsx("div",{className:"node-connected-body",children:e.jsxs("section",{className:"node-main",children:[e.jsxs("header",{className:"node-main-head",children:[e.jsx("strong",{children:l.name}),e.jsxs("span",{children:[l.username,"@",l.host,":",l.port]}),e.jsx("em",{children:t(K[c]==="connected"?"nodeConnected":"nodeDisconnected")}),e.jsx("button",{onClick:()=>K[c]==="connected"?j("disconnect",c):He(l),children:t(K[c]==="connected"?"nodeDisconnect":"nodeConnect")})]}),e.jsxs("nav",{className:"node-workbench-modes","aria-label":t("nodeWorkbench"),children:[e.jsx("button",{className:D==="agent"?"active":"",onClick:()=>W("agent"),children:t("nodeAgentTitle")}),e.jsx("button",{className:D==="terminal"?"active":"",onClick:()=>W("terminal"),children:t("nodeAgentTerminal")})]}),e.jsx("div",{className:"node-agent-container",hidden:D!=="agent",children:e.jsx(Rn,{nodeId:c,defaultDir:l.defaultDir,connected:K[c]==="connected",agent:H[c],request:j,draft:x[c]??"",setDraft:n=>A(i=>({...i,[c]:typeof n=="function"?n(i[c]??""):n})),consumedEditors:Q.current},c)}),e.jsxs("div",{className:"node-manual-workbench",hidden:D!=="terminal",children:[e.jsxs("div",{className:"node-tabs",children:[pe.map((n,i)=>e.jsxs("div",{className:(g==null?void 0:g.id)===n.id?"active":"",children:[e.jsxs("button",{onClick:()=>Te(o=>({...o,[c]:n.id})),children:[t("nodeTab",{n:i+1}),n.closed?` · ${t("nodeTabClosed")}`:n.busy?` · ${t("nodeTabBusy")}`:""]}),e.jsx("button",{"aria-label":t("nodeCloseTab"),onClick:()=>{n.closed||j("terminal_close",c,{},n.id,n.conversationId),Z.current.delete(`${c}:${n.id}`),ue(o=>({...o,[c]:(o[c]??[]).filter(h=>h.id!==n.id)}))},children:"×"})]},n.id)),e.jsx("button",{disabled:K[c]!=="connected",onClick:()=>j("terminal_open",c,{},je()),children:"+"})]}),e.jsxs("div",{className:"node-terminals",children:[pe.map(n=>e.jsx(Tn,{nodeId:c,tab:n,active:w&&D==="terminal"&&(g==null?void 0:g.id)===n.id,send:P,initialOutput:Z.current.get(`${c}:${n.id}`)??n.output},`${c}:${n.id}`)),!pe.length&&e.jsx("div",{className:"node-empty",children:t("nodeOpenHint")})]}),e.jsxs("div",{className:"node-terminal-footer",children:[e.jsx("span",{children:t("nodeTerminalHint")}),e.jsx("button",{onClick:()=>le(!z),children:t("nodeShowFiles")})]}),z&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"node-filebar",children:[e.jsx("input",{value:we,onChange:n=>Ae(n.target.value),"aria-label":t("nodeDefaultDir")}),e.jsx("button",{disabled:K[c]!=="connected",onClick:()=>Oe(we),children:t("nodeBrowse")})]}),e.jsx("div",{className:"node-files",children:kn.map(n=>{const i=`${we.replace(/\/$/,"")}/${n.name}`;return e.jsxs("button",{onClick:()=>n.type==="dir"?(Ae(i),Oe(i)):Nn(i),children:[n.type==="dir"?"▸":"·"," ",n.name]},n.name)})})]}),(L==null?void 0:L.nodeId)===c&&e.jsxs("div",{className:"node-file-editor",children:[e.jsx("span",{children:L.path}),e.jsx("button",{onClick:In,children:t("save")}),e.jsx("button",{onClick:()=>ke(null),children:t("close")}),e.jsx("textarea",{value:L.text,onChange:n=>ke({...L,text:n.target.value})})]})]})]})})]}),C&&e.jsx("div",{className:"node-modal-backdrop",children:e.jsxs("form",{className:"node-modal",onSubmit:n=>{n.preventDefault(),j("source_add",void 0,{...C,enabled:!0})},children:[e.jsx("h2",{children:t("nodeAddSource")}),e.jsxs("label",{children:[t("nodeSources"),e.jsxs("select",{value:C.kind,onChange:n=>y({...C,kind:n.target.value}),children:[e.jsx("option",{value:"xshell",children:"Xshell 8"}),e.jsx("option",{value:"ssh",children:"SSH config"})]})]}),e.jsxs("label",{children:[t("nodeSourcePath"),e.jsx("input",{required:!0,autoFocus:!0,value:C.path,onChange:n=>y({...C,path:n.target.value})})]}),e.jsxs("div",{className:"node-modal-actions",children:[e.jsx("button",{type:"button",onClick:()=>y(null),children:t("cancel")}),e.jsx("button",{type:"submit",children:t("nodeSyncKeep")}),e.jsx("button",{type:"button",disabled:!C.path.trim(),onClick:()=>j("source_add",void 0,{...C,enabled:!1}),children:t("nodeImportOnce")})]})]})}),V&&e.jsx("div",{className:"node-modal-backdrop",children:e.jsxs("form",{className:"node-modal",onSubmit:n=>{n.preventDefault();const i={auth:U,secret:be,persist:S,sameGroup:E,...U==="key"?{keyPath:fe}:{}};re.current={nodeId:V.id,payload:i},ge(!0),j("credential_test",V.id,i)},children:[e.jsx("h2",{children:V.name}),e.jsxs("code",{children:[V.username,"@",V.host,":",V.port]}),e.jsxs("label",{children:[t("nodeAuth"),e.jsxs("select",{disabled:R,value:U,onChange:n=>{ce(n.target.value),F(""),k(!1)},children:[e.jsx("option",{value:"password",children:t("nodePassword")}),e.jsx("option",{value:"key",children:t("nodeKey")})]})]}),e.jsx("p",{className:"node-muted",children:t("nodeLocalAuthHint")}),e.jsx(e.Fragment,{children:U==="key"&&e.jsxs(e.Fragment,{children:[e.jsxs("label",{children:[t("nodeKeyPath"),e.jsx("input",{required:!0,value:fe,onChange:n=>oe(n.target.value)})]}),e.jsx("p",{className:"node-muted",children:t("nodePrivateKeyHint")})]})}),e.jsxs("label",{children:[t(U==="key"?"nodePassphrase":"nodePassword"),e.jsx("input",{autoFocus:!0,type:"password",autoComplete:"new-password",required:U==="password",value:be,onChange:n=>F(n.target.value)})]}),e.jsxs("label",{className:"node-checkbox",children:[e.jsx("input",{type:"checkbox",checked:S,onChange:n=>N(n.target.checked)}),t("nodePersistSecret")]}),U==="password"&&e.jsxs("label",{className:"node-checkbox",children:[e.jsx("input",{type:"checkbox",checked:E,onChange:n=>k(n.target.checked)}),t("nodeGroupSecret")]}),E&&e.jsx("p",{className:"node-muted",children:t("nodeGroupHint")}),e.jsx("p",{className:"node-muted",children:t("nodeCredentialHint")}),e.jsxs("div",{className:"node-modal-actions",children:[e.jsx("button",{type:"button",disabled:R,onClick:()=>{Y.current.delete(V.id),a(null),F(""),re.current=null},children:t("cancel")}),e.jsx("button",{disabled:R,className:"node-primary",children:t(R?"nodeTesting":"nodeTestSave")})]})]})}),v&&e.jsx("div",{className:"node-modal-backdrop",children:e.jsxs("form",{className:"node-modal",onSubmit:n=>{n.preventDefault(),j("save",v.id,{...v,secret:be||void 0})},children:[e.jsx("h2",{children:v.id?t("nodeEdit"):t("nodeAdd")}),[["group",t("nodeGroup")],["name",t("nodeName")],["host",t("nodeAddress")],["port",t("nodePort")],["username",t("nodeUsername")],["defaultDir",t("nodeDefaultDir")]].map(([n,i])=>e.jsxs("label",{children:[i,e.jsx("input",{required:n!=="group",value:String(v[n]??""),onChange:o=>B({...v,[n]:n==="port"?Number(o.target.value):o.target.value})})]},n)),e.jsxs("label",{children:[t("nodeAuth"),e.jsxs("select",{value:v.auth,onChange:n=>B({...v,auth:n.target.value}),children:[e.jsx("option",{value:"password",children:t("nodePassword")}),e.jsx("option",{value:"key",children:t("nodeKey")}),e.jsx("option",{value:"agent",children:t("nodeAgentAuth")})]})]}),v.auth==="key"&&e.jsxs("label",{children:[t("nodeKeyPath"),e.jsx("input",{value:v.keyPath??"",onChange:n=>B({...v,keyPath:n.target.value})})]}),v.auth!=="agent"&&e.jsxs("label",{children:[v.auth==="key"?t("nodePassphrase"):t("nodePassword"),e.jsx("input",{type:"password",value:be,onChange:n=>F(n.target.value),placeholder:v.hasSecret?t("nodeKeepSecret"):""})]}),e.jsxs("div",{className:"node-modal-actions",children:[v.id&&v.fingerprint&&e.jsx("button",{type:"button",onClick:()=>{window.confirm(t("nodeForgetHostKeyConfirm",{name:v.name??""}))&&j("forget_host_key",v.id)},children:t("nodeForgetHostKey")}),v.id&&e.jsx("button",{type:"button",onClick:()=>{window.confirm(t("nodeDeleteConfirm",{name:v.name??""}))&&(j("delete",v.id),B(null))},children:t("nodeDelete")}),e.jsx("button",{type:"button",onClick:()=>B(null),children:t("cancel")}),e.jsx("button",{type:"submit",children:t("save")})]})]})})," "]})}export{Un as NodeWorkbench};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a as m,j as n}from"./markdown-
|
|
1
|
+
import{a as m,j as n}from"./markdown-BhJ_LInD.js";import{b as $,T as K,u as P,F as q,a as A,c as L,d as B,e as J,f as W,g as X,h as G,r as Q}from"./index-Bab44wX8.js";import{D as U,o as V}from"./xterm-D1D2FVe3.js";/* empty css */import"./react-BddUxV-9.js";function Y({conversationId:s,terminalId:r,command:f,cwd:R,active:t,send:u,register:x}){const v=m.useRef(null),w=m.useRef(t);w.current=t;const N=m.useRef(null),b=f?JSON.stringify(f):"";return m.useEffect(()=>{const d=v.current;if(!d)return;const a=new U({theme:$(),fontFamily:'"SF Mono", "JetBrains Mono", ui-monospace, Menlo, Consolas, monospace',fontSize:13,cursorBlink:!0,scrollback:8e3}),l=new V;a.loadAddon(l),a.open(d),N.current={term:a,fit:l},t&&a.focus();const j=()=>{a.options.theme=$()};window.addEventListener(K,j),a.attachCustomKeyEventHandler(o=>{var F;if(o.type!=="keydown")return!0;const E=(F=o.key)==null?void 0:F.toLowerCase();if((o.ctrlKey||o.metaKey)&&E==="v")return!1;if(o.ctrlKey&&!o.shiftKey&&!o.altKey&&E==="c"&&a.hasSelection()){const T=a.textarea;return T&&(T.value=a.getSelection(),T.select()),!1}return!0});const g=x(s,r,{write:o=>a.write(o),dispose:()=>a.dispose()}),C=()=>{if(!(!w.current||!d.clientWidth||!d.clientHeight))try{l.fit(),u({type:"terminal_resize",terminalId:r,conversationId:s,cols:a.cols,rows:a.rows})}catch{}},h=requestAnimationFrame(()=>{try{l.fit()}catch{}u(f?{type:"run_command",terminalId:r,conversationId:s,command:f,cols:a.cols,rows:a.rows}:{type:"terminal_create",terminalId:r,conversationId:s,cwd:R,cols:a.cols,rows:a.rows})}),k=a.onData(o=>{u({type:"terminal_input",terminalId:r,conversationId:s,data:o})});let y=null;return typeof ResizeObserver<"u"&&(y=new ResizeObserver(()=>{d.offsetWidth>0&&d.offsetHeight>0&&C()}),y.observe(d)),()=>{cancelAnimationFrame(h),k.dispose(),window.removeEventListener(K,j),y==null||y.disconnect(),g(),a.dispose(),N.current=null}},[s,r,b,u,x]),m.useEffect(()=>{if(!t)return;const d=requestAnimationFrame(()=>{const a=N.current;if(a){try{a.fit.fit(),u({type:"terminal_resize",terminalId:r,conversationId:s,cols:a.term.cols,rows:a.term.rows})}catch{}a.term.focus()}});return()=>cancelAnimationFrame(d)},[t]),n.jsx("div",{ref:v,className:`term-xterm ${t?"":"hidden"}`})}const H={name:"",command:"",cwd:"${pwd}"};function ae({chat:s,send:r,terminal:f,active:R}){const t=P(),[u,x]=m.useState(null),[v,w]=m.useState(!1),[N,b]=m.useState(!1),[d,a]=m.useState(null),[l,j]=m.useState(H),[g,C]=m.useState(null),h=m.useRef(null);m.useEffect(()=>{s.terminals.length===0?x(null):s.terminals.some(e=>e.id===u)||x(s.terminals[s.terminals.length-1].id)},[s.terminals,u]),m.useEffect(()=>()=>{h.current&&clearTimeout(h.current)},[]);const k=e=>{var p;if(!s.ready)return;const i=Q(),c=s.activeConversationId||((p=s.state)==null?void 0:p.conversationId)||"";f.create({...e,id:i,conversationId:c,cols:e.cols??80,rows:e.rows??24,running:!0,exitCode:null}),x(i),w(!1)},y=()=>{var e;return k({title:t("terminalTitle",{n:s.terminals.length+1}),cwd:((e=s.state)==null?void 0:e.cwd)??""})},o=e=>{var p;const i=e.name||e.command,c=s.terminals.find(_=>_.title===i);if(c){f.restart(c.id),x(c.id),r({type:"run_command",terminalId:c.id,conversationId:c.conversationId,command:e,cols:80,rows:24});return}k({title:i,cwd:((p=s.state)==null?void 0:p.cwd)??"",command:e})},E=e=>{const i=s.terminals.find(c=>c.id===e);if(i&&r({type:"terminal_kill",terminalId:e,conversationId:i.conversationId}),f.close(e),u===e){const c=s.terminals.filter(p=>p.id!==e);x(c.length>0?c[c.length-1].id:null)}},F=()=>{b(!0),a(null),j(H)},T=e=>{const i=s.commands[e];i&&(b(!1),a(e),j({name:i.name,command:i.command,cwd:i.cwd??""}))},D=()=>{b(!1),a(null)},S=()=>{const e=l.name.trim(),i=l.command.trim();if(!e||!i)return;const c=l.cwd.trim(),p={name:e,command:i,cwd:c||void 0},_=N?[...s.commands,p]:d!==null?s.commands.map((z,O)=>O===d?p:z):s.commands;r({type:"save_commands",commands:_}),D()},I=e=>{if(g===e){const i=s.commands.filter((c,p)=>p!==e);r({type:"save_commands",commands:i}),C(null),h.current&&clearTimeout(h.current)}else C(e),h.current&&clearTimeout(h.current),h.current=setTimeout(()=>C(null),2500)},M=N||d!==null;return n.jsxs("div",{className:"terminal-view",children:[n.jsxs("aside",{className:`term-side term-commands ${v?"open":""}`,children:[n.jsxs("div",{className:"panel-header",children:[n.jsx("span",{className:"panel-title",children:t("commands")}),n.jsxs("div",{className:"panel-header-actions",children:[n.jsx("button",{type:"button",className:"panel-refresh",title:t("rerun"),onClick:()=>r({type:"list_commands"}),children:n.jsx(q,{})}),n.jsx("button",{type:"button",className:"panel-new",title:t("newCommand"),onClick:F,children:n.jsx(A,{})})]})]}),n.jsx("div",{className:"panel-body",children:M?n.jsxs("div",{className:"cmd-form",children:[n.jsx("label",{htmlFor:"cmd-name",children:t("name")}),n.jsx("input",{id:"cmd-name",className:"cmd-input",value:l.name,placeholder:t("exampleName"),autoFocus:!0,onChange:e=>j({...l,name:e.target.value})}),n.jsx("label",{htmlFor:"cmd-command",children:t("command")}),n.jsx("input",{id:"cmd-command",className:"cmd-input",value:l.command,placeholder:t("exampleCommand"),onChange:e=>j({...l,command:e.target.value}),onKeyDown:e=>{e.key==="Enter"&&!e.nativeEvent.isComposing&&S()}}),n.jsxs("label",{htmlFor:"cmd-cwd",children:[t("directory")," ",n.jsx("span",{className:"cmd-hint",children:t("cwdHint")})]}),n.jsx("input",{id:"cmd-cwd",className:"cmd-input",value:l.cwd,placeholder:"${pwd}",onChange:e=>j({...l,cwd:e.target.value}),onKeyDown:e=>{e.key==="Enter"&&!e.nativeEvent.isComposing&&S()}}),n.jsxs("div",{className:"cmd-form-actions",children:[n.jsx("button",{type:"button",className:"btn",onClick:D,children:t("cancel")}),n.jsx("button",{type:"button",className:"btn primary",disabled:!l.name.trim()||!l.command.trim(),onClick:S,children:t("save")})]})]}):n.jsxs(n.Fragment,{children:[s.commands.length===0&&n.jsx("div",{className:"panel-empty",children:t("noCommands")}),s.commands.map((e,i)=>n.jsxs("div",{className:"cmd-item",children:[n.jsx("button",{type:"button",className:"cmd-run",title:t("clickToRun"),onClick:()=>o(e),children:n.jsx(L,{})}),n.jsxs("button",{type:"button",className:"cmd-main",title:t("clickToRun"),onClick:()=>o(e),children:[n.jsx("span",{className:"cmd-name",children:e.name}),n.jsx("span",{className:"cmd-command",children:e.command}),e.cwd&&n.jsx("span",{className:"cmd-cwd",children:e.cwd})]}),n.jsx("button",{type:"button",className:"cmd-act",title:t("edit"),onClick:()=>T(i),children:n.jsx(B,{})}),n.jsx("button",{type:"button",className:`cmd-act del ${g===i?"confirm":""}`,title:t("delete"),onClick:()=>I(i),children:g===i?t("confirmQ"):n.jsx(J,{})})]},i))]})}),n.jsxs("div",{className:"term-tabs-block",children:[n.jsxs("div",{className:"panel-header",children:[n.jsx("span",{className:"panel-title",children:t("terminal")}),n.jsx("button",{type:"button",className:"panel-new",title:t("newTerminal"),onClick:y,children:n.jsx(A,{})})]}),n.jsxs("div",{className:"panel-body",children:[s.terminals.length===0&&n.jsx("div",{className:"panel-empty",children:t("noTerminal")}),s.terminals.map(e=>n.jsxs("div",{className:`term-tab ${e.id===u?"active":""}`,children:[n.jsxs("button",{type:"button",className:"term-tab-main",title:`${e.cwd}${e.command?`
|
|
2
2
|
> ${e.command.command}`:""}`,onClick:()=>{x(e.id),w(!1)},children:[n.jsx("span",{className:`term-tab-dot ${e.running?"run":"exit"}`}),n.jsxs("span",{className:"term-tab-title",children:[e.title,!e.running&&n.jsx("span",{className:"term-tab-exit",children:t("exited",{code:e.exitCode===null?"":` ${e.exitCode}`})})]})]}),n.jsx("button",{type:"button",className:"term-tab-close",title:t("closeTerminal"),onClick:()=>E(e.id),children:n.jsx(W,{})})]},e.id))]})]})]}),n.jsxs("div",{className:"term-main",children:[v&&n.jsx("div",{className:"drawer-backdrop",onClick:()=>w(!1)}),n.jsx("button",{type:"button",className:"term-side-toggle",title:t("commands"),onClick:()=>w(e=>!e),children:n.jsx(X,{})}),s.terminals.length===0?n.jsxs("div",{className:"term-empty",children:[n.jsx(G,{className:"term-empty-icon"}),n.jsx("div",{className:"term-empty-title",children:t("builtinTerminal")}),n.jsx("div",{className:"term-empty-sub",children:t("termEmptySub")})]}):s.terminals.map(e=>n.jsx(Y,{conversationId:e.conversationId,terminalId:e.id,command:e.command,cwd:e.cwd,active:R&&e.id===u,send:r,register:f.register},`${e.conversationId}:${e.id}`))]})]})}export{ae as TerminalPanel};
|
package/web/dist/assets/{abnfDiagram-VCTEODGH-D5Ti0Kpa.js → abnfDiagram-VCTEODGH-CcM7f7oX.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as p,r as u,d as a}from"./chunk-SVP7TREG-
|
|
1
|
+
import{g as p,r as u,d as a}from"./chunk-SVP7TREG-BcdlBuOQ.js";import{p as f}from"./chunk-JWPE2WC7-B5iJgXvl.js";import{_ as n,l as o}from"./mermaid.core-CvQEO3ue.js";import{M as c,b as d}from"./cynefin-OW5HDTMX-DY0BDLjc.js";import"./index-Bab44wX8.js";import"./markdown-BhJ_LInD.js";import"./react-BddUxV-9.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},I={parser:R,db:a,renderer:u,styles:p};export{I as diagram};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a0 as ln,a1 as an,a2 as y,a3 as tn,a4 as H,a5 as q,a6 as _,a7 as un,a8 as rn,a9 as L,aa as o,ab as B,ac as sn,ad as on,ae as fn}from"./mermaid.core-
|
|
1
|
+
import{a0 as ln,a1 as an,a2 as y,a3 as tn,a4 as H,a5 as q,a6 as _,a7 as un,a8 as rn,a9 as L,aa as o,ab as B,ac as sn,ad as on,ae as fn}from"./mermaid.core-CvQEO3ue.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,C,a){var O=I-l,i=D-h,n=C-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function W(l,h,I,D,v,A,C){var a=l-I,O=h-D,i=(C?A:-A)/L(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,F=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*L(fn(0,T*T*R-P*P)),j=(P*g-m*S)/R,z=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=j-F,e=z-t,r=w-F,G=p-t;return x*x+e*e>r*r+G*G&&(j=w,z=p),{cx:j,cy:z,x01:-n,y01:-d,x11:j*(v/T-1),y11:z*(v/T-1)}}function hn(){var l=cn,h=yn,I=B(0),D=null,v=gn,A=dn,C=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-an,c=A.apply(this,arguments)-an,F=un(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(F>tn-y)a.moveTo(s*H(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*H(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=F,S=F,j=C.apply(this,arguments)/2,z=j>y&&(D?+D.apply(this,arguments):L(u*u+s*s)),w=_(un(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if(z>y){var G=sn(z/u*q(j)),M=sn(z/s*q(j));(P-=G*2)>y?(G*=t?1:-1,R+=G,T-=G):(P=0,R=T=(f+c)/2),(S-=M*2)>y?(M*=t?1:-1,m+=M,g-=M):(S=0,m=g=(f+c)/2)}var J=s*H(m),K=s*q(m),N=u*H(T),Q=u*q(T);if(w>y){var U=s*H(g),V=s*q(g),X=u*H(R),Y=u*q(R),E;if(F<rn)if(E=pn(J,K,X,Y,U,V,N,Q)){var Z=J-E[0],$=K-E[1],b=U-E[0],k=V-E[1],nn=1/q(on((Z*b+$*k)/(L(Z*Z+$*$)*L(b*b+k*k)))/2),en=L(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=W(X,Y,J,K,s,x,t),r=W(U,V,N,Q,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(J,K),a.arc(0,0,s,m,g,!t)):a.moveTo(J,K),!(u>y)||!(P>y)?a.lineTo(N,Q):p>y?(e=W(N,Q,U,V,u,-p,t),r=W(J,K,X,Y,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-rn/2;return[H(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:B(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:B(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:B(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:B(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:B(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:B(+n),i):A},i.padAngle=function(n){return arguments.length?(C=typeof n=="function"?n:B(+n),i):C},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
|