@shgroup/dsh-serenity-hooks 1.34.0 → 1.34.2
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 +1 -0
- package/dsh.plugin.json +1 -1
- package/lib/acp-core.d.ts +2 -22
- package/lib/acp-http.d.ts +8 -0
- package/lib/api.d.ts +12 -4
- package/lib/autopilot-chain.d.ts +76 -0
- package/lib/autopilot-core-DCRkiWEe.js +229 -0
- package/lib/autopilot-core.d.ts +132 -0
- package/lib/autopilot-ops.d.ts +48 -0
- package/lib/autopilot-trajectory-D9Znxo75.js +782 -0
- package/lib/autopilot-trajectory.d.ts +64 -97
- package/lib/ccc-roots-Bd_SjEs7.js +152 -0
- package/lib/ccc-roots.d.ts +105 -0
- package/lib/ccc.d.ts +3 -6
- package/lib/client.js +2 -2
- package/lib/clock-runtime.d.ts +197 -0
- package/lib/config-ops.d.ts +9 -14
- package/lib/constants.d.ts +0 -2
- package/lib/container-status.d.ts +183 -0
- package/lib/diag-ops.d.ts +25 -10
- package/lib/face-host.d.ts +110 -0
- package/lib/fs-ops.d.ts +3 -2
- package/lib/gateway-auth.d.ts +2 -5
- package/lib/gateway.d.ts +0 -34
- package/lib/git-ops.d.ts +3 -2
- package/lib/handyman-ops.d.ts +3 -2
- package/lib/handyman-preset-inherit.d.ts +2 -1
- package/lib/host/access.d.ts +8 -7
- package/lib/host/contract.d.ts +29 -6
- package/lib/host/type-contract.d.ts +1 -2
- package/lib/im-bridge.d.ts +6 -5
- package/lib/index.js +8336 -5607
- package/lib/invariant.d.ts +0 -15
- package/lib/invariant.js +1 -1
- package/lib/kit-ops.d.ts +11 -31
- package/lib/localstore-ops.d.ts +3 -17
- package/lib/msm-ops.d.ts +34 -52
- package/lib/opencode-provider.d.ts +4 -5
- package/lib/output-guard-seam.d.ts +0 -3
- package/lib/output-guard.d.ts +12 -3
- package/lib/ports.d.ts +59 -0
- package/lib/rebuild.d.ts +1 -1
- package/lib/seams/bootstrap.d.ts +4 -3
- package/lib/seams/compact.d.ts +2 -1
- package/lib/seams/context.d.ts +2 -4
- package/lib/seams/env.d.ts +2 -1
- package/lib/seams/guards.d.ts +4 -43
- package/lib/seams/keeper.d.ts +1 -1
- package/lib/seams/system-prompt.d.ts +1 -5
- package/lib/session-cleanup.d.ts +3 -2
- package/lib/{settings-section-BuPGQv_H.js → settings-section-DTYZjwUs.js} +65 -9
- package/lib/settings-section.d.ts +3 -31
- package/lib/skiff-core.d.ts +4 -9
- package/lib/skiff-debug.d.ts +15 -21
- package/lib/skiff-registry-FTjoWQTQ.js +25 -0
- package/lib/skiff-registry.d.ts +2 -1
- package/lib/{skiff-role-DAfPb6YQ.js → skiff-role-CEHL5cek.js} +1 -1
- package/lib/skiff-role.d.ts +2 -2
- package/lib/skills/opencode-scan.d.ts +3 -2
- package/lib/skills-discovery.d.ts +15 -2
- package/lib/status.d.ts +2 -1
- package/lib/tools/cce.d.ts +1 -2
- package/lib/tools/container-admin.d.ts +7 -3
- package/lib/tools/eap.d.ts +2 -3
- package/lib/tools/handyman.d.ts +0 -29
- package/lib/tools/msm.d.ts +0 -4
- package/lib/tools/neat.d.ts +1 -2
- package/lib/tools/praxis.d.ts +0 -1
- package/lib/tools/skiff-admin.d.ts +2 -2
- package/lib/tools/trajectory.d.ts +7 -15
- package/lib/totp.d.ts +0 -4
- package/lib/trajectory-assistant.d.ts +2 -1
- package/lib/trajectory-bound-Cax1p9ut.js +190 -0
- package/lib/trajectory-bound.d.ts +3 -4
- package/lib/{trajectory-bound-C8DUxi6_.js → trajectory-ops-mrXK0H0I.js} +2 -159
- package/lib/trajectory-ops.d.ts +4 -9
- package/lib/trajectory-skills.d.ts +71 -0
- package/lib/{wake-registry-D0Vj0WXb.js → wake-registry-CYxIb484.js} +7 -20
- package/lib/wake-registry.d.ts +3 -11
- package/lib/wake-scheduler.d.ts +13 -49
- package/lib/web-fetch-provider.d.ts +0 -21
- package/lib/{weixin-api-PjEhmjlZ.js → weixin-api-CtFYm53S.js} +3 -9
- package/lib/weixin-api.d.ts +6 -37
- package/lib/weixin-bridge.d.ts +5 -24
- package/lib/weixin-hook.d.ts +6 -12
- package/lib/weixin-output-guard.d.ts +2 -1
- package/lib/{weixin-route-D1ND823E.js → weixin-route-B2ajFypI.js} +4 -5
- package/lib/weixin-route.d.ts +0 -2
- package/lib/weixin-send-api.d.ts +15 -6
- package/package.json +2 -4
- package/experiments/autopilot-trajectory/SKILL.md +0 -115
- package/experiments/autopilot-trajectory/scripts/autopilot-trajectory.ts +0 -546
- package/lib/autopilot-script.d.ts +0 -40
- package/lib/skiff-debug-D83-Njy7.js +0 -2867
- package/lib/{ccc-eu_IPF94.js → ccc-NlLr_sxy.js} +1 -1
- package/lib/{session-cleanup-CMT4mHWt.js → session-cleanup-B6IHZgww.js} +1 -1
|
@@ -1,2867 +0,0 @@
|
|
|
1
|
-
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { a as findSerenityRoot, p as readHandymanConfig } from "./ccc-eu_IPF94.js";
|
|
3
|
-
import { a as readSkiffRoles, c as roleMsmWhitelist, f as trajectorySubset, i as isSkiffSessionId, n as buildSkiffBasePrompt, o as resolveRoleSystemPrompt, r as createRolePromptReader, s as resolveSkiffKind } from "./skiff-role-DAfPb6YQ.js";
|
|
4
|
-
import { i as hostSessions, r as hostService, t as hostAgents } from "./access-fiehjxV6.js";
|
|
5
|
-
import { c as createSession, d as getActiveSessionInfo, r as readLastBound, t as appendBound, v as setActiveSessionInfo } from "./trajectory-bound-C8DUxi6_.js";
|
|
6
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
7
|
-
import { basename, join } from "node:path";
|
|
8
|
-
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
9
|
-
import { randomBytes, randomUUID } from "node:crypto";
|
|
10
|
-
import { createServer } from "node:http";
|
|
11
|
-
//#region src/skiff-registry.ts
|
|
12
|
-
const skiffSessions = /* @__PURE__ */ new Map();
|
|
13
|
-
/** 查 sessionId 的 Skiff 角色名(无 → null)——向后兼容(guards/seams 依赖) */
|
|
14
|
-
function skiffRoleFor$1(sessionId) {
|
|
15
|
-
return skiffSessions.get(sessionId)?.role ?? null;
|
|
16
|
-
}
|
|
17
|
-
/** 查 sessionId 的完整绑定(role + ccc;无 → null)——v1.25.10 会话追问校验 */
|
|
18
|
-
function skiffSessionInfo$1(sessionId) {
|
|
19
|
-
return skiffSessions.get(sessionId) ?? null;
|
|
20
|
-
}
|
|
21
|
-
function registerSkiffSession$1(sessionId, role, ccc) {
|
|
22
|
-
skiffSessions.set(sessionId, {
|
|
23
|
-
role,
|
|
24
|
-
ccc
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
function unregisterSkiffSession$1(sessionId) {
|
|
28
|
-
skiffSessions.delete(sessionId);
|
|
29
|
-
}
|
|
30
|
-
/** 测试/调试:注册表快照(sessionId → {role, ccc}) */
|
|
31
|
-
function skiffSessionSnapshot$1() {
|
|
32
|
-
return new Map(skiffSessions);
|
|
33
|
-
}
|
|
34
|
-
//#endregion
|
|
35
|
-
//#region src/handyman-ops.ts
|
|
36
|
-
/**
|
|
37
|
-
* handyman-ops.ts — handyman(杂工)纯逻辑层(零 DSH 依赖,可独立单测)
|
|
38
|
-
*
|
|
39
|
-
* v1.24.0:loop(牛马)→ handyman(杂工)重命名。语义对齐 osp loop:
|
|
40
|
-
* 进度文件(handyman-<label>.md/.json)、续跑、轮次 prompt 结构、stop token。
|
|
41
|
-
* 不兼容旧 loop- 进度文件(用户拍板:仅新 handyman- 前缀)。
|
|
42
|
-
*/
|
|
43
|
-
/** label 脱敏(Windows 审计问题 17):非法字符 → '-',去尾点/空格,限长(按码点截断,修复代理对切散 U+FFFD) */
|
|
44
|
-
function sanitizeLabel(label) {
|
|
45
|
-
return [...label.replace(/[<>:"/\\|?*\u0000-\u001f]/g, "-").replace(/[ .]+$/g, "")].slice(0, 50).join("");
|
|
46
|
-
}
|
|
47
|
-
function handymanProgressPaths(root, label) {
|
|
48
|
-
const dir = join(root, "AGENT_SESSIONS");
|
|
49
|
-
const safe = sanitizeLabel(label);
|
|
50
|
-
return {
|
|
51
|
-
md: join(dir, `handyman-${safe}.md`),
|
|
52
|
-
json: join(dir, `handyman-${safe}.json`)
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
/** 读取进度(续跑);无文件返回 round 0 */
|
|
56
|
-
function readProgress(root, label) {
|
|
57
|
-
const { json } = handymanProgressPaths(root, label);
|
|
58
|
-
if (!existsSync(json)) return null;
|
|
59
|
-
try {
|
|
60
|
-
return JSON.parse(readFileSync(json, "utf-8"));
|
|
61
|
-
} catch {
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
function writeProgress(root, label, p) {
|
|
66
|
-
const { md, json } = handymanProgressPaths(root, label);
|
|
67
|
-
mkdirSync(join(root, "AGENT_SESSIONS"), { recursive: true });
|
|
68
|
-
writeFileSync(json, JSON.stringify({
|
|
69
|
-
...p,
|
|
70
|
-
status: p.status ?? "running",
|
|
71
|
-
updated: (/* @__PURE__ */ new Date()).toISOString()
|
|
72
|
-
}, null, 2) + "\n", "utf-8");
|
|
73
|
-
const lines = [
|
|
74
|
-
`# handyman: ${label}`,
|
|
75
|
-
`- Model: ${p.model}`,
|
|
76
|
-
`- Round: ${p.round}`,
|
|
77
|
-
`- Done: ${p.done}`,
|
|
78
|
-
"",
|
|
79
|
-
`## Latest response`,
|
|
80
|
-
"",
|
|
81
|
-
p.lastResponse,
|
|
82
|
-
""
|
|
83
|
-
];
|
|
84
|
-
writeFileSync(md, lines.join("\n"), "utf-8");
|
|
85
|
-
}
|
|
86
|
-
/** 失败状态落盘(对齐 osp writeFailedStatus:done=true / status=failed / errorCode) */
|
|
87
|
-
function writeFailedStatus(root, label, info) {
|
|
88
|
-
const { json } = handymanProgressPaths(root, label);
|
|
89
|
-
mkdirSync(join(root, "AGENT_SESSIONS"), { recursive: true });
|
|
90
|
-
const prev = readProgress(root, label);
|
|
91
|
-
writeFileSync(json, JSON.stringify({
|
|
92
|
-
round: prev?.round ?? 0,
|
|
93
|
-
done: true,
|
|
94
|
-
label,
|
|
95
|
-
model: prev?.model ?? "",
|
|
96
|
-
status: "failed",
|
|
97
|
-
errorCode: info.errorCode,
|
|
98
|
-
errorMessage: info.errorMessage,
|
|
99
|
-
updated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
100
|
-
lastResponse: prev?.lastResponse ?? ""
|
|
101
|
-
}, null, 2) + "\n", "utf-8");
|
|
102
|
-
}
|
|
103
|
-
function newStopToken() {
|
|
104
|
-
return `SERENITY_HANDYMAN_DONE_${randomBytes(8).toString("hex")}`;
|
|
105
|
-
}
|
|
106
|
-
/** 解析 model 字符串(provider/model)→ {provider, model};无 / 视为 model-only */
|
|
107
|
-
function splitModel(model) {
|
|
108
|
-
const idx = model.indexOf("/");
|
|
109
|
-
if (idx < 0) return {
|
|
110
|
-
provider: void 0,
|
|
111
|
-
model
|
|
112
|
-
};
|
|
113
|
-
return {
|
|
114
|
-
provider: model.slice(0, idx),
|
|
115
|
-
model: model.slice(idx + 1)
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
/** 校验模型在白名单内;不在 → 抛错(用户拍板:只能使用 CCC 配置的模型) */
|
|
119
|
-
function requireWhitelistedModel(model, models) {
|
|
120
|
-
if (!models.includes(model)) throw new Error(`handyman: model "${model}" is not in the CCC whitelist. Configure .opencode/serenity.json "handyman.models" with one of: ${models.join(", ")}`);
|
|
121
|
-
}
|
|
122
|
-
/** 轮次 prompt(对齐老 loop 结构:回顾进度 → 自由工作 → 汇报;S134 EAP 化:固定详尽) */
|
|
123
|
-
function buildRoundPrompt(opts) {
|
|
124
|
-
const { root, session, label, round, stopToken, progress, task } = opts;
|
|
125
|
-
const resumeNote = progress && progress.round > 0 ? `Previous round (round ${progress.round}) completed: ${progress.lastResponse.slice(0, 300)}\nAlways continue from where you left off; never redo completed work.` : "This is the first round.";
|
|
126
|
-
return `# ${label} — handyman round ${round}
|
|
127
|
-
|
|
128
|
-
CCC root: ${root}
|
|
129
|
-
${session ? `Work session: ${session} (progress recorded in AGENT_SESSIONS/${session}/SESSION.md)` : ""}
|
|
130
|
-
${task ? `Task: ${task}` : `Task: follow the work corresponding to label "${label}" (if a work session exists, read SESSION.md first to clarify the goal)`}
|
|
131
|
-
${resumeNote}
|
|
132
|
-
|
|
133
|
-
## Work rules (fixed every round, must follow)
|
|
134
|
-
1. Work freely within this round: read files, modify code, execute commands — use every means to advance the task.
|
|
135
|
-
2. If this task is **reading/curating or text-writing work** (extracting from files, summarizing, writing docs, generating text, etc.),
|
|
136
|
-
first load eap (acc-eap skill) and organize output per the EAP standard:
|
|
137
|
-
- E↑ Explicit: entities/variables clearly defined, relationships with direction and cardinality, boundaries drawn, no ambiguous words
|
|
138
|
-
- R↓ Reconstructable: key conclusions record sources and reasoning, rebuildable by later agents
|
|
139
|
-
- S↑ Stable: output structure regenerates repeatably, no reliance on implicit context
|
|
140
|
-
3. Reports must be concrete and verifiable — no filler.
|
|
141
|
-
|
|
142
|
-
## Per-round report (fixed format, answer each item)
|
|
143
|
-
1. What was done this round (concrete)
|
|
144
|
-
2. Next-step plan
|
|
145
|
-
3. Whether the task is complete (if complete, output only ${stopToken})
|
|
146
|
-
|
|
147
|
-
If the task is complete, output only ${stopToken}.`;
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* handyman 规模化使用指引(guide 子命令输出;S134 继承 + v1.24.0 更新):
|
|
151
|
-
* 使用 handyman 前必须先加载 eap 设计方案;并行策略(jobs 编排);提示词规范(详尽固定 EAP);
|
|
152
|
-
* 阅读/文字编写类 handyman 内部也加载 eap。
|
|
153
|
-
*/
|
|
154
|
-
const HANDYMAN_GUIDE = `# handyman — Scale-Up Usage Guide (guide)
|
|
155
|
-
|
|
156
|
-
## Two modes (v1.31.3; default = foreground)
|
|
157
|
-
| | foreground (default) | background |
|
|
158
|
-
|---|---|---|
|
|
159
|
-
| What | one serial child agent runs the task ONCE and returns its final text | loop-validated worker: hard while-loop + random completion code |
|
|
160
|
-
| Use when | you need exactly one result back now (per-case testers, single conversions) | long/unattended work needing anti-early-finish, resumability, or parallel jobs |
|
|
161
|
-
| Loop / validation | none | stop-token is the ONLY completion condition; round cap (default 100); auto-restart (≤100) |
|
|
162
|
-
| Progress file | none | AGENT_SESSIONS/handyman-<label>.md/.json (same label resumes) |
|
|
163
|
-
| Parallel jobs | no (call once per task) | yes (jobs=[...], handyman.maxParallel default 10) |
|
|
164
|
-
| Mechanism | host delegation service ctx.subagents.start("spawn") + agentOptions | ctx.agents.create + internal while-loop |
|
|
165
|
-
| Model source | CCC handyman.models whitelist (same for both) | same |
|
|
166
|
-
|
|
167
|
-
## ⚠️ Before using: load eap and design the plan
|
|
168
|
-
Before calling handyman, load eap (acc-eap skill) and design the "scale-up handyman plan" based on the EAP framework.
|
|
169
|
-
|
|
170
|
-
### 1. Task decomposition (E↑ Explicit)
|
|
171
|
-
- Split large tasks into explicit subtasks: each subtask defines goal / input / boundaries (what to do, what not to do) / acceptance criteria
|
|
172
|
-
- Make dependencies explicit: dependent tasks run serially, independent ones can run in parallel (background mode)
|
|
173
|
-
|
|
174
|
-
### 2. Prompt design (handyman's task parameter)
|
|
175
|
-
- task must be detailed, fixed, and EAP-compliant: clear goal, drawn boundaries, decidable acceptance criteria
|
|
176
|
-
- foreground mode: the child does NOT share your conversation — restate everything it needs in task
|
|
177
|
-
- Anti-example "handle this file" — ambiguous; good example "read <path>, extract all rows of the「关键决策」table,
|
|
178
|
-
output a JSON array (fields id/conclusion/evidence), do not modify the original file"
|
|
179
|
-
- Reading/curating or text-writing work (extracting from files, summarizing, writing docs, generating text, etc.):
|
|
180
|
-
the handyman-internal agent is also required to load eap and organize output per the EAP standard
|
|
181
|
-
|
|
182
|
-
### 3. Model whitelist (CCC-configured, mandatory)
|
|
183
|
-
- handyman only uses models listed in .opencode/serenity.json "handyman.models" — never arbitrary models
|
|
184
|
-
- The CCC is expected to point this whitelist at LOW-COST models: handyman is the bulk-execution channel
|
|
185
|
-
- background mode: recursive subagents inside a worker inherit the worker's model automatically (DSH native)
|
|
186
|
-
- foreground mode: the child is started through the host delegation service with the resolved model
|
|
187
|
-
|
|
188
|
-
### 4. Parallel strategy (background mode only)
|
|
189
|
-
- Independent subtasks can run in parallel via handyman(mode="background", jobs=[...]): each job gets its own label + task + stop token + progress file
|
|
190
|
-
- Concurrency safety guaranteed: unique sessionId (handyman-<label>-<uuid>), progress files isolated per label
|
|
191
|
-
(AGENT_SESSIONS/handyman-<label>.json) — same label resumes, different labels never interfere
|
|
192
|
-
- Parallel cap: handyman.maxParallel (default 10 — cheap models are cheap)
|
|
193
|
-
- Aggregation: after each parallel job produces progress, the main agent merges (or spawns one aggregation handyman)
|
|
194
|
-
- For programmable pipeline/phase orchestration at scale, use the platform's workflow tool instead
|
|
195
|
-
|
|
196
|
-
## Completion criteria
|
|
197
|
-
- foreground: the child's run settles; done=true only when stopReason="completed" (otherwise read diagnostic)
|
|
198
|
-
- background (osp loop standard): the only completion condition = the worker echoes this round's random verification
|
|
199
|
-
code (stop token); dialogue round cap (default 100, osp fail-safe, forced stop beyond the cap, resumable);
|
|
200
|
-
automatic restart on abnormal agent stop (≤100 restarts, anti-infinite-loop)
|
|
201
|
-
|
|
202
|
-
## Waiting UI
|
|
203
|
-
- The WebUI session-header Serenity detail card shows running background handymen's progress
|
|
204
|
-
(label / round / last response), one line per parallel job, ~3s refresh (foreground mode writes no progress)
|
|
205
|
-
`;
|
|
206
|
-
/** 列出 AGENT_SESSIONS/handyman-*.json 的全部进度(按 updated 倒序;坏文件跳过) */
|
|
207
|
-
function listActiveHandymen(root) {
|
|
208
|
-
const dir = join(root, "AGENT_SESSIONS");
|
|
209
|
-
if (!existsSync(dir)) return [];
|
|
210
|
-
const out = [];
|
|
211
|
-
for (const entry of readdirSync(dir)) {
|
|
212
|
-
if (!entry.startsWith("handyman-") || !entry.endsWith(".json")) continue;
|
|
213
|
-
try {
|
|
214
|
-
const data = JSON.parse(readFileSync(join(dir, entry), "utf-8"));
|
|
215
|
-
if (typeof data.label !== "string" || typeof data.round !== "number") continue;
|
|
216
|
-
out.push({
|
|
217
|
-
label: data.label,
|
|
218
|
-
round: data.round,
|
|
219
|
-
done: data.done === true,
|
|
220
|
-
model: typeof data.model === "string" ? data.model : "",
|
|
221
|
-
updated: typeof data.updated === "string" ? data.updated : "",
|
|
222
|
-
lastResponse: typeof data.lastResponse === "string" ? data.lastResponse : ""
|
|
223
|
-
});
|
|
224
|
-
} catch {}
|
|
225
|
-
}
|
|
226
|
-
out.sort((a, b) => a.updated < b.updated ? 1 : -1);
|
|
227
|
-
return out;
|
|
228
|
-
}
|
|
229
|
-
//#endregion
|
|
230
|
-
//#region src/agent-idle.ts
|
|
231
|
-
/**
|
|
232
|
-
* 等待 agent 空闲或销毁。
|
|
233
|
-
* @param ctx 宿主插件上下文(事件通道来源)
|
|
234
|
-
* @param agent 目标 agent
|
|
235
|
-
* @returns 空闲、agent 销毁、或会话销毁时结算(永不 reject)
|
|
236
|
-
*/
|
|
237
|
-
function waitAgentIdle(ctx, agent) {
|
|
238
|
-
return new Promise((resolve) => {
|
|
239
|
-
let settled = false;
|
|
240
|
-
const disposers = [];
|
|
241
|
-
const finish = () => {
|
|
242
|
-
if (settled) return;
|
|
243
|
-
settled = true;
|
|
244
|
-
for (const dispose of disposers) try {
|
|
245
|
-
dispose();
|
|
246
|
-
} catch {}
|
|
247
|
-
resolve();
|
|
248
|
-
};
|
|
249
|
-
const subscribe = (name, cb) => {
|
|
250
|
-
try {
|
|
251
|
-
const off = ctx.on?.(name, cb);
|
|
252
|
-
if (typeof off === "function") disposers.push(off);
|
|
253
|
-
} catch {}
|
|
254
|
-
};
|
|
255
|
-
subscribe("agent/status", (payload) => {
|
|
256
|
-
const p = payload;
|
|
257
|
-
if (p?.agent === agent && p?.status === "idle") finish();
|
|
258
|
-
});
|
|
259
|
-
subscribe("agent/disposed", (payload) => {
|
|
260
|
-
if (payload?.agent === agent) finish();
|
|
261
|
-
});
|
|
262
|
-
const sessionId = agent.session?.id;
|
|
263
|
-
subscribe("session/disposed", (session) => {
|
|
264
|
-
const id = session?.id;
|
|
265
|
-
if (typeof id === "string" && id === sessionId) finish();
|
|
266
|
-
});
|
|
267
|
-
if (agent.status === "idle") finish();
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
//#endregion
|
|
271
|
-
//#region src/skiff-core.ts
|
|
272
|
-
const PLUGIN_SOURCE = {
|
|
273
|
-
kind: "plugin",
|
|
274
|
-
plugin: "dsh-serenity-hooks"
|
|
275
|
-
};
|
|
276
|
-
const skiffAgents = /* @__PURE__ */ new Map();
|
|
277
|
-
/** 查 sessionId 的 Skiff 角色名(无 → null) */
|
|
278
|
-
function skiffRoleFor(sessionId) {
|
|
279
|
-
return skiffRoleFor$1(sessionId);
|
|
280
|
-
}
|
|
281
|
-
/** 查 sessionId 的会话绑定(role + ccc;无 → null)——v1.25.10 追问校验 */
|
|
282
|
-
function skiffSessionInfo(sessionId) {
|
|
283
|
-
return skiffSessionInfo$1(sessionId);
|
|
284
|
-
}
|
|
285
|
-
/** 查 sessionId 的活体 agent(进程内会话延续用;未注册/已清理 → undefined) */
|
|
286
|
-
function getSkiffAgent(sessionId) {
|
|
287
|
-
return skiffAgents.get(sessionId);
|
|
288
|
-
}
|
|
289
|
-
function registerSkiffSession(sessionId, role, ccc, agent) {
|
|
290
|
-
skiffAgents.set(sessionId, agent);
|
|
291
|
-
registerSkiffSession$1(sessionId, role, ccc);
|
|
292
|
-
}
|
|
293
|
-
function unregisterSkiffSession(sessionId) {
|
|
294
|
-
skiffAgents.delete(sessionId);
|
|
295
|
-
unregisterSkiffSession$1(sessionId);
|
|
296
|
-
}
|
|
297
|
-
/** 测试/调试:注册表快照(sessionId → {role} 兼容展示) */
|
|
298
|
-
function skiffSessionSnapshot() {
|
|
299
|
-
const out = /* @__PURE__ */ new Map();
|
|
300
|
-
for (const [id, b] of skiffSessionSnapshot$1()) out.set(id, {
|
|
301
|
-
role: b.role,
|
|
302
|
-
ccc: b.ccc
|
|
303
|
-
});
|
|
304
|
-
return out;
|
|
305
|
-
}
|
|
306
|
-
/** 自动创建的 bound 标记(readLastBound 恢复判定——只恢复本机制建的;手工 use 的旧 bound 不认) */
|
|
307
|
-
const AUTO_BOUND_NOTE_PREFIX = "auto-created for skiff role";
|
|
308
|
-
/**
|
|
309
|
-
* 系统提示词中的**工作台纪律块**(v1.30.4 升级:从单行路径 → 完整 ACC 层约束)。
|
|
310
|
-
* 用户点破(S142):只给路径 LLM 不会主动用——需注入「已绑定 + 使用纪律 + 动作指引」。
|
|
311
|
-
* SESSION = 工作台——skiff 与主舱同一套机制(零特调,用户拍板):
|
|
312
|
-
* 自动绑定已生效(无需 container_trajectory use)、SESSION.md 是持久记忆载体、rebuild 自动从本 SESSION 续接。
|
|
313
|
-
* 注入 agent 系统提示词(用户对话面不可见)。
|
|
314
|
-
*
|
|
315
|
-
* v1.30.13(S142 诊断 D5):写入纪律**不点名具体工具**。旧文案写死 "with write/edit",
|
|
316
|
-
* 而 zhaocai 角色的工具白名单已移除 write/edit(写能力收归 CCC 的 session-write MSM)
|
|
317
|
-
* → 指令指向它根本没有的工具(注入纪律与能力面矛盾,模型只能猜)。
|
|
318
|
-
* 现文案改为「用本角色被授权的写入通道」——ACC 只声明义务(写进工作台),
|
|
319
|
-
* 具体通道由 CCC 的角色配置与角色提示词决定(ACC/CCC 归属二分;也不把某个 CCC 的
|
|
320
|
-
* MSM 名硬编码进 ACC 源码)。
|
|
321
|
-
*/
|
|
322
|
-
function workspaceTrajectoryLine(mdPath) {
|
|
323
|
-
return [
|
|
324
|
-
"── Serenity Session Workspace ──",
|
|
325
|
-
"This role session is AUTO-BOUND to a trajectory workspace SESSION (no container_trajectory use needed):",
|
|
326
|
-
` SESSION.md: ${mdPath}`,
|
|
327
|
-
"",
|
|
328
|
-
"Rules of the workspace:",
|
|
329
|
-
" 1. This SESSION.md is your persistent memory carrier (like the main cabin SESSION) — ",
|
|
330
|
-
" read it first when context feels thin or work spans turns.",
|
|
331
|
-
" 2. Record key decisions/progress/unresolved items INTO this SESSION.md using the write channel",
|
|
332
|
-
" your role is actually granted (use whatever write tool or MSM this role has — never assume",
|
|
333
|
-
" a specific write tool exists; if none is granted, report that instead of pretending to write).",
|
|
334
|
-
" Sections: 目标 / 状态 / 关键决策 / 进度记录 / 未解决的问题. Keep it current — it is",
|
|
335
|
-
" what a rebuild resumes from.",
|
|
336
|
-
" 3. When context pressure is high, run container_trajectory rebuild — it auto-resumes from THIS SESSION.md",
|
|
337
|
-
" (no manual use; do not switch to or read other SESSIONs unless the user explicitly asks).",
|
|
338
|
-
" 4. Never expose this internal path in user-facing replies (chat stays clean).",
|
|
339
|
-
"── ──"
|
|
340
|
-
].join("\n");
|
|
341
|
-
}
|
|
342
|
-
/**
|
|
343
|
-
* 已挂工作台提示词段的 agent(幂等:同一 agent 只注册一次,避免 duplicate section)。
|
|
344
|
-
* WeakSet → agent 被回收即自动清理,无泄漏。
|
|
345
|
-
*/
|
|
346
|
-
const workspaceSectionAgents = /* @__PURE__ */ new WeakSet();
|
|
347
|
-
/**
|
|
348
|
-
* 把**工作台纪律块**挂到 agent 的系统提示词段(单一真相源,v1.30.13 用户需求)。
|
|
349
|
-
*
|
|
350
|
-
* 用户原话(S142,2026-09-08):"微信桥的注入机制,每个用户消息都会注入,skiff 本身也会注入,
|
|
351
|
-
* 这样就重复,能否微信桥情况下,注入内容直接取 skiff 的,这样不用配两遍"。
|
|
352
|
-
*
|
|
353
|
-
* 现状(改前):`createSkiffAgent` 把 `workspaceTrajectoryLine(mdPath)` 塞进基础段
|
|
354
|
-
* (创建时一次快照),微信桥又**每条消息**把同一文本拼进 question(v1.30.4 为覆盖
|
|
355
|
-
* "live/existing 快路径不重挂提示词"而加)→ 新建 agent 首轮起即重复,且每轮重复付 token。
|
|
356
|
-
*
|
|
357
|
-
* 现形态(单一注入点):工作台行**只**经本函数挂系统提示词段(name `serenity-skiff-workspace`,
|
|
358
|
-
* 动态 `text()` 每轮按 scope 读当前活跃 mdPath → 绑定变化自动跟随,无需重挂):
|
|
359
|
-
* - `createSkiffAgent` 调它(不再把工作台行拼进基础段);
|
|
360
|
-
* - 微信桥对 live/existing agent 调它(不再拼 question)。
|
|
361
|
-
*
|
|
362
|
-
* @returns true = 段已在位(本次注册成功或此前已注册);false = 该 agent 无法挂系统提示词段
|
|
363
|
-
* (宿主 systemPrompt 缺失/注册抛错)→ 调用方降级为 question 前缀注入(约束必须在场)
|
|
364
|
-
*/
|
|
365
|
-
function ensureWorkspacePromptSection(agent, scope) {
|
|
366
|
-
if (!agent || typeof agent !== "object") return false;
|
|
367
|
-
if (workspaceSectionAgents.has(agent)) return true;
|
|
368
|
-
const api = agent.ctx?.systemPrompt;
|
|
369
|
-
if (typeof api?.section !== "function") return false;
|
|
370
|
-
try {
|
|
371
|
-
api.section({
|
|
372
|
-
name: "serenity-skiff-workspace",
|
|
373
|
-
order: -55,
|
|
374
|
-
text: () => {
|
|
375
|
-
const mdPath = getActiveSessionInfo(scope)?.mdPath;
|
|
376
|
-
return mdPath ? workspaceTrajectoryLine(mdPath) : "";
|
|
377
|
-
}
|
|
378
|
-
});
|
|
379
|
-
workspaceSectionAgents.add(agent);
|
|
380
|
-
return true;
|
|
381
|
-
} catch (err) {
|
|
382
|
-
console.warn(`[serenity-hooks] skiff 工作台提示词段注册失败(回退 question 注入): ${String(err?.message ?? err)}`);
|
|
383
|
-
return false;
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
/**
|
|
387
|
-
* 确保启用 session 能力的 skiff 会话拥有**专属 SESSION**(工作台):
|
|
388
|
-
* 懒绑定 + 幂等(per skiff 会话 scope 隔离——微信多用户各自独立,永不共享同一份)。
|
|
389
|
-
*
|
|
390
|
-
* 判定链:
|
|
391
|
-
* ⓪ **临时身份不建工作台**(v1.30.9,S142 用户报"SESSION 爆炸"根治):`persistent=false`
|
|
392
|
-
* (ACP/调试页/问答页等**无固定会话 id**的临时 agent)→ null。理由(R↓):这些 agent 每次
|
|
393
|
-
* 调用都是新 id、无身份延续,为其建 SESSION = 每次问答留一个永久空目录(无界熵增);
|
|
394
|
-
* 用户模型「skiff 本质是临时会话机制」——工作台属于**持久身份**,不属于单次调用。
|
|
395
|
-
* ① 角色未启用 session 能力(trajectory.session !== true)→ null(现状零变化)
|
|
396
|
-
* ② 已有**本机制自动创建**的 bound(note 前缀 auto-created)→ 恢复(重启/续接记得)
|
|
397
|
-
* ②b 内存活跃记录(同 scope)→ 恢复 + **自愈补写绑定**(防御纵深:绑定写失败/事件形态未落盘时
|
|
398
|
-
* 仍复用同一 SESSION,而不是每条消息新建一个——v1.30.5 及更早"每条消息一个 SESSION"的放大效应)
|
|
399
|
-
* ③ 无 → 自动创建专属 SESSION(createSession desc=`<role> skiff`——编号自动递增,用户不关心是谁)
|
|
400
|
-
*
|
|
401
|
-
* @param persistent 该 skiff 会话是否有**持久身份**(固定会话 id)。默认 true(向后兼容调用点);
|
|
402
|
-
* `createSkiffAgent` 传 `sessionId !== undefined`,微信桥的 existing 快路径传 true。
|
|
403
|
-
* @returns 绑定后的 SESSION.md 绝对路径(供提示词注入);不适用/失败 → null
|
|
404
|
-
*/
|
|
405
|
-
function ensureSkiffSession(root, agent, roleName, role, persistent = true) {
|
|
406
|
-
if (!trajectorySubset(role).session) return null;
|
|
407
|
-
if (!persistent) return null;
|
|
408
|
-
const session = agent.session;
|
|
409
|
-
const scope = String(agent.session.id ?? agent.id);
|
|
410
|
-
const lastBound = session ? readLastBound(session) : null;
|
|
411
|
-
if (lastBound && lastBound.note?.startsWith(AUTO_BOUND_NOTE_PREFIX) && existsSync(lastBound.mdPath)) {
|
|
412
|
-
setActiveSessionInfo(scope, {
|
|
413
|
-
sessionId: lastBound.sessionId ?? "",
|
|
414
|
-
dirName: lastBound.dirName,
|
|
415
|
-
mdPath: lastBound.mdPath
|
|
416
|
-
});
|
|
417
|
-
return lastBound.mdPath;
|
|
418
|
-
}
|
|
419
|
-
const inMemory = getActiveSessionInfo(scope);
|
|
420
|
-
if (inMemory?.mdPath && existsSync(inMemory.mdPath)) {
|
|
421
|
-
appendBound(session, "reconcile", {
|
|
422
|
-
dirName: inMemory.dirName,
|
|
423
|
-
mdPath: inMemory.mdPath,
|
|
424
|
-
...inMemory.sessionId ? { sessionId: inMemory.sessionId } : {},
|
|
425
|
-
note: `${AUTO_BOUND_NOTE_PREFIX} ${roleName}`
|
|
426
|
-
});
|
|
427
|
-
return inMemory.mdPath;
|
|
428
|
-
}
|
|
429
|
-
const desc = `${roleName} skiff`;
|
|
430
|
-
const created = createSession({
|
|
431
|
-
root,
|
|
432
|
-
desc,
|
|
433
|
-
dryRun: false
|
|
434
|
-
});
|
|
435
|
-
const mdPath = join(created.sessionPath, "SESSION.md");
|
|
436
|
-
setActiveSessionInfo(scope, {
|
|
437
|
-
sessionId: created.sessionId,
|
|
438
|
-
dirName: created.dirName,
|
|
439
|
-
mdPath
|
|
440
|
-
});
|
|
441
|
-
appendBound(session, "create", {
|
|
442
|
-
dirName: created.dirName,
|
|
443
|
-
mdPath,
|
|
444
|
-
sessionId: created.sessionId,
|
|
445
|
-
note: `${AUTO_BOUND_NOTE_PREFIX} ${roleName}`
|
|
446
|
-
});
|
|
447
|
-
return mdPath;
|
|
448
|
-
}
|
|
449
|
-
/** Skiff agent 挂载的 DSH preset(v1.25.3 修复:read/grep/glob 等平台工具由 preset 决定工具面;
|
|
450
|
-
* handyman 经 composeFrom 继承父、skiff 无父上下文——直接挂 DSH 默认 standard preset;
|
|
451
|
-
* guard 角色白名单再按角色过滤可见/可用面——白名单外工具仍 deny) */
|
|
452
|
-
const SKIFF_PRESET = "standard";
|
|
453
|
-
/**
|
|
454
|
-
* 创建 Skiff agent:标准 DSH agent + cwd=CCC root + 角色模型 +
|
|
455
|
-
* standard preset(平台工具面)+ scoped 系统提示词(基础提示词 + CCC 定义段,全替换 ACC 默认注入)。
|
|
456
|
-
*
|
|
457
|
-
* **固定 sessionId 的 resume-or-create(v1.27.2,微信桥 id collision 修复)**:
|
|
458
|
-
* 指定 sessionId 且磁盘已有持久化 log → `ctx.agents.resume`(DSH 持久化语义:
|
|
459
|
-
* sessionId 即身份,已持久化会话必须 resume——历史 + turn 续号延续,同用户长期记忆保留);
|
|
460
|
-
* 无 log(首次)/持久化未配置 → `ctx.agents.create`(新建)。不指定 sessionId →
|
|
461
|
-
* 随机 id 恒 create(ACP/调试页默认路径)。
|
|
462
|
-
*
|
|
463
|
-
* @param sessionId 指定会话 id(可选;微信桥等外部面用固定 id 实现用户↔会话长期映射——
|
|
464
|
-
* 不传则随机生成 skiff-<role>-<uuid>,ACP/调试页默认路径)
|
|
465
|
-
*/
|
|
466
|
-
async function createSkiffAgent(ctx, root, roleName, role, defaultModel, sessionId) {
|
|
467
|
-
if (!ctx.agents) throw new Error("skiff: ctx.agents unavailable");
|
|
468
|
-
const model = role.model?.trim() || defaultModel || "";
|
|
469
|
-
const id = sessionId ?? `skiff-${roleName}-${randomUUID()}`;
|
|
470
|
-
const setup = async (agentCtx) => {
|
|
471
|
-
try {
|
|
472
|
-
await agentCtx.get("agentPresets")?.mount?.(agentCtx, SKIFF_PRESET);
|
|
473
|
-
} catch {}
|
|
474
|
-
};
|
|
475
|
-
const handle = await createOrResumeAgent(ctx, id, root, model, setup, sessionId !== void 0);
|
|
476
|
-
const agent = handle.agent;
|
|
477
|
-
const kind = resolveSkiffKind(role, sessionId !== void 0);
|
|
478
|
-
const rolePrompt = kind === "persistent" ? createRolePromptReader(root, role) : (() => {
|
|
479
|
-
let snapshot = "";
|
|
480
|
-
try {
|
|
481
|
-
snapshot = resolveRoleSystemPrompt(root, role);
|
|
482
|
-
} catch (err) {
|
|
483
|
-
console.warn(`[serenity-hooks] skiff 角色 "${roleName}" 系统提示词解析失败(回退仅基础段): ${String(err?.message ?? err)}`);
|
|
484
|
-
}
|
|
485
|
-
return () => snapshot;
|
|
486
|
-
})();
|
|
487
|
-
rolePrompt();
|
|
488
|
-
let workspaceMdPath = null;
|
|
489
|
-
try {
|
|
490
|
-
workspaceMdPath = ensureSkiffSession(root, agent, roleName, role, kind === "persistent");
|
|
491
|
-
} catch (err) {
|
|
492
|
-
console.warn(`[serenity-hooks] skiff 角色 "${roleName}" 专属 SESSION 处理失败(不影响 agent 创建): ${String(err?.message ?? err)}`);
|
|
493
|
-
}
|
|
494
|
-
try {
|
|
495
|
-
agent.ctx.systemPrompt.section({
|
|
496
|
-
name: "serenity-skiff",
|
|
497
|
-
order: -60,
|
|
498
|
-
text: () => [buildSkiffBasePrompt(roleName, role), rolePrompt()].filter(Boolean).join("\n")
|
|
499
|
-
});
|
|
500
|
-
} catch (err) {
|
|
501
|
-
console.warn(`[serenity-hooks] skiff 系统提示词注册失败: ${String(err?.message ?? err)}`);
|
|
502
|
-
}
|
|
503
|
-
if (workspaceMdPath) ensureWorkspacePromptSection(agent, id);
|
|
504
|
-
registerSkiffSession(id, roleName, root, agent);
|
|
505
|
-
return {
|
|
506
|
-
handle,
|
|
507
|
-
agent,
|
|
508
|
-
sessionId: id,
|
|
509
|
-
resumed: handle.resumed
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
/** 取 live agent(DSH 文档:`ctx.agents.get(id)` 返回 bare Agent;会话 live 时 resume/create 均不可用) */
|
|
513
|
-
function getLiveAgent(ctx, id) {
|
|
514
|
-
try {
|
|
515
|
-
return hostAgents(ctx)?.get?.(id);
|
|
516
|
-
} catch {
|
|
517
|
-
return;
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
/** live 复用返回(resumed:true 历史延续;dispose 空操作——live agent 不归本模块所有,不能拆) */
|
|
521
|
-
function liveReuseRef(live, setup) {
|
|
522
|
-
return setup(live.ctx).catch(() => {}).then(() => ({
|
|
523
|
-
agent: live,
|
|
524
|
-
dispose: async () => {},
|
|
525
|
-
resumed: true
|
|
526
|
-
}));
|
|
527
|
-
}
|
|
528
|
-
/**
|
|
529
|
-
* resume-or-create 分派:固定 id → 优先 resume(持久化历史延续),失败降级 create;
|
|
530
|
-
* 随机 id(无固定 sessionId)→ 恒 create。返回 handle + resumed 标志。
|
|
531
|
-
*
|
|
532
|
-
* **v1.27.3 修复(微信桥"重启后不响应"根因):live 会话优先复用**——重启后 DSH 把
|
|
533
|
-
* 关闭时仍 live 的持久化会话恢复为 live(crash/restart 恢复语义)。此时 resume 报
|
|
534
|
-
* "cannot prepare session X while it is live"(session-persistence coordinator 显式
|
|
535
|
-
* 拒绝 live 会话)、create 报 "session X already exists"(磁盘有持久化 log 必拒)→
|
|
536
|
-
* 微信桥 catch 吞错 → 用户消息无回复。唯一可行路径 = `ctx.agents.get(id)` 取 live
|
|
537
|
-
* agent 直接续用(resumed:true;角色提示词重挂与内存注册表登记由 createSkiffAgent 统一做)。
|
|
538
|
-
*/
|
|
539
|
-
async function createOrResumeAgent(ctx, id, root, model, setup, fixedId) {
|
|
540
|
-
if (!fixedId) return {
|
|
541
|
-
...await ctx.agents.create({
|
|
542
|
-
sessionId: id,
|
|
543
|
-
meta: {
|
|
544
|
-
cwd: root,
|
|
545
|
-
agentPreset: SKIFF_PRESET
|
|
546
|
-
},
|
|
547
|
-
setup,
|
|
548
|
-
...model ? { agentOptions: splitModel(model) } : {}
|
|
549
|
-
}),
|
|
550
|
-
resumed: false
|
|
551
|
-
};
|
|
552
|
-
const liveAgent = getLiveAgent(ctx, id);
|
|
553
|
-
if (liveAgent) return liveReuseRef(liveAgent, setup);
|
|
554
|
-
const agentsWithResume = ctx.agents;
|
|
555
|
-
if (typeof agentsWithResume.resume !== "function") return {
|
|
556
|
-
...await ctx.agents.create({
|
|
557
|
-
sessionId: id,
|
|
558
|
-
meta: {
|
|
559
|
-
cwd: root,
|
|
560
|
-
agentPreset: SKIFF_PRESET
|
|
561
|
-
},
|
|
562
|
-
setup,
|
|
563
|
-
...model ? { agentOptions: splitModel(model) } : {}
|
|
564
|
-
}),
|
|
565
|
-
resumed: false
|
|
566
|
-
};
|
|
567
|
-
try {
|
|
568
|
-
return {
|
|
569
|
-
...await agentsWithResume.resume({
|
|
570
|
-
resumeSessionId: id,
|
|
571
|
-
setup,
|
|
572
|
-
...model ? { agentOptions: splitModel(model) } : {}
|
|
573
|
-
}),
|
|
574
|
-
resumed: true
|
|
575
|
-
};
|
|
576
|
-
} catch (err) {
|
|
577
|
-
const liveNow = getLiveAgent(ctx, id);
|
|
578
|
-
if (liveNow) return liveReuseRef(liveNow, setup);
|
|
579
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
580
|
-
const stack = err instanceof Error ? err.stack ?? "" : "";
|
|
581
|
-
console.log(`[serenity-hooks] skiff resume 失败降级 create (id=${id}): ${msg}`);
|
|
582
|
-
if (stack) console.log(`[serenity-hooks] skiff resume stack:\n${stack.slice(0, 1200)}`);
|
|
583
|
-
return {
|
|
584
|
-
...await ctx.agents.create({
|
|
585
|
-
sessionId: id,
|
|
586
|
-
meta: {
|
|
587
|
-
cwd: root,
|
|
588
|
-
agentPreset: SKIFF_PRESET
|
|
589
|
-
},
|
|
590
|
-
setup,
|
|
591
|
-
...model ? { agentOptions: splitModel(model) } : {}
|
|
592
|
-
}),
|
|
593
|
-
resumed: false
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
/**
|
|
598
|
-
* 会话事件读取(v1.28.0 适配 0.1.2-rc.1):rc.1 Session 移除 `.events` 属性 → `snapshotEvents()` 方法。
|
|
599
|
-
* 兼容双形态(运行时 rc.1 snapshotEvents;测试替身 events)——同一函数两处语义,测试注入零侵入。
|
|
600
|
-
* 注:与 trajectory-ops.ts 导出的共享 `sessionEvents` 同构(v1.28.1 收敛基准)。本模块保留本地副本
|
|
601
|
-
* 以维持独立 import 面(零 DSH 依赖测试替身形态一致);若未来重构可统一 import 共享版。
|
|
602
|
-
*/
|
|
603
|
-
function sessionEvents(session) {
|
|
604
|
-
const s = session;
|
|
605
|
-
if (typeof s.snapshotEvents === "function") return s.snapshotEvents();
|
|
606
|
-
return s.events ?? [];
|
|
607
|
-
}
|
|
608
|
-
/** 读会话最后一个 assistant/message 文本(handyman 同款) */
|
|
609
|
-
function lastAssistantText(agent) {
|
|
610
|
-
const events = sessionEvents(agent.session);
|
|
611
|
-
for (let i = events.length - 1; i >= 0; i--) {
|
|
612
|
-
const e = events[i];
|
|
613
|
-
if (e && e.type === "assistant/message") {
|
|
614
|
-
const text = (e.data?.message?.content ?? e.data?.content ?? []).filter((b) => b.type === "text" && b.text).map((b) => b.text).join("\n");
|
|
615
|
-
if (text) return text;
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
return "";
|
|
619
|
-
}
|
|
620
|
-
/** events → 可读轨迹(user/assistant 文本 + 工具调用 + 工具结果;单条解析失败跳过) */
|
|
621
|
-
function eventsToTrajectory(events) {
|
|
622
|
-
const out = [];
|
|
623
|
-
for (const raw of events) try {
|
|
624
|
-
const ev = raw;
|
|
625
|
-
if (ev.type === "user/message") {
|
|
626
|
-
const text = extractText(ev.data);
|
|
627
|
-
if (text) out.push({
|
|
628
|
-
role: "user",
|
|
629
|
-
text
|
|
630
|
-
});
|
|
631
|
-
} else if (ev.type === "assistant/message") {
|
|
632
|
-
const d = ev.data;
|
|
633
|
-
const text = (d?.message?.content ?? d?.content ?? []).filter((b) => b.type === "text" && b.text).map((b) => b.text).join("\n");
|
|
634
|
-
const calls = d?.message?.tool_calls ?? [];
|
|
635
|
-
if (text) out.push({
|
|
636
|
-
role: "assistant",
|
|
637
|
-
text
|
|
638
|
-
});
|
|
639
|
-
for (const c of calls ?? []) {
|
|
640
|
-
const args = typeof c.arguments === "string" ? c.arguments : JSON.stringify(c.arguments ?? {});
|
|
641
|
-
out.push({
|
|
642
|
-
role: "assistant",
|
|
643
|
-
text: `→ ${c.name ?? "(tool)"} ${truncate(args, 300)}`,
|
|
644
|
-
tool: c.name
|
|
645
|
-
});
|
|
646
|
-
}
|
|
647
|
-
} else if (ev.type === "tool/result") {
|
|
648
|
-
const outText = extractText(ev.data);
|
|
649
|
-
if (outText) out.push({
|
|
650
|
-
role: "tool",
|
|
651
|
-
text: truncate(outText, 500),
|
|
652
|
-
tool: String(ev.data?.name ?? "")
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
} catch {}
|
|
656
|
-
return out;
|
|
657
|
-
}
|
|
658
|
-
function extractText(data) {
|
|
659
|
-
const content = data?.content;
|
|
660
|
-
if (!Array.isArray(content)) return "";
|
|
661
|
-
return content.filter((b) => b.type === "text" && b.text).map((b) => b.text).join("\n");
|
|
662
|
-
}
|
|
663
|
-
function truncate(s, n) {
|
|
664
|
-
return s.length > n ? `${s.slice(0, n)}…` : s;
|
|
665
|
-
}
|
|
666
|
-
/**
|
|
667
|
-
* 提问一轮:followup → 等 idle → 读答案 + 轨迹。
|
|
668
|
-
* @param eventsStart 轨迹起点:显式传 0 = 全量轨迹(会话追问时页面重绘完整时间线,
|
|
669
|
-
* v1.25.10 用户拍板);不传(undefined)= 本轮增量(followup 前 events 之后)
|
|
670
|
-
* @param options.includeTrajectory 是否计算轨迹(v1.26.10:**3100 对外只提供问答**——
|
|
671
|
-
* 公开问答页 / ACP JSON-RPC 不返回 trajectory,传 false 跳过计算;3099 调试页默认 true 保留)
|
|
672
|
-
*/
|
|
673
|
-
async function askSkiff(ctx, agent, question, eventsStart, options) {
|
|
674
|
-
const before = eventsStart === void 0 ? sessionEvents(agent.session).length : eventsStart;
|
|
675
|
-
agent.followup(createUserMessage({
|
|
676
|
-
content: [{
|
|
677
|
-
type: "text",
|
|
678
|
-
text: question
|
|
679
|
-
}],
|
|
680
|
-
source: PLUGIN_SOURCE
|
|
681
|
-
}));
|
|
682
|
-
await waitAgentIdle(ctx, agent);
|
|
683
|
-
const answer = lastAssistantText(agent);
|
|
684
|
-
const trajectory = options?.includeTrajectory === false ? [] : eventsToTrajectory(sessionEvents(agent.session).slice(before));
|
|
685
|
-
return {
|
|
686
|
-
answer,
|
|
687
|
-
sessionId: String(agent.session.id ?? ""),
|
|
688
|
-
trajectory
|
|
689
|
-
};
|
|
690
|
-
}
|
|
691
|
-
/**
|
|
692
|
-
* Skiff 会话的轨迹纪律参与判定:非 skiff 会话恒 true(正常参与);
|
|
693
|
-
* skiff 会话按角色 trajectory 子集(session/keeper/rebuild)决定;
|
|
694
|
-
* 注册表缺失(进程重启遗留等)→ 保守旁路(false,完全独立)。
|
|
695
|
-
*/
|
|
696
|
-
function skiffTrajectoryEnabled(root, sessionId, key) {
|
|
697
|
-
if (!isSkiffSessionId(sessionId)) return true;
|
|
698
|
-
const roleName = sessionId ? skiffRoleFor(sessionId) : null;
|
|
699
|
-
if (!roleName) return false;
|
|
700
|
-
const role = readSkiffRoles(root).get(roleName);
|
|
701
|
-
return trajectorySubset(role)[key];
|
|
702
|
-
}
|
|
703
|
-
/**
|
|
704
|
-
* msm 工具的 Skiff 门控:非 skiff 会话恒放行;skiff 会话——
|
|
705
|
-
* exec 非白名单 MSM 拒绝(不列名单)、register/deregister 必拒、
|
|
706
|
-
* list 白名单过滤、check/guide/ccc-config 只读放行。
|
|
707
|
-
*/
|
|
708
|
-
function skiffMsmGate(root, sessionId, action, name) {
|
|
709
|
-
if (!isSkiffSessionId(sessionId)) return {};
|
|
710
|
-
const roleName = sessionId ? skiffRoleFor(sessionId) : null;
|
|
711
|
-
const role = roleName ? readSkiffRoles(root).get(roleName) : void 0;
|
|
712
|
-
if (!roleName || !role) return { reject: "MSM not allowed in this skiff session" };
|
|
713
|
-
if (action === "register" || action === "deregister") return { reject: "register/deregister is not allowed in skiff sessions" };
|
|
714
|
-
if (action === "exec") {
|
|
715
|
-
if (!name || !(role.msms ?? []).includes(name)) return { reject: "MSM not allowed" };
|
|
716
|
-
return {};
|
|
717
|
-
}
|
|
718
|
-
if (action === "list") return { whitelist: roleMsmWhitelist(role) };
|
|
719
|
-
return {};
|
|
720
|
-
}
|
|
721
|
-
//#endregion
|
|
722
|
-
//#region node_modules/.pnpm/marked@18.0.11/node_modules/marked/lib/marked.esm.js
|
|
723
|
-
/**
|
|
724
|
-
* marked v18.0.11 - a markdown parser
|
|
725
|
-
* Copyright (c) 2018-2026, MarkedJS. (MIT License)
|
|
726
|
-
* Copyright (c) 2011-2018, Christopher Jeffrey. (MIT License)
|
|
727
|
-
* https://github.com/markedjs/marked
|
|
728
|
-
*/
|
|
729
|
-
/**
|
|
730
|
-
* DO NOT EDIT THIS FILE
|
|
731
|
-
* The code in this file is generated from files in ./src/
|
|
732
|
-
*/
|
|
733
|
-
function A() {
|
|
734
|
-
return {
|
|
735
|
-
async: !1,
|
|
736
|
-
breaks: !1,
|
|
737
|
-
extensions: null,
|
|
738
|
-
gfm: !0,
|
|
739
|
-
hooks: null,
|
|
740
|
-
pedantic: !1,
|
|
741
|
-
renderer: null,
|
|
742
|
-
silent: !1,
|
|
743
|
-
tokenizer: null,
|
|
744
|
-
walkTokens: null
|
|
745
|
-
};
|
|
746
|
-
}
|
|
747
|
-
var R = A();
|
|
748
|
-
function j(l) {
|
|
749
|
-
R = l;
|
|
750
|
-
}
|
|
751
|
-
var z = { exec: () => null };
|
|
752
|
-
function I(l) {
|
|
753
|
-
let e = [];
|
|
754
|
-
return (t) => {
|
|
755
|
-
let n = Math.max(0, Math.min(3, t - 1)), s = e[n];
|
|
756
|
-
return s || (s = l(n), e[n] = s), s;
|
|
757
|
-
};
|
|
758
|
-
}
|
|
759
|
-
function k(l, e = "") {
|
|
760
|
-
let t = typeof l == "string" ? l : l.source, n = {
|
|
761
|
-
replace: (s, r) => {
|
|
762
|
-
let i = typeof r == "string" ? r : r.source;
|
|
763
|
-
return i = i.replace(m.caret, "$1"), t = t.replace(s, i), n;
|
|
764
|
-
},
|
|
765
|
-
getRegex: () => new RegExp(t, e)
|
|
766
|
-
};
|
|
767
|
-
return n;
|
|
768
|
-
}
|
|
769
|
-
var Oe = ((l = "") => {
|
|
770
|
-
try {
|
|
771
|
-
return !!new RegExp("(?<=1)(?<!1)" + l);
|
|
772
|
-
} catch {
|
|
773
|
-
return !1;
|
|
774
|
-
}
|
|
775
|
-
})();
|
|
776
|
-
var m = {
|
|
777
|
-
codeRemoveIndent: /^(?: {1,4}| {0,3}\t)/gm,
|
|
778
|
-
outputLinkReplace: /\\([\[\]])/g,
|
|
779
|
-
indentCodeCompensation: /^(\s+)(?:```)/,
|
|
780
|
-
beginningSpace: /^\s+/,
|
|
781
|
-
endingHash: /#$/,
|
|
782
|
-
startingSpaceChar: /^ /,
|
|
783
|
-
endingSpaceChar: / $/,
|
|
784
|
-
nonSpaceChar: /[^ ]/,
|
|
785
|
-
newLineCharGlobal: /\n/g,
|
|
786
|
-
tabCharGlobal: /\t/g,
|
|
787
|
-
multipleSpaceGlobal: /\s+/g,
|
|
788
|
-
blankLine: /^[ \t]*$/,
|
|
789
|
-
doubleBlankLine: /\n[ \t]*\n[ \t]*$/,
|
|
790
|
-
blockquoteStart: /^ {0,3}>/,
|
|
791
|
-
blockquoteSetextReplace: /\n {0,3}((?:=+|-+) *)(?=\n|$)/g,
|
|
792
|
-
blockquoteSetextReplace2: /^ {0,3}>[ \t]?/gm,
|
|
793
|
-
listReplaceNesting: /^ {1,4}(?=( {4})*[^ ])/g,
|
|
794
|
-
listIsTask: /^\[[ xX]\] +\S/,
|
|
795
|
-
listReplaceTask: /^\[[ xX]\] +/,
|
|
796
|
-
listTaskCheckbox: /\[[ xX]\]/,
|
|
797
|
-
anyLine: /\n.*\n/,
|
|
798
|
-
hrefBrackets: /^<(.*)>$/,
|
|
799
|
-
tableDelimiter: /[:|]/,
|
|
800
|
-
tableAlignChars: /^\||\| *$/g,
|
|
801
|
-
tableRowBlankLine: /\n[ \t]*$/,
|
|
802
|
-
tableAlignRight: /^ *-+: *$/,
|
|
803
|
-
tableAlignCenter: /^ *:-+: *$/,
|
|
804
|
-
tableAlignLeft: /^ *:-+ *$/,
|
|
805
|
-
startATag: /^<a /i,
|
|
806
|
-
endATag: /^<\/a>/i,
|
|
807
|
-
startPreScriptTag: /^<(pre|code|kbd|script)(\s|>)/i,
|
|
808
|
-
endPreScriptTag: /^<\/(pre|code|kbd|script)(\s|>)/i,
|
|
809
|
-
startAngleBracket: /^</,
|
|
810
|
-
endAngleBracket: />$/,
|
|
811
|
-
pedanticHrefTitle: /^([^'"]*[^\s])\s+(['"])(.*)\2/,
|
|
812
|
-
unicodeAlphaNumeric: /[\p{L}\p{N}]/u,
|
|
813
|
-
escapeTest: /[&<>"']/,
|
|
814
|
-
escapeReplace: /[&<>"']/g,
|
|
815
|
-
escapeTestNoEncode: /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,
|
|
816
|
-
escapeReplaceNoEncode: /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,
|
|
817
|
-
caret: /(^|[^\[])\^/g,
|
|
818
|
-
percentDecode: /%25/g,
|
|
819
|
-
findPipe: /\|/g,
|
|
820
|
-
splitPipe: / \|/,
|
|
821
|
-
slashPipe: /\\\|/g,
|
|
822
|
-
carriageReturn: /\r\n|\r/g,
|
|
823
|
-
spaceLine: /^ +$/gm,
|
|
824
|
-
notSpaceStart: /^\S*/,
|
|
825
|
-
endingNewline: /\n$/,
|
|
826
|
-
listItemRegex: (l) => new RegExp(`^( {0,3}${l})((?:[ ][^\\n]*)?(?:\\n|$))`),
|
|
827
|
-
nextBulletRegex: I((l) => new RegExp(`^ {0,${l}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`)),
|
|
828
|
-
hrRegex: I((l) => new RegExp(`^ {0,${l}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`)),
|
|
829
|
-
fencesBeginRegex: I((l) => new RegExp(`^ {0,${l}}(?:\`\`\`|~~~)`)),
|
|
830
|
-
headingBeginRegex: I((l) => new RegExp(`^ {0,${l}}#`)),
|
|
831
|
-
htmlBeginRegex: I((l) => new RegExp(`^ {0,${l}}<(?:[a-z].*>|!--)`, "i")),
|
|
832
|
-
blockquoteBeginRegex: I((l) => new RegExp(`^ {0,${l}}>`))
|
|
833
|
-
};
|
|
834
|
-
var Te = /^(?:[ \t]*(?:\n|$))+/;
|
|
835
|
-
var we = /^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/;
|
|
836
|
-
var ye = /^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/;
|
|
837
|
-
var q = /^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/;
|
|
838
|
-
var Pe = /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/;
|
|
839
|
-
var U = / {0,3}(?:[*+-]|\d{1,9}[.)])/;
|
|
840
|
-
var oe = /^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/;
|
|
841
|
-
var ae = k(oe).replace(/bull/g, U).replace(/blockCode/g, /(?: {4}| {0,3}\t)/).replace(/fences/g, / {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g, / {0,3}>/).replace(/heading/g, / {0,3}#{1,6}(?:\s|$)/).replace(/html/g, / {0,3}<[^\n>]+>\n/).replace(/\|table/g, "").getRegex();
|
|
842
|
-
var Se = k(oe).replace(/bull/g, U).replace(/blockCode/g, /(?: {4}| {0,3}\t)/).replace(/fences/g, / {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g, / {0,3}>/).replace(/heading/g, / {0,3}#{1,6}(?:\s|$)/).replace(/html/g, / {0,3}<[^\n>]+>\n/).replace(/table/g, / {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex();
|
|
843
|
-
var K = /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table|[ \t]+\n)[^\n]+)*)/;
|
|
844
|
-
var _e = /^[^\n]+/;
|
|
845
|
-
var W = /(?!\s*\])(?:\\[\s\S]|[^\[\]\\])+/;
|
|
846
|
-
var $e = k(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label", W).replace("title", /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex();
|
|
847
|
-
var Le = k(/^(bull)([ \t][^\n]*?)?(?:\n|$)/).replace(/bull/g, U).getRegex();
|
|
848
|
-
var Q = "address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul";
|
|
849
|
-
var X = /<!--(?:-?>|[\s\S]*?(?:-->|$))/;
|
|
850
|
-
var Ee = k("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n*|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>[^\\n]*\\n*|$)|<![A-Z][\\s\\S]*?(?:>[^\\n]*\\n*|$)|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>[^\\n]*\\n*|$)|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|</(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$))", "i").replace("comment", X).replace("tag", Q).replace("attribute", / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex();
|
|
851
|
-
var le = (l) => k(K).replace("hr", q).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("|lheading", "").replace("|table", "").replace("blockquote", " {0,3}>").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*(?:\\n|$))|~~~)[^\\n]*(?:\\n|$)").replace("list", l).replace("html", "</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag", Q).getRegex();
|
|
852
|
-
var ze = le(/ {0,3}(?:[*+-]|1[.)])[ \t]+[^ \t\n]/);
|
|
853
|
-
var Me = le(/ {0,3}(?:[*+-]|\d{1,9}[.)])(?:[ \t]|\n|$)/);
|
|
854
|
-
var J = {
|
|
855
|
-
blockquote: k(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph", Me).getRegex(),
|
|
856
|
-
code: we,
|
|
857
|
-
def: $e,
|
|
858
|
-
fences: ye,
|
|
859
|
-
heading: Pe,
|
|
860
|
-
hr: q,
|
|
861
|
-
html: Ee,
|
|
862
|
-
lheading: ae,
|
|
863
|
-
list: Le,
|
|
864
|
-
newline: Te,
|
|
865
|
-
paragraph: ze,
|
|
866
|
-
table: z,
|
|
867
|
-
text: _e
|
|
868
|
-
};
|
|
869
|
-
var se = k("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr", q).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("blockquote", " {0,3}>").replace("code", "(?: {4}| {0,3} )[^\\n]").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*(?:\\n|$))|~~~)[^\\n]*(?:\\n|$)").replace("list", " {0,3}(?:[*+-]|1[.)])[ \\t]").replace("html", "</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag", Q).getRegex();
|
|
870
|
-
var Ie = {
|
|
871
|
-
...J,
|
|
872
|
-
lheading: Se,
|
|
873
|
-
table: se,
|
|
874
|
-
paragraph: k(K).replace("hr", q).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("|lheading", "").replace("table", se).replace("blockquote", " {0,3}>").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*(?:\\n|$))|~~~)[^\\n]*(?:\\n|$)").replace("list", " {0,3}(?:[*+-]|1[.)])[ \\t]+[^ \\t\\n]").replace("html", "</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag", Q).getRegex()
|
|
875
|
-
};
|
|
876
|
-
var Ce = {
|
|
877
|
-
...J,
|
|
878
|
-
html: k(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)|<tag(?:"[^"]*"|'[^']*'|\\s[^'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment", X).replace(/tag/g, "(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),
|
|
879
|
-
def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
|
|
880
|
-
heading: /^(#{1,6})(.*)(?:\n+|$)/,
|
|
881
|
-
fences: z,
|
|
882
|
-
lheading: /^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,
|
|
883
|
-
paragraph: k(K).replace("hr", q).replace("heading", ` *#{1,6} *[^
|
|
884
|
-
]`).replace("lheading", ae).replace("|table", "").replace("blockquote", " {0,3}>").replace("|fences", "").replace("|list", "").replace("|html", "").replace("|tag", "").getRegex()
|
|
885
|
-
};
|
|
886
|
-
var Be = /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/;
|
|
887
|
-
var De = /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/;
|
|
888
|
-
var ue = /^( {2,}|\\)\n(?!\s*$)/;
|
|
889
|
-
var qe = /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/;
|
|
890
|
-
var _ = /[\p{P}\p{S}]/u;
|
|
891
|
-
var C = /[\s\p{P}\p{S}]/u;
|
|
892
|
-
var v = /[^\s\p{P}\p{S}]/u;
|
|
893
|
-
var ve = k(/^((?![*_])punctSpace)/, "u").replace(/punctSpace/g, C).getRegex();
|
|
894
|
-
var He = /[\p{Pi}\p{Ps}"']/u;
|
|
895
|
-
var pe = /(?!~)[\p{P}\p{S}]/u;
|
|
896
|
-
var Ze = /(?!~)[\s\p{P}\p{S}]/u;
|
|
897
|
-
var Ge = /(?:[^\s\p{P}\p{S}]|~)/u;
|
|
898
|
-
var Qe = k(/link|precode-code|html/, "g").replace("link", /\[(?:[^\[\]`]|(?<a>`+)[^`]+\k<a>(?!`))*?\]\((?:\\[\s\S]|[^\\\(\)]|\((?:\\[\s\S]|[^\\\(\)])*\))*\)/).replace("precode-", Oe ? "(?<!`)()" : "(^^|[^`])").replace("code", /(?<b>`+)[^`]+\k<b>(?!`)/).replace("html", /<(?! )[^<>]*?>/).getRegex();
|
|
899
|
-
var ce = /^(?:\*+(?:((?!\*)punct)|([^\s*]))?)|^_+(?:((?!_)punct)|([^\s_]))?/;
|
|
900
|
-
var Ne = k(ce, "u").replace(/punct/g, _).getRegex();
|
|
901
|
-
var je = k(ce, "u").replace(/punct/g, pe).getRegex();
|
|
902
|
-
var Ue = k(/^(?:\*+(?:((?!\*)(?!openQuote)punct)|([^\s*]))?)|^_+(?:((?!_)(?!openQuote)punct)|([^\s_]))?/, "u").replace(/openQuote/g, He).replace(/punct/g, _).getRegex();
|
|
903
|
-
var he = "^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)";
|
|
904
|
-
var Ke = k(he, "gu").replace(/notPunctSpace/g, v).replace(/punctSpace/g, C).replace(/punct/g, _).getRegex();
|
|
905
|
-
var We = k(he, "gu").replace(/notPunctSpace/g, Ge).replace(/punctSpace/g, Ze).replace(/punct/g, pe).getRegex();
|
|
906
|
-
var Je = k("^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)[\\s](\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|(?:(?!\\*)punct|notPunctSpace)(\\*+)(?!\\*)(?=notPunctSpace)", "gu").replace(/notPunctSpace/g, v).replace(/punctSpace/g, C).replace(/punct/g, _).getRegex();
|
|
907
|
-
var Ve = k("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)", "gu").replace(/notPunctSpace/g, v).replace(/punctSpace/g, C).replace(/punct/g, _).getRegex();
|
|
908
|
-
var et = k("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)[\\s](_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)|(?:(?!_)punct|notPunctSpace)(_+)(?!_)(?=notPunctSpace)", "gu").replace(/notPunctSpace/g, v).replace(/punctSpace/g, C).replace(/punct/g, _).getRegex();
|
|
909
|
-
var tt = k(/^~~?(?:((?!~)punct)|[^\s~])/, "u").replace(/punct/g, _).getRegex();
|
|
910
|
-
var rt = k("^[^~]+(?=[^~])|(?!~)punct(~~?)(?=[\\s]|$)|notPunctSpace(~~?)(?!~)(?=punctSpace|$)|(?!~)punctSpace(~~?)(?=notPunctSpace)|[\\s](~~?)(?!~)(?=punct)|(?!~)punct(~~?)(?!~)(?=punct)|notPunctSpace(~~?)(?=notPunctSpace)", "gu").replace(/notPunctSpace/g, v).replace(/punctSpace/g, C).replace(/punct/g, _).getRegex();
|
|
911
|
-
var st = k(/\\(punct)/, "gu").replace(/punct/g, _).getRegex();
|
|
912
|
-
var it = k(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme", /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email", /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex();
|
|
913
|
-
var ot = k(X).replace("(?:-->|$)", "-->").getRegex();
|
|
914
|
-
var at = k("^comment|^</[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^<![a-zA-Z]+\\s[\\s\\S]*?>|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>").replace("comment", ot).replace("attribute", /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex();
|
|
915
|
-
var G = /(?:\[(?:\\[\s\S]|[^\[\]\\])*\]|\\[\s\S]|`+(?!`)[^`]*?`+(?!`)|``+(?=\])|[^\[\]\\`])*?/;
|
|
916
|
-
var lt = k(/^!?\[(label)\]\(\s*(href)(?:(?:[ \t]+(?:\n[ \t]*)?|\n[ \t]*)(title))?\s*\)/).replace("label", G).replace("href", /<(?:\\.|[^\n<>\\])+>|[^ \t\n\x00-\x1f]+|(?=\))/).replace("title", /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex();
|
|
917
|
-
var ke = k(/^!?\[(label)\]\[(ref)\]/).replace("label", G).replace("ref", W).getRegex();
|
|
918
|
-
var de = k(/^!?\[(ref)\](?:\[\])?/).replace("ref", W).getRegex();
|
|
919
|
-
var ut = k("reflink|nolink(?!\\()", "g").replace("reflink", ke).replace("nolink", de).getRegex();
|
|
920
|
-
var ie = /[hH][tT][tT][pP][sS]?|[fF][tT][pP]/;
|
|
921
|
-
var V = {
|
|
922
|
-
_backpedal: z,
|
|
923
|
-
anyPunctuation: st,
|
|
924
|
-
autolink: it,
|
|
925
|
-
blockSkip: Qe,
|
|
926
|
-
br: ue,
|
|
927
|
-
code: De,
|
|
928
|
-
del: z,
|
|
929
|
-
delLDelim: z,
|
|
930
|
-
delRDelim: z,
|
|
931
|
-
emStrongLDelim: Ne,
|
|
932
|
-
emStrongRDelimAst: Ke,
|
|
933
|
-
emStrongRDelimUnd: Ve,
|
|
934
|
-
escape: Be,
|
|
935
|
-
link: lt,
|
|
936
|
-
nolink: de,
|
|
937
|
-
punctuation: ve,
|
|
938
|
-
reflink: ke,
|
|
939
|
-
reflinkSearch: ut,
|
|
940
|
-
tag: at,
|
|
941
|
-
text: qe,
|
|
942
|
-
url: z
|
|
943
|
-
};
|
|
944
|
-
var pt = {
|
|
945
|
-
...V,
|
|
946
|
-
emStrongLDelim: Ue,
|
|
947
|
-
emStrongRDelimAst: Je,
|
|
948
|
-
emStrongRDelimUnd: et,
|
|
949
|
-
link: k(/^!?\[(label)\]\((.*?)\)/).replace("label", G).getRegex(),
|
|
950
|
-
reflink: k(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label", G).getRegex()
|
|
951
|
-
};
|
|
952
|
-
var F = {
|
|
953
|
-
...V,
|
|
954
|
-
emStrongRDelimAst: We,
|
|
955
|
-
emStrongLDelim: je,
|
|
956
|
-
delLDelim: tt,
|
|
957
|
-
delRDelim: rt,
|
|
958
|
-
url: k(/^((?:protocol):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/).replace("protocol", ie).replace("email", /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),
|
|
959
|
-
_backpedal: /(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,
|
|
960
|
-
del: /^(~~?)(?=[^\s~])((?:\\[\s\S]|[^\\])*?(?:\\[\s\S]|[^\s~\\]))\1(?=[^~]|$)/,
|
|
961
|
-
text: k(/^(`+|~+|[^`~])(?:(?=[`~])|(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|protocol:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/).replace("protocol", ie).getRegex()
|
|
962
|
-
};
|
|
963
|
-
var ct = {
|
|
964
|
-
...F,
|
|
965
|
-
br: k(ue).replace("{2,}", "*").getRegex(),
|
|
966
|
-
text: k(F.text).replace("\\b_", "\\b_| {2,}\\n").replace(/\{2,\}/g, "*").getRegex()
|
|
967
|
-
};
|
|
968
|
-
var H = {
|
|
969
|
-
normal: J,
|
|
970
|
-
gfm: Ie,
|
|
971
|
-
pedantic: Ce
|
|
972
|
-
};
|
|
973
|
-
var B = {
|
|
974
|
-
normal: V,
|
|
975
|
-
gfm: F,
|
|
976
|
-
breaks: ct,
|
|
977
|
-
pedantic: pt
|
|
978
|
-
};
|
|
979
|
-
var ht = {
|
|
980
|
-
"&": "&",
|
|
981
|
-
"<": "<",
|
|
982
|
-
">": ">",
|
|
983
|
-
"\"": """,
|
|
984
|
-
"'": "'"
|
|
985
|
-
};
|
|
986
|
-
var ge = (l) => ht[l];
|
|
987
|
-
function T(l, e) {
|
|
988
|
-
if (e) {
|
|
989
|
-
if (m.escapeTest.test(l)) return l.replace(m.escapeReplace, ge);
|
|
990
|
-
} else if (m.escapeTestNoEncode.test(l)) return l.replace(m.escapeReplaceNoEncode, ge);
|
|
991
|
-
return l;
|
|
992
|
-
}
|
|
993
|
-
function Y(l) {
|
|
994
|
-
try {
|
|
995
|
-
l = encodeURI(l).replace(m.percentDecode, "%");
|
|
996
|
-
} catch {
|
|
997
|
-
return null;
|
|
998
|
-
}
|
|
999
|
-
return l;
|
|
1000
|
-
}
|
|
1001
|
-
function ee(l, e) {
|
|
1002
|
-
let n = l.replace(m.findPipe, (r, i, o) => {
|
|
1003
|
-
let u = !1, a = i;
|
|
1004
|
-
for (; --a >= 0 && o[a] === "\\";) u = !u;
|
|
1005
|
-
return u ? "|" : " |";
|
|
1006
|
-
}).split(m.splitPipe), s = 0;
|
|
1007
|
-
if (n[0].trim() || n.shift(), n.length > 0 && !n.at(-1)?.trim() && n.pop(), e) if (n.length > e) n.splice(e);
|
|
1008
|
-
else for (; n.length < e;) n.push("");
|
|
1009
|
-
for (; s < n.length; s++) n[s] = n[s].trim().replace(m.slashPipe, "|");
|
|
1010
|
-
return n;
|
|
1011
|
-
}
|
|
1012
|
-
function $(l, e, t) {
|
|
1013
|
-
let n = l.length;
|
|
1014
|
-
if (n === 0) return "";
|
|
1015
|
-
let s = 0;
|
|
1016
|
-
for (; s < n;) {
|
|
1017
|
-
let r = l.charAt(n - s - 1);
|
|
1018
|
-
if (r === e && !t) s++;
|
|
1019
|
-
else if (r !== e && t) s++;
|
|
1020
|
-
else break;
|
|
1021
|
-
}
|
|
1022
|
-
return l.slice(0, n - s);
|
|
1023
|
-
}
|
|
1024
|
-
function te(l) {
|
|
1025
|
-
let e = l.split(`
|
|
1026
|
-
`), t = e.length - 1;
|
|
1027
|
-
for (; t >= 0 && m.blankLine.test(e[t]);) t--;
|
|
1028
|
-
return e.length - t <= 2 ? l : e.slice(0, t + 1).join(`
|
|
1029
|
-
`);
|
|
1030
|
-
}
|
|
1031
|
-
function fe(l, e) {
|
|
1032
|
-
if (l.indexOf(e[1]) === -1) return -1;
|
|
1033
|
-
let t = 0;
|
|
1034
|
-
for (let n = 0; n < l.length; n++) if (l[n] === "\\") n++;
|
|
1035
|
-
else if (l[n] === e[0]) t++;
|
|
1036
|
-
else if (l[n] === e[1] && (t--, t < 0)) return n;
|
|
1037
|
-
return t > 0 ? -2 : -1;
|
|
1038
|
-
}
|
|
1039
|
-
function me(l, e = 0) {
|
|
1040
|
-
let t = e, n = "";
|
|
1041
|
-
for (let s of l) if (s === " ") {
|
|
1042
|
-
let r = 4 - t % 4;
|
|
1043
|
-
n += " ".repeat(r), t += r;
|
|
1044
|
-
} else n += s, t++;
|
|
1045
|
-
return n;
|
|
1046
|
-
}
|
|
1047
|
-
function xe(l, e, t, n, s) {
|
|
1048
|
-
let r = e.href, i = e.title || null, o = l[1].replace(s.other.outputLinkReplace, "$1"), u = l[0].charAt(0) === "!";
|
|
1049
|
-
n.state.inLink = !0;
|
|
1050
|
-
let a = n.state.linkEmitted, p = n.state.inRawBlock;
|
|
1051
|
-
n.state.linkEmitted = !1;
|
|
1052
|
-
let c = n.inlineTokens(o), h = n.state.linkEmitted;
|
|
1053
|
-
if (n.state.linkEmitted = a, n.state.inLink = !1, !u) {
|
|
1054
|
-
if (h) {
|
|
1055
|
-
n.state.inRawBlock = p;
|
|
1056
|
-
return;
|
|
1057
|
-
}
|
|
1058
|
-
n.state.linkEmitted = !0;
|
|
1059
|
-
}
|
|
1060
|
-
return {
|
|
1061
|
-
type: u ? "image" : "link",
|
|
1062
|
-
raw: t,
|
|
1063
|
-
href: r,
|
|
1064
|
-
title: i,
|
|
1065
|
-
text: o,
|
|
1066
|
-
tokens: c
|
|
1067
|
-
};
|
|
1068
|
-
}
|
|
1069
|
-
function kt(l, e, t) {
|
|
1070
|
-
let n = l.match(t.other.indentCodeCompensation);
|
|
1071
|
-
if (n === null) return e;
|
|
1072
|
-
let s = n[1];
|
|
1073
|
-
return e.split(`
|
|
1074
|
-
`).map((r) => {
|
|
1075
|
-
let i = r.match(t.other.beginningSpace);
|
|
1076
|
-
if (i === null) return r;
|
|
1077
|
-
let [o] = i;
|
|
1078
|
-
return o.length >= s.length ? r.slice(s.length) : r;
|
|
1079
|
-
}).join(`
|
|
1080
|
-
`);
|
|
1081
|
-
}
|
|
1082
|
-
var y = class {
|
|
1083
|
-
options;
|
|
1084
|
-
rules;
|
|
1085
|
-
lexer;
|
|
1086
|
-
constructor(e) {
|
|
1087
|
-
this.options = e || R;
|
|
1088
|
-
}
|
|
1089
|
-
space(e) {
|
|
1090
|
-
let t = this.rules.block.newline.exec(e);
|
|
1091
|
-
if (t && t[0].length > 0) return {
|
|
1092
|
-
type: "space",
|
|
1093
|
-
raw: t[0]
|
|
1094
|
-
};
|
|
1095
|
-
}
|
|
1096
|
-
code(e) {
|
|
1097
|
-
let t = this.rules.block.code.exec(e);
|
|
1098
|
-
if (t) {
|
|
1099
|
-
let n = this.options.pedantic ? t[0] : te(t[0]);
|
|
1100
|
-
return {
|
|
1101
|
-
type: "code",
|
|
1102
|
-
raw: n,
|
|
1103
|
-
codeBlockStyle: "indented",
|
|
1104
|
-
text: n.replace(this.rules.other.codeRemoveIndent, "")
|
|
1105
|
-
};
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
fences(e) {
|
|
1109
|
-
let t = this.rules.block.fences.exec(e);
|
|
1110
|
-
if (t) {
|
|
1111
|
-
let n = t[0], s = kt(n, t[3] || "", this.rules);
|
|
1112
|
-
return {
|
|
1113
|
-
type: "code",
|
|
1114
|
-
raw: n,
|
|
1115
|
-
lang: t[2] ? t[2].trim().replace(this.rules.inline.anyPunctuation, "$1") : t[2],
|
|
1116
|
-
text: s
|
|
1117
|
-
};
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
heading(e) {
|
|
1121
|
-
let t = this.rules.block.heading.exec(e);
|
|
1122
|
-
if (t) {
|
|
1123
|
-
let n = t[2].trim();
|
|
1124
|
-
if (this.rules.other.endingHash.test(n)) {
|
|
1125
|
-
let s = $(n, "#");
|
|
1126
|
-
(this.options.pedantic || !s || this.rules.other.endingSpaceChar.test(s)) && (n = s.trim());
|
|
1127
|
-
}
|
|
1128
|
-
return {
|
|
1129
|
-
type: "heading",
|
|
1130
|
-
raw: $(t[0], `
|
|
1131
|
-
`),
|
|
1132
|
-
depth: t[1].length,
|
|
1133
|
-
text: n,
|
|
1134
|
-
tokens: this.lexer.inline(n)
|
|
1135
|
-
};
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
hr(e) {
|
|
1139
|
-
let t = this.rules.block.hr.exec(e);
|
|
1140
|
-
if (t) return {
|
|
1141
|
-
type: "hr",
|
|
1142
|
-
raw: $(t[0], `
|
|
1143
|
-
`)
|
|
1144
|
-
};
|
|
1145
|
-
}
|
|
1146
|
-
blockquote(e) {
|
|
1147
|
-
let t = this.rules.block.blockquote.exec(e);
|
|
1148
|
-
if (t) {
|
|
1149
|
-
let n = $(t[0], `
|
|
1150
|
-
`).split(`
|
|
1151
|
-
`), s = "", r = "", i = [];
|
|
1152
|
-
for (; n.length > 0;) {
|
|
1153
|
-
let o = !1, u = [], a;
|
|
1154
|
-
for (a = 0; a < n.length; a++) if (this.rules.other.blockquoteStart.test(n[a])) u.push(n[a]), o = !0;
|
|
1155
|
-
else if (!o) u.push(n[a]);
|
|
1156
|
-
else break;
|
|
1157
|
-
n = n.slice(a);
|
|
1158
|
-
let p = u.join(`
|
|
1159
|
-
`), c = p.replace(this.rules.other.blockquoteSetextReplace, `
|
|
1160
|
-
$1`).replace(this.rules.other.blockquoteSetextReplace2, "");
|
|
1161
|
-
s = s ? `${s}
|
|
1162
|
-
${p}` : p, r = r ? `${r}
|
|
1163
|
-
${c}` : c;
|
|
1164
|
-
let h = this.lexer.state.top;
|
|
1165
|
-
if (this.lexer.state.top = !0, this.lexer.blockTokens(c, i, !0), this.lexer.state.top = h, n.length === 0) break;
|
|
1166
|
-
let d = i.at(-1);
|
|
1167
|
-
if (d?.type === "code") break;
|
|
1168
|
-
if (d?.type === "blockquote") {
|
|
1169
|
-
let O = d, g = n.join(`
|
|
1170
|
-
`), w = O.raw + `
|
|
1171
|
-
` + g.replace(this.rules.other.blockquoteSetextReplace2, ""), E = this.blockquote(w);
|
|
1172
|
-
i[i.length - 1] = E, s = `${s}
|
|
1173
|
-
${g}`, r = r.substring(0, r.length - O.text.length) + E.text;
|
|
1174
|
-
break;
|
|
1175
|
-
} else if (d?.type === "list") {
|
|
1176
|
-
let O = d, g = O.raw + `
|
|
1177
|
-
` + n.join(`
|
|
1178
|
-
`), w = this.list(g);
|
|
1179
|
-
i[i.length - 1] = w, s = s.substring(0, s.length - d.raw.length) + w.raw, r = r.substring(0, r.length - O.raw.length) + w.raw, n = g.substring(i.at(-1).raw.length).split(`
|
|
1180
|
-
`);
|
|
1181
|
-
continue;
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1184
|
-
return {
|
|
1185
|
-
type: "blockquote",
|
|
1186
|
-
raw: s,
|
|
1187
|
-
tokens: i,
|
|
1188
|
-
text: r
|
|
1189
|
-
};
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
list(e) {
|
|
1193
|
-
let t = this.rules.block.list.exec(e);
|
|
1194
|
-
if (t) {
|
|
1195
|
-
let n = t[1].trim(), s = n.length > 1, r = {
|
|
1196
|
-
type: "list",
|
|
1197
|
-
raw: "",
|
|
1198
|
-
ordered: s,
|
|
1199
|
-
start: s ? +n.slice(0, -1) : "",
|
|
1200
|
-
loose: !1,
|
|
1201
|
-
items: []
|
|
1202
|
-
};
|
|
1203
|
-
n = s ? `\\d{1,9}\\${n.slice(-1)}` : `\\${n}`, this.options.pedantic && (n = s ? n : "[*+-]");
|
|
1204
|
-
let i = this.rules.other.listItemRegex(n), o = !1;
|
|
1205
|
-
for (; e;) {
|
|
1206
|
-
let a = !1, p = "", c = "";
|
|
1207
|
-
if (!(t = i.exec(e)) || this.rules.block.hr.test(e)) break;
|
|
1208
|
-
p = t[0], e = e.substring(p.length);
|
|
1209
|
-
let h = me(t[2].split(`
|
|
1210
|
-
`, 1)[0], t[1].length), d = e.split(`
|
|
1211
|
-
`, 1)[0], O = !h.trim(), g = 0;
|
|
1212
|
-
if (this.options.pedantic ? (g = 2, c = h.trimStart()) : O ? g = t[1].length + 1 : (g = h.search(this.rules.other.nonSpaceChar), g = g > 4 ? 1 : g, c = h.slice(g), g += t[1].length), O && this.rules.other.blankLine.test(d) && (p += d + `
|
|
1213
|
-
`, e = e.substring(d.length + 1), a = !0), !a) {
|
|
1214
|
-
let w = this.rules.other.nextBulletRegex(g), E = this.rules.other.hrRegex(g), ne = this.rules.other.fencesBeginRegex(g), re = this.rules.other.headingBeginRegex(g), be = this.rules.other.htmlBeginRegex(g), Re = this.rules.other.blockquoteBeginRegex(g);
|
|
1215
|
-
for (; e;) {
|
|
1216
|
-
let N = e.split(`
|
|
1217
|
-
`, 1)[0], D;
|
|
1218
|
-
if (d = N, this.options.pedantic ? (d = d.replace(this.rules.other.listReplaceNesting, " "), D = d) : D = d.replace(this.rules.other.tabCharGlobal, " "), ne.test(d) || re.test(d) || be.test(d) || Re.test(d) || w.test(d) || E.test(d)) break;
|
|
1219
|
-
if (D.search(this.rules.other.nonSpaceChar) >= g || !d.trim()) c += `
|
|
1220
|
-
` + D.slice(g);
|
|
1221
|
-
else {
|
|
1222
|
-
if (O || h.replace(this.rules.other.tabCharGlobal, " ").search(this.rules.other.nonSpaceChar) >= 4 || ne.test(h) || re.test(h) || E.test(h)) break;
|
|
1223
|
-
c += `
|
|
1224
|
-
` + d;
|
|
1225
|
-
}
|
|
1226
|
-
O = !d.trim(), p += N + `
|
|
1227
|
-
`, e = e.substring(N.length + 1), h = D.slice(g);
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
r.loose || (o ? r.loose = !0 : this.rules.other.doubleBlankLine.test(p) && (o = !0)), r.items.push({
|
|
1231
|
-
type: "list_item",
|
|
1232
|
-
raw: p,
|
|
1233
|
-
task: !!this.options.gfm && this.rules.other.listIsTask.test(c),
|
|
1234
|
-
loose: !1,
|
|
1235
|
-
text: c,
|
|
1236
|
-
tokens: []
|
|
1237
|
-
}), r.raw += p;
|
|
1238
|
-
}
|
|
1239
|
-
let u = r.items.at(-1);
|
|
1240
|
-
if (u) u.raw = u.raw.trimEnd(), u.text = u.text.trimEnd();
|
|
1241
|
-
else return;
|
|
1242
|
-
r.raw = r.raw.trimEnd();
|
|
1243
|
-
for (let a of r.items) if (this.lexer.state.top = !1, a.tokens = this.lexer.blockTokens(a.text, []), !r.loose) {
|
|
1244
|
-
let p = a.tokens.filter((h) => h.type === "space");
|
|
1245
|
-
r.loose = p.length > 0 && p.some((h) => this.rules.other.anyLine.test(h.raw));
|
|
1246
|
-
}
|
|
1247
|
-
for (let a of r.items) {
|
|
1248
|
-
let p = a.tokens[0];
|
|
1249
|
-
if (a.task && (p?.type === "text" || p?.type === "paragraph")) {
|
|
1250
|
-
a.text = a.text.replace(this.rules.other.listReplaceTask, ""), p.raw = p.raw.replace(this.rules.other.listReplaceTask, ""), p.text = p.text.replace(this.rules.other.listReplaceTask, "");
|
|
1251
|
-
for (let h = this.lexer.inlineQueue.length - 1; h >= 0; h--) if (this.rules.other.listIsTask.test(this.lexer.inlineQueue[h].src)) {
|
|
1252
|
-
this.lexer.inlineQueue[h].src = this.lexer.inlineQueue[h].src.replace(this.rules.other.listReplaceTask, "");
|
|
1253
|
-
break;
|
|
1254
|
-
}
|
|
1255
|
-
let c = this.rules.other.listTaskCheckbox.exec(a.raw);
|
|
1256
|
-
if (c) {
|
|
1257
|
-
let h = {
|
|
1258
|
-
type: "checkbox",
|
|
1259
|
-
raw: c[0] + " ",
|
|
1260
|
-
checked: c[0] !== "[ ]"
|
|
1261
|
-
};
|
|
1262
|
-
a.checked = h.checked, r.loose ? a.tokens[0] && ["paragraph", "text"].includes(a.tokens[0].type) && "tokens" in a.tokens[0] && a.tokens[0].tokens ? (a.tokens[0].raw = h.raw + a.tokens[0].raw, a.tokens[0].text = h.raw + a.tokens[0].text, a.tokens[0].tokens.unshift(h)) : a.tokens.unshift({
|
|
1263
|
-
type: "paragraph",
|
|
1264
|
-
raw: h.raw,
|
|
1265
|
-
text: h.raw,
|
|
1266
|
-
tokens: [h]
|
|
1267
|
-
}) : a.tokens.unshift(h);
|
|
1268
|
-
}
|
|
1269
|
-
} else a.task && (a.task = !1);
|
|
1270
|
-
}
|
|
1271
|
-
if (r.loose) for (let a of r.items) {
|
|
1272
|
-
a.loose = !0;
|
|
1273
|
-
for (let p of a.tokens) p.type === "text" && (p.type = "paragraph");
|
|
1274
|
-
}
|
|
1275
|
-
return r;
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
html(e) {
|
|
1279
|
-
let t = this.rules.block.html.exec(e);
|
|
1280
|
-
if (t) {
|
|
1281
|
-
let n = te(t[0]);
|
|
1282
|
-
return {
|
|
1283
|
-
type: "html",
|
|
1284
|
-
block: !0,
|
|
1285
|
-
raw: n,
|
|
1286
|
-
pre: t[1] === "pre" || t[1] === "script" || t[1] === "style",
|
|
1287
|
-
text: n
|
|
1288
|
-
};
|
|
1289
|
-
}
|
|
1290
|
-
}
|
|
1291
|
-
def(e) {
|
|
1292
|
-
let t = this.rules.block.def.exec(e);
|
|
1293
|
-
if (t) {
|
|
1294
|
-
let n = t[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal, " "), s = t[2] ? t[2].replace(this.rules.other.hrefBrackets, "$1").replace(this.rules.inline.anyPunctuation, "$1") : "", r = t[3] ? t[3].substring(1, t[3].length - 1).replace(this.rules.inline.anyPunctuation, "$1") : t[3];
|
|
1295
|
-
return {
|
|
1296
|
-
type: "def",
|
|
1297
|
-
tag: n,
|
|
1298
|
-
raw: $(t[0], `
|
|
1299
|
-
`),
|
|
1300
|
-
href: s,
|
|
1301
|
-
title: r
|
|
1302
|
-
};
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
table(e) {
|
|
1306
|
-
let t = this.rules.block.table.exec(e);
|
|
1307
|
-
if (!t || !this.rules.other.tableDelimiter.test(t[2])) return;
|
|
1308
|
-
let n = ee(t[1]), s = t[2].replace(this.rules.other.tableAlignChars, "").split("|"), r = t[3]?.trim() ? t[3].replace(this.rules.other.tableRowBlankLine, "").split(`
|
|
1309
|
-
`) : [], i = {
|
|
1310
|
-
type: "table",
|
|
1311
|
-
raw: $(t[0], `
|
|
1312
|
-
`),
|
|
1313
|
-
header: [],
|
|
1314
|
-
align: [],
|
|
1315
|
-
rows: []
|
|
1316
|
-
};
|
|
1317
|
-
if (n.length === s.length) {
|
|
1318
|
-
for (let o of s) this.rules.other.tableAlignRight.test(o) ? i.align.push("right") : this.rules.other.tableAlignCenter.test(o) ? i.align.push("center") : this.rules.other.tableAlignLeft.test(o) ? i.align.push("left") : i.align.push(null);
|
|
1319
|
-
for (let o = 0; o < n.length; o++) i.header.push({
|
|
1320
|
-
text: n[o],
|
|
1321
|
-
tokens: this.lexer.inline(n[o]),
|
|
1322
|
-
header: !0,
|
|
1323
|
-
align: i.align[o]
|
|
1324
|
-
});
|
|
1325
|
-
for (let o of r) i.rows.push(ee(o, i.header.length).map((u, a) => ({
|
|
1326
|
-
text: u,
|
|
1327
|
-
tokens: this.lexer.inline(u),
|
|
1328
|
-
header: !1,
|
|
1329
|
-
align: i.align[a]
|
|
1330
|
-
})));
|
|
1331
|
-
return i;
|
|
1332
|
-
}
|
|
1333
|
-
}
|
|
1334
|
-
lheading(e) {
|
|
1335
|
-
let t = this.rules.block.lheading.exec(e);
|
|
1336
|
-
if (t) {
|
|
1337
|
-
let n = t[1].trim();
|
|
1338
|
-
return {
|
|
1339
|
-
type: "heading",
|
|
1340
|
-
raw: $(t[0], `
|
|
1341
|
-
`),
|
|
1342
|
-
depth: t[2].charAt(0) === "=" ? 1 : 2,
|
|
1343
|
-
text: n,
|
|
1344
|
-
tokens: this.lexer.inline(n)
|
|
1345
|
-
};
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
paragraph(e) {
|
|
1349
|
-
let t = this.rules.block.paragraph.exec(e);
|
|
1350
|
-
if (t) {
|
|
1351
|
-
let n = t[1].charAt(t[1].length - 1) === `
|
|
1352
|
-
` ? t[1].slice(0, -1) : t[1];
|
|
1353
|
-
return {
|
|
1354
|
-
type: "paragraph",
|
|
1355
|
-
raw: t[0],
|
|
1356
|
-
text: n,
|
|
1357
|
-
tokens: this.lexer.inline(n)
|
|
1358
|
-
};
|
|
1359
|
-
}
|
|
1360
|
-
}
|
|
1361
|
-
text(e) {
|
|
1362
|
-
let t = this.rules.block.text.exec(e);
|
|
1363
|
-
if (t) return {
|
|
1364
|
-
type: "text",
|
|
1365
|
-
raw: t[0],
|
|
1366
|
-
text: t[0],
|
|
1367
|
-
tokens: this.lexer.inline(t[0])
|
|
1368
|
-
};
|
|
1369
|
-
}
|
|
1370
|
-
escape(e) {
|
|
1371
|
-
let t = this.rules.inline.escape.exec(e);
|
|
1372
|
-
if (t) return {
|
|
1373
|
-
type: "escape",
|
|
1374
|
-
raw: t[0],
|
|
1375
|
-
text: t[1]
|
|
1376
|
-
};
|
|
1377
|
-
}
|
|
1378
|
-
tag(e) {
|
|
1379
|
-
let t = this.rules.inline.tag.exec(e);
|
|
1380
|
-
if (t) return !this.lexer.state.inLink && this.rules.other.startATag.test(t[0]) ? this.lexer.state.inLink = !0 : this.lexer.state.inLink && this.rules.other.endATag.test(t[0]) && (this.lexer.state.inLink = !1), !this.lexer.state.inRawBlock && this.rules.other.startPreScriptTag.test(t[0]) ? this.lexer.state.inRawBlock = !0 : this.lexer.state.inRawBlock && this.rules.other.endPreScriptTag.test(t[0]) && (this.lexer.state.inRawBlock = !1), {
|
|
1381
|
-
type: "html",
|
|
1382
|
-
raw: t[0],
|
|
1383
|
-
inLink: this.lexer.state.inLink,
|
|
1384
|
-
inRawBlock: this.lexer.state.inRawBlock,
|
|
1385
|
-
block: !1,
|
|
1386
|
-
text: t[0]
|
|
1387
|
-
};
|
|
1388
|
-
}
|
|
1389
|
-
link(e) {
|
|
1390
|
-
let t = this.rules.inline.link.exec(e);
|
|
1391
|
-
if (t) {
|
|
1392
|
-
let n = t[2].trim();
|
|
1393
|
-
if (!this.options.pedantic && this.rules.other.startAngleBracket.test(n)) {
|
|
1394
|
-
if (!this.rules.other.endAngleBracket.test(n)) return;
|
|
1395
|
-
let i = $(n.slice(0, -1), "\\");
|
|
1396
|
-
if ((n.length - i.length) % 2 === 0) return;
|
|
1397
|
-
} else {
|
|
1398
|
-
let i = fe(t[2], "()");
|
|
1399
|
-
if (i === -2) return;
|
|
1400
|
-
if (i > -1) {
|
|
1401
|
-
let u = (t[0].indexOf("!") === 0 ? 5 : 4) + t[1].length + i;
|
|
1402
|
-
t[2] = t[2].substring(0, i), t[0] = t[0].substring(0, u).trim(), t[3] = "";
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
let s = t[2], r = "";
|
|
1406
|
-
if (this.options.pedantic) {
|
|
1407
|
-
let i = this.rules.other.pedanticHrefTitle.exec(s);
|
|
1408
|
-
i && (s = i[1], r = i[3]);
|
|
1409
|
-
} else r = t[3] ? t[3].slice(1, -1) : "";
|
|
1410
|
-
return s = s.trim(), this.rules.other.startAngleBracket.test(s) && (this.options.pedantic && !this.rules.other.endAngleBracket.test(n) ? s = s.slice(1) : s = s.slice(1, -1)), xe(t, {
|
|
1411
|
-
href: s && s.replace(this.rules.inline.anyPunctuation, "$1"),
|
|
1412
|
-
title: r && r.replace(this.rules.inline.anyPunctuation, "$1")
|
|
1413
|
-
}, t[0], this.lexer, this.rules);
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
|
-
reflink(e, t) {
|
|
1417
|
-
let n;
|
|
1418
|
-
if ((n = this.rules.inline.reflink.exec(e)) || (n = this.rules.inline.nolink.exec(e))) {
|
|
1419
|
-
let r = t[(n[2] || n[1]).replace(this.rules.other.multipleSpaceGlobal, " ").toLowerCase()];
|
|
1420
|
-
if (!r) {
|
|
1421
|
-
let i = n[0].charAt(0);
|
|
1422
|
-
return {
|
|
1423
|
-
type: "text",
|
|
1424
|
-
raw: i,
|
|
1425
|
-
text: i
|
|
1426
|
-
};
|
|
1427
|
-
}
|
|
1428
|
-
return xe(n, r, n[0], this.lexer, this.rules);
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
|
-
emStrong(e, t, n = "") {
|
|
1432
|
-
let s = this.rules.inline.emStrongLDelim.exec(e);
|
|
1433
|
-
if (!s || !s[1] && !s[2] && !s[3] && !s[4] || s[4] && n.match(this.rules.other.unicodeAlphaNumeric)) return;
|
|
1434
|
-
if (!(s[1] || s[3] || "") || !n || this.rules.inline.punctuation.exec(n)) {
|
|
1435
|
-
let i = [...s[0]].length - 1, o, u, a = i, p = 0, c = s[0][0], h = n === c, d = c === "*" ? this.rules.inline.emStrongRDelimAst : this.rules.inline.emStrongRDelimUnd;
|
|
1436
|
-
for (d.lastIndex = 0, t = t.slice(-1 * e.length + i); (s = d.exec(t)) !== null;) {
|
|
1437
|
-
if (o = s[1] || s[2] || s[3] || s[4] || s[5] || s[6], !o) continue;
|
|
1438
|
-
if (u = [...o].length, s[3] || s[4]) {
|
|
1439
|
-
a += u;
|
|
1440
|
-
continue;
|
|
1441
|
-
} else if (s[5] || s[6]) {
|
|
1442
|
-
if (i % 3 && !((i + u) % 3)) {
|
|
1443
|
-
p += u;
|
|
1444
|
-
continue;
|
|
1445
|
-
}
|
|
1446
|
-
if (h) break;
|
|
1447
|
-
}
|
|
1448
|
-
if (a -= u, a > 0) continue;
|
|
1449
|
-
u = Math.min(u, u + a + p);
|
|
1450
|
-
let O = [...s[0]][0].length, g = e.slice(0, i + s.index + O + u);
|
|
1451
|
-
if (Math.min(i, u) % 2) {
|
|
1452
|
-
let E = g.slice(1, -1);
|
|
1453
|
-
return {
|
|
1454
|
-
type: "em",
|
|
1455
|
-
raw: g,
|
|
1456
|
-
text: E,
|
|
1457
|
-
tokens: this.lexer.inlineTokens(E)
|
|
1458
|
-
};
|
|
1459
|
-
}
|
|
1460
|
-
let w = g.slice(2, -2);
|
|
1461
|
-
return {
|
|
1462
|
-
type: "strong",
|
|
1463
|
-
raw: g,
|
|
1464
|
-
text: w,
|
|
1465
|
-
tokens: this.lexer.inlineTokens(w)
|
|
1466
|
-
};
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
}
|
|
1470
|
-
codespan(e) {
|
|
1471
|
-
let t = this.rules.inline.code.exec(e);
|
|
1472
|
-
if (t) {
|
|
1473
|
-
let n = t[2].replace(this.rules.other.newLineCharGlobal, " "), s = this.rules.other.nonSpaceChar.test(n), r = this.rules.other.startingSpaceChar.test(n) && this.rules.other.endingSpaceChar.test(n);
|
|
1474
|
-
return s && r && (n = n.substring(1, n.length - 1)), {
|
|
1475
|
-
type: "codespan",
|
|
1476
|
-
raw: t[0],
|
|
1477
|
-
text: n
|
|
1478
|
-
};
|
|
1479
|
-
}
|
|
1480
|
-
}
|
|
1481
|
-
br(e) {
|
|
1482
|
-
let t = this.rules.inline.br.exec(e);
|
|
1483
|
-
if (t) return {
|
|
1484
|
-
type: "br",
|
|
1485
|
-
raw: t[0]
|
|
1486
|
-
};
|
|
1487
|
-
}
|
|
1488
|
-
del(e, t, n = "") {
|
|
1489
|
-
let s = this.rules.inline.delLDelim.exec(e);
|
|
1490
|
-
if (!s) return;
|
|
1491
|
-
if (!(s[1] || "") || !n || this.rules.inline.punctuation.exec(n)) {
|
|
1492
|
-
let i = [...s[0]].length - 1, o, u, a = i, p = this.rules.inline.delRDelim;
|
|
1493
|
-
for (p.lastIndex = 0, t = t.slice(-1 * e.length + i); (s = p.exec(t)) !== null;) {
|
|
1494
|
-
if (o = s[1] || s[2] || s[3] || s[4] || s[5] || s[6], !o || (u = [...o].length, u !== i)) continue;
|
|
1495
|
-
if (s[3] || s[4]) {
|
|
1496
|
-
a += u;
|
|
1497
|
-
continue;
|
|
1498
|
-
}
|
|
1499
|
-
if (a -= u, a > 0) continue;
|
|
1500
|
-
u = Math.min(u, u + a);
|
|
1501
|
-
let c = [...s[0]][0].length, h = e.slice(0, i + s.index + c + u), d = h.slice(i, -i);
|
|
1502
|
-
return {
|
|
1503
|
-
type: "del",
|
|
1504
|
-
raw: h,
|
|
1505
|
-
text: d,
|
|
1506
|
-
tokens: this.lexer.inlineTokens(d)
|
|
1507
|
-
};
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1510
|
-
}
|
|
1511
|
-
autolink(e) {
|
|
1512
|
-
let t = this.rules.inline.autolink.exec(e);
|
|
1513
|
-
if (t) {
|
|
1514
|
-
let n, s;
|
|
1515
|
-
return t[2] === "@" ? (n = t[1], s = "mailto:" + n) : (n = t[1], s = n), {
|
|
1516
|
-
type: "link",
|
|
1517
|
-
raw: t[0],
|
|
1518
|
-
text: n,
|
|
1519
|
-
href: s,
|
|
1520
|
-
tokens: [{
|
|
1521
|
-
type: "text",
|
|
1522
|
-
raw: n,
|
|
1523
|
-
text: n
|
|
1524
|
-
}]
|
|
1525
|
-
};
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
1528
|
-
url(e) {
|
|
1529
|
-
let t;
|
|
1530
|
-
if (t = this.rules.inline.url.exec(e)) {
|
|
1531
|
-
let n, s;
|
|
1532
|
-
if (t[2] === "@") n = t[0], s = "mailto:" + n;
|
|
1533
|
-
else {
|
|
1534
|
-
let r;
|
|
1535
|
-
do
|
|
1536
|
-
r = t[0], t[0] = this.rules.inline._backpedal.exec(t[0])?.[0] ?? "";
|
|
1537
|
-
while (r !== t[0]);
|
|
1538
|
-
n = t[0], t[1] === "www." ? s = "http://" + t[0] : s = t[0];
|
|
1539
|
-
}
|
|
1540
|
-
return {
|
|
1541
|
-
type: "link",
|
|
1542
|
-
raw: t[0],
|
|
1543
|
-
text: n,
|
|
1544
|
-
href: s,
|
|
1545
|
-
tokens: [{
|
|
1546
|
-
type: "text",
|
|
1547
|
-
raw: n,
|
|
1548
|
-
text: n
|
|
1549
|
-
}]
|
|
1550
|
-
};
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
1553
|
-
inlineText(e) {
|
|
1554
|
-
let t = this.rules.inline.text.exec(e);
|
|
1555
|
-
if (t) {
|
|
1556
|
-
let n = this.lexer.state.inRawBlock;
|
|
1557
|
-
return {
|
|
1558
|
-
type: "text",
|
|
1559
|
-
raw: t[0],
|
|
1560
|
-
text: t[0],
|
|
1561
|
-
escaped: n
|
|
1562
|
-
};
|
|
1563
|
-
}
|
|
1564
|
-
}
|
|
1565
|
-
};
|
|
1566
|
-
var x = class l {
|
|
1567
|
-
tokens;
|
|
1568
|
-
options;
|
|
1569
|
-
state;
|
|
1570
|
-
inlineQueue;
|
|
1571
|
-
tokenizer;
|
|
1572
|
-
constructor(e) {
|
|
1573
|
-
this.tokens = [], this.tokens.links = Object.create(null), this.options = e || R, this.options.tokenizer = this.options.tokenizer || new y(), this.tokenizer = this.options.tokenizer, this.tokenizer.options = this.options, this.tokenizer.lexer = this, this.inlineQueue = [], this.state = {
|
|
1574
|
-
inLink: !1,
|
|
1575
|
-
inRawBlock: !1,
|
|
1576
|
-
linkEmitted: !1,
|
|
1577
|
-
top: !0
|
|
1578
|
-
};
|
|
1579
|
-
let t = {
|
|
1580
|
-
other: m,
|
|
1581
|
-
block: H.normal,
|
|
1582
|
-
inline: B.normal
|
|
1583
|
-
};
|
|
1584
|
-
this.options.pedantic ? (t.block = H.pedantic, t.inline = B.pedantic) : this.options.gfm && (t.block = H.gfm, this.options.breaks ? t.inline = B.breaks : t.inline = B.gfm), this.tokenizer.rules = t;
|
|
1585
|
-
}
|
|
1586
|
-
static get rules() {
|
|
1587
|
-
return {
|
|
1588
|
-
block: H,
|
|
1589
|
-
inline: B
|
|
1590
|
-
};
|
|
1591
|
-
}
|
|
1592
|
-
static lex(e, t) {
|
|
1593
|
-
return new l(t).lex(e);
|
|
1594
|
-
}
|
|
1595
|
-
static lexInline(e, t) {
|
|
1596
|
-
return new l(t).inlineTokens(e);
|
|
1597
|
-
}
|
|
1598
|
-
lex(e) {
|
|
1599
|
-
e = e.replace(m.carriageReturn, `
|
|
1600
|
-
`), this.blockTokens(e, this.tokens);
|
|
1601
|
-
for (let t = 0; t < this.inlineQueue.length; t++) {
|
|
1602
|
-
let n = this.inlineQueue[t];
|
|
1603
|
-
this.inlineTokens(n.src, n.tokens);
|
|
1604
|
-
}
|
|
1605
|
-
return this.inlineQueue = [], this.tokens;
|
|
1606
|
-
}
|
|
1607
|
-
blockTokens(e, t = [], n = !1) {
|
|
1608
|
-
this.tokenizer.lexer = this, this.options.pedantic && (e = e.replace(m.tabCharGlobal, " ").replace(m.spaceLine, ""));
|
|
1609
|
-
let s = 1 / 0;
|
|
1610
|
-
for (; e;) {
|
|
1611
|
-
if (e.length < s) s = e.length;
|
|
1612
|
-
else {
|
|
1613
|
-
this.infiniteLoopError(e.charCodeAt(0));
|
|
1614
|
-
break;
|
|
1615
|
-
}
|
|
1616
|
-
let r;
|
|
1617
|
-
if (this.options.extensions?.block?.some((o) => (r = o.call({ lexer: this }, e, t)) ? (e = e.substring(r.raw.length), t.push(r), !0) : !1)) continue;
|
|
1618
|
-
if (r = this.tokenizer.space(e)) {
|
|
1619
|
-
e = e.substring(r.raw.length);
|
|
1620
|
-
let o = t.at(-1);
|
|
1621
|
-
r.raw.length === 1 && o !== void 0 ? o.raw += `
|
|
1622
|
-
` : t.push(r);
|
|
1623
|
-
continue;
|
|
1624
|
-
}
|
|
1625
|
-
if (r = this.tokenizer.code(e)) {
|
|
1626
|
-
e = e.substring(r.raw.length);
|
|
1627
|
-
let o = t.at(-1);
|
|
1628
|
-
o?.type === "paragraph" || o?.type === "text" ? (o.raw += (o.raw.endsWith(`
|
|
1629
|
-
`) ? "" : `
|
|
1630
|
-
`) + r.raw, o.text += `
|
|
1631
|
-
` + r.text, this.inlineQueue.at(-1).src = o.text) : t.push(r);
|
|
1632
|
-
continue;
|
|
1633
|
-
}
|
|
1634
|
-
if (r = this.tokenizer.fences(e)) {
|
|
1635
|
-
e = e.substring(r.raw.length), t.push(r);
|
|
1636
|
-
continue;
|
|
1637
|
-
}
|
|
1638
|
-
if (r = this.tokenizer.heading(e)) {
|
|
1639
|
-
e = e.substring(r.raw.length), t.push(r);
|
|
1640
|
-
continue;
|
|
1641
|
-
}
|
|
1642
|
-
if (r = this.tokenizer.hr(e)) {
|
|
1643
|
-
e = e.substring(r.raw.length), t.push(r);
|
|
1644
|
-
continue;
|
|
1645
|
-
}
|
|
1646
|
-
if (r = this.tokenizer.blockquote(e)) {
|
|
1647
|
-
e = e.substring(r.raw.length), t.push(r);
|
|
1648
|
-
continue;
|
|
1649
|
-
}
|
|
1650
|
-
if (r = this.tokenizer.list(e)) {
|
|
1651
|
-
e = e.substring(r.raw.length), t.push(r);
|
|
1652
|
-
continue;
|
|
1653
|
-
}
|
|
1654
|
-
if (r = this.tokenizer.html(e)) {
|
|
1655
|
-
e = e.substring(r.raw.length), t.push(r);
|
|
1656
|
-
continue;
|
|
1657
|
-
}
|
|
1658
|
-
if (r = this.tokenizer.def(e)) {
|
|
1659
|
-
e = e.substring(r.raw.length);
|
|
1660
|
-
let o = t.at(-1);
|
|
1661
|
-
o?.type === "paragraph" || o?.type === "text" ? (o.raw += (o.raw.endsWith(`
|
|
1662
|
-
`) ? "" : `
|
|
1663
|
-
`) + r.raw, o.text += `
|
|
1664
|
-
` + r.raw, this.inlineQueue.at(-1).src = o.text) : this.tokens.links[r.tag] || (this.tokens.links[r.tag] = {
|
|
1665
|
-
href: r.href,
|
|
1666
|
-
title: r.title
|
|
1667
|
-
}, t.push(r));
|
|
1668
|
-
continue;
|
|
1669
|
-
}
|
|
1670
|
-
if (r = this.tokenizer.table(e)) {
|
|
1671
|
-
e = e.substring(r.raw.length), t.push(r);
|
|
1672
|
-
continue;
|
|
1673
|
-
}
|
|
1674
|
-
if (r = this.tokenizer.lheading(e)) {
|
|
1675
|
-
e = e.substring(r.raw.length), t.push(r);
|
|
1676
|
-
continue;
|
|
1677
|
-
}
|
|
1678
|
-
let i = e;
|
|
1679
|
-
if (this.options.extensions?.startBlock) {
|
|
1680
|
-
let o = 1 / 0, u = e.slice(1), a;
|
|
1681
|
-
this.options.extensions.startBlock.forEach((p) => {
|
|
1682
|
-
a = p.call({ lexer: this }, u), typeof a == "number" && a >= 0 && (o = Math.min(o, a));
|
|
1683
|
-
}), o < 1 / 0 && o >= 0 && (i = e.substring(0, o + 1));
|
|
1684
|
-
}
|
|
1685
|
-
if (this.state.top && (r = this.tokenizer.paragraph(i))) {
|
|
1686
|
-
let o = t.at(-1);
|
|
1687
|
-
n && o?.type === "paragraph" ? (o.raw += (o.raw.endsWith(`
|
|
1688
|
-
`) ? "" : `
|
|
1689
|
-
`) + r.raw, o.text += `
|
|
1690
|
-
` + r.text, this.inlineQueue.pop(), this.inlineQueue.at(-1).src = o.text) : t.push(r), n = i.length !== e.length, e = e.substring(r.raw.length);
|
|
1691
|
-
continue;
|
|
1692
|
-
}
|
|
1693
|
-
if (r = this.tokenizer.text(e)) {
|
|
1694
|
-
e = e.substring(r.raw.length);
|
|
1695
|
-
let o = t.at(-1);
|
|
1696
|
-
o?.type === "text" ? (o.raw += (o.raw.endsWith(`
|
|
1697
|
-
`) ? "" : `
|
|
1698
|
-
`) + r.raw, o.text += `
|
|
1699
|
-
` + r.text, this.inlineQueue.pop(), this.inlineQueue.at(-1).src = o.text) : t.push(r);
|
|
1700
|
-
continue;
|
|
1701
|
-
}
|
|
1702
|
-
if (e) {
|
|
1703
|
-
this.infiniteLoopError(e.charCodeAt(0));
|
|
1704
|
-
break;
|
|
1705
|
-
}
|
|
1706
|
-
}
|
|
1707
|
-
return this.state.top = !0, t;
|
|
1708
|
-
}
|
|
1709
|
-
inline(e, t = []) {
|
|
1710
|
-
return this.inlineQueue.push({
|
|
1711
|
-
src: e,
|
|
1712
|
-
tokens: t
|
|
1713
|
-
}), t;
|
|
1714
|
-
}
|
|
1715
|
-
linkInText(e) {
|
|
1716
|
-
if (!e.includes("[")) return !1;
|
|
1717
|
-
let t = this.tokenizer.rules.inline.link;
|
|
1718
|
-
for (let n of e.matchAll(this.tokenizer.rules.inline.blockSkip)) if (t.test(n[0]) && e.charAt(n.index - 1) !== "!") return !0;
|
|
1719
|
-
for (let n of e.matchAll(this.tokenizer.rules.inline.reflinkSearch)) {
|
|
1720
|
-
let s = n[0], r = s.lastIndexOf("[");
|
|
1721
|
-
if (!(s.charAt(0) === "!" || !Object.hasOwn(this.tokens.links, s.slice(r + 1, -1))) && !(r > 1 && this.linkInText(s.slice(1, r - 1)))) return !0;
|
|
1722
|
-
}
|
|
1723
|
-
return !1;
|
|
1724
|
-
}
|
|
1725
|
-
inlineTokens(e, t = []) {
|
|
1726
|
-
this.tokenizer.lexer = this;
|
|
1727
|
-
let n = e;
|
|
1728
|
-
if (this.tokens.links && e.includes("[")) {
|
|
1729
|
-
let o = this.tokenizer.rules.inline.reflinkSearch, u = (a) => {
|
|
1730
|
-
let p = a.lastIndexOf("[");
|
|
1731
|
-
if (!Object.hasOwn(this.tokens.links, a.slice(p + 1, -1))) return a;
|
|
1732
|
-
if (p > 1 && a.charAt(0) !== "!") {
|
|
1733
|
-
let c = a.slice(1, p - 1);
|
|
1734
|
-
if (this.linkInText(c)) return "[" + c.replace(o, u) + "][" + "a".repeat(a.length - p - 2) + "]";
|
|
1735
|
-
}
|
|
1736
|
-
return "[" + "a".repeat(a.length - 2) + "]";
|
|
1737
|
-
};
|
|
1738
|
-
n = n.replace(o, u);
|
|
1739
|
-
}
|
|
1740
|
-
n = n.replace(this.tokenizer.rules.inline.anyPunctuation, (o) => "+".repeat(o.length)), n = n.replace(this.tokenizer.rules.inline.blockSkip, (o, u, a) => {
|
|
1741
|
-
let p = a ? a.length : 0;
|
|
1742
|
-
return o.slice(0, p) + "[" + "a".repeat(o.length - p - 2) + "]";
|
|
1743
|
-
}), n = this.options.hooks?.emStrongMask?.call({ lexer: this }, n) ?? n;
|
|
1744
|
-
let s = !1, r = "", i = 1 / 0;
|
|
1745
|
-
for (; e;) {
|
|
1746
|
-
if (e.length < i) i = e.length;
|
|
1747
|
-
else {
|
|
1748
|
-
this.infiniteLoopError(e.charCodeAt(0));
|
|
1749
|
-
break;
|
|
1750
|
-
}
|
|
1751
|
-
s || (r = ""), s = !1;
|
|
1752
|
-
let o;
|
|
1753
|
-
if (this.options.extensions?.inline?.some((a) => (o = a.call({ lexer: this }, e, t)) ? (e = e.substring(o.raw.length), t.push(o), !0) : !1)) continue;
|
|
1754
|
-
if (o = this.tokenizer.escape(e)) {
|
|
1755
|
-
e = e.substring(o.raw.length), t.push(o);
|
|
1756
|
-
continue;
|
|
1757
|
-
}
|
|
1758
|
-
if (o = this.tokenizer.tag(e)) {
|
|
1759
|
-
e = e.substring(o.raw.length), t.push(o);
|
|
1760
|
-
continue;
|
|
1761
|
-
}
|
|
1762
|
-
if (o = this.tokenizer.link(e)) {
|
|
1763
|
-
e = e.substring(o.raw.length), t.push(o);
|
|
1764
|
-
continue;
|
|
1765
|
-
}
|
|
1766
|
-
if (o = this.tokenizer.reflink(e, this.tokens.links)) {
|
|
1767
|
-
e = e.substring(o.raw.length);
|
|
1768
|
-
let a = t.at(-1);
|
|
1769
|
-
o.type === "text" && a?.type === "text" ? (a.raw += o.raw, a.text += o.text) : t.push(o);
|
|
1770
|
-
continue;
|
|
1771
|
-
}
|
|
1772
|
-
if (o = this.tokenizer.emStrong(e, n, r)) {
|
|
1773
|
-
e = e.substring(o.raw.length), t.push(o);
|
|
1774
|
-
continue;
|
|
1775
|
-
}
|
|
1776
|
-
if (o = this.tokenizer.codespan(e)) {
|
|
1777
|
-
e = e.substring(o.raw.length), t.push(o);
|
|
1778
|
-
continue;
|
|
1779
|
-
}
|
|
1780
|
-
if (o = this.tokenizer.br(e)) {
|
|
1781
|
-
e = e.substring(o.raw.length), t.push(o);
|
|
1782
|
-
continue;
|
|
1783
|
-
}
|
|
1784
|
-
if (o = this.tokenizer.del(e, n, r)) {
|
|
1785
|
-
e = e.substring(o.raw.length), t.push(o);
|
|
1786
|
-
continue;
|
|
1787
|
-
}
|
|
1788
|
-
if (o = this.tokenizer.autolink(e)) {
|
|
1789
|
-
e = e.substring(o.raw.length), t.push(o);
|
|
1790
|
-
continue;
|
|
1791
|
-
}
|
|
1792
|
-
if (!this.state.inLink && (o = this.tokenizer.url(e))) {
|
|
1793
|
-
e = e.substring(o.raw.length), t.push(o);
|
|
1794
|
-
continue;
|
|
1795
|
-
}
|
|
1796
|
-
let u = e;
|
|
1797
|
-
if (this.options.extensions?.startInline) {
|
|
1798
|
-
let a = 1 / 0, p = e.slice(1), c;
|
|
1799
|
-
this.options.extensions.startInline.forEach((h) => {
|
|
1800
|
-
c = h.call({ lexer: this }, p), typeof c == "number" && c >= 0 && (a = Math.min(a, c));
|
|
1801
|
-
}), a < 1 / 0 && a >= 0 && (u = e.substring(0, a + 1));
|
|
1802
|
-
}
|
|
1803
|
-
if (o = this.tokenizer.inlineText(u)) {
|
|
1804
|
-
e = e.substring(o.raw.length), o.raw.slice(-1) !== "_" && (r = o.raw.slice(-1)), s = !0;
|
|
1805
|
-
let a = t.at(-1);
|
|
1806
|
-
a?.type === "text" ? (a.raw += o.raw, a.text += o.text) : t.push(o);
|
|
1807
|
-
continue;
|
|
1808
|
-
}
|
|
1809
|
-
if (e) {
|
|
1810
|
-
this.infiniteLoopError(e.charCodeAt(0));
|
|
1811
|
-
break;
|
|
1812
|
-
}
|
|
1813
|
-
}
|
|
1814
|
-
return t;
|
|
1815
|
-
}
|
|
1816
|
-
infiniteLoopError(e) {
|
|
1817
|
-
let t = "Infinite loop on byte: " + e;
|
|
1818
|
-
if (this.options.silent) console.error(t);
|
|
1819
|
-
else throw new Error(t);
|
|
1820
|
-
}
|
|
1821
|
-
};
|
|
1822
|
-
var P = class {
|
|
1823
|
-
options;
|
|
1824
|
-
parser;
|
|
1825
|
-
constructor(e) {
|
|
1826
|
-
this.options = e || R;
|
|
1827
|
-
}
|
|
1828
|
-
space(e) {
|
|
1829
|
-
return "";
|
|
1830
|
-
}
|
|
1831
|
-
code({ text: e, lang: t, escaped: n }) {
|
|
1832
|
-
let s = (t || "").match(m.notSpaceStart)?.[0], r = e.replace(m.endingNewline, "") + `
|
|
1833
|
-
`;
|
|
1834
|
-
return s ? "<pre><code class=\"language-" + T(s) + "\">" + (n ? r : T(r, !0)) + `</code></pre>
|
|
1835
|
-
` : "<pre><code>" + (n ? r : T(r, !0)) + `</code></pre>
|
|
1836
|
-
`;
|
|
1837
|
-
}
|
|
1838
|
-
blockquote({ tokens: e }) {
|
|
1839
|
-
return `<blockquote>
|
|
1840
|
-
${this.parser.parse(e)}</blockquote>
|
|
1841
|
-
`;
|
|
1842
|
-
}
|
|
1843
|
-
html({ text: e }) {
|
|
1844
|
-
return e;
|
|
1845
|
-
}
|
|
1846
|
-
def(e) {
|
|
1847
|
-
return "";
|
|
1848
|
-
}
|
|
1849
|
-
heading({ tokens: e, depth: t }) {
|
|
1850
|
-
return `<h${t}>${this.parser.parseInline(e)}</h${t}>
|
|
1851
|
-
`;
|
|
1852
|
-
}
|
|
1853
|
-
hr(e) {
|
|
1854
|
-
return `<hr>
|
|
1855
|
-
`;
|
|
1856
|
-
}
|
|
1857
|
-
list(e) {
|
|
1858
|
-
let t = e.ordered, n = e.start, s = "";
|
|
1859
|
-
for (let o = 0; o < e.items.length; o++) {
|
|
1860
|
-
let u = e.items[o];
|
|
1861
|
-
s += this.listitem(u);
|
|
1862
|
-
}
|
|
1863
|
-
let r = t ? "ol" : "ul", i = t && n !== 1 ? " start=\"" + n + "\"" : "";
|
|
1864
|
-
return "<" + r + i + `>
|
|
1865
|
-
` + s + "</" + r + `>
|
|
1866
|
-
`;
|
|
1867
|
-
}
|
|
1868
|
-
listitem(e) {
|
|
1869
|
-
return `<li>${this.parser.parse(e.tokens)}</li>
|
|
1870
|
-
`;
|
|
1871
|
-
}
|
|
1872
|
-
checkbox({ checked: e }) {
|
|
1873
|
-
return "<input " + (e ? "checked=\"\" " : "") + "disabled=\"\" type=\"checkbox\"> ";
|
|
1874
|
-
}
|
|
1875
|
-
paragraph({ tokens: e }) {
|
|
1876
|
-
return `<p>${this.parser.parseInline(e)}</p>
|
|
1877
|
-
`;
|
|
1878
|
-
}
|
|
1879
|
-
table(e) {
|
|
1880
|
-
let t = "", n = "";
|
|
1881
|
-
for (let r = 0; r < e.header.length; r++) n += this.tablecell(e.header[r]);
|
|
1882
|
-
t += this.tablerow({ text: n });
|
|
1883
|
-
let s = "";
|
|
1884
|
-
for (let r = 0; r < e.rows.length; r++) {
|
|
1885
|
-
let i = e.rows[r];
|
|
1886
|
-
n = "";
|
|
1887
|
-
for (let o = 0; o < i.length; o++) n += this.tablecell(i[o]);
|
|
1888
|
-
s += this.tablerow({ text: n });
|
|
1889
|
-
}
|
|
1890
|
-
return s && (s = `<tbody>${s}</tbody>`), `<table>
|
|
1891
|
-
<thead>
|
|
1892
|
-
` + t + `</thead>
|
|
1893
|
-
` + s + `</table>
|
|
1894
|
-
`;
|
|
1895
|
-
}
|
|
1896
|
-
tablerow({ text: e }) {
|
|
1897
|
-
return `<tr>
|
|
1898
|
-
${e}</tr>
|
|
1899
|
-
`;
|
|
1900
|
-
}
|
|
1901
|
-
tablecell(e) {
|
|
1902
|
-
let t = this.parser.parseInline(e.tokens), n = e.header ? "th" : "td";
|
|
1903
|
-
return (e.align ? `<${n} align="${e.align}">` : `<${n}>`) + t + `</${n}>
|
|
1904
|
-
`;
|
|
1905
|
-
}
|
|
1906
|
-
strong({ tokens: e }) {
|
|
1907
|
-
return `<strong>${this.parser.parseInline(e)}</strong>`;
|
|
1908
|
-
}
|
|
1909
|
-
em({ tokens: e }) {
|
|
1910
|
-
return `<em>${this.parser.parseInline(e)}</em>`;
|
|
1911
|
-
}
|
|
1912
|
-
codespan({ text: e }) {
|
|
1913
|
-
return `<code>${T(e, !0)}</code>`;
|
|
1914
|
-
}
|
|
1915
|
-
br(e) {
|
|
1916
|
-
return "<br>";
|
|
1917
|
-
}
|
|
1918
|
-
del({ tokens: e }) {
|
|
1919
|
-
return `<del>${this.parser.parseInline(e)}</del>`;
|
|
1920
|
-
}
|
|
1921
|
-
link({ href: e, title: t, tokens: n }) {
|
|
1922
|
-
let s = this.parser.parseInline(n), r = Y(e);
|
|
1923
|
-
if (r === null) return s;
|
|
1924
|
-
e = r;
|
|
1925
|
-
let i = "<a href=\"" + e + "\"";
|
|
1926
|
-
return t && (i += " title=\"" + T(t) + "\""), i += ">" + s + "</a>", i;
|
|
1927
|
-
}
|
|
1928
|
-
image({ href: e, title: t, text: n, tokens: s }) {
|
|
1929
|
-
s && (n = this.parser.parseInline(s, this.parser.textRenderer));
|
|
1930
|
-
let r = Y(e);
|
|
1931
|
-
if (r === null) return T(n);
|
|
1932
|
-
e = r;
|
|
1933
|
-
let i = `<img src="${e}" alt="${T(n)}"`;
|
|
1934
|
-
return t && (i += ` title="${T(t)}"`), i += ">", i;
|
|
1935
|
-
}
|
|
1936
|
-
text(e) {
|
|
1937
|
-
return "tokens" in e && e.tokens ? this.parser.parseInline(e.tokens) : "escaped" in e && e.escaped ? e.text : T(e.text);
|
|
1938
|
-
}
|
|
1939
|
-
};
|
|
1940
|
-
var L = class {
|
|
1941
|
-
strong({ text: e }) {
|
|
1942
|
-
return e;
|
|
1943
|
-
}
|
|
1944
|
-
em({ text: e }) {
|
|
1945
|
-
return e;
|
|
1946
|
-
}
|
|
1947
|
-
codespan({ text: e }) {
|
|
1948
|
-
return e;
|
|
1949
|
-
}
|
|
1950
|
-
del({ text: e }) {
|
|
1951
|
-
return e;
|
|
1952
|
-
}
|
|
1953
|
-
html({ text: e }) {
|
|
1954
|
-
return e;
|
|
1955
|
-
}
|
|
1956
|
-
text({ text: e }) {
|
|
1957
|
-
return e;
|
|
1958
|
-
}
|
|
1959
|
-
link({ text: e }) {
|
|
1960
|
-
return "" + e;
|
|
1961
|
-
}
|
|
1962
|
-
image({ text: e }) {
|
|
1963
|
-
return "" + e;
|
|
1964
|
-
}
|
|
1965
|
-
br() {
|
|
1966
|
-
return "";
|
|
1967
|
-
}
|
|
1968
|
-
checkbox({ raw: e }) {
|
|
1969
|
-
return e;
|
|
1970
|
-
}
|
|
1971
|
-
};
|
|
1972
|
-
var b = class l {
|
|
1973
|
-
options;
|
|
1974
|
-
renderer;
|
|
1975
|
-
textRenderer;
|
|
1976
|
-
constructor(e) {
|
|
1977
|
-
this.options = e || R, this.options.renderer = this.options.renderer || new P(), this.renderer = this.options.renderer, this.renderer.options = this.options, this.renderer.parser = this, this.textRenderer = new L();
|
|
1978
|
-
}
|
|
1979
|
-
static parse(e, t) {
|
|
1980
|
-
return new l(t).parse(e);
|
|
1981
|
-
}
|
|
1982
|
-
static parseInline(e, t) {
|
|
1983
|
-
return new l(t).parseInline(e);
|
|
1984
|
-
}
|
|
1985
|
-
parse(e) {
|
|
1986
|
-
this.renderer.parser = this;
|
|
1987
|
-
let t = "";
|
|
1988
|
-
for (let n = 0; n < e.length; n++) {
|
|
1989
|
-
let s = e[n];
|
|
1990
|
-
if (this.options.extensions?.renderers?.[s.type]) {
|
|
1991
|
-
let i = s, o = this.options.extensions.renderers[i.type].call({ parser: this }, i);
|
|
1992
|
-
if (o !== !1 || ![
|
|
1993
|
-
"space",
|
|
1994
|
-
"hr",
|
|
1995
|
-
"heading",
|
|
1996
|
-
"code",
|
|
1997
|
-
"table",
|
|
1998
|
-
"blockquote",
|
|
1999
|
-
"list",
|
|
2000
|
-
"checkbox",
|
|
2001
|
-
"html",
|
|
2002
|
-
"def",
|
|
2003
|
-
"paragraph",
|
|
2004
|
-
"text"
|
|
2005
|
-
].includes(i.type)) {
|
|
2006
|
-
t += o || "";
|
|
2007
|
-
continue;
|
|
2008
|
-
}
|
|
2009
|
-
}
|
|
2010
|
-
let r = s;
|
|
2011
|
-
switch (r.type) {
|
|
2012
|
-
case "space":
|
|
2013
|
-
t += this.renderer.space(r);
|
|
2014
|
-
break;
|
|
2015
|
-
case "hr":
|
|
2016
|
-
t += this.renderer.hr(r);
|
|
2017
|
-
break;
|
|
2018
|
-
case "heading":
|
|
2019
|
-
t += this.renderer.heading(r);
|
|
2020
|
-
break;
|
|
2021
|
-
case "code":
|
|
2022
|
-
t += this.renderer.code(r);
|
|
2023
|
-
break;
|
|
2024
|
-
case "table":
|
|
2025
|
-
t += this.renderer.table(r);
|
|
2026
|
-
break;
|
|
2027
|
-
case "blockquote":
|
|
2028
|
-
t += this.renderer.blockquote(r);
|
|
2029
|
-
break;
|
|
2030
|
-
case "list":
|
|
2031
|
-
t += this.renderer.list(r);
|
|
2032
|
-
break;
|
|
2033
|
-
case "checkbox":
|
|
2034
|
-
t += this.renderer.checkbox(r);
|
|
2035
|
-
break;
|
|
2036
|
-
case "html":
|
|
2037
|
-
t += this.renderer.html(r);
|
|
2038
|
-
break;
|
|
2039
|
-
case "def":
|
|
2040
|
-
t += this.renderer.def(r);
|
|
2041
|
-
break;
|
|
2042
|
-
case "paragraph":
|
|
2043
|
-
t += this.renderer.paragraph(r);
|
|
2044
|
-
break;
|
|
2045
|
-
case "text":
|
|
2046
|
-
t += this.renderer.text(r);
|
|
2047
|
-
break;
|
|
2048
|
-
default: {
|
|
2049
|
-
let i = "Token with \"" + r.type + "\" type was not found.";
|
|
2050
|
-
if (this.options.silent) return console.error(i), "";
|
|
2051
|
-
throw new Error(i);
|
|
2052
|
-
}
|
|
2053
|
-
}
|
|
2054
|
-
}
|
|
2055
|
-
return t;
|
|
2056
|
-
}
|
|
2057
|
-
parseInline(e, t = this.renderer) {
|
|
2058
|
-
this.renderer.parser = this;
|
|
2059
|
-
let n = "";
|
|
2060
|
-
for (let s = 0; s < e.length; s++) {
|
|
2061
|
-
let r = e[s];
|
|
2062
|
-
if (this.options.extensions?.renderers?.[r.type]) {
|
|
2063
|
-
let o = this.options.extensions.renderers[r.type].call({ parser: this }, r);
|
|
2064
|
-
if (o !== !1 || ![
|
|
2065
|
-
"escape",
|
|
2066
|
-
"html",
|
|
2067
|
-
"link",
|
|
2068
|
-
"image",
|
|
2069
|
-
"checkbox",
|
|
2070
|
-
"strong",
|
|
2071
|
-
"em",
|
|
2072
|
-
"codespan",
|
|
2073
|
-
"br",
|
|
2074
|
-
"del",
|
|
2075
|
-
"text"
|
|
2076
|
-
].includes(r.type)) {
|
|
2077
|
-
n += o || "";
|
|
2078
|
-
continue;
|
|
2079
|
-
}
|
|
2080
|
-
}
|
|
2081
|
-
let i = r;
|
|
2082
|
-
switch (i.type) {
|
|
2083
|
-
case "escape":
|
|
2084
|
-
n += t.text(i);
|
|
2085
|
-
break;
|
|
2086
|
-
case "html":
|
|
2087
|
-
n += t.html(i);
|
|
2088
|
-
break;
|
|
2089
|
-
case "link":
|
|
2090
|
-
n += t.link(i);
|
|
2091
|
-
break;
|
|
2092
|
-
case "image":
|
|
2093
|
-
n += t.image(i);
|
|
2094
|
-
break;
|
|
2095
|
-
case "checkbox":
|
|
2096
|
-
n += t.checkbox(i);
|
|
2097
|
-
break;
|
|
2098
|
-
case "strong":
|
|
2099
|
-
n += t.strong(i);
|
|
2100
|
-
break;
|
|
2101
|
-
case "em":
|
|
2102
|
-
n += t.em(i);
|
|
2103
|
-
break;
|
|
2104
|
-
case "codespan":
|
|
2105
|
-
n += t.codespan(i);
|
|
2106
|
-
break;
|
|
2107
|
-
case "br":
|
|
2108
|
-
n += t.br(i);
|
|
2109
|
-
break;
|
|
2110
|
-
case "del":
|
|
2111
|
-
n += t.del(i);
|
|
2112
|
-
break;
|
|
2113
|
-
case "text":
|
|
2114
|
-
n += t.text(i);
|
|
2115
|
-
break;
|
|
2116
|
-
default: {
|
|
2117
|
-
let o = "Token with \"" + i.type + "\" type was not found.";
|
|
2118
|
-
if (this.options.silent) return console.error(o), "";
|
|
2119
|
-
throw new Error(o);
|
|
2120
|
-
}
|
|
2121
|
-
}
|
|
2122
|
-
}
|
|
2123
|
-
return n;
|
|
2124
|
-
}
|
|
2125
|
-
};
|
|
2126
|
-
var S = class {
|
|
2127
|
-
options;
|
|
2128
|
-
block;
|
|
2129
|
-
constructor(e) {
|
|
2130
|
-
this.options = e || R;
|
|
2131
|
-
}
|
|
2132
|
-
static passThroughHooks = /* @__PURE__ */ new Set([
|
|
2133
|
-
"preprocess",
|
|
2134
|
-
"postprocess",
|
|
2135
|
-
"processAllTokens",
|
|
2136
|
-
"emStrongMask"
|
|
2137
|
-
]);
|
|
2138
|
-
static passThroughHooksRespectAsync = /* @__PURE__ */ new Set([
|
|
2139
|
-
"preprocess",
|
|
2140
|
-
"postprocess",
|
|
2141
|
-
"processAllTokens"
|
|
2142
|
-
]);
|
|
2143
|
-
preprocess(e) {
|
|
2144
|
-
return e;
|
|
2145
|
-
}
|
|
2146
|
-
postprocess(e) {
|
|
2147
|
-
return e;
|
|
2148
|
-
}
|
|
2149
|
-
processAllTokens(e) {
|
|
2150
|
-
return e;
|
|
2151
|
-
}
|
|
2152
|
-
emStrongMask(e) {
|
|
2153
|
-
return e;
|
|
2154
|
-
}
|
|
2155
|
-
provideLexer(e = this.block) {
|
|
2156
|
-
return e ? x.lex : x.lexInline;
|
|
2157
|
-
}
|
|
2158
|
-
provideParser(e = this.block) {
|
|
2159
|
-
return e ? b.parse : b.parseInline;
|
|
2160
|
-
}
|
|
2161
|
-
};
|
|
2162
|
-
var Z = class {
|
|
2163
|
-
defaults = A();
|
|
2164
|
-
options = this.setOptions;
|
|
2165
|
-
parse = this.parseMarkdown(!0);
|
|
2166
|
-
parseInline = this.parseMarkdown(!1);
|
|
2167
|
-
Parser = b;
|
|
2168
|
-
Renderer = P;
|
|
2169
|
-
TextRenderer = L;
|
|
2170
|
-
Lexer = x;
|
|
2171
|
-
Tokenizer = y;
|
|
2172
|
-
Hooks = S;
|
|
2173
|
-
constructor(...e) {
|
|
2174
|
-
this.use(...e);
|
|
2175
|
-
}
|
|
2176
|
-
walkTokens(e, t) {
|
|
2177
|
-
let n = [];
|
|
2178
|
-
for (let s of e) switch (n = n.concat(t.call(this, s)), s.type) {
|
|
2179
|
-
case "table": {
|
|
2180
|
-
let r = s;
|
|
2181
|
-
for (let i of r.header) n = n.concat(this.walkTokens(i.tokens, t));
|
|
2182
|
-
for (let i of r.rows) for (let o of i) n = n.concat(this.walkTokens(o.tokens, t));
|
|
2183
|
-
break;
|
|
2184
|
-
}
|
|
2185
|
-
case "list": {
|
|
2186
|
-
let r = s;
|
|
2187
|
-
n = n.concat(this.walkTokens(r.items, t));
|
|
2188
|
-
break;
|
|
2189
|
-
}
|
|
2190
|
-
default: {
|
|
2191
|
-
let r = s;
|
|
2192
|
-
this.defaults.extensions?.childTokens?.[r.type] ? this.defaults.extensions.childTokens[r.type].forEach((i) => {
|
|
2193
|
-
let o = r[i].flat(1 / 0);
|
|
2194
|
-
n = n.concat(this.walkTokens(o, t));
|
|
2195
|
-
}) : r.tokens && (n = n.concat(this.walkTokens(r.tokens, t)));
|
|
2196
|
-
}
|
|
2197
|
-
}
|
|
2198
|
-
return n;
|
|
2199
|
-
}
|
|
2200
|
-
use(...e) {
|
|
2201
|
-
let t = this.defaults.extensions || {
|
|
2202
|
-
renderers: {},
|
|
2203
|
-
childTokens: {}
|
|
2204
|
-
};
|
|
2205
|
-
return e.forEach((n) => {
|
|
2206
|
-
let s = { ...n };
|
|
2207
|
-
if (s.async = this.defaults.async || s.async || !1, n.extensions && (n.extensions.forEach((r) => {
|
|
2208
|
-
if (!r.name) throw new Error("extension name required");
|
|
2209
|
-
if ("renderer" in r) {
|
|
2210
|
-
let i = t.renderers[r.name];
|
|
2211
|
-
i ? t.renderers[r.name] = function(...o) {
|
|
2212
|
-
let u = r.renderer.apply(this, o);
|
|
2213
|
-
return u === !1 && (u = i.apply(this, o)), u;
|
|
2214
|
-
} : t.renderers[r.name] = r.renderer;
|
|
2215
|
-
}
|
|
2216
|
-
if ("tokenizer" in r) {
|
|
2217
|
-
if (!r.level || r.level !== "block" && r.level !== "inline") throw new Error("extension level must be 'block' or 'inline'");
|
|
2218
|
-
let i = t[r.level];
|
|
2219
|
-
i ? i.unshift(r.tokenizer) : t[r.level] = [r.tokenizer], r.start && (r.level === "block" ? t.startBlock ? t.startBlock.push(r.start) : t.startBlock = [r.start] : r.level === "inline" && (t.startInline ? t.startInline.push(r.start) : t.startInline = [r.start]));
|
|
2220
|
-
}
|
|
2221
|
-
"childTokens" in r && r.childTokens && (t.childTokens[r.name] = r.childTokens);
|
|
2222
|
-
}), s.extensions = t), n.renderer) {
|
|
2223
|
-
let r = this.defaults.renderer || new P(this.defaults);
|
|
2224
|
-
for (let i in n.renderer) {
|
|
2225
|
-
if (!(i in r)) throw new Error(`renderer '${i}' does not exist`);
|
|
2226
|
-
if (["options", "parser"].includes(i)) continue;
|
|
2227
|
-
let o = i, u = n.renderer[o], a = r[o];
|
|
2228
|
-
r[o] = (...p) => {
|
|
2229
|
-
let c = u.apply(r, p);
|
|
2230
|
-
return c === !1 && (c = a.apply(r, p)), c || "";
|
|
2231
|
-
};
|
|
2232
|
-
}
|
|
2233
|
-
s.renderer = r;
|
|
2234
|
-
}
|
|
2235
|
-
if (n.tokenizer) {
|
|
2236
|
-
let r = this.defaults.tokenizer || new y(this.defaults);
|
|
2237
|
-
for (let i in n.tokenizer) {
|
|
2238
|
-
if (!(i in r)) throw new Error(`tokenizer '${i}' does not exist`);
|
|
2239
|
-
if ([
|
|
2240
|
-
"options",
|
|
2241
|
-
"rules",
|
|
2242
|
-
"lexer"
|
|
2243
|
-
].includes(i)) continue;
|
|
2244
|
-
let o = i, u = n.tokenizer[o], a = r[o];
|
|
2245
|
-
r[o] = (...p) => {
|
|
2246
|
-
let c = u.apply(r, p);
|
|
2247
|
-
return c === !1 && (c = a.apply(r, p)), c;
|
|
2248
|
-
};
|
|
2249
|
-
}
|
|
2250
|
-
s.tokenizer = r;
|
|
2251
|
-
}
|
|
2252
|
-
if (n.hooks) {
|
|
2253
|
-
let r = this.defaults.hooks || new S();
|
|
2254
|
-
for (let i in n.hooks) {
|
|
2255
|
-
if (!(i in r)) throw new Error(`hook '${i}' does not exist`);
|
|
2256
|
-
if (["options", "block"].includes(i)) continue;
|
|
2257
|
-
let o = i, u = n.hooks[o], a = r[o];
|
|
2258
|
-
S.passThroughHooks.has(i) ? r[o] = (p) => {
|
|
2259
|
-
if (this.defaults.async && S.passThroughHooksRespectAsync.has(i)) return (async () => {
|
|
2260
|
-
let h = await u.call(r, p);
|
|
2261
|
-
return a.call(r, h);
|
|
2262
|
-
})();
|
|
2263
|
-
let c = u.call(r, p);
|
|
2264
|
-
return a.call(r, c);
|
|
2265
|
-
} : r[o] = (...p) => {
|
|
2266
|
-
if (this.defaults.async) return (async () => {
|
|
2267
|
-
let h = await u.apply(r, p);
|
|
2268
|
-
return h === !1 && (h = await a.apply(r, p)), h;
|
|
2269
|
-
})();
|
|
2270
|
-
let c = u.apply(r, p);
|
|
2271
|
-
return c === !1 && (c = a.apply(r, p)), c;
|
|
2272
|
-
};
|
|
2273
|
-
}
|
|
2274
|
-
s.hooks = r;
|
|
2275
|
-
}
|
|
2276
|
-
if (n.walkTokens) {
|
|
2277
|
-
let r = this.defaults.walkTokens, i = n.walkTokens;
|
|
2278
|
-
s.walkTokens = function(o) {
|
|
2279
|
-
let u = [];
|
|
2280
|
-
return u.push(i.call(this, o)), r && (u = u.concat(r.call(this, o))), u;
|
|
2281
|
-
};
|
|
2282
|
-
}
|
|
2283
|
-
this.defaults = {
|
|
2284
|
-
...this.defaults,
|
|
2285
|
-
...s
|
|
2286
|
-
};
|
|
2287
|
-
}), this;
|
|
2288
|
-
}
|
|
2289
|
-
setOptions(e) {
|
|
2290
|
-
return this.defaults = {
|
|
2291
|
-
...this.defaults,
|
|
2292
|
-
...e
|
|
2293
|
-
}, this;
|
|
2294
|
-
}
|
|
2295
|
-
lexer(e, t) {
|
|
2296
|
-
return x.lex(e, t ?? this.defaults);
|
|
2297
|
-
}
|
|
2298
|
-
parser(e, t) {
|
|
2299
|
-
return b.parse(e, t ?? this.defaults);
|
|
2300
|
-
}
|
|
2301
|
-
parseMarkdown(e) {
|
|
2302
|
-
return (n, s) => {
|
|
2303
|
-
let r = { ...s }, i = {
|
|
2304
|
-
...this.defaults,
|
|
2305
|
-
...r
|
|
2306
|
-
}, o = this.onError(!!i.silent, !!i.async);
|
|
2307
|
-
if (this.defaults.async === !0 && r.async === !1) return o(/* @__PURE__ */ new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));
|
|
2308
|
-
if (typeof n > "u" || n === null) return o(/* @__PURE__ */ new Error("marked(): input parameter is undefined or null"));
|
|
2309
|
-
if (typeof n != "string") return o(/* @__PURE__ */ new Error("marked(): input parameter is of type " + Object.prototype.toString.call(n) + ", string expected"));
|
|
2310
|
-
if (i.hooks && (i.hooks.options = i, i.hooks.block = e), i.async) return (async () => {
|
|
2311
|
-
let u = i.hooks ? await i.hooks.preprocess(n) : n, p = await (i.hooks ? await i.hooks.provideLexer(e) : e ? x.lex : x.lexInline)(u, i), c = i.hooks ? await i.hooks.processAllTokens(p) : p;
|
|
2312
|
-
i.walkTokens && await Promise.all(this.walkTokens(c, i.walkTokens));
|
|
2313
|
-
let d = await (i.hooks ? await i.hooks.provideParser(e) : e ? b.parse : b.parseInline)(c, i);
|
|
2314
|
-
return i.hooks ? await i.hooks.postprocess(d) : d;
|
|
2315
|
-
})().catch(o);
|
|
2316
|
-
try {
|
|
2317
|
-
i.hooks && (n = i.hooks.preprocess(n));
|
|
2318
|
-
let a = (i.hooks ? i.hooks.provideLexer(e) : e ? x.lex : x.lexInline)(n, i);
|
|
2319
|
-
i.hooks && (a = i.hooks.processAllTokens(a)), i.walkTokens && this.walkTokens(a, i.walkTokens);
|
|
2320
|
-
let c = (i.hooks ? i.hooks.provideParser(e) : e ? b.parse : b.parseInline)(a, i);
|
|
2321
|
-
return i.hooks && (c = i.hooks.postprocess(c)), c;
|
|
2322
|
-
} catch (u) {
|
|
2323
|
-
return o(u);
|
|
2324
|
-
}
|
|
2325
|
-
};
|
|
2326
|
-
}
|
|
2327
|
-
onError(e, t) {
|
|
2328
|
-
return (n) => {
|
|
2329
|
-
if (n.message += `
|
|
2330
|
-
Please report this to https://github.com/markedjs/marked.`, e) {
|
|
2331
|
-
let s = "<p>An error occurred:</p><pre>" + T(n.message + "", !0) + "</pre>";
|
|
2332
|
-
return t ? Promise.resolve(s) : s;
|
|
2333
|
-
}
|
|
2334
|
-
if (t) return Promise.reject(n);
|
|
2335
|
-
throw n;
|
|
2336
|
-
};
|
|
2337
|
-
}
|
|
2338
|
-
};
|
|
2339
|
-
var M = new Z();
|
|
2340
|
-
function f(l, e) {
|
|
2341
|
-
return M.parse(l, e);
|
|
2342
|
-
}
|
|
2343
|
-
f.options = f.setOptions = function(l) {
|
|
2344
|
-
return M.setOptions(l), f.defaults = M.defaults, j(f.defaults), f;
|
|
2345
|
-
};
|
|
2346
|
-
f.getDefaults = A;
|
|
2347
|
-
f.defaults = R;
|
|
2348
|
-
function dt(...l) {
|
|
2349
|
-
return M.use(...l), f.defaults = M.defaults, j(f.defaults), f;
|
|
2350
|
-
}
|
|
2351
|
-
f.use = dt;
|
|
2352
|
-
f.walkTokens = function(l, e) {
|
|
2353
|
-
return M.walkTokens(l, e);
|
|
2354
|
-
};
|
|
2355
|
-
f.parseInline = M.parseInline;
|
|
2356
|
-
f.Parser = b;
|
|
2357
|
-
f.parser = b.parse;
|
|
2358
|
-
f.Renderer = P;
|
|
2359
|
-
f.TextRenderer = L;
|
|
2360
|
-
f.Lexer = x;
|
|
2361
|
-
f.lexer = x.lex;
|
|
2362
|
-
f.Tokenizer = y;
|
|
2363
|
-
f.Hooks = S;
|
|
2364
|
-
f.parse = f;
|
|
2365
|
-
f.options;
|
|
2366
|
-
f.setOptions;
|
|
2367
|
-
f.walkTokens;
|
|
2368
|
-
f.parseInline;
|
|
2369
|
-
b.parse;
|
|
2370
|
-
x.lex;
|
|
2371
|
-
//#endregion
|
|
2372
|
-
//#region src/skiff-debug.ts
|
|
2373
|
-
/**
|
|
2374
|
-
* skiff-debug.ts — Skiff 调试问答页(F4a',v1.25.x 实验性)
|
|
2375
|
-
*
|
|
2376
|
-
* node:http 调试端口(默认关,仅监听 127.0.0.1;启停 = 人工——设置面板「Serenity」
|
|
2377
|
-
* 页 Skiff 区块开关,不随插件加载自动启动)。
|
|
2378
|
-
*
|
|
2379
|
-
* - GET / → 问答 HTML 页(**CCC 选择器** + 角色下拉 + 输入框 + 答案区 + 轨迹区 + WebUI 链接)
|
|
2380
|
-
* - POST /ask → {ccc?, role, question} → 走会话核心(skiff-core)→ {answer, sessionId, trajectory}
|
|
2381
|
-
*
|
|
2382
|
-
* **多 CCC 手工切换(v1.25.4,S142 用户)**:dsh 管理多个 CCC 时,调试页顶部 CCC
|
|
2383
|
-
* 下拉列出全部候选(live 会话 cwd 上溯 .serenity 去重 + 默认绑定 root 兜底),
|
|
2384
|
-
* 切换后角色下拉联动(各 CCC 的 skiff.roles 实时读取),提问按所选 CCC 创建 agent。
|
|
2385
|
-
*
|
|
2386
|
-
* 与 ACP stdio 协议(F4c 后续)共用同一会话核心(createSkiffAgent + askSkiff),
|
|
2387
|
-
* 协议层后加不返工。轨迹 = session.events 结构化返回(与 dsh WebUI 同源数据),
|
|
2388
|
-
* 页面 JS 渲染成对话时间线;同时保留原生 WebUI 会话链接供完整交互。
|
|
2389
|
-
*
|
|
2390
|
-
* 实验性质:未开启时零资源占用(无监听、无 agent 创建)。
|
|
2391
|
-
*/
|
|
2392
|
-
var skiff_debug_exports = /* @__PURE__ */ __exportAll({
|
|
2393
|
-
discoverCccs: () => discoverCccs,
|
|
2394
|
-
jscSafeJsonText: () => jscSafeJsonText,
|
|
2395
|
-
renderSkiffMarkdown: () => renderSkiffMarkdown,
|
|
2396
|
-
skiffDebugPage: () => skiffDebugPage,
|
|
2397
|
-
startSkiffDebugServer: () => startSkiffDebugServer,
|
|
2398
|
-
stopSkiffDebugServer: () => stopSkiffDebugServer,
|
|
2399
|
-
stripThink: () => stripThink
|
|
2400
|
-
});
|
|
2401
|
-
/** 运行中的调试服务(单实例;进程级) */
|
|
2402
|
-
let active = null;
|
|
2403
|
-
function readBody(req) {
|
|
2404
|
-
return new Promise((resolve, reject) => {
|
|
2405
|
-
const chunks = [];
|
|
2406
|
-
req.on("data", (c) => chunks.push(c));
|
|
2407
|
-
req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
2408
|
-
req.on("error", reject);
|
|
2409
|
-
});
|
|
2410
|
-
}
|
|
2411
|
-
function sendJson(res, status, payload) {
|
|
2412
|
-
const body = JSON.stringify(payload);
|
|
2413
|
-
res.writeHead(status, {
|
|
2414
|
-
"Content-Type": "application/json; charset=utf-8",
|
|
2415
|
-
"Cache-Control": "no-store"
|
|
2416
|
-
});
|
|
2417
|
-
res.end(body);
|
|
2418
|
-
}
|
|
2419
|
-
function sendHtml(res, html) {
|
|
2420
|
-
res.writeHead(200, {
|
|
2421
|
-
"Content-Type": "text/html; charset=utf-8",
|
|
2422
|
-
"Cache-Control": "no-store"
|
|
2423
|
-
});
|
|
2424
|
-
res.end(html);
|
|
2425
|
-
}
|
|
2426
|
-
/**
|
|
2427
|
-
* 发现候选 CCC 列表(多 CCC 手工切换,v1.25.6):
|
|
2428
|
-
* ① **dsh 工作区注册表**(workspaceRegistry.list,持久化——所有工作目录即使无 live 会话;
|
|
2429
|
-
* S142 用户 2026-08-29:应直接拉 dsh 工作区,且只列具体 CCC)
|
|
2430
|
-
* ② **sessionPersistence 兜底**(持久化会话 headers——必装配服务,覆盖所有历史会话工作目录;
|
|
2431
|
-
* 用户实测 workspaceRegistry 拉取仍空时兜底)
|
|
2432
|
-
* ③ live 会话兜底
|
|
2433
|
-
* ④ 默认绑定 root 兜底(不在列表时放首位)
|
|
2434
|
-
*/
|
|
2435
|
-
async function discoverCccs(ctx, defaultRoot) {
|
|
2436
|
-
const roots = [];
|
|
2437
|
-
const pushRoot = (cwd) => {
|
|
2438
|
-
if (typeof cwd !== "string" || cwd === "") return;
|
|
2439
|
-
const r = findSerenityRoot(cwd);
|
|
2440
|
-
if (r && !roots.includes(r)) roots.push(r);
|
|
2441
|
-
};
|
|
2442
|
-
try {
|
|
2443
|
-
const registry = hostService(ctx, "workspaceRegistry");
|
|
2444
|
-
for (const ws of registry?.list?.() ?? []) pushRoot(ws?.path);
|
|
2445
|
-
} catch {}
|
|
2446
|
-
if (roots.length === 0) try {
|
|
2447
|
-
const sp = hostService(ctx, "sessionPersistence");
|
|
2448
|
-
for (const h of await sp?.list?.() ?? []) pushRoot(h?.header?.cwd);
|
|
2449
|
-
} catch {}
|
|
2450
|
-
if (roots.length === 0) try {
|
|
2451
|
-
const sessions = hostSessions(ctx);
|
|
2452
|
-
for (const s of sessions?.list?.() ?? []) pushRoot(s?.header?.cwd);
|
|
2453
|
-
} catch {}
|
|
2454
|
-
if (!roots.includes(defaultRoot)) roots.unshift(defaultRoot);
|
|
2455
|
-
return roots.map((root) => ({
|
|
2456
|
-
root,
|
|
2457
|
-
name: basename(root) || root,
|
|
2458
|
-
roles: [...readSkiffRoles(root).keys()]
|
|
2459
|
-
}));
|
|
2460
|
-
}
|
|
2461
|
-
/** 问答页 HTML:CCC 切换器 + 角色下拉 + 输入 + 答案区 + 轨迹区(JS 渲染)+ WebUI 链接 */
|
|
2462
|
-
function skiffDebugPage(cccs, defaultRoot, webPort) {
|
|
2463
|
-
const data = jscSafeJsonText(JSON.stringify(cccs).replace(/</g, "\\u003c"));
|
|
2464
|
-
return `<!DOCTYPE html>
|
|
2465
|
-
<html lang="zh">
|
|
2466
|
-
<head>
|
|
2467
|
-
<meta charset="utf-8">
|
|
2468
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
2469
|
-
<title>Skiff Debug — CCC cognitive subset roles</title>
|
|
2470
|
-
<style>
|
|
2471
|
-
:root { color-scheme: light dark; }
|
|
2472
|
-
body { font-family: system-ui, -apple-system, sans-serif; margin: 0; padding: 24px; background: #f6f7f9; color: #1f2328; }
|
|
2473
|
-
@media (prefers-color-scheme: dark) { body { background: #1a1b1e; color: #e6e6e6; } }
|
|
2474
|
-
main { max-width: 720px; margin: 0 auto; }
|
|
2475
|
-
h1 { font-size: 18px; margin: 0 0 4px; }
|
|
2476
|
-
.sub { opacity: .65; font-size: 13px; margin-bottom: 16px; }
|
|
2477
|
-
.ccc { display: inline-block; padding: 3px 10px; border-radius: 999px; background: rgba(11,168,117,.12); color: #0ba875; font-size: 12px; font-weight: 600; margin-bottom: 12px; word-break: break-all; }
|
|
2478
|
-
@media (prefers-color-scheme: dark) { .ccc { color: #3ddc9a; } }
|
|
2479
|
-
.sessionbar { display: flex; align-items: center; gap: 10px; margin-bottom: 4px; }
|
|
2480
|
-
.session-badge { font-family: ui-monospace, SFMono-Regular, monospace; font-size: 12px; padding: 3px 10px; border-radius: 999px; background: rgba(127,127,127,.12); word-break: break-all; }
|
|
2481
|
-
.session-badge.continued { background: rgba(11,168,117,.12); color: #0ba875; }
|
|
2482
|
-
@media (prefers-color-scheme: dark) { .session-badge.continued { color: #3ddc9a; } }
|
|
2483
|
-
button.ghost { background: transparent; color: #57606a; border: 1px solid #d0d7de; padding: 4px 12px; font-size: 12px; border-radius: 8px; cursor: pointer; margin-top: 0; }
|
|
2484
|
-
@media (prefers-color-scheme: dark) { button.ghost { color: #8b949e; border-color: #3a3d42; } }
|
|
2485
|
-
button.ghost:hover { background: rgba(127,127,127,.1); }
|
|
2486
|
-
label { font-size: 13px; font-weight: 600; display: block; margin: 12px 0 4px; }
|
|
2487
|
-
select, textarea { width: 100%; box-sizing: border-box; padding: 8px 10px; border-radius: 8px; border: 1px solid #d0d7de; background: #fff; color: inherit; font-size: 14px; }
|
|
2488
|
-
@media (prefers-color-scheme: dark) { select, textarea { background: #26282c; border-color: #3a3d42; } }
|
|
2489
|
-
textarea { min-height: 72px; resize: vertical; }
|
|
2490
|
-
button { margin-top: 12px; padding: 9px 18px; border-radius: 8px; border: 0; background: #0ba875; color: #fff; font-size: 14px; font-weight: 600; cursor: pointer; }
|
|
2491
|
-
button:disabled { opacity: .55; cursor: wait; }
|
|
2492
|
-
#answer { background: #fff; border: 1px solid #d0d7de; border-radius: 8px; padding: 12px; margin-top: 16px; font-size: 14px; line-height: 1.6; min-height: 48px; word-break: break-word; }
|
|
2493
|
-
@media (prefers-color-scheme: dark) { #answer { background: #26282c; border-color: #3a3d42; } }
|
|
2494
|
-
/* Markdown 渲染(v1.25.8):代码/标题/列表/引用/think 折叠 */
|
|
2495
|
-
#answer p { margin: 6px 0; }
|
|
2496
|
-
#answer h1, #answer h2, #answer h3 { margin: 12px 0 6px; font-size: 15px; line-height: 1.4; }
|
|
2497
|
-
#answer h1 { font-size: 17px; }
|
|
2498
|
-
#answer code { background: rgba(127,127,127,.14); border-radius: 4px; padding: 1px 5px; font-size: 13px; font-family: ui-monospace, SFMono-Regular, monospace; }
|
|
2499
|
-
#answer pre { background: rgba(127,127,127,.1); border-radius: 8px; padding: 10px 12px; overflow-x: auto; margin: 8px 0; }
|
|
2500
|
-
#answer pre code { background: none; padding: 0; font-size: 13px; line-height: 1.5; }
|
|
2501
|
-
#answer ul, #answer ol { margin: 6px 0; padding-left: 22px; }
|
|
2502
|
-
#answer li { margin: 2px 0; }
|
|
2503
|
-
#answer blockquote { margin: 6px 0; padding: 2px 12px; border-left: 3px solid rgba(127,127,127,.35); opacity: .88; }
|
|
2504
|
-
#answer a { color: #0ba875; text-decoration: underline; }
|
|
2505
|
-
details.think { margin: 8px 0; border: 1px solid rgba(210,153,34,.4); border-radius: 8px; background: rgba(210,153,34,.06); }
|
|
2506
|
-
details.think summary { cursor: pointer; padding: 6px 10px; font-size: 12px; color: #d29922; font-weight: 600; user-select: none; list-style-position: inside; }
|
|
2507
|
-
details.think summary:hover { opacity: .8; }
|
|
2508
|
-
details.think .think-body { padding: 2px 12px 10px; font-size: 13px; opacity: .78; white-space: normal; }
|
|
2509
|
-
#trajectory { margin-top: 12px; font-size: 13px; }
|
|
2510
|
-
.t-entry { border-left: 2px solid #d0d7de; padding: 6px 10px; margin: 6px 0; border-radius: 0 6px 6px 0; background: rgba(127,127,127,.06); white-space: pre-wrap; word-break: break-word; }
|
|
2511
|
-
.t-user { border-left-color: #0ba875; }
|
|
2512
|
-
.t-tool { border-left-color: #d29922; opacity: .85; font-family: ui-monospace, monospace; font-size: 12px; }
|
|
2513
|
-
.t-role { font-weight: 600; font-size: 11px; text-transform: uppercase; letter-spacing: .04em; opacity: .6; margin-bottom: 2px; }
|
|
2514
|
-
.muted { opacity: .6; font-size: 13px; }
|
|
2515
|
-
a { color: #0ba875; }
|
|
2516
|
-
.err { color: #cf222e; white-space: pre-wrap; }
|
|
2517
|
-
</style>
|
|
2518
|
-
</head>
|
|
2519
|
-
<body>
|
|
2520
|
-
<main>
|
|
2521
|
-
<h1>Skiff Debug</h1>
|
|
2522
|
-
<div class="sub">宁静号 trajectory 子集角色问答页(v1.25.10 实验性)— 多 CCC 可切换,同会话可追问(新对话按钮开新会话),回答 marked 服务端渲染 + think 折叠</div>
|
|
2523
|
-
<div class="ccc" id="cccBadge"></div>
|
|
2524
|
-
<div class="sessionbar">
|
|
2525
|
-
<span id="sessionBadge" class="session-badge muted">(新会话)</span>
|
|
2526
|
-
<button id="newChat" type="button" class="ghost">新对话</button>
|
|
2527
|
-
</div>
|
|
2528
|
-
<label for="ccc">认知容器</label>
|
|
2529
|
-
<select id="ccc"></select>
|
|
2530
|
-
<label for="role">角色</label>
|
|
2531
|
-
<select id="role"></select>
|
|
2532
|
-
<label for="q">问题</label>
|
|
2533
|
-
<textarea id="q" placeholder="向该角色提问…(追问直接继续输入)"></textarea>
|
|
2534
|
-
<button id="ask">提问</button>
|
|
2535
|
-
<div id="answer" class="muted">等待提问…</div>
|
|
2536
|
-
<div id="trajectory"></div>
|
|
2537
|
-
<p class="muted"><a href="${`http://127.0.0.1:${webPort}`}" target="_blank" rel="noopener">在 dsh WebUI 查看完整会话</a>(会话列表搜索 sessionId;WebUI 有完整交互)</p>
|
|
2538
|
-
</main>
|
|
2539
|
-
<script id="skiff-data" type="application/json" data-default="${escapeHtml(defaultRoot)}">${data}<\/script>
|
|
2540
|
-
<script>
|
|
2541
|
-
const CCCS = JSON.parse(document.getElementById('skiff-data').textContent)
|
|
2542
|
-
const defaultRoot = document.getElementById('skiff-data').dataset.default
|
|
2543
|
-
const cccSel = document.getElementById('ccc')
|
|
2544
|
-
const roleSel = document.getElementById('role')
|
|
2545
|
-
const badge = document.getElementById('cccBadge')
|
|
2546
|
-
const sessionBadge = document.getElementById('sessionBadge')
|
|
2547
|
-
const newChatBtn = document.getElementById('newChat')
|
|
2548
|
-
const btn = document.getElementById('ask')
|
|
2549
|
-
const answer = document.getElementById('answer')
|
|
2550
|
-
const traj = document.getElementById('trajectory')
|
|
2551
|
-
const esc = (s) => String(s ?? '').replace(/[&<>"']/g, (c) => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]))
|
|
2552
|
-
let sessionId = ''
|
|
2553
|
-
function shortId(id) { return id.length > 24 ? id.slice(0, 18) + '…' + id.slice(-6) : id }
|
|
2554
|
-
function renderSessionBadge() {
|
|
2555
|
-
sessionBadge.textContent = sessionId ? '会话 ' + shortId(sessionId) + '(追问续接)' : '(新会话)'
|
|
2556
|
-
sessionBadge.className = 'session-badge' + (sessionId ? ' continued' : '')
|
|
2557
|
-
}
|
|
2558
|
-
function newConversation() {
|
|
2559
|
-
sessionId = ''
|
|
2560
|
-
renderSessionBadge()
|
|
2561
|
-
answer.className = 'muted'
|
|
2562
|
-
answer.textContent = '已开启新对话'
|
|
2563
|
-
traj.innerHTML = ''
|
|
2564
|
-
document.getElementById('q').value = ''
|
|
2565
|
-
}
|
|
2566
|
-
function currentCcc() {
|
|
2567
|
-
return CCCS.find((c) => c.root === cccSel.value) || (CCCS[0] || { root: '', name: '', roles: [] })
|
|
2568
|
-
}
|
|
2569
|
-
function fillCccs() {
|
|
2570
|
-
cccSel.innerHTML = CCCS.map((c) => {
|
|
2571
|
-
const n = c.roles.length
|
|
2572
|
-
return '<option value="' + esc(c.root) + '">' + esc(c.name) + (n ? ' (' + n + ' 角色)' : ' (无角色)') + '</option>'
|
|
2573
|
-
}).join('') || '<option value="">(未发现 CCC)</option>'
|
|
2574
|
-
if (defaultRoot) cccSel.value = CCCS.some((c) => c.root === defaultRoot) ? defaultRoot : (CCCS[0] && CCCS[0].root)
|
|
2575
|
-
fillRoles()
|
|
2576
|
-
}
|
|
2577
|
-
function fillRoles() {
|
|
2578
|
-
const c = currentCcc()
|
|
2579
|
-
badge.textContent = 'CCC: ' + c.root
|
|
2580
|
-
roleSel.innerHTML = (c.roles && c.roles.length)
|
|
2581
|
-
? c.roles.map((r) => '<option value="' + esc(r) + '">' + esc(r) + '</option>').join('')
|
|
2582
|
-
: '<option value="">(未配置角色)</option>'
|
|
2583
|
-
}
|
|
2584
|
-
cccSel.addEventListener('change', () => { fillRoles(); newConversation() })
|
|
2585
|
-
roleSel.addEventListener('change', () => newConversation())
|
|
2586
|
-
newChatBtn.addEventListener('click', newConversation)
|
|
2587
|
-
fillCccs()
|
|
2588
|
-
renderSessionBadge()
|
|
2589
|
-
btn.addEventListener('click', async () => {
|
|
2590
|
-
const c = currentCcc()
|
|
2591
|
-
const role = roleSel.value
|
|
2592
|
-
const q = document.getElementById('q').value.trim()
|
|
2593
|
-
if (!c.root || !role || !q) { answer.className = 'err'; answer.textContent = '请选择认知容器与角色并输入问题'; return }
|
|
2594
|
-
btn.disabled = true
|
|
2595
|
-
answer.className = 'muted'
|
|
2596
|
-
answer.textContent = '运行中…'
|
|
2597
|
-
traj.innerHTML = ''
|
|
2598
|
-
try {
|
|
2599
|
-
const res = await fetch('/ask', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ccc: c.root, role, question: q, sessionId: sessionId || undefined }) })
|
|
2600
|
-
const data = await res.json()
|
|
2601
|
-
if (!res.ok) {
|
|
2602
|
-
// 会话不可恢复/绑定不匹配 → 前端清空 sessionId 重建(用户拍板:切换即新对话)
|
|
2603
|
-
sessionId = ''
|
|
2604
|
-
renderSessionBadge()
|
|
2605
|
-
throw new Error(data.error || ('HTTP ' + res.status))
|
|
2606
|
-
}
|
|
2607
|
-
answer.className = ''
|
|
2608
|
-
answer.innerHTML = data.answer_html || esc(data.answer || '') || '(空回答)'
|
|
2609
|
-
sessionId = data.sessionId || sessionId
|
|
2610
|
-
renderSessionBadge()
|
|
2611
|
-
renderTrajectory(data.trajectory || [], data.sessionId || '')
|
|
2612
|
-
} catch (err) {
|
|
2613
|
-
answer.className = 'err'
|
|
2614
|
-
answer.textContent = String(err.message || err)
|
|
2615
|
-
} finally {
|
|
2616
|
-
btn.disabled = false
|
|
2617
|
-
}
|
|
2618
|
-
})
|
|
2619
|
-
function renderTrajectory(entries, sessionId) {
|
|
2620
|
-
if (entries.length === 0) { traj.innerHTML = '<div class="muted">(本轮无轨迹)</div>'; return }
|
|
2621
|
-
traj.innerHTML = '<div class="sub">本轮轨迹 · ' + esc(sessionId) + '</div>' + entries.map((e) => {
|
|
2622
|
-
const cls = e.role === 'user' ? 't-user' : (e.role === 'tool' ? 't-tool' : '')
|
|
2623
|
-
const role = e.role === 'tool' ? 'tool' + (e.tool ? ' · ' + esc(e.tool) : '') : e.role
|
|
2624
|
-
return '<div class="t-entry ' + cls + '"><div class="t-role">' + role + '</div>' + esc(e.text) + '</div>'
|
|
2625
|
-
}).join('')
|
|
2626
|
-
}
|
|
2627
|
-
<\/script>
|
|
2628
|
-
</body>
|
|
2629
|
-
</html>`;
|
|
2630
|
-
}
|
|
2631
|
-
function escapeHtml(s) {
|
|
2632
|
-
return s.replace(/[&<>"']/g, (c) => ({
|
|
2633
|
-
"&": "&",
|
|
2634
|
-
"<": "<",
|
|
2635
|
-
">": ">",
|
|
2636
|
-
"\"": """,
|
|
2637
|
-
"'": "'"
|
|
2638
|
-
})[c] ?? c);
|
|
2639
|
-
}
|
|
2640
|
-
/**
|
|
2641
|
-
* JSC (Safari/iOS) JSON.parse 快速路径正则兼容化(v1.26.9,S142 调研定稿)。
|
|
2642
|
-
*
|
|
2643
|
-
* 背景:WebKit bug 200190「JavaScriptCore's Regex can't match the content」——JSC 的
|
|
2644
|
-
* JSON.parse 用**内部正则**预校验字符串;内容含**原始** `\u2028`(行分隔符)/`\u2029`
|
|
2645
|
-
* (段分隔符)时正则无法匹配 → 对**合法 JSON** 也抛
|
|
2646
|
-
* `SyntaxError: The string did not match the expected pattern`(sentry-javascript #2487 同源)。
|
|
2647
|
-
* JSON.stringify **不转义** `\u2028`/`\u2029`/`\uFEFF`(它们都是合法 JSON 字符串字符)→
|
|
2648
|
-
* 在 JSON **文本层**把它们替换为 `\uXXXX` 转义序列:JSON.parse 后语义完全一致(还原原字符),
|
|
2649
|
-
* 且 JSC 正则看到的是常规 ASCII 转义(与 JSON.stringify 对控制字符的输出同形态,安全)。
|
|
2650
|
-
*
|
|
2651
|
-
* 3100 问答页客户端 `await res.json()`(acp-http.ts)与页面内嵌 JSON(skiff-debug/acp-http)
|
|
2652
|
-
* 均需此兼容层——iOS Safari 用户实测"复杂回答"触发。
|
|
2653
|
-
*
|
|
2654
|
-
* @param jsonText JSON.stringify 的输出文本;原地等价替换,返回 JSC 安全文本
|
|
2655
|
-
*/
|
|
2656
|
-
function jscSafeJsonText(jsonText) {
|
|
2657
|
-
return jsonText.replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029").replace(/\uFEFF/g, "\\uFEFF");
|
|
2658
|
-
}
|
|
2659
|
-
/**
|
|
2660
|
-
* 提取 `<think>…</think>` 块(v1.26.8:**状态机扫描,弃正则**——用户批评"老用正则不是个办法")。
|
|
2661
|
-
*
|
|
2662
|
-
* 逐字符扫描识别开/闭标签(大小写不敏感;`<think ...>` 允许属性变体;`</think >` 允许尾随空格),
|
|
2663
|
-
* 不依赖正则回溯,天然处理:
|
|
2664
|
-
* - **嵌套 `<think>`**(内层按内容处理,不递归——DeepSeek think 不会嵌套)
|
|
2665
|
-
* - **未闭合 `<think>`**(优雅截断:剩余全部作为 think 内容,不泄漏标记)
|
|
2666
|
-
* - **占位符冲突**(用 \u0001T<idx>\u0001——ASCII 控制字符,正文几乎不可能出现)
|
|
2667
|
-
*
|
|
2668
|
-
* @returns body(占位符替换后的正文)+ thinks(提取的 think 内容数组,保序)
|
|
2669
|
-
*/
|
|
2670
|
-
function extractThinkBlocks(raw) {
|
|
2671
|
-
const thinks = [];
|
|
2672
|
-
const parts = [];
|
|
2673
|
-
let i = 0;
|
|
2674
|
-
while (i < raw.length) {
|
|
2675
|
-
const openTag = matchOpenThink(raw, i);
|
|
2676
|
-
if (!openTag) break;
|
|
2677
|
-
parts.push(raw.slice(i, openTag.tagStart));
|
|
2678
|
-
const closeTag = matchCloseThink(raw, openTag.contentStart);
|
|
2679
|
-
if (closeTag < 0) {
|
|
2680
|
-
const inner = raw.slice(openTag.contentStart);
|
|
2681
|
-
thinks.push(inner.trim());
|
|
2682
|
-
parts.push(`\u0001T${thinks.length - 1}\u0001`);
|
|
2683
|
-
i = raw.length;
|
|
2684
|
-
break;
|
|
2685
|
-
}
|
|
2686
|
-
const inner = raw.slice(openTag.contentStart, closeTag);
|
|
2687
|
-
thinks.push(inner.trim());
|
|
2688
|
-
parts.push(`\u0001T${thinks.length - 1}\u0001`);
|
|
2689
|
-
let j = closeTag + 7;
|
|
2690
|
-
while (j < raw.length && (raw[j] === " " || raw[j] === " " || raw[j] === "\n" || raw[j] === "\r")) j++;
|
|
2691
|
-
i = raw[j] === ">" ? j + 1 : closeTag + 8;
|
|
2692
|
-
}
|
|
2693
|
-
if (i < raw.length) parts.push(raw.slice(i));
|
|
2694
|
-
return {
|
|
2695
|
-
body: parts.join(""),
|
|
2696
|
-
thinks
|
|
2697
|
-
};
|
|
2698
|
-
}
|
|
2699
|
-
/** 从 from 起找 `<think` 开标签(大小写不敏感,允许属性);返回标签起点与内容起点,或 null */
|
|
2700
|
-
function matchOpenThink(s, from) {
|
|
2701
|
-
for (let i = from; i <= s.length - 7; i++) {
|
|
2702
|
-
if (s[i] !== "<" || s[i + 1] !== "t" && s[i + 1] !== "T") continue;
|
|
2703
|
-
if (s.slice(i + 1, i + 6).toLowerCase() !== "think") continue;
|
|
2704
|
-
const after = s[i + 6];
|
|
2705
|
-
if (after === ">") return {
|
|
2706
|
-
tagStart: i,
|
|
2707
|
-
contentStart: i + 7
|
|
2708
|
-
};
|
|
2709
|
-
if (after === " " || after === " " || after === "\n" || after === "\r") {
|
|
2710
|
-
const gt = s.indexOf(">", i + 7);
|
|
2711
|
-
if (gt < 0) return null;
|
|
2712
|
-
return {
|
|
2713
|
-
tagStart: i,
|
|
2714
|
-
contentStart: gt + 1
|
|
2715
|
-
};
|
|
2716
|
-
}
|
|
2717
|
-
}
|
|
2718
|
-
return null;
|
|
2719
|
-
}
|
|
2720
|
-
/** 从 contentStart 起找 `</think>` 闭合标签(大小写不敏感,允许 `</think >` 尾随空格);返回闭合标签起点或 -1 */
|
|
2721
|
-
function matchCloseThink(s, contentStart) {
|
|
2722
|
-
for (let i = contentStart; i <= s.length - 8; i++) {
|
|
2723
|
-
if (s[i] !== "<" || s[i + 1] !== "/") continue;
|
|
2724
|
-
if (s.slice(i + 2, i + 7).toLowerCase() !== "think") continue;
|
|
2725
|
-
const after = s[i + 7];
|
|
2726
|
-
if (after === ">") return i;
|
|
2727
|
-
if (after === " " || after === " " || after === "\n" || after === "\r") {
|
|
2728
|
-
let j = i + 8;
|
|
2729
|
-
while (j < s.length && (s[j] === " " || s[j] === " " || s[j] === "\n" || s[j] === "\r")) j++;
|
|
2730
|
-
if (s[j] === ">") return i;
|
|
2731
|
-
}
|
|
2732
|
-
}
|
|
2733
|
-
return -1;
|
|
2734
|
-
}
|
|
2735
|
-
/**
|
|
2736
|
-
* Markdown 渲染(v1.25.9,正经库 marked 服务端渲染——替代手写正则渲染器,S142 用户要求):
|
|
2737
|
-
* ① 提取 `<think>…</think>` 块(v1.26.8 状态机扫描,占位符 \u0001T<idx>\u0001)
|
|
2738
|
-
* ② 正文与 think 内容**先 escapeHtml 再 marked.parse**(GFM + breaks)——markdown 语法不受
|
|
2739
|
-
* 转义影响,原始 HTML 注入被消除(安全);代码块内 `<` 显示为实体(可接受)
|
|
2740
|
-
* ③ think 占位符:默认还原为 `<details class="think">` 折叠卡(🧠 思考过程,默认收起);
|
|
2741
|
-
* **hideThink=true(v1.26.4,public 口)→ 直接移除**(思考过程对普通用户不展示)
|
|
2742
|
-
* @param hideThink 为 true 时 `<think>` 内容完全不渲染(public 问答页体验)
|
|
2743
|
-
*/
|
|
2744
|
-
function renderSkiffMarkdown(raw, hideThink = false) {
|
|
2745
|
-
const { body, thinks } = extractThinkBlocks(raw);
|
|
2746
|
-
const parsed = f.parse(escapeHtml(body), {
|
|
2747
|
-
breaks: true,
|
|
2748
|
-
gfm: true
|
|
2749
|
-
});
|
|
2750
|
-
return (typeof parsed === "string" ? parsed : "").replace(/\u0001T(\d+)\u0001/g, (_m, idx) => {
|
|
2751
|
-
if (hideThink) return "";
|
|
2752
|
-
const inner = thinks[Number(idx)] ?? "";
|
|
2753
|
-
const innerHtml = inner === "" ? "" : f.parse(escapeHtml(inner), {
|
|
2754
|
-
breaks: true,
|
|
2755
|
-
gfm: true
|
|
2756
|
-
});
|
|
2757
|
-
return `<details class="think"><summary>🧠 思考过程</summary><div class="think-body">${typeof innerHtml === "string" ? innerHtml : ""}</div></details>`;
|
|
2758
|
-
});
|
|
2759
|
-
}
|
|
2760
|
-
/**
|
|
2761
|
-
* 剥离 `<think>…</think>` 块(v1.27.1,微信桥用户反馈:"过微信桥回复给用户的消息,
|
|
2762
|
-
* 要去掉 think 标签,用户不应看到")。复用 extractThinkBlocks 状态机(v1.26.8,
|
|
2763
|
-
* 弃正则)——提取后**只保留正文**(占位符替换为空,think 内容丢弃),
|
|
2764
|
-
* 未闭合 think 优雅截断。适用于纯文本输出面(微信等不支持 think 折叠的通道)。
|
|
2765
|
-
*/
|
|
2766
|
-
function stripThink(raw) {
|
|
2767
|
-
const { body } = extractThinkBlocks(raw);
|
|
2768
|
-
return body.replace(/\u0001T(\d+)\u0001/g, "");
|
|
2769
|
-
}
|
|
2770
|
-
/**
|
|
2771
|
-
* 启动调试问答服务(单实例;重复启动幂等返回既有实例)。
|
|
2772
|
-
*
|
|
2773
|
-
* **CCC 绑定(v1.25.2 用户指出)**:服务绑定一个默认 CCC root(调用方 resolveSkiffRoot
|
|
2774
|
-
* 解析:live 会话中**含 skiff.roles 的 CCC 优先**);v1.25.4 起页面可**手工切换**到
|
|
2775
|
-
* 其它候选 CCC(live 会话发现的全部 CCC);角色配置**每次请求实时读取**(不缓存快照)。
|
|
2776
|
-
*
|
|
2777
|
-
* @param root 默认绑定的 CCC 根(首次加载选中;角色配置读取 + skiff agent cwd)
|
|
2778
|
-
* @param port 调试端口(仅 127.0.0.1)
|
|
2779
|
-
* @param webPort 主 WebUI 端口(WebUI 链接)
|
|
2780
|
-
*/
|
|
2781
|
-
async function startSkiffDebugServer(ctx, root, port, webPort) {
|
|
2782
|
-
if (active) return;
|
|
2783
|
-
const server = createServer((req, res) => {
|
|
2784
|
-
handle(ctx, root, webPort, req, res);
|
|
2785
|
-
});
|
|
2786
|
-
await new Promise((resolve, reject) => {
|
|
2787
|
-
server.once("error", reject);
|
|
2788
|
-
server.listen(port, "127.0.0.1", () => resolve());
|
|
2789
|
-
});
|
|
2790
|
-
active = {
|
|
2791
|
-
server,
|
|
2792
|
-
port
|
|
2793
|
-
};
|
|
2794
|
-
console.log(`[serenity-hooks] ✓ Skiff 调试问答页: http://127.0.0.1:${port}(默认 CCC: ${root},WebUI: ${webPort})`);
|
|
2795
|
-
}
|
|
2796
|
-
function stopSkiffDebugServer() {
|
|
2797
|
-
if (!active) return;
|
|
2798
|
-
try {
|
|
2799
|
-
active.server.close();
|
|
2800
|
-
} catch {}
|
|
2801
|
-
active = null;
|
|
2802
|
-
}
|
|
2803
|
-
async function handle(ctx, defaultRoot, webPort, req, res) {
|
|
2804
|
-
try {
|
|
2805
|
-
const url = (req.url ?? "/").split("?")[0] ?? "/";
|
|
2806
|
-
if (req.method === "GET" && url === "/") {
|
|
2807
|
-
sendHtml(res, skiffDebugPage(await discoverCccs(ctx, defaultRoot), defaultRoot, webPort));
|
|
2808
|
-
return;
|
|
2809
|
-
}
|
|
2810
|
-
if (req.method === "POST" && url === "/ask") {
|
|
2811
|
-
let body;
|
|
2812
|
-
try {
|
|
2813
|
-
const parsed = JSON.parse(await readBody(req));
|
|
2814
|
-
if (parsed === null || typeof parsed !== "object") throw new Error("not an object");
|
|
2815
|
-
body = parsed;
|
|
2816
|
-
} catch {
|
|
2817
|
-
sendJson(res, 400, { error: "invalid JSON body" });
|
|
2818
|
-
return;
|
|
2819
|
-
}
|
|
2820
|
-
const ccc = typeof body.ccc === "string" && body.ccc !== "" ? body.ccc : defaultRoot;
|
|
2821
|
-
const roleName = typeof body.role === "string" ? body.role : "";
|
|
2822
|
-
const question = typeof body.question === "string" ? body.question : "";
|
|
2823
|
-
const sessionId = typeof body.sessionId === "string" && body.sessionId !== "" ? body.sessionId : void 0;
|
|
2824
|
-
const role = readSkiffRoles(ccc).get(roleName);
|
|
2825
|
-
if (!roleName || !role) {
|
|
2826
|
-
sendJson(res, 400, { error: `unknown role: ${roleName} (ccc: ${ccc})` });
|
|
2827
|
-
return;
|
|
2828
|
-
}
|
|
2829
|
-
if (!question.trim()) {
|
|
2830
|
-
sendJson(res, 400, { error: "empty question" });
|
|
2831
|
-
return;
|
|
2832
|
-
}
|
|
2833
|
-
let agent;
|
|
2834
|
-
let continued = false;
|
|
2835
|
-
if (sessionId) {
|
|
2836
|
-
const info = skiffSessionInfo(sessionId);
|
|
2837
|
-
const live = getSkiffAgent(sessionId);
|
|
2838
|
-
if (!info || !live) {
|
|
2839
|
-
sendJson(res, 400, { error: "session is not recoverable (process restarted or session unknown) — start a new conversation" });
|
|
2840
|
-
return;
|
|
2841
|
-
}
|
|
2842
|
-
if (info.role !== roleName || info.ccc !== ccc) {
|
|
2843
|
-
sendJson(res, 400, { error: `session "${sessionId}" belongs to role "${info.role}" in another CCC — start a new conversation` });
|
|
2844
|
-
return;
|
|
2845
|
-
}
|
|
2846
|
-
agent = live;
|
|
2847
|
-
continued = true;
|
|
2848
|
-
}
|
|
2849
|
-
const hc = readHandymanConfig(ccc);
|
|
2850
|
-
if (!agent) agent = (await createSkiffAgent(ctx, ccc, roleName, role, hc?.defaultModel)).agent;
|
|
2851
|
-
const result = await askSkiff(ctx, agent, question, 0);
|
|
2852
|
-
sendJson(res, 200, {
|
|
2853
|
-
answer: result.answer,
|
|
2854
|
-
answer_html: renderSkiffMarkdown(result.answer),
|
|
2855
|
-
sessionId: result.sessionId,
|
|
2856
|
-
continued,
|
|
2857
|
-
trajectory: result.trajectory
|
|
2858
|
-
});
|
|
2859
|
-
return;
|
|
2860
|
-
}
|
|
2861
|
-
sendJson(res, 404, { error: "not found" });
|
|
2862
|
-
} catch (err) {
|
|
2863
|
-
sendJson(res, 500, { error: err?.message ?? String(err) });
|
|
2864
|
-
}
|
|
2865
|
-
}
|
|
2866
|
-
//#endregion
|
|
2867
|
-
export { skiffRoleFor$1 as A, listActiveHandymen as C, splitModel as D, requireWhitelistedModel as E, writeFailedStatus as O, handymanProgressPaths as S, readProgress as T, unregisterSkiffSession as _, startSkiffDebugServer as a, HANDYMAN_GUIDE as b, askSkiff as c, ensureWorkspacePromptSection as d, getSkiffAgent as f, skiffTrajectoryEnabled as g, skiffSessionSnapshot as h, skiff_debug_exports as i, writeProgress as k, createSkiffAgent as l, skiffSessionInfo as m, jscSafeJsonText as n, stopSkiffDebugServer as o, skiffMsmGate as p, renderSkiffMarkdown as r, stripThink as s, discoverCccs as t, ensureSkiffSession as u, workspaceTrajectoryLine as v, newStopToken as w, buildRoundPrompt as x, waitAgentIdle as y };
|