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