tagword-codeagent 1.1.6__py3-none-any.whl
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.
- codeagent/__init__.py +10 -0
- codeagent/__main__.py +13 -0
- codeagent/cli/__init__.py +13 -0
- codeagent/cli/entrypoint.py +161 -0
- codeagent/cli/main.py +979 -0
- codeagent/core/__init__.py +2 -0
- codeagent/core/attachments.py +342 -0
- codeagent/core/audio_models.py +50 -0
- codeagent/core/bootstrap.py +178 -0
- codeagent/core/default_presets.py +159 -0
- codeagent/core/env.py +106 -0
- codeagent/core/folder_picker.py +151 -0
- codeagent/core/image_understanding.py +90 -0
- codeagent/core/models.py +6 -0
- codeagent/core/paths.py +238 -0
- codeagent/core/pricing.py +107 -0
- codeagent/core/process_ports.py +258 -0
- codeagent/core/seed_bridge.py +21 -0
- codeagent/core/settings.py +98 -0
- codeagent/core/speech_synth.py +330 -0
- codeagent/core/team_config.py +247 -0
- codeagent/core/team_manager.py +245 -0
- codeagent/core/token_counter.py +41 -0
- codeagent/core/tts_voices.py +103 -0
- codeagent/core/usage_billing.py +72 -0
- codeagent/core/vision_models.py +93 -0
- codeagent/memory/diary.py +144 -0
- codeagent/persona_defaults/README.md +12 -0
- codeagent/persona_defaults/agent.md +352 -0
- codeagent/persona_defaults/identity.md +29 -0
- codeagent/persona_defaults/memory.md +4 -0
- codeagent/persona_defaults/skills.md +325 -0
- codeagent/persona_defaults/soul.md +38 -0
- codeagent/persona_defaults/tools.md +41 -0
- codeagent/persona_defaults/user.md +18 -0
- codeagent/runtime/__init__.py +11 -0
- codeagent/runtime/agent_context.py +6 -0
- codeagent/runtime/agent_runtime.py +21 -0
- codeagent/runtime/compact_prompt.py +9 -0
- codeagent/runtime/compact_state.py +40 -0
- codeagent/runtime/llm_worker.py +132 -0
- codeagent/runtime/orchestrator.py +161 -0
- codeagent/runtime/prompt_enrichment.py +324 -0
- codeagent/runtime/session_manager.py +30 -0
- codeagent/runtime/task_split.py +38 -0
- codeagent/runtime/turn_loop.py +13 -0
- codeagent/runtime/worker.py +48 -0
- codeagent/server/__init__.py +505 -0
- codeagent/server/app_factory.py +1292 -0
- codeagent/server/attachment_api.py +261 -0
- codeagent/server/hub.py +175 -0
- codeagent/server/icon.png +0 -0
- codeagent/server/self_healing.py +143 -0
- codeagent/server/team_engine.py +243 -0
- codeagent/server/tts_api.py +51 -0
- codeagent/server/webui_api_app.py +3180 -0
- codeagent/skills/__init__.py +2 -0
- codeagent/skills/select.py +144 -0
- codeagent/tools/__init__.py +6 -0
- codeagent/tools/agent_tools.py +10 -0
- codeagent/web/__init__.py +1 -0
- codeagent/web/__pycache__/__init__.cpython-313.pyc +0 -0
- codeagent/web/__pycache__/app.cpython-313.pyc +0 -0
- codeagent/web/__pycache__/auth_impl.cpython-313.pyc +0 -0
- codeagent/web/auth_impl.py +326 -0
- codeagent/web/static/00-marked.min.js +6 -0
- codeagent/web/static/00-mermaid.min.js +2314 -0
- codeagent/web/static/00-purify.min.js +3 -0
- codeagent/web/static/00-storage.js +83 -0
- codeagent/web/static/00-utils.js +51 -0
- codeagent/web/static/01-theme.css +96 -0
- codeagent/web/static/01a-scroll.js +86 -0
- codeagent/web/static/01b-think.js +62 -0
- codeagent/web/static/01c-session-identity.js +81 -0
- codeagent/web/static/01c-session-thinking.js +61 -0
- codeagent/web/static/01c-session-tree-menus.js +349 -0
- codeagent/web/static/01c-session-tree.js +531 -0
- codeagent/web/static/01c-session-utilities.js +83 -0
- codeagent/web/static/01c-vision-model.js +132 -0
- codeagent/web/static/01d-image-gen-model.js +84 -0
- codeagent/web/static/01e-audio-model.js +97 -0
- codeagent/web/static/01f-compose-ui.js +88 -0
- codeagent/web/static/01g-tts-settings.js +128 -0
- codeagent/web/static/01h-music-model.js +85 -0
- codeagent/web/static/01i-model-stack.js +291 -0
- codeagent/web/static/01j-video-gen-model.js +85 -0
- codeagent/web/static/01k-model-stack-bootstrap.js +66 -0
- codeagent/web/static/01k-model-stack-state.js +209 -0
- codeagent/web/static/01q-inflight.js +195 -0
- codeagent/web/static/02-reset.css +22 -0
- codeagent/web/static/02-session-ui.js +201 -0
- codeagent/web/static/03-layout-p1.css +2 -0
- codeagent/web/static/03-layout-p2.css +2 -0
- codeagent/web/static/03a-links-stream.js +88 -0
- codeagent/web/static/03b-stream-text.js +119 -0
- codeagent/web/static/03c-render-bubble.js +520 -0
- codeagent/web/static/03d-agent-bubble.js +126 -0
- codeagent/web/static/03e-tool-trace.js +182 -0
- codeagent/web/static/03f-timeline.js +54 -0
- codeagent/web/static/03g-bubble-tts.js +292 -0
- codeagent/web/static/04-components-p1.css +1 -0
- codeagent/web/static/04-components-p2b.css +1 -0
- codeagent/web/static/04-components-p3.css +2 -0
- codeagent/web/static/04-components-p4.css +1 -0
- codeagent/web/static/04-components-p5.css +1 -0
- codeagent/web/static/04-components-p6.css +58 -0
- codeagent/web/static/04-components-p7.css +4 -0
- codeagent/web/static/04-components-p8.css +1 -0
- codeagent/web/static/04-components-p9.css +1 -0
- codeagent/web/static/04-ws-connect.js +351 -0
- codeagent/web/static/04-ws-helpers.js +116 -0
- codeagent/web/static/05-chat-p1.css +1058 -0
- codeagent/web/static/05-chat-p2.css +1 -0
- codeagent/web/static/05-chat-p3.css +295 -0
- codeagent/web/static/05a-session-history.js +176 -0
- codeagent/web/static/05b-session-list.js +114 -0
- codeagent/web/static/05c-dialog.js +123 -0
- codeagent/web/static/05d-project-create.js +172 -0
- codeagent/web/static/05e-project-remote.js +143 -0
- codeagent/web/static/06-chat.js +552 -0
- codeagent/web/static/06-utilities.css +181 -0
- codeagent/web/static/06b-attachments.js +304 -0
- codeagent/web/static/06c-camera.js +488 -0
- codeagent/web/static/07-activity-bar.css +127 -0
- codeagent/web/static/09-git-p1.css +1 -0
- codeagent/web/static/09-git-p2.css +1 -0
- codeagent/web/static/09a-init-bind.js +110 -0
- codeagent/web/static/09b-panel-toggle.js +80 -0
- codeagent/web/static/09c-api-refresh.js +100 -0
- codeagent/web/static/09d-status-log.js +80 -0
- codeagent/web/static/09e-commit.js +80 -0
- codeagent/web/static/09f-remote.js +93 -0
- codeagent/web/static/11a-cron-parse.js +117 -0
- codeagent/web/static/11b-cron-panel.js +100 -0
- codeagent/web/static/11c-cron-card.js +133 -0
- codeagent/web/static/11d-cron-form.js +105 -0
- codeagent/web/static/11e-env-config.js +168 -0
- codeagent/web/static/11f-env-llm-a-state.js +136 -0
- codeagent/web/static/11f-env-llm-b-form.js +409 -0
- codeagent/web/static/11f-env-llm-c-board.js +322 -0
- codeagent/web/static/11g-env-paths-git.js +973 -0
- codeagent/web/static/11h-env-mcp-a-state.js +161 -0
- codeagent/web/static/11h-env-mcp-b-form.js +229 -0
- codeagent/web/static/11h-env-mcp-c-board.js +565 -0
- codeagent/web/static/12-plugins.js +127 -0
- codeagent/web/static/13a-skills.js +156 -0
- codeagent/web/static/13a-todo-utils.js +140 -0
- codeagent/web/static/13b-todo-fetch.js +106 -0
- codeagent/web/static/13b-tools.js +127 -0
- codeagent/web/static/13c-todo-crud.js +92 -0
- codeagent/web/static/14-activity-bar.js +177 -0
- codeagent/web/static/15-plans.css +194 -0
- codeagent/web/static/15-plans.js +219 -0
- codeagent/web/static/16-mobile.css +612 -0
- codeagent/web/static/16-mobile.js +178 -0
- codeagent/web/static/17-preset-ux.css +287 -0
- codeagent/web/static/18-agent-mgr.css +616 -0
- codeagent/web/static/18-agent-mgr.js +204 -0
- codeagent/web/static/24-health-dashboard.js +68 -0
- codeagent/web/static/99-brand-polish.css +152 -0
- codeagent/web/static/body.html +824 -0
- codeagent/web/static/setup/setup_form.html +171 -0
- codeagent/web/static/setup/setup_head.html +100 -0
- codeagent/web/static/setup/setup_tail.html +264 -0
- codeagent/web/static/setup/setup_test.html +38 -0
- codeagent/web/web_login.html +265 -0
- codeagent/web/webui.html +35 -0
- tagword_codeagent-1.1.6.dist-info/METADATA +273 -0
- tagword_codeagent-1.1.6.dist-info/RECORD +173 -0
- tagword_codeagent-1.1.6.dist-info/WHEEL +5 -0
- tagword_codeagent-1.1.6.dist-info/entry_points.txt +3 -0
- tagword_codeagent-1.1.6.dist-info/licenses/LICENSE +21 -0
- tagword_codeagent-1.1.6.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""Agent-private skills selection via routing scorer (strategy B)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from codeagent.core.paths import agent_skills_dir
|
|
11
|
+
from seed.core.config_plane import project_root
|
|
12
|
+
from seed.core.routing import score_entries
|
|
13
|
+
from seed.models import CommandEntry
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class SelectedSkill:
|
|
18
|
+
skill_id: str
|
|
19
|
+
path: Path
|
|
20
|
+
description: str
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _skill_description_from_file(path: Path, *, max_lines: int = 10, max_chars: int = 400) -> str:
|
|
24
|
+
try:
|
|
25
|
+
raw = path.read_text(encoding="utf-8")
|
|
26
|
+
except OSError:
|
|
27
|
+
return ""
|
|
28
|
+
lines = raw.splitlines()
|
|
29
|
+
take = "\n".join(lines[: max(1, int(max_lines))]).strip()
|
|
30
|
+
if len(take) > max_chars:
|
|
31
|
+
take = take[: max_chars - 1].rstrip() + "…"
|
|
32
|
+
return take
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _read_skills_state(agent_id: str) -> dict[str, bool]:
|
|
36
|
+
state_path = agent_skills_dir(agent_id) / "_state.json"
|
|
37
|
+
if not state_path.is_file():
|
|
38
|
+
return {}
|
|
39
|
+
try:
|
|
40
|
+
raw = json.loads(state_path.read_text(encoding="utf-8"))
|
|
41
|
+
if isinstance(raw, dict):
|
|
42
|
+
return {str(k): bool(v) for k, v in raw.items()}
|
|
43
|
+
except (json.JSONDecodeError, OSError):
|
|
44
|
+
pass
|
|
45
|
+
return {}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _skill_enabled(state: dict[str, bool], filename: str) -> bool:
|
|
49
|
+
return state.get(filename, True)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def list_agent_skill_entries(agent_id: str) -> list[CommandEntry]:
|
|
53
|
+
d = agent_skills_dir(agent_id)
|
|
54
|
+
state = _read_skills_state(agent_id)
|
|
55
|
+
out: list[CommandEntry] = []
|
|
56
|
+
if d.is_dir():
|
|
57
|
+
for p in sorted(d.glob("*.md")):
|
|
58
|
+
if p.name.startswith("."):
|
|
59
|
+
continue
|
|
60
|
+
if not _skill_enabled(state, p.name):
|
|
61
|
+
continue
|
|
62
|
+
sid = p.stem.strip()
|
|
63
|
+
if not sid:
|
|
64
|
+
continue
|
|
65
|
+
desc = _skill_description_from_file(p)
|
|
66
|
+
out.append(CommandEntry(sid, desc, "skill"))
|
|
67
|
+
# Global config/skills/*.md (same enable map keyed by filename)
|
|
68
|
+
gdir = project_root() / "config" / "skills"
|
|
69
|
+
if gdir.is_dir():
|
|
70
|
+
seen = {e.name for e in out}
|
|
71
|
+
for p in sorted(gdir.glob("*.md")):
|
|
72
|
+
if p.name.startswith(".") or p.stem in seen:
|
|
73
|
+
continue
|
|
74
|
+
if not _skill_enabled(state, p.name):
|
|
75
|
+
continue
|
|
76
|
+
sid = p.stem.strip()
|
|
77
|
+
if not sid:
|
|
78
|
+
continue
|
|
79
|
+
desc = _skill_description_from_file(p)
|
|
80
|
+
out.append(CommandEntry(sid, desc, "skill"))
|
|
81
|
+
return out
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _resolve_skill_path(agent_id: str, skill_id: str) -> Path:
|
|
85
|
+
agent_path = agent_skills_dir(agent_id) / f"{skill_id}.md"
|
|
86
|
+
if agent_path.is_file():
|
|
87
|
+
return agent_path.resolve()
|
|
88
|
+
global_path = project_root() / "config" / "skills" / f"{skill_id}.md"
|
|
89
|
+
return global_path.resolve()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def select_skills(agent_id: str, *, user_text: str, k: int = 3) -> list[SelectedSkill]:
|
|
93
|
+
entries = list_agent_skill_entries(agent_id)
|
|
94
|
+
if not entries:
|
|
95
|
+
return []
|
|
96
|
+
top_k = max(1, int(k))
|
|
97
|
+
picked = score_entries(user_text or "", entries, limit=top_k)
|
|
98
|
+
out: list[SelectedSkill] = []
|
|
99
|
+
for e in picked[:top_k]:
|
|
100
|
+
path = _resolve_skill_path(agent_id, e.name)
|
|
101
|
+
if not path.is_file():
|
|
102
|
+
continue
|
|
103
|
+
out.append(
|
|
104
|
+
SelectedSkill(skill_id=e.name, path=path, description=e.description or "")
|
|
105
|
+
)
|
|
106
|
+
return out
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def build_selected_skills_appendix(
|
|
110
|
+
agent_id: str, *, user_text: str, k: int | None = None
|
|
111
|
+
) -> str:
|
|
112
|
+
"""Markdown block appended to system prompt for this turn (empty if none)."""
|
|
113
|
+
if k is None:
|
|
114
|
+
from codeagent.core import env as ca_env
|
|
115
|
+
|
|
116
|
+
k = ca_env.pick_int(3, ca_env.SKILLS_TOP_K)
|
|
117
|
+
picked = select_skills(agent_id, user_text=user_text, k=k)
|
|
118
|
+
if not picked:
|
|
119
|
+
return ""
|
|
120
|
+
parts = [
|
|
121
|
+
"\n\n---\n",
|
|
122
|
+
"## Active skills (matched this turn)\n",
|
|
123
|
+
"Follow these skill instructions when relevant.\n",
|
|
124
|
+
]
|
|
125
|
+
for sk in picked:
|
|
126
|
+
body = read_skill_text(sk)
|
|
127
|
+
if not body:
|
|
128
|
+
continue
|
|
129
|
+
parts.append(f"\n### Skill: {sk.skill_id}\n\n{body}\n")
|
|
130
|
+
if len(parts) <= 3:
|
|
131
|
+
return ""
|
|
132
|
+
return "".join(parts)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def read_skill_text(skill: SelectedSkill, *, max_chars: int = 8000) -> str:
|
|
136
|
+
try:
|
|
137
|
+
raw = skill.path.read_text(encoding="utf-8")
|
|
138
|
+
except OSError:
|
|
139
|
+
return ""
|
|
140
|
+
t = raw.strip()
|
|
141
|
+
if len(t) > max_chars:
|
|
142
|
+
t = t[: max_chars - 24].rstrip() + "\n…[skill 内容已截断]"
|
|
143
|
+
return t
|
|
144
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Per-agent tools — re-export from seed.integrations.agent_tools."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from seed.integrations.agent_tools import ( # noqa: F401
|
|
6
|
+
get_tools_for_agent,
|
|
7
|
+
reset_agent_tools_cache,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = ("get_tools_for_agent", "reset_agent_tools_cache")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
"""Web UI optional token gate: secret in env or local file; reset only via CLI."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import hashlib
|
|
5
|
+
import hmac
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
import time
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from urllib.parse import parse_qs, unquote
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
from seed.core.config_plane import project_root # noqa: E402
|
|
15
|
+
|
|
16
|
+
TOKEN_FILENAME = "webui.token"
|
|
17
|
+
LEGACY_TOKEN_FILENAME = "codeagent.webui.token"
|
|
18
|
+
|
|
19
|
+
# Cookie 名按端口隔离,防止本地多实例 cookie 互相覆盖(localhost 同域名)
|
|
20
|
+
_DEFAULT_PORT = 8099
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def cookie_name(port: int = 0) -> str:
|
|
24
|
+
"""返回端口限定的 cookie 名。port=0 时用默认值,兼容旧 cookie 名。"""
|
|
25
|
+
p = port or _DEFAULT_PORT
|
|
26
|
+
return f"ca_webui_{p}"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _port_from_scope(scope: dict) -> int:
|
|
30
|
+
"""从 ASGI scope 提取服务端口。"""
|
|
31
|
+
try:
|
|
32
|
+
server = scope.get("server")
|
|
33
|
+
if server and len(server) >= 2:
|
|
34
|
+
return int(server[1])
|
|
35
|
+
except (TypeError, ValueError):
|
|
36
|
+
pass
|
|
37
|
+
return _DEFAULT_PORT
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _project_root() -> Path:
|
|
41
|
+
return project_root()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _token_file_path(base: Path | None = None) -> Path:
|
|
45
|
+
root = base if base is not None else _project_root()
|
|
46
|
+
p = root / "config" / TOKEN_FILENAME
|
|
47
|
+
if p.is_file():
|
|
48
|
+
return p
|
|
49
|
+
# Legacy fallback
|
|
50
|
+
leg = root / "config" / LEGACY_TOKEN_FILENAME
|
|
51
|
+
if leg.is_file():
|
|
52
|
+
return leg
|
|
53
|
+
# Neither exists — return new name (write path)
|
|
54
|
+
return p
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_webui_token(project_root: Path | None = None) -> str:
|
|
58
|
+
raw = os.environ.get("CODEAGENT_WEBUI_TOKEN", "").strip()
|
|
59
|
+
if raw:
|
|
60
|
+
return raw
|
|
61
|
+
path = _token_file_path(project_root)
|
|
62
|
+
if not path.is_file():
|
|
63
|
+
return ""
|
|
64
|
+
try:
|
|
65
|
+
return path.read_text(encoding="utf-8").strip()
|
|
66
|
+
except OSError:
|
|
67
|
+
return ""
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def webui_auth_active(project_root: Path | None = None) -> bool:
|
|
71
|
+
return bool(get_webui_token(project_root))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _cookie_signing_key(token: str) -> bytes:
|
|
75
|
+
return hmac.new(b"codeagent-webui-cookie-v1", token.encode("utf-8"), hashlib.sha256).digest()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def make_webui_cookie_value(token: str, ttl_sec: int = 604800) -> str:
|
|
79
|
+
exp = int(time.time()) + max(60, int(ttl_sec))
|
|
80
|
+
exp_s = str(exp).encode("utf-8")
|
|
81
|
+
sig = hmac.new(_cookie_signing_key(token), exp_s, hashlib.sha256).hexdigest()
|
|
82
|
+
return f"{exp}.{sig}"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def verify_webui_cookie(token: str, cookie_val: str | None) -> bool:
|
|
86
|
+
if not token or not cookie_val or "." not in cookie_val:
|
|
87
|
+
return False
|
|
88
|
+
try:
|
|
89
|
+
exp_s, sig = cookie_val.rsplit(".", 1)
|
|
90
|
+
exp = int(exp_s)
|
|
91
|
+
if time.time() > exp:
|
|
92
|
+
return False
|
|
93
|
+
expected = hmac.new(
|
|
94
|
+
_cookie_signing_key(token), exp_s.encode("utf-8"), hashlib.sha256
|
|
95
|
+
).hexdigest()
|
|
96
|
+
return hmac.compare_digest(expected.lower(), sig.lower())
|
|
97
|
+
except (ValueError, TypeError, OSError):
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _setup_incomplete(project_root: Path | None = None) -> bool:
|
|
102
|
+
"""True until ``config/setup.json`` exists with ``\"done\": true``."""
|
|
103
|
+
root = Path(project_root).resolve() if project_root is not None else _project_root()
|
|
104
|
+
marker = root / "config" / "setup.json"
|
|
105
|
+
if not marker.is_file():
|
|
106
|
+
marker = root / "config" / "codeagent.setup.json"
|
|
107
|
+
if not marker.is_file():
|
|
108
|
+
return True
|
|
109
|
+
try:
|
|
110
|
+
import json
|
|
111
|
+
|
|
112
|
+
j = json.loads(marker.read_text(encoding="utf-8") or "{}")
|
|
113
|
+
return not bool(j.get("done"))
|
|
114
|
+
except Exception:
|
|
115
|
+
return True
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def is_setup_bootstrap_route(path: str, method: str) -> bool:
|
|
119
|
+
"""Allow setup wizard APIs without cookie (even when ``CODEAGENT_WEBUI_TOKEN`` is preset in env)."""
|
|
120
|
+
if path.startswith("/api/ui/setup/"):
|
|
121
|
+
return True
|
|
122
|
+
m = (method or "").upper()
|
|
123
|
+
if path == "/api/ui/llm/presets" and m in ("GET", "HEAD", "POST", "OPTIONS"):
|
|
124
|
+
return True
|
|
125
|
+
if path == "/api/ui/llm/providers" and m in ("GET", "HEAD", "OPTIONS"):
|
|
126
|
+
return True
|
|
127
|
+
if path == "/api/ui/llm/presets/test" and m in ("POST", "OPTIONS"):
|
|
128
|
+
return True
|
|
129
|
+
if path == "/api/ui/llm/presets/default" and m in ("POST", "OPTIONS"):
|
|
130
|
+
return True
|
|
131
|
+
if path == "/api/ui/env/chat" and m in ("POST", "OPTIONS"):
|
|
132
|
+
return True
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def is_public_webui_route(path: str, method: str) -> bool:
|
|
137
|
+
if path.startswith("/webhook/"):
|
|
138
|
+
return True
|
|
139
|
+
if path == "/setup" and method == "GET":
|
|
140
|
+
return True
|
|
141
|
+
if path.startswith("/api/ui/setup/"):
|
|
142
|
+
return True
|
|
143
|
+
# Web UI bootstrap (lets the frontend decide whether WS is enabled)
|
|
144
|
+
if path == "/api/ui/flags" and method == "GET":
|
|
145
|
+
return True
|
|
146
|
+
if path == "/api/ui/auth/status":
|
|
147
|
+
return True
|
|
148
|
+
if path == "/api/ui/auth" and method == "POST":
|
|
149
|
+
return True
|
|
150
|
+
if path == "/api/ui/auth/logout" and method == "POST":
|
|
151
|
+
return True
|
|
152
|
+
if path == "/api/ui/llm/providers" and method == "GET":
|
|
153
|
+
return True
|
|
154
|
+
# File-serve for local images (path validation in handler)
|
|
155
|
+
if path == "/api/file-serve" and method == "GET":
|
|
156
|
+
return True
|
|
157
|
+
return bool(method == "GET" and path in ("/icon.png", "/favicon.ico", "/favicon.svg", "/health", "/api/health"))
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def get_login_html() -> str:
|
|
161
|
+
p = Path(__file__).resolve().parent / "web_login.html"
|
|
162
|
+
if p.is_file():
|
|
163
|
+
return p.read_text(encoding="utf-8")
|
|
164
|
+
return (
|
|
165
|
+
"<!DOCTYPE html><html><body><p>Missing web_login.html</p></body></html>"
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _http_method_from_scope(scope: dict) -> str:
|
|
170
|
+
"""Starlette 0.x used bytes for scope['method']; 1.x may use str."""
|
|
171
|
+
raw = scope.get("method") or "GET"
|
|
172
|
+
if isinstance(raw, bytes):
|
|
173
|
+
return raw.decode("ascii", errors="replace").upper()
|
|
174
|
+
return str(raw).upper()
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _env_truthy(name: str, default: str = "0") -> bool:
|
|
178
|
+
return os.environ.get(name, default).strip().lower() in ("1", "true", "yes", "on")
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def ws_query_token_bridge_enabled() -> bool:
|
|
182
|
+
"""When True, /ws may authenticate via ?webui_token= (same value as CODEAGENT_WEBUI_TOKEN).
|
|
183
|
+
|
|
184
|
+
Use only when embedded browsers / previews do not send cookies on WebSocket handshake.
|
|
185
|
+
Token appears in URLs and logs — keep off in production unless you accept that risk.
|
|
186
|
+
"""
|
|
187
|
+
return _env_truthy("CODEAGENT_WEBUI_WS_QUERY_TOKEN", "0")
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _parse_query_params(scope: dict) -> dict[str, list[str]]:
|
|
191
|
+
raw = scope.get("query_string") or b""
|
|
192
|
+
qs = raw.decode("latin-1", errors="replace") if isinstance(raw, bytes) else str(raw)
|
|
193
|
+
return parse_qs(qs, keep_blank_values=False)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _first_query_value(scope: dict, key: str) -> str:
|
|
197
|
+
vals = _parse_query_params(scope).get(key) or []
|
|
198
|
+
return (vals[0] or "").strip() if vals else ""
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _raw_token_matches(server_token: str, presented: str) -> bool:
|
|
202
|
+
if not server_token or not presented:
|
|
203
|
+
return False
|
|
204
|
+
a = server_token.strip().encode("utf-8")
|
|
205
|
+
b = presented.strip().encode("utf-8")
|
|
206
|
+
if len(a) != len(b):
|
|
207
|
+
return False
|
|
208
|
+
return hmac.compare_digest(a, b)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _read_cookie_from_scope(scope: dict, name: str) -> str | None:
|
|
212
|
+
prefix = name + "="
|
|
213
|
+
for k, v in scope.get("headers") or []:
|
|
214
|
+
key = k.decode("latin-1") if isinstance(k, bytes) else str(k)
|
|
215
|
+
if key.lower() != "cookie":
|
|
216
|
+
continue
|
|
217
|
+
try:
|
|
218
|
+
text = v.decode("latin-1") if isinstance(v, bytes) else str(v)
|
|
219
|
+
except Exception:
|
|
220
|
+
continue
|
|
221
|
+
for segment in text.split(";"):
|
|
222
|
+
segment = segment.strip()
|
|
223
|
+
if segment.startswith(prefix):
|
|
224
|
+
try:
|
|
225
|
+
return unquote(segment[len(prefix) :].strip())
|
|
226
|
+
except Exception:
|
|
227
|
+
return None
|
|
228
|
+
return None
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class WebUIAuthMiddleware:
|
|
232
|
+
"""ASGI middleware: require signed cookie when Web UI token is configured."""
|
|
233
|
+
|
|
234
|
+
def __init__(self, app, *, project_root: Path):
|
|
235
|
+
self.app = app
|
|
236
|
+
self.project_root = Path(project_root).resolve()
|
|
237
|
+
|
|
238
|
+
async def __call__(self, scope, receive, send):
|
|
239
|
+
if scope["type"] == "websocket":
|
|
240
|
+
path = scope.get("path") or ""
|
|
241
|
+
tok = get_webui_token(self.project_root)
|
|
242
|
+
# Some dev proxies / IDE previews rewrite websocket paths like:
|
|
243
|
+
# /ws//127.0.0.1%3A8765/ws?... (still our Web UI websocket)
|
|
244
|
+
if tok and (path == "/ws" or path.startswith("/ws/")):
|
|
245
|
+
port = _port_from_scope(scope)
|
|
246
|
+
ck = _read_cookie_from_scope(scope, cookie_name(port))
|
|
247
|
+
ok = verify_webui_cookie(tok, ck)
|
|
248
|
+
if not ok and ws_query_token_bridge_enabled():
|
|
249
|
+
ok = _raw_token_matches(tok, _first_query_value(scope, "webui_token"))
|
|
250
|
+
if not ok:
|
|
251
|
+
from starlette.responses import PlainTextResponse
|
|
252
|
+
from starlette.websockets import WebSocket
|
|
253
|
+
|
|
254
|
+
ws = WebSocket(scope, receive, send)
|
|
255
|
+
logger.info(
|
|
256
|
+
"WebSocket /ws rejected (403): invalid or missing Web UI auth "
|
|
257
|
+
"(cookie / optional webui_token query). Path=%r",
|
|
258
|
+
path,
|
|
259
|
+
)
|
|
260
|
+
try:
|
|
261
|
+
await ws.send_denial_response(
|
|
262
|
+
PlainTextResponse(
|
|
263
|
+
"Web UI auth required: open the app in a normal browser and log in, "
|
|
264
|
+
"or set CODEAGENT_WEBUI_WS_QUERY_TOKEN=1 and reconnect after login "
|
|
265
|
+
"(webui_token query bridge).\n",
|
|
266
|
+
status_code=403,
|
|
267
|
+
)
|
|
268
|
+
)
|
|
269
|
+
except RuntimeError:
|
|
270
|
+
await ws.close(code=4401)
|
|
271
|
+
return
|
|
272
|
+
await self.app(scope, receive, send)
|
|
273
|
+
return
|
|
274
|
+
|
|
275
|
+
if scope["type"] != "http":
|
|
276
|
+
await self.app(scope, receive, send)
|
|
277
|
+
return
|
|
278
|
+
|
|
279
|
+
path = scope.get("path") or ""
|
|
280
|
+
method = _http_method_from_scope(scope)
|
|
281
|
+
tok = get_webui_token(self.project_root)
|
|
282
|
+
# If setup hasn't been completed yet, allow the root page to load the setup UI
|
|
283
|
+
# even when a token exists (otherwise users get stuck at login before setup).
|
|
284
|
+
if tok and method == "GET" and path == "/":
|
|
285
|
+
try:
|
|
286
|
+
marker = self.project_root / "config" / "setup.json"
|
|
287
|
+
if not marker.is_file():
|
|
288
|
+
marker = self.project_root / "config" / "codeagent.setup.json"
|
|
289
|
+
if marker.is_file():
|
|
290
|
+
import json
|
|
291
|
+
|
|
292
|
+
j = json.loads(marker.read_text(encoding="utf-8") or "{}")
|
|
293
|
+
if not bool(j.get("done")):
|
|
294
|
+
await self.app(scope, receive, send)
|
|
295
|
+
return
|
|
296
|
+
else:
|
|
297
|
+
# No marker yet => not set up
|
|
298
|
+
await self.app(scope, receive, send)
|
|
299
|
+
return
|
|
300
|
+
except Exception:
|
|
301
|
+
# Fail open for setup bootstrap
|
|
302
|
+
await self.app(scope, receive, send)
|
|
303
|
+
return
|
|
304
|
+
if not tok or is_public_webui_route(path, method):
|
|
305
|
+
await self.app(scope, receive, send)
|
|
306
|
+
return
|
|
307
|
+
port = _port_from_scope(scope)
|
|
308
|
+
ck = _read_cookie_from_scope(scope, cookie_name(port))
|
|
309
|
+
if verify_webui_cookie(tok, ck):
|
|
310
|
+
await self.app(scope, receive, send)
|
|
311
|
+
return
|
|
312
|
+
|
|
313
|
+
# Wizard step 1 saves LLM presets before user gets a signed cookie; env token alone must not block.
|
|
314
|
+
if tok and _setup_incomplete(self.project_root) and is_setup_bootstrap_route(path, method):
|
|
315
|
+
await self.app(scope, receive, send)
|
|
316
|
+
return
|
|
317
|
+
|
|
318
|
+
from starlette.responses import HTMLResponse, JSONResponse
|
|
319
|
+
|
|
320
|
+
if method == "GET" and path == "/":
|
|
321
|
+
resp = HTMLResponse(get_login_html(), status_code=200)
|
|
322
|
+
await resp(scope, receive, send)
|
|
323
|
+
return
|
|
324
|
+
|
|
325
|
+
resp = JSONResponse({"detail": "webui auth required"}, status_code=401)
|
|
326
|
+
await resp(scope, receive, send)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* marked v12.0.2 - a markdown parser
|
|
3
|
+
* Copyright (c) 2011-2024, Christopher Jeffrey. (MIT Licensed)
|
|
4
|
+
* https://github.com/markedjs/marked
|
|
5
|
+
*/
|
|
6
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).marked={})}(this,(function(e){"use strict";function t(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}function n(t){e.defaults=t}e.defaults={async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null};const s=/[&<>"']/,r=new RegExp(s.source,"g"),i=/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,l=new RegExp(i.source,"g"),o={"&":"&","<":"<",">":">",'"':""","'":"'"},a=e=>o[e];function c(e,t){if(t){if(s.test(e))return e.replace(r,a)}else if(i.test(e))return e.replace(l,a);return e}const h=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi;function p(e){return e.replace(h,((e,t)=>"colon"===(t=t.toLowerCase())?":":"#"===t.charAt(0)?"x"===t.charAt(1)?String.fromCharCode(parseInt(t.substring(2),16)):String.fromCharCode(+t.substring(1)):""))}const u=/(^|[^\[])\^/g;function k(e,t){let n="string"==typeof e?e:e.source;t=t||"";const s={replace:(e,t)=>{let r="string"==typeof t?t:t.source;return r=r.replace(u,"$1"),n=n.replace(e,r),s},getRegex:()=>new RegExp(n,t)};return s}function g(e){try{e=encodeURI(e).replace(/%25/g,"%")}catch(e){return null}return e}const f={exec:()=>null};function d(e,t){const n=e.replace(/\|/g,((e,t,n)=>{let s=!1,r=t;for(;--r>=0&&"\\"===n[r];)s=!s;return s?"|":" |"})).split(/ \|/);let s=0;if(n[0].trim()||n.shift(),n.length>0&&!n[n.length-1].trim()&&n.pop(),t)if(n.length>t)n.splice(t);else for(;n.length<t;)n.push("");for(;s<n.length;s++)n[s]=n[s].trim().replace(/\\\|/g,"|");return n}function x(e,t,n){const s=e.length;if(0===s)return"";let r=0;for(;r<s;){const i=e.charAt(s-r-1);if(i!==t||n){if(i===t||!n)break;r++}else r++}return e.slice(0,s-r)}function b(e,t,n,s){const r=t.href,i=t.title?c(t.title):null,l=e[1].replace(/\\([\[\]])/g,"$1");if("!"!==e[0].charAt(0)){s.state.inLink=!0;const e={type:"link",raw:n,href:r,title:i,text:l,tokens:s.inlineTokens(l)};return s.state.inLink=!1,e}return{type:"image",raw:n,href:r,title:i,text:c(l)}}class w{options;rules;lexer;constructor(t){this.options=t||e.defaults}space(e){const t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:"space",raw:t[0]}}code(e){const t=this.rules.block.code.exec(e);if(t){const e=t[0].replace(/^ {1,4}/gm,"");return{type:"code",raw:t[0],codeBlockStyle:"indented",text:this.options.pedantic?e:x(e,"\n")}}}fences(e){const t=this.rules.block.fences.exec(e);if(t){const e=t[0],n=function(e,t){const n=e.match(/^(\s+)(?:```)/);if(null===n)return t;const s=n[1];return t.split("\n").map((e=>{const t=e.match(/^\s+/);if(null===t)return e;const[n]=t;return n.length>=s.length?e.slice(s.length):e})).join("\n")}(e,t[3]||"");return{type:"code",raw:e,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:n}}}heading(e){const t=this.rules.block.heading.exec(e);if(t){let e=t[2].trim();if(/#$/.test(e)){const t=x(e,"#");this.options.pedantic?e=t.trim():t&&!/ $/.test(t)||(e=t.trim())}return{type:"heading",raw:t[0],depth:t[1].length,text:e,tokens:this.lexer.inline(e)}}}hr(e){const t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:t[0]}}blockquote(e){const t=this.rules.block.blockquote.exec(e);if(t){let e=t[0].replace(/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,"\n $1");e=x(e.replace(/^ *>[ \t]?/gm,""),"\n");const n=this.lexer.state.top;this.lexer.state.top=!0;const s=this.lexer.blockTokens(e);return this.lexer.state.top=n,{type:"blockquote",raw:t[0],tokens:s,text:e}}}list(e){let t=this.rules.block.list.exec(e);if(t){let n=t[1].trim();const s=n.length>1,r={type:"list",raw:"",ordered:s,start:s?+n.slice(0,-1):"",loose:!1,items:[]};n=s?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=s?n:"[*+-]");const i=new RegExp(`^( {0,3}${n})((?:[\t ][^\\n]*)?(?:\\n|$))`);let l="",o="",a=!1;for(;e;){let n=!1;if(!(t=i.exec(e)))break;if(this.rules.block.hr.test(e))break;l=t[0],e=e.substring(l.length);let s=t[2].split("\n",1)[0].replace(/^\t+/,(e=>" ".repeat(3*e.length))),c=e.split("\n",1)[0],h=0;this.options.pedantic?(h=2,o=s.trimStart()):(h=t[2].search(/[^ ]/),h=h>4?1:h,o=s.slice(h),h+=t[1].length);let p=!1;if(!s&&/^ *$/.test(c)&&(l+=c+"\n",e=e.substring(c.length+1),n=!0),!n){const t=new RegExp(`^ {0,${Math.min(3,h-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`),n=new RegExp(`^ {0,${Math.min(3,h-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),r=new RegExp(`^ {0,${Math.min(3,h-1)}}(?:\`\`\`|~~~)`),i=new RegExp(`^ {0,${Math.min(3,h-1)}}#`);for(;e;){const a=e.split("\n",1)[0];if(c=a,this.options.pedantic&&(c=c.replace(/^ {1,4}(?=( {4})*[^ ])/g," ")),r.test(c))break;if(i.test(c))break;if(t.test(c))break;if(n.test(e))break;if(c.search(/[^ ]/)>=h||!c.trim())o+="\n"+c.slice(h);else{if(p)break;if(s.search(/[^ ]/)>=4)break;if(r.test(s))break;if(i.test(s))break;if(n.test(s))break;o+="\n"+c}p||c.trim()||(p=!0),l+=a+"\n",e=e.substring(a.length+1),s=c.slice(h)}}r.loose||(a?r.loose=!0:/\n *\n *$/.test(l)&&(a=!0));let u,k=null;this.options.gfm&&(k=/^\[[ xX]\] /.exec(o),k&&(u="[ ] "!==k[0],o=o.replace(/^\[[ xX]\] +/,""))),r.items.push({type:"list_item",raw:l,task:!!k,checked:u,loose:!1,text:o,tokens:[]}),r.raw+=l}r.items[r.items.length-1].raw=l.trimEnd(),r.items[r.items.length-1].text=o.trimEnd(),r.raw=r.raw.trimEnd();for(let e=0;e<r.items.length;e++)if(this.lexer.state.top=!1,r.items[e].tokens=this.lexer.blockTokens(r.items[e].text,[]),!r.loose){const t=r.items[e].tokens.filter((e=>"space"===e.type)),n=t.length>0&&t.some((e=>/\n.*\n/.test(e.raw)));r.loose=n}if(r.loose)for(let e=0;e<r.items.length;e++)r.items[e].loose=!0;return r}}html(e){const t=this.rules.block.html.exec(e);if(t){return{type:"html",block:!0,raw:t[0],pre:"pre"===t[1]||"script"===t[1]||"style"===t[1],text:t[0]}}}def(e){const t=this.rules.block.def.exec(e);if(t){const e=t[1].toLowerCase().replace(/\s+/g," "),n=t[2]?t[2].replace(/^<(.*)>$/,"$1").replace(this.rules.inline.anyPunctuation,"$1"):"",s=t[3]?t[3].substring(1,t[3].length-1).replace(this.rules.inline.anyPunctuation,"$1"):t[3];return{type:"def",tag:e,raw:t[0],href:n,title:s}}}table(e){const t=this.rules.block.table.exec(e);if(!t)return;if(!/[:|]/.test(t[2]))return;const n=d(t[1]),s=t[2].replace(/^\||\| *$/g,"").split("|"),r=t[3]&&t[3].trim()?t[3].replace(/\n[ \t]*$/,"").split("\n"):[],i={type:"table",raw:t[0],header:[],align:[],rows:[]};if(n.length===s.length){for(const e of s)/^ *-+: *$/.test(e)?i.align.push("right"):/^ *:-+: *$/.test(e)?i.align.push("center"):/^ *:-+ *$/.test(e)?i.align.push("left"):i.align.push(null);for(const e of n)i.header.push({text:e,tokens:this.lexer.inline(e)});for(const e of r)i.rows.push(d(e,i.header.length).map((e=>({text:e,tokens:this.lexer.inline(e)}))));return i}}lheading(e){const t=this.rules.block.lheading.exec(e);if(t)return{type:"heading",raw:t[0],depth:"="===t[2].charAt(0)?1:2,text:t[1],tokens:this.lexer.inline(t[1])}}paragraph(e){const t=this.rules.block.paragraph.exec(e);if(t){const e="\n"===t[1].charAt(t[1].length-1)?t[1].slice(0,-1):t[1];return{type:"paragraph",raw:t[0],text:e,tokens:this.lexer.inline(e)}}}text(e){const t=this.rules.block.text.exec(e);if(t)return{type:"text",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){const t=this.rules.inline.escape.exec(e);if(t)return{type:"escape",raw:t[0],text:c(t[1])}}tag(e){const t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&/^<a /i.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&/^<\/a>/i.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&/^<(pre|code|kbd|script)(\s|>)/i.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){const t=this.rules.inline.link.exec(e);if(t){const e=t[2].trim();if(!this.options.pedantic&&/^</.test(e)){if(!/>$/.test(e))return;const t=x(e.slice(0,-1),"\\");if((e.length-t.length)%2==0)return}else{const e=function(e,t){if(-1===e.indexOf(t[1]))return-1;let n=0;for(let s=0;s<e.length;s++)if("\\"===e[s])s++;else if(e[s]===t[0])n++;else if(e[s]===t[1]&&(n--,n<0))return s;return-1}(t[2],"()");if(e>-1){const n=(0===t[0].indexOf("!")?5:4)+t[1].length+e;t[2]=t[2].substring(0,e),t[0]=t[0].substring(0,n).trim(),t[3]=""}}let n=t[2],s="";if(this.options.pedantic){const e=/^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(n);e&&(n=e[1],s=e[3])}else s=t[3]?t[3].slice(1,-1):"";return n=n.trim(),/^</.test(n)&&(n=this.options.pedantic&&!/>$/.test(e)?n.slice(1):n.slice(1,-1)),b(t,{href:n?n.replace(this.rules.inline.anyPunctuation,"$1"):n,title:s?s.replace(this.rules.inline.anyPunctuation,"$1"):s},t[0],this.lexer)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){const e=t[(n[2]||n[1]).replace(/\s+/g," ").toLowerCase()];if(!e){const e=n[0].charAt(0);return{type:"text",raw:e,text:e}}return b(n,e,n[0],this.lexer)}}emStrong(e,t,n=""){let s=this.rules.inline.emStrongLDelim.exec(e);if(!s)return;if(s[3]&&n.match(/[\p{L}\p{N}]/u))return;if(!(s[1]||s[2]||"")||!n||this.rules.inline.punctuation.exec(n)){const n=[...s[0]].length-1;let r,i,l=n,o=0;const a="*"===s[0][0]?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(a.lastIndex=0,t=t.slice(-1*e.length+n);null!=(s=a.exec(t));){if(r=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!r)continue;if(i=[...r].length,s[3]||s[4]){l+=i;continue}if((s[5]||s[6])&&n%3&&!((n+i)%3)){o+=i;continue}if(l-=i,l>0)continue;i=Math.min(i,i+l+o);const t=[...s[0]][0].length,a=e.slice(0,n+s.index+t+i);if(Math.min(n,i)%2){const e=a.slice(1,-1);return{type:"em",raw:a,text:e,tokens:this.lexer.inlineTokens(e)}}const c=a.slice(2,-2);return{type:"strong",raw:a,text:c,tokens:this.lexer.inlineTokens(c)}}}}codespan(e){const t=this.rules.inline.code.exec(e);if(t){let e=t[2].replace(/\n/g," ");const n=/[^ ]/.test(e),s=/^ /.test(e)&&/ $/.test(e);return n&&s&&(e=e.substring(1,e.length-1)),e=c(e,!0),{type:"codespan",raw:t[0],text:e}}}br(e){const t=this.rules.inline.br.exec(e);if(t)return{type:"br",raw:t[0]}}del(e){const t=this.rules.inline.del.exec(e);if(t)return{type:"del",raw:t[0],text:t[2],tokens:this.lexer.inlineTokens(t[2])}}autolink(e){const t=this.rules.inline.autolink.exec(e);if(t){let e,n;return"@"===t[2]?(e=c(t[1]),n="mailto:"+e):(e=c(t[1]),n=e),{type:"link",raw:t[0],text:e,href:n,tokens:[{type:"text",raw:e,text:e}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let e,n;if("@"===t[2])e=c(t[0]),n="mailto:"+e;else{let s;do{s=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??""}while(s!==t[0]);e=c(t[0]),n="www."===t[1]?"http://"+t[0]:t[0]}return{type:"link",raw:t[0],text:e,href:n,tokens:[{type:"text",raw:e,text:e}]}}}inlineText(e){const t=this.rules.inline.text.exec(e);if(t){let e;return e=this.lexer.state.inRawBlock?t[0]:c(t[0]),{type:"text",raw:t[0],text:e}}}}const m=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,y=/(?:[*+-]|\d{1,9}[.)])/,$=k(/^(?!bull |blockCode|fences|blockquote|heading|html)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html))+?)\n {0,3}(=+|-+) *(?:\n+|$)/).replace(/bull/g,y).replace(/blockCode/g,/ {4}/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).getRegex(),z=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,T=/(?!\s*\])(?:\\.|[^\[\]\\])+/,R=k(/^ {0,3}\[(label)\]: *(?:\n *)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n *)?| *\n *)(title))? *(?:\n+|$)/).replace("label",T).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),_=k(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,y).getRegex(),A="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",S=/<!--(?:-?>|[\s\S]*?(?:-->|$))/,I=k("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|<![A-Z][\\s\\S]*?(?:>\\n*|$)|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\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",S).replace("tag",A).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),E=k(z).replace("hr",m).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",A).getRegex(),q={blockquote:k(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",E).getRegex(),code:/^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,def:R,fences:/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,heading:/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,hr:m,html:I,lheading:$,list:_,newline:/^(?: *(?:\n|$))+/,paragraph:E,table:f,text:/^[^\n]+/},Z=k("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",m).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",A).getRegex(),L={...q,table:Z,paragraph:k(z).replace("hr",m).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",Z).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",A).getRegex()},P={...q,html:k("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)|<tag(?:\"[^\"]*\"|'[^']*'|\\s[^'\"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",S).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(),def:/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:f,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:k(z).replace("hr",m).replace("heading"," *#{1,6} *[^\n]").replace("lheading",$).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},Q=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,v=/^( {2,}|\\)\n(?!\s*$)/,B="\\p{P}\\p{S}",C=k(/^((?![*_])[\spunctuation])/,"u").replace(/punctuation/g,B).getRegex(),M=k(/^(?:\*+(?:((?!\*)[punct])|[^\s*]))|^_+(?:((?!_)[punct])|([^\s_]))/,"u").replace(/punct/g,B).getRegex(),O=k("^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)[punct](\\*+)(?=[\\s]|$)|[^punct\\s](\\*+)(?!\\*)(?=[punct\\s]|$)|(?!\\*)[punct\\s](\\*+)(?=[^punct\\s])|[\\s](\\*+)(?!\\*)(?=[punct])|(?!\\*)[punct](\\*+)(?!\\*)(?=[punct])|[^punct\\s](\\*+)(?=[^punct\\s])","gu").replace(/punct/g,B).getRegex(),D=k("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)[punct](_+)(?=[\\s]|$)|[^punct\\s](_+)(?!_)(?=[punct\\s]|$)|(?!_)[punct\\s](_+)(?=[^punct\\s])|[\\s](_+)(?!_)(?=[punct])|(?!_)[punct](_+)(?!_)(?=[punct])","gu").replace(/punct/g,B).getRegex(),j=k(/\\([punct])/,"gu").replace(/punct/g,B).getRegex(),H=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(),U=k(S).replace("(?:--\x3e|$)","--\x3e").getRegex(),X=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",U).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),F=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,N=k(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/).replace("label",F).replace("href",/<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),G=k(/^!?\[(label)\]\[(ref)\]/).replace("label",F).replace("ref",T).getRegex(),J=k(/^!?\[(ref)\](?:\[\])?/).replace("ref",T).getRegex(),K={_backpedal:f,anyPunctuation:j,autolink:H,blockSkip:/\[[^[\]]*?\]\([^\(\)]*?\)|`[^`]*?`|<[^<>]*?>/g,br:v,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,del:f,emStrongLDelim:M,emStrongRDelimAst:O,emStrongRDelimUnd:D,escape:Q,link:N,nolink:J,punctuation:C,reflink:G,reflinkSearch:k("reflink|nolink(?!\\()","g").replace("reflink",G).replace("nolink",J).getRegex(),tag:X,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/,url:f},V={...K,link:k(/^!?\[(label)\]\((.*?)\)/).replace("label",F).getRegex(),reflink:k(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",F).getRegex()},W={...K,escape:k(Q).replace("])","~|])").getRegex(),url:k(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,"i").replace("email",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,text:/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/},Y={...W,br:k(v).replace("{2,}","*").getRegex(),text:k(W.text).replace("\\b_","\\b_| {2,}\\n").replace(/\{2,\}/g,"*").getRegex()},ee={normal:q,gfm:L,pedantic:P},te={normal:K,gfm:W,breaks:Y,pedantic:V};class ne{tokens;options;state;tokenizer;inlineQueue;constructor(t){this.tokens=[],this.tokens.links=Object.create(null),this.options=t||e.defaults,this.options.tokenizer=this.options.tokenizer||new w,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};const n={block:ee.normal,inline:te.normal};this.options.pedantic?(n.block=ee.pedantic,n.inline=te.pedantic):this.options.gfm&&(n.block=ee.gfm,this.options.breaks?n.inline=te.breaks:n.inline=te.gfm),this.tokenizer.rules=n}static get rules(){return{block:ee,inline:te}}static lex(e,t){return new ne(t).lex(e)}static lexInline(e,t){return new ne(t).inlineTokens(e)}lex(e){e=e.replace(/\r\n|\r/g,"\n"),this.blockTokens(e,this.tokens);for(let e=0;e<this.inlineQueue.length;e++){const t=this.inlineQueue[e];this.inlineTokens(t.src,t.tokens)}return this.inlineQueue=[],this.tokens}blockTokens(e,t=[]){let n,s,r,i;for(e=this.options.pedantic?e.replace(/\t/g," ").replace(/^ +$/gm,""):e.replace(/^( *)(\t+)/gm,((e,t,n)=>t+" ".repeat(n.length)));e;)if(!(this.options.extensions&&this.options.extensions.block&&this.options.extensions.block.some((s=>!!(n=s.call({lexer:this},e,t))&&(e=e.substring(n.raw.length),t.push(n),!0)))))if(n=this.tokenizer.space(e))e=e.substring(n.raw.length),1===n.raw.length&&t.length>0?t[t.length-1].raw+="\n":t.push(n);else if(n=this.tokenizer.code(e))e=e.substring(n.raw.length),s=t[t.length-1],!s||"paragraph"!==s.type&&"text"!==s.type?t.push(n):(s.raw+="\n"+n.raw,s.text+="\n"+n.text,this.inlineQueue[this.inlineQueue.length-1].src=s.text);else if(n=this.tokenizer.fences(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.heading(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.hr(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.blockquote(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.list(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.html(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.def(e))e=e.substring(n.raw.length),s=t[t.length-1],!s||"paragraph"!==s.type&&"text"!==s.type?this.tokens.links[n.tag]||(this.tokens.links[n.tag]={href:n.href,title:n.title}):(s.raw+="\n"+n.raw,s.text+="\n"+n.raw,this.inlineQueue[this.inlineQueue.length-1].src=s.text);else if(n=this.tokenizer.table(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.lheading(e))e=e.substring(n.raw.length),t.push(n);else{if(r=e,this.options.extensions&&this.options.extensions.startBlock){let t=1/0;const n=e.slice(1);let s;this.options.extensions.startBlock.forEach((e=>{s=e.call({lexer:this},n),"number"==typeof s&&s>=0&&(t=Math.min(t,s))})),t<1/0&&t>=0&&(r=e.substring(0,t+1))}if(this.state.top&&(n=this.tokenizer.paragraph(r)))s=t[t.length-1],i&&"paragraph"===s.type?(s.raw+="\n"+n.raw,s.text+="\n"+n.text,this.inlineQueue.pop(),this.inlineQueue[this.inlineQueue.length-1].src=s.text):t.push(n),i=r.length!==e.length,e=e.substring(n.raw.length);else if(n=this.tokenizer.text(e))e=e.substring(n.raw.length),s=t[t.length-1],s&&"text"===s.type?(s.raw+="\n"+n.raw,s.text+="\n"+n.text,this.inlineQueue.pop(),this.inlineQueue[this.inlineQueue.length-1].src=s.text):t.push(n);else if(e){const t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}inlineTokens(e,t=[]){let n,s,r,i,l,o,a=e;if(this.tokens.links){const e=Object.keys(this.tokens.links);if(e.length>0)for(;null!=(i=this.tokenizer.rules.inline.reflinkSearch.exec(a));)e.includes(i[0].slice(i[0].lastIndexOf("[")+1,-1))&&(a=a.slice(0,i.index)+"["+"a".repeat(i[0].length-2)+"]"+a.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;null!=(i=this.tokenizer.rules.inline.blockSkip.exec(a));)a=a.slice(0,i.index)+"["+"a".repeat(i[0].length-2)+"]"+a.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);for(;null!=(i=this.tokenizer.rules.inline.anyPunctuation.exec(a));)a=a.slice(0,i.index)+"++"+a.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);for(;e;)if(l||(o=""),l=!1,!(this.options.extensions&&this.options.extensions.inline&&this.options.extensions.inline.some((s=>!!(n=s.call({lexer:this},e,t))&&(e=e.substring(n.raw.length),t.push(n),!0)))))if(n=this.tokenizer.escape(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.tag(e))e=e.substring(n.raw.length),s=t[t.length-1],s&&"text"===n.type&&"text"===s.type?(s.raw+=n.raw,s.text+=n.text):t.push(n);else if(n=this.tokenizer.link(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.reflink(e,this.tokens.links))e=e.substring(n.raw.length),s=t[t.length-1],s&&"text"===n.type&&"text"===s.type?(s.raw+=n.raw,s.text+=n.text):t.push(n);else if(n=this.tokenizer.emStrong(e,a,o))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.codespan(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.br(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.del(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.autolink(e))e=e.substring(n.raw.length),t.push(n);else if(this.state.inLink||!(n=this.tokenizer.url(e))){if(r=e,this.options.extensions&&this.options.extensions.startInline){let t=1/0;const n=e.slice(1);let s;this.options.extensions.startInline.forEach((e=>{s=e.call({lexer:this},n),"number"==typeof s&&s>=0&&(t=Math.min(t,s))})),t<1/0&&t>=0&&(r=e.substring(0,t+1))}if(n=this.tokenizer.inlineText(r))e=e.substring(n.raw.length),"_"!==n.raw.slice(-1)&&(o=n.raw.slice(-1)),l=!0,s=t[t.length-1],s&&"text"===s.type?(s.raw+=n.raw,s.text+=n.text):t.push(n);else if(e){const t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}else e=e.substring(n.raw.length),t.push(n);return t}}class se{options;constructor(t){this.options=t||e.defaults}code(e,t,n){const s=(t||"").match(/^\S*/)?.[0];return e=e.replace(/\n$/,"")+"\n",s?'<pre><code class="language-'+c(s)+'">'+(n?e:c(e,!0))+"</code></pre>\n":"<pre><code>"+(n?e:c(e,!0))+"</code></pre>\n"}blockquote(e){return`<blockquote>\n${e}</blockquote>\n`}html(e,t){return e}heading(e,t,n){return`<h${t}>${e}</h${t}>\n`}hr(){return"<hr>\n"}list(e,t,n){const s=t?"ol":"ul";return"<"+s+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+"</"+s+">\n"}listitem(e,t,n){return`<li>${e}</li>\n`}checkbox(e){return"<input "+(e?'checked="" ':"")+'disabled="" type="checkbox">'}paragraph(e){return`<p>${e}</p>\n`}table(e,t){return t&&(t=`<tbody>${t}</tbody>`),"<table>\n<thead>\n"+e+"</thead>\n"+t+"</table>\n"}tablerow(e){return`<tr>\n${e}</tr>\n`}tablecell(e,t){const n=t.header?"th":"td";return(t.align?`<${n} align="${t.align}">`:`<${n}>`)+e+`</${n}>\n`}strong(e){return`<strong>${e}</strong>`}em(e){return`<em>${e}</em>`}codespan(e){return`<code>${e}</code>`}br(){return"<br>"}del(e){return`<del>${e}</del>`}link(e,t,n){const s=g(e);if(null===s)return n;let r='<a href="'+(e=s)+'"';return t&&(r+=' title="'+t+'"'),r+=">"+n+"</a>",r}image(e,t,n){const s=g(e);if(null===s)return n;let r=`<img src="${e=s}" alt="${n}"`;return t&&(r+=` title="${t}"`),r+=">",r}text(e){return e}}class re{strong(e){return e}em(e){return e}codespan(e){return e}del(e){return e}html(e){return e}text(e){return e}link(e,t,n){return""+n}image(e,t,n){return""+n}br(){return""}}class ie{options;renderer;textRenderer;constructor(t){this.options=t||e.defaults,this.options.renderer=this.options.renderer||new se,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new re}static parse(e,t){return new ie(t).parse(e)}static parseInline(e,t){return new ie(t).parseInline(e)}parse(e,t=!0){let n="";for(let s=0;s<e.length;s++){const r=e[s];if(this.options.extensions&&this.options.extensions.renderers&&this.options.extensions.renderers[r.type]){const e=r,t=this.options.extensions.renderers[e.type].call({parser:this},e);if(!1!==t||!["space","hr","heading","code","table","blockquote","list","html","paragraph","text"].includes(e.type)){n+=t||"";continue}}switch(r.type){case"space":continue;case"hr":n+=this.renderer.hr();continue;case"heading":{const e=r;n+=this.renderer.heading(this.parseInline(e.tokens),e.depth,p(this.parseInline(e.tokens,this.textRenderer)));continue}case"code":{const e=r;n+=this.renderer.code(e.text,e.lang,!!e.escaped);continue}case"table":{const e=r;let t="",s="";for(let t=0;t<e.header.length;t++)s+=this.renderer.tablecell(this.parseInline(e.header[t].tokens),{header:!0,align:e.align[t]});t+=this.renderer.tablerow(s);let i="";for(let t=0;t<e.rows.length;t++){const n=e.rows[t];s="";for(let t=0;t<n.length;t++)s+=this.renderer.tablecell(this.parseInline(n[t].tokens),{header:!1,align:e.align[t]});i+=this.renderer.tablerow(s)}n+=this.renderer.table(t,i);continue}case"blockquote":{const e=r,t=this.parse(e.tokens);n+=this.renderer.blockquote(t);continue}case"list":{const e=r,t=e.ordered,s=e.start,i=e.loose;let l="";for(let t=0;t<e.items.length;t++){const n=e.items[t],s=n.checked,r=n.task;let o="";if(n.task){const e=this.renderer.checkbox(!!s);i?n.tokens.length>0&&"paragraph"===n.tokens[0].type?(n.tokens[0].text=e+" "+n.tokens[0].text,n.tokens[0].tokens&&n.tokens[0].tokens.length>0&&"text"===n.tokens[0].tokens[0].type&&(n.tokens[0].tokens[0].text=e+" "+n.tokens[0].tokens[0].text)):n.tokens.unshift({type:"text",text:e+" "}):o+=e+" "}o+=this.parse(n.tokens,i),l+=this.renderer.listitem(o,r,!!s)}n+=this.renderer.list(l,t,s);continue}case"html":{const e=r;n+=this.renderer.html(e.text,e.block);continue}case"paragraph":{const e=r;n+=this.renderer.paragraph(this.parseInline(e.tokens));continue}case"text":{let i=r,l=i.tokens?this.parseInline(i.tokens):i.text;for(;s+1<e.length&&"text"===e[s+1].type;)i=e[++s],l+="\n"+(i.tokens?this.parseInline(i.tokens):i.text);n+=t?this.renderer.paragraph(l):l;continue}default:{const e='Token with "'+r.type+'" type was not found.';if(this.options.silent)return console.error(e),"";throw new Error(e)}}}return n}parseInline(e,t){t=t||this.renderer;let n="";for(let s=0;s<e.length;s++){const r=e[s];if(this.options.extensions&&this.options.extensions.renderers&&this.options.extensions.renderers[r.type]){const e=this.options.extensions.renderers[r.type].call({parser:this},r);if(!1!==e||!["escape","html","link","image","strong","em","codespan","br","del","text"].includes(r.type)){n+=e||"";continue}}switch(r.type){case"escape":{const e=r;n+=t.text(e.text);break}case"html":{const e=r;n+=t.html(e.text);break}case"link":{const e=r;n+=t.link(e.href,e.title,this.parseInline(e.tokens,t));break}case"image":{const e=r;n+=t.image(e.href,e.title,e.text);break}case"strong":{const e=r;n+=t.strong(this.parseInline(e.tokens,t));break}case"em":{const e=r;n+=t.em(this.parseInline(e.tokens,t));break}case"codespan":{const e=r;n+=t.codespan(e.text);break}case"br":n+=t.br();break;case"del":{const e=r;n+=t.del(this.parseInline(e.tokens,t));break}case"text":{const e=r;n+=t.text(e.text);break}default:{const e='Token with "'+r.type+'" type was not found.';if(this.options.silent)return console.error(e),"";throw new Error(e)}}}return n}}class le{options;constructor(t){this.options=t||e.defaults}static passThroughHooks=new Set(["preprocess","postprocess","processAllTokens"]);preprocess(e){return e}postprocess(e){return e}processAllTokens(e){return e}}class oe{defaults={async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null};options=this.setOptions;parse=this.#e(ne.lex,ie.parse);parseInline=this.#e(ne.lexInline,ie.parseInline);Parser=ie;Renderer=se;TextRenderer=re;Lexer=ne;Tokenizer=w;Hooks=le;constructor(...e){this.use(...e)}walkTokens(e,t){let n=[];for(const s of e)switch(n=n.concat(t.call(this,s)),s.type){case"table":{const e=s;for(const s of e.header)n=n.concat(this.walkTokens(s.tokens,t));for(const s of e.rows)for(const e of s)n=n.concat(this.walkTokens(e.tokens,t));break}case"list":{const e=s;n=n.concat(this.walkTokens(e.items,t));break}default:{const e=s;this.defaults.extensions?.childTokens?.[e.type]?this.defaults.extensions.childTokens[e.type].forEach((s=>{const r=e[s].flat(1/0);n=n.concat(this.walkTokens(r,t))})):e.tokens&&(n=n.concat(this.walkTokens(e.tokens,t)))}}return n}use(...e){const t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach((e=>{const n={...e};if(n.async=this.defaults.async||n.async||!1,e.extensions&&(e.extensions.forEach((e=>{if(!e.name)throw new Error("extension name required");if("renderer"in e){const n=t.renderers[e.name];t.renderers[e.name]=n?function(...t){let s=e.renderer.apply(this,t);return!1===s&&(s=n.apply(this,t)),s}:e.renderer}if("tokenizer"in e){if(!e.level||"block"!==e.level&&"inline"!==e.level)throw new Error("extension level must be 'block' or 'inline'");const n=t[e.level];n?n.unshift(e.tokenizer):t[e.level]=[e.tokenizer],e.start&&("block"===e.level?t.startBlock?t.startBlock.push(e.start):t.startBlock=[e.start]:"inline"===e.level&&(t.startInline?t.startInline.push(e.start):t.startInline=[e.start]))}"childTokens"in e&&e.childTokens&&(t.childTokens[e.name]=e.childTokens)})),n.extensions=t),e.renderer){const t=this.defaults.renderer||new se(this.defaults);for(const n in e.renderer){if(!(n in t))throw new Error(`renderer '${n}' does not exist`);if("options"===n)continue;const s=n,r=e.renderer[s],i=t[s];t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n||""}}n.renderer=t}if(e.tokenizer){const t=this.defaults.tokenizer||new w(this.defaults);for(const n in e.tokenizer){if(!(n in t))throw new Error(`tokenizer '${n}' does not exist`);if(["options","rules","lexer"].includes(n))continue;const s=n,r=e.tokenizer[s],i=t[s];t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n}}n.tokenizer=t}if(e.hooks){const t=this.defaults.hooks||new le;for(const n in e.hooks){if(!(n in t))throw new Error(`hook '${n}' does not exist`);if("options"===n)continue;const s=n,r=e.hooks[s],i=t[s];le.passThroughHooks.has(n)?t[s]=e=>{if(this.defaults.async)return Promise.resolve(r.call(t,e)).then((e=>i.call(t,e)));const n=r.call(t,e);return i.call(t,n)}:t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n}}n.hooks=t}if(e.walkTokens){const t=this.defaults.walkTokens,s=e.walkTokens;n.walkTokens=function(e){let n=[];return n.push(s.call(this,e)),t&&(n=n.concat(t.call(this,e))),n}}this.defaults={...this.defaults,...n}})),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return ne.lex(e,t??this.defaults)}parser(e,t){return ie.parse(e,t??this.defaults)}#e(e,t){return(n,s)=>{const r={...s},i={...this.defaults,...r};!0===this.defaults.async&&!1===r.async&&(i.silent||console.warn("marked(): The async option was set to true by an extension. The async: false option sent to parse will be ignored."),i.async=!0);const l=this.#t(!!i.silent,!!i.async);if(null==n)return l(new Error("marked(): input parameter is undefined or null"));if("string"!=typeof n)return l(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(n)+", string expected"));if(i.hooks&&(i.hooks.options=i),i.async)return Promise.resolve(i.hooks?i.hooks.preprocess(n):n).then((t=>e(t,i))).then((e=>i.hooks?i.hooks.processAllTokens(e):e)).then((e=>i.walkTokens?Promise.all(this.walkTokens(e,i.walkTokens)).then((()=>e)):e)).then((e=>t(e,i))).then((e=>i.hooks?i.hooks.postprocess(e):e)).catch(l);try{i.hooks&&(n=i.hooks.preprocess(n));let s=e(n,i);i.hooks&&(s=i.hooks.processAllTokens(s)),i.walkTokens&&this.walkTokens(s,i.walkTokens);let r=t(s,i);return i.hooks&&(r=i.hooks.postprocess(r)),r}catch(e){return l(e)}}}#t(e,t){return n=>{if(n.message+="\nPlease report this to https://github.com/markedjs/marked.",e){const e="<p>An error occurred:</p><pre>"+c(n.message+"",!0)+"</pre>";return t?Promise.resolve(e):e}if(t)return Promise.reject(n);throw n}}}const ae=new oe;function ce(e,t){return ae.parse(e,t)}ce.options=ce.setOptions=function(e){return ae.setOptions(e),ce.defaults=ae.defaults,n(ce.defaults),ce},ce.getDefaults=t,ce.defaults=e.defaults,ce.use=function(...e){return ae.use(...e),ce.defaults=ae.defaults,n(ce.defaults),ce},ce.walkTokens=function(e,t){return ae.walkTokens(e,t)},ce.parseInline=ae.parseInline,ce.Parser=ie,ce.parser=ie.parse,ce.Renderer=se,ce.TextRenderer=re,ce.Lexer=ne,ce.lexer=ne.lex,ce.Tokenizer=w,ce.Hooks=le,ce.parse=ce;const he=ce.options,pe=ce.setOptions,ue=ce.use,ke=ce.walkTokens,ge=ce.parseInline,fe=ce,de=ie.parse,xe=ne.lex;e.Hooks=le,e.Lexer=ne,e.Marked=oe,e.Parser=ie,e.Renderer=se,e.TextRenderer=re,e.Tokenizer=w,e.getDefaults=t,e.lexer=xe,e.marked=ce,e.options=he,e.parse=fe,e.parseInline=ge,e.parser=de,e.setOptions=pe,e.use=ue,e.walkTokens=ke}));
|