@qlingzzy/qling 1.0.0 → 1.1.1

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.
Files changed (254) hide show
  1. package/CHANGELOG.md +158 -0
  2. package/README.en.md +83 -18
  3. package/README.md +19 -9
  4. package/dist/agent/main-loop.d.ts +120 -0
  5. package/dist/agent/main-loop.d.ts.map +1 -0
  6. package/dist/agent/main-loop.js +279 -0
  7. package/dist/agent/subtask-parallel.d.ts +29 -0
  8. package/dist/agent/subtask-parallel.d.ts.map +1 -0
  9. package/dist/agent/subtask-parallel.js +107 -0
  10. package/dist/agent/subtask.d.ts +13 -0
  11. package/dist/agent/subtask.d.ts.map +1 -1
  12. package/dist/agent/subtask.js +79 -24
  13. package/dist/agent/system-prompt.d.ts +39 -0
  14. package/dist/agent/system-prompt.d.ts.map +1 -0
  15. package/dist/agent/system-prompt.js +89 -0
  16. package/dist/agent/tool-orchestrator.d.ts +66 -0
  17. package/dist/agent/tool-orchestrator.d.ts.map +1 -0
  18. package/dist/agent/tool-orchestrator.js +355 -0
  19. package/dist/agent-loop.d.ts +29 -16
  20. package/dist/agent-loop.d.ts.map +1 -1
  21. package/dist/agent-loop.js +262 -807
  22. package/dist/agents/roles.d.ts +57 -0
  23. package/dist/agents/roles.d.ts.map +1 -0
  24. package/dist/agents/roles.js +198 -0
  25. package/dist/commands/agents.d.ts.map +1 -1
  26. package/dist/commands/agents.js +17 -3
  27. package/dist/commands/claude-style.d.ts.map +1 -1
  28. package/dist/commands/claude-style.js +19 -10
  29. package/dist/commands/help.d.ts.map +1 -1
  30. package/dist/commands/help.js +3 -2
  31. package/dist/commands/index.d.ts.map +1 -1
  32. package/dist/commands/index.js +6 -0
  33. package/dist/commands/mode.d.ts +16 -0
  34. package/dist/commands/mode.d.ts.map +1 -0
  35. package/dist/commands/mode.js +67 -0
  36. package/dist/commands/recover.d.ts +3 -0
  37. package/dist/commands/recover.d.ts.map +1 -0
  38. package/dist/commands/recover.js +69 -0
  39. package/dist/commands/runtime.d.ts +2 -0
  40. package/dist/commands/runtime.d.ts.map +1 -1
  41. package/dist/commands/runtime.js +2 -0
  42. package/dist/commands/trace.d.ts +3 -0
  43. package/dist/commands/trace.d.ts.map +1 -0
  44. package/dist/commands/trace.js +45 -0
  45. package/dist/commands/verify.js +2 -2
  46. package/dist/config-report.d.ts +0 -1
  47. package/dist/config-report.d.ts.map +1 -1
  48. package/dist/config-report.js +1 -2
  49. package/dist/config.d.ts +0 -1
  50. package/dist/config.d.ts.map +1 -1
  51. package/dist/config.js +0 -1
  52. package/dist/context-report.d.ts +13 -7
  53. package/dist/context-report.d.ts.map +1 -1
  54. package/dist/context-report.js +64 -61
  55. package/dist/context-tool-hygiene.d.ts +36 -0
  56. package/dist/context-tool-hygiene.d.ts.map +1 -0
  57. package/dist/context-tool-hygiene.js +125 -0
  58. package/dist/dashboard/client.d.ts +2 -0
  59. package/dist/dashboard/client.d.ts.map +1 -0
  60. package/dist/dashboard/client.js +305 -0
  61. package/dist/dashboard/model.d.ts +13 -0
  62. package/dist/dashboard/model.d.ts.map +1 -0
  63. package/dist/dashboard/model.js +112 -0
  64. package/dist/dashboard/page.d.ts +3 -0
  65. package/dist/dashboard/page.d.ts.map +1 -0
  66. package/dist/dashboard/page.js +204 -0
  67. package/dist/dashboard/types.d.ts +63 -0
  68. package/dist/dashboard/types.d.ts.map +1 -0
  69. package/dist/dashboard/types.js +1 -0
  70. package/dist/dashboard-server.d.ts +24 -4
  71. package/dist/dashboard-server.d.ts.map +1 -1
  72. package/dist/dashboard-server.js +397 -302
  73. package/dist/discovery-registry.d.ts +17 -3
  74. package/dist/discovery-registry.d.ts.map +1 -1
  75. package/dist/discovery-registry.js +78 -3
  76. package/dist/doctor.d.ts +8 -0
  77. package/dist/doctor.d.ts.map +1 -1
  78. package/dist/doctor.js +116 -0
  79. package/dist/eval/llm-tasks.d.ts +5 -0
  80. package/dist/eval/llm-tasks.d.ts.map +1 -0
  81. package/dist/eval/llm-tasks.js +125 -0
  82. package/dist/eval/repo-tasks.d.ts +12 -0
  83. package/dist/eval/repo-tasks.d.ts.map +1 -0
  84. package/dist/eval/repo-tasks.js +220 -0
  85. package/dist/eval/runner.d.ts +3 -1
  86. package/dist/eval/runner.d.ts.map +1 -1
  87. package/dist/eval/runner.js +23 -4
  88. package/dist/eval/tasks.d.ts.map +1 -1
  89. package/dist/eval/tasks.js +186 -0
  90. package/dist/eval/types.d.ts +8 -5
  91. package/dist/eval/types.d.ts.map +1 -1
  92. package/dist/execution/event-bus.d.ts +44 -0
  93. package/dist/execution/event-bus.d.ts.map +1 -0
  94. package/dist/execution/event-bus.js +76 -0
  95. package/dist/execution/failure-classifier.d.ts +11 -0
  96. package/dist/execution/failure-classifier.d.ts.map +1 -0
  97. package/dist/execution/failure-classifier.js +66 -0
  98. package/dist/execution/progress-detector.d.ts +3 -0
  99. package/dist/execution/progress-detector.d.ts.map +1 -0
  100. package/dist/execution/progress-detector.js +23 -0
  101. package/dist/execution/recovery-controller.d.ts +24 -0
  102. package/dist/execution/recovery-controller.d.ts.map +1 -0
  103. package/dist/execution/recovery-controller.js +114 -0
  104. package/dist/execution/recovery-messages.d.ts +24 -0
  105. package/dist/execution/recovery-messages.d.ts.map +1 -0
  106. package/dist/execution/recovery-messages.js +48 -0
  107. package/dist/execution/recovery-metrics.d.ts +16 -0
  108. package/dist/execution/recovery-metrics.d.ts.map +1 -0
  109. package/dist/execution/recovery-metrics.js +73 -0
  110. package/dist/execution/recovery-strategy-planner.d.ts +6 -0
  111. package/dist/execution/recovery-strategy-planner.d.ts.map +1 -0
  112. package/dist/execution/recovery-strategy-planner.js +24 -0
  113. package/dist/execution/run-trace-store.d.ts +29 -0
  114. package/dist/execution/run-trace-store.d.ts.map +1 -0
  115. package/dist/execution/run-trace-store.js +140 -0
  116. package/dist/execution/staged-verifier.d.ts +33 -0
  117. package/dist/execution/staged-verifier.d.ts.map +1 -0
  118. package/dist/execution/staged-verifier.js +39 -0
  119. package/dist/execution/types.d.ts +58 -0
  120. package/dist/execution/types.d.ts.map +1 -0
  121. package/dist/execution/types.js +1 -0
  122. package/dist/execution/verification-loop.d.ts +58 -0
  123. package/dist/execution/verification-loop.d.ts.map +1 -0
  124. package/dist/execution/verification-loop.js +195 -0
  125. package/dist/execution/verification-stages.d.ts +17 -0
  126. package/dist/execution/verification-stages.d.ts.map +1 -0
  127. package/dist/execution/verification-stages.js +102 -0
  128. package/dist/help-topics.d.ts.map +1 -1
  129. package/dist/help-topics.js +11 -0
  130. package/dist/index.js +0 -8
  131. package/dist/lsp/ts-service.d.ts +36 -0
  132. package/dist/lsp/ts-service.d.ts.map +1 -0
  133. package/dist/lsp/ts-service.js +291 -0
  134. package/dist/memory/lifecycle.d.ts +19 -0
  135. package/dist/memory/lifecycle.d.ts.map +1 -0
  136. package/dist/memory/lifecycle.js +53 -0
  137. package/dist/memory.d.ts +0 -16
  138. package/dist/memory.d.ts.map +1 -1
  139. package/dist/memory.js +0 -36
  140. package/dist/metrics/collector.d.ts +7 -0
  141. package/dist/metrics/collector.d.ts.map +1 -1
  142. package/dist/metrics/collector.js +60 -0
  143. package/dist/mission/manager.d.ts +1 -0
  144. package/dist/mission/manager.d.ts.map +1 -1
  145. package/dist/mission/manager.js +9 -0
  146. package/dist/mission/progress-notify.d.ts +39 -0
  147. package/dist/mission/progress-notify.d.ts.map +1 -0
  148. package/dist/mission/progress-notify.js +386 -0
  149. package/dist/pipeline/hooks.d.ts +1 -1
  150. package/dist/pipeline/hooks.d.ts.map +1 -1
  151. package/dist/pipeline/hooks.js +1 -0
  152. package/dist/pipeline/sections.d.ts +8 -4
  153. package/dist/pipeline/sections.d.ts.map +1 -1
  154. package/dist/pipeline/sections.js +70 -27
  155. package/dist/pipeline/verification.d.ts +1 -0
  156. package/dist/pipeline/verification.d.ts.map +1 -1
  157. package/dist/pipeline/verification.js +10 -7
  158. package/dist/providers/llm-client.d.ts +38 -0
  159. package/dist/providers/llm-client.d.ts.map +1 -0
  160. package/dist/providers/llm-client.js +105 -0
  161. package/dist/sdk.d.ts +1 -0
  162. package/dist/sdk.d.ts.map +1 -1
  163. package/dist/sdk.js +1 -0
  164. package/dist/session/durable-session-supervisor.d.ts.map +1 -1
  165. package/dist/session/durable-session-supervisor.js +13 -6
  166. package/dist/session/session-persistence.d.ts +29 -0
  167. package/dist/session/session-persistence.d.ts.map +1 -0
  168. package/dist/session/session-persistence.js +45 -0
  169. package/dist/session/session-scheduler.d.ts +8 -1
  170. package/dist/session/session-scheduler.d.ts.map +1 -1
  171. package/dist/session/session-scheduler.js +33 -4
  172. package/dist/session-task-report.d.ts +1 -0
  173. package/dist/session-task-report.d.ts.map +1 -1
  174. package/dist/session-task-report.js +2 -1
  175. package/dist/shortcuts.d.ts.map +1 -1
  176. package/dist/shortcuts.js +1 -0
  177. package/dist/skills/registry.d.ts.map +1 -1
  178. package/dist/skills/registry.js +10 -0
  179. package/dist/skills/security-scan.d.ts +15 -0
  180. package/dist/skills/security-scan.d.ts.map +1 -0
  181. package/dist/skills/security-scan.js +95 -0
  182. package/dist/skills/types.d.ts +2 -0
  183. package/dist/skills/types.d.ts.map +1 -1
  184. package/dist/statusline.d.ts +3 -3
  185. package/dist/statusline.d.ts.map +1 -1
  186. package/dist/statusline.js +9 -17
  187. package/dist/token-usage.d.ts +27 -0
  188. package/dist/token-usage.d.ts.map +1 -0
  189. package/dist/token-usage.js +107 -0
  190. package/dist/tools/bash.d.ts.map +1 -1
  191. package/dist/tools/bash.js +5 -3
  192. package/dist/tools/browser-act-session.d.ts +48 -0
  193. package/dist/tools/browser-act-session.d.ts.map +1 -0
  194. package/dist/tools/browser-act-session.js +168 -0
  195. package/dist/tools/browser-act.d.ts +14 -0
  196. package/dist/tools/browser-act.d.ts.map +1 -0
  197. package/dist/tools/browser-act.js +297 -0
  198. package/dist/tools/code-symbols.d.ts +35 -0
  199. package/dist/tools/code-symbols.d.ts.map +1 -0
  200. package/dist/tools/code-symbols.js +257 -0
  201. package/dist/tools/index.d.ts +4 -1
  202. package/dist/tools/index.d.ts.map +1 -1
  203. package/dist/tools/index.js +22 -2
  204. package/dist/tools/lsp.d.ts +13 -0
  205. package/dist/tools/lsp.d.ts.map +1 -0
  206. package/dist/tools/lsp.js +136 -0
  207. package/dist/tools/patch.d.ts +5 -0
  208. package/dist/tools/patch.d.ts.map +1 -1
  209. package/dist/tools/patch.js +25 -4
  210. package/dist/tools/search.d.ts +7 -0
  211. package/dist/tools/search.d.ts.map +1 -1
  212. package/dist/tools/search.js +20 -3
  213. package/dist/tools/skill.d.ts +2 -0
  214. package/dist/tools/skill.d.ts.map +1 -1
  215. package/dist/tools/skill.js +47 -14
  216. package/dist/tools/subtask.d.ts +3 -1
  217. package/dist/tools/subtask.d.ts.map +1 -1
  218. package/dist/tools/subtask.js +151 -56
  219. package/dist/tui/shell.d.ts +12 -0
  220. package/dist/tui/shell.d.ts.map +1 -1
  221. package/dist/tui/shell.js +36 -3
  222. package/dist/tui/streaming-repl.d.ts +1 -0
  223. package/dist/tui/streaming-repl.d.ts.map +1 -1
  224. package/dist/tui/streaming-repl.js +27 -4
  225. package/dist/tui/streaming-tui.d.ts +10 -0
  226. package/dist/tui/streaming-tui.d.ts.map +1 -1
  227. package/dist/tui/streaming-tui.js +70 -12
  228. package/dist/types.d.ts +0 -8
  229. package/dist/types.d.ts.map +1 -1
  230. package/dist/workflow-runtime.d.ts.map +1 -1
  231. package/dist/workflow-runtime.js +10 -2
  232. package/dist/workflow-types.d.ts +2 -0
  233. package/dist/workflow-types.d.ts.map +1 -1
  234. package/docs/install.md +46 -12
  235. package/docs/skills.md +56 -7
  236. package/docs/web-routing.md +83 -0
  237. package/package.json +18 -4
  238. package/scripts/dep-layers.mjs +275 -0
  239. package/scripts/eval-llm.mjs +27 -0
  240. package/scripts/eval-recovery.mjs +101 -0
  241. package/scripts/eval-tasks.mjs +20 -0
  242. package/scripts/validate-packaging.mjs +57 -0
  243. package/skills/examples/README.md +23 -0
  244. package/skills/examples/add-function/SKILL.md +31 -0
  245. package/skills/examples/fix-failing-test/SKILL.md +32 -0
  246. package/skills/examples/pr-summary/SKILL.md +34 -0
  247. package/skills/lifecycle-build/SKILL.md +22 -0
  248. package/skills/lifecycle-plan/SKILL.md +32 -0
  249. package/skills/lifecycle-review/SKILL.md +25 -0
  250. package/skills/lifecycle-ship/SKILL.md +22 -0
  251. package/skills/lifecycle-spec/SKILL.md +24 -0
  252. package/skills/lifecycle-test/SKILL.md +21 -0
  253. package/skills/opencli/SKILL.md +222 -0
  254. package/skills/qling.md +14 -3
@@ -0,0 +1,305 @@
1
+ const ACTIVE = new Set(["running", "blocked", "queued", "paused"]);
2
+ let snapshot = null;
3
+ let selectedId = "";
4
+ let selectedKind = "";
5
+ let kindFilter = "all";
6
+ let statusFilter = "all";
7
+ let search = "";
8
+ let etag = "";
9
+ let activeRequest = null;
10
+ let refreshTimer = null;
11
+ let retryDelay = 3000;
12
+ let toastTimer = null;
13
+ function byId(id) {
14
+ const node = document.getElementById(id);
15
+ if (!node)
16
+ throw new Error(`dashboard element missing: ${id}`);
17
+ return node;
18
+ }
19
+ function node(tag, className, text) {
20
+ const el = document.createElement(tag);
21
+ if (className)
22
+ el.className = className;
23
+ if (text !== undefined)
24
+ el.textContent = text;
25
+ return el;
26
+ }
27
+ function formatTime(value) {
28
+ if (!value)
29
+ return "—";
30
+ return new Intl.DateTimeFormat("zh-CN", {
31
+ hour: "2-digit", minute: "2-digit", second: "2-digit",
32
+ }).format(value);
33
+ }
34
+ function relativeTime(value) {
35
+ if (!value)
36
+ return "—";
37
+ const seconds = Math.max(0, Math.floor((Date.now() - value) / 1000));
38
+ if (seconds < 60)
39
+ return `${seconds}s 前`;
40
+ if (seconds < 3600)
41
+ return `${Math.floor(seconds / 60)}m 前`;
42
+ if (seconds < 86400)
43
+ return `${Math.floor(seconds / 3600)}h 前`;
44
+ return `${Math.floor(seconds / 86400)}d 前`;
45
+ }
46
+ function statusLabel(status) {
47
+ return { running: "运行中", queued: "等待", blocked: "阻塞", paused: "暂停", succeeded: "完成", failed: "失败", canceled: "取消" }[status] ?? status;
48
+ }
49
+ function kindLabel(kind) {
50
+ return { mission: "MISSION", loop: "LOOP", workflow: "WORKFLOW" }[kind] ?? kind;
51
+ }
52
+ function filteredTasks() {
53
+ if (!snapshot)
54
+ return [];
55
+ const query = search.toLowerCase();
56
+ return snapshot.tasks.filter((task) => {
57
+ if (kindFilter !== "all" && task.kind !== kindFilter)
58
+ return false;
59
+ if (statusFilter === "active" && !ACTIVE.has(task.status))
60
+ return false;
61
+ if (statusFilter === "failed" && task.status !== "failed")
62
+ return false;
63
+ return !query || `${task.title} ${task.description} ${task.id} ${task.sessionId ?? ""}`.toLowerCase().includes(query);
64
+ });
65
+ }
66
+ function renderSummary() {
67
+ if (!snapshot)
68
+ return;
69
+ byId("summary-total").textContent = String(snapshot.summary.total);
70
+ byId("summary-running").textContent = String(snapshot.summary.running);
71
+ byId("summary-queued").textContent = String(snapshot.summary.queued + snapshot.summary.paused);
72
+ byId("summary-blocked").textContent = String(snapshot.summary.blocked);
73
+ byId("summary-failed").textContent = String(snapshot.summary.failed);
74
+ byId("updated-at").textContent = `同步于 ${formatTime(snapshot.generatedAt)}`;
75
+ byId("source-label").textContent = snapshot.runtime.daemonHealthy ? "DAEMON" : "LOCAL";
76
+ byId("runtime-label").textContent = snapshot.runtime.ready ? "运行时就绪" : "等待运行时";
77
+ byId("runtime-signal").classList.toggle("ready", snapshot.runtime.ready);
78
+ byId("permission-label").textContent = `权限 ${snapshot.runtime.permissionMode}`;
79
+ }
80
+ function renderTasks() {
81
+ const list = byId("task-list");
82
+ list.replaceChildren();
83
+ list.classList.remove("skeleton-stack");
84
+ list.setAttribute("aria-busy", "false");
85
+ const tasks = filteredTasks();
86
+ byId("visible-count").textContent = `${tasks.length} 项`;
87
+ if (tasks.length === 0) {
88
+ list.append(node("div", "empty-copy", "当前筛选下没有任务。可在 TUI 使用 /mission 或 /loop 创建。"));
89
+ return;
90
+ }
91
+ for (const task of tasks) {
92
+ const row = node("button", `task-row${task.id === selectedId ? " selected" : ""}`);
93
+ row.type = "button";
94
+ row.dataset.taskId = task.id;
95
+ row.dataset.taskKind = task.kind;
96
+ row.setAttribute("aria-pressed", String(task.id === selectedId));
97
+ const line = node("span", `status-line ${task.status}`);
98
+ line.setAttribute("aria-hidden", "true");
99
+ const copy = node("span", "task-copy");
100
+ const meta = node("span", "task-meta");
101
+ meta.append(node("span", "", kindLabel(task.kind)), node("span", "", statusLabel(task.status)), node("span", "", task.source.toUpperCase()));
102
+ copy.append(meta, node("span", "task-title", task.title), node("span", "task-description", task.description || "无补充说明"));
103
+ row.append(line, copy, node("time", "task-time", relativeTime(task.updatedAt)));
104
+ row.addEventListener("click", () => void selectTask(task));
105
+ list.append(row);
106
+ }
107
+ }
108
+ function renderActivity() {
109
+ const list = byId("activity-list");
110
+ list.replaceChildren();
111
+ const activity = snapshot?.activity ?? [];
112
+ if (activity.length === 0) {
113
+ list.append(node("li", "muted", "暂无本地活动指标"));
114
+ return;
115
+ }
116
+ for (const event of activity) {
117
+ const item = node("li");
118
+ item.append(node("time", "", formatTime(event.ts)), node("span", "activity-type", event.type), node("span", "", summarizeData(event.data)));
119
+ list.append(item);
120
+ }
121
+ byId("activity-boundary").textContent = snapshot?.boundary.activityTruncated ? "已按 1 MiB 边界截断" : `最近 ${activity.length} 条`;
122
+ }
123
+ function summarizeData(data) {
124
+ return Object.entries(data).slice(0, 4).map(([key, value]) => `${key}=${String(value).slice(0, 60)}`).join(" · ") || "本地事件";
125
+ }
126
+ async function selectTask(task) {
127
+ selectedId = task.id;
128
+ selectedKind = task.kind;
129
+ renderTasks();
130
+ const pane = byId("detail-pane");
131
+ pane.classList.add("open");
132
+ const empty = byId("detail-empty");
133
+ const content = byId("detail-content");
134
+ empty.hidden = true;
135
+ content.hidden = false;
136
+ content.replaceChildren(node("p", "muted", "正在读取本地详情…"));
137
+ try {
138
+ const response = await fetch(`/api/tasks/${encodeURIComponent(task.kind)}/${encodeURIComponent(task.id)}`);
139
+ if (!response.ok)
140
+ throw new Error(`详情请求失败 (${response.status})`);
141
+ renderDetail(await response.json());
142
+ }
143
+ catch (error) {
144
+ content.replaceChildren(node("p", "error-copy", error instanceof Error ? error.message : String(error)));
145
+ }
146
+ }
147
+ function renderDetail(detail) {
148
+ const task = detail.task;
149
+ const content = byId("detail-content");
150
+ content.replaceChildren();
151
+ const close = node("button", "icon-button detail-close", "关闭");
152
+ close.type = "button";
153
+ close.addEventListener("click", () => byId("detail-pane").classList.remove("open"));
154
+ content.append(close);
155
+ content.append(node("p", "detail-kicker", `${kindLabel(task.kind)} / ${statusLabel(task.status)} / ${task.source.toUpperCase()}`));
156
+ const title = node("h2", "detail-title", task.title);
157
+ content.append(title, node("p", "detail-description", task.description || "无补充说明"));
158
+ const stats = node("div", "detail-grid");
159
+ const values = [
160
+ ["更新时间", formatTime(task.updatedAt)],
161
+ ["会话", task.sessionId ?? "—"],
162
+ ["工具调用", String(task.progress?.toolCalls ?? "—")],
163
+ ["轮次", String(task.progress?.turns ?? "—")],
164
+ ["Token", String(task.progress?.tokens ?? "—")],
165
+ ["下次运行", formatTime(task.nextRunAt)],
166
+ ];
167
+ for (const [label, value] of values) {
168
+ const stat = node("div", "detail-stat");
169
+ stat.append(node("span", "", label), node("strong", "", value));
170
+ stats.append(stat);
171
+ }
172
+ content.append(stats);
173
+ if (task.error)
174
+ content.append(node("p", "error-copy", `${task.error.code ? `[${task.error.code}] ` : ""}${task.error.message}`));
175
+ const actions = node("div", "detail-actions");
176
+ for (const action of task.actions) {
177
+ const button = node("button", `action-button ${action === "resume" || action === "retry" ? "primary" : action === "cancel" ? "danger" : ""}`, actionLabel(action));
178
+ button.type = "button";
179
+ button.addEventListener("click", () => void runAction(button, task, action));
180
+ actions.append(button);
181
+ }
182
+ if (task.actions.length)
183
+ content.append(actions);
184
+ const events = node("ol", "event-log");
185
+ for (const event of detail.events.slice(-30).reverse()) {
186
+ const item = node("li");
187
+ item.append(node("time", "", formatTime(Number(event.timestamp))), node("span", "", eventMessage(event)));
188
+ events.append(item);
189
+ }
190
+ if (detail.events.length)
191
+ content.append(node("p", "detail-kicker", "RECENT LOG"), events);
192
+ }
193
+ function eventMessage(event) {
194
+ const data = event.data && typeof event.data === "object" ? event.data : {};
195
+ return String(data.message ?? data.reason ?? data.action ?? event.type ?? "事件");
196
+ }
197
+ function actionLabel(action) {
198
+ return { pause: "暂停", resume: "恢复", cancel: "取消", retry: "重新执行" }[action] ?? action;
199
+ }
200
+ async function runAction(button, task, action) {
201
+ button.disabled = true;
202
+ const previous = button.textContent;
203
+ button.textContent = "处理中…";
204
+ try {
205
+ const response = await fetch(`/api/tasks/${encodeURIComponent(task.kind)}/${encodeURIComponent(task.id)}/${encodeURIComponent(action)}`, { method: "POST" });
206
+ const body = await response.json();
207
+ if (!response.ok)
208
+ throw new Error(body.message ?? `操作失败 (${response.status})`);
209
+ showToast(body.message ?? "操作已完成");
210
+ etag = "";
211
+ await refreshSnapshot(true);
212
+ const current = snapshot?.tasks.find((item) => item.id === selectedId && item.kind === selectedKind);
213
+ if (current)
214
+ await selectTask(current);
215
+ }
216
+ catch (error) {
217
+ showToast(error instanceof Error ? error.message : String(error), true);
218
+ }
219
+ finally {
220
+ button.disabled = false;
221
+ button.textContent = previous;
222
+ }
223
+ }
224
+ function showToast(message, error = false) {
225
+ const toast = byId("toast");
226
+ toast.textContent = message;
227
+ toast.classList.toggle("error", error);
228
+ toast.hidden = false;
229
+ if (toastTimer !== null)
230
+ window.clearTimeout(toastTimer);
231
+ toastTimer = window.setTimeout(() => { toast.hidden = true; }, 3200);
232
+ }
233
+ async function refreshSnapshot(force = false) {
234
+ if (document.hidden && !force)
235
+ return;
236
+ activeRequest?.abort();
237
+ activeRequest = new AbortController();
238
+ try {
239
+ const headers = {};
240
+ if (etag && !force)
241
+ headers["If-None-Match"] = etag;
242
+ const response = await fetch("/api/dashboard/snapshot", { headers, signal: activeRequest.signal });
243
+ if (response.status === 304) {
244
+ retryDelay = 3000;
245
+ scheduleRefresh();
246
+ return;
247
+ }
248
+ if (!response.ok)
249
+ throw new Error(`快照请求失败 (${response.status})`);
250
+ etag = response.headers.get("etag") ?? "";
251
+ snapshot = await response.json();
252
+ renderSummary();
253
+ renderTasks();
254
+ renderActivity();
255
+ retryDelay = 3000;
256
+ scheduleRefresh();
257
+ }
258
+ catch (error) {
259
+ if (error instanceof DOMException && error.name === "AbortError")
260
+ return;
261
+ byId("runtime-label").textContent = "连接中断";
262
+ showToast(error instanceof Error ? error.message : String(error), true);
263
+ retryDelay = Math.min(30000, retryDelay * 2);
264
+ scheduleRefresh();
265
+ }
266
+ }
267
+ function scheduleRefresh() {
268
+ if (refreshTimer !== null)
269
+ window.clearTimeout(refreshTimer);
270
+ if (!document.hidden)
271
+ refreshTimer = window.setTimeout(() => void refreshSnapshot(), retryDelay);
272
+ }
273
+ function bindFilters() {
274
+ byId("task-search").addEventListener("input", (event) => {
275
+ search = event.target.value.trim();
276
+ renderTasks();
277
+ });
278
+ document.querySelectorAll("[data-filter-kind]").forEach((button) => button.addEventListener("click", () => {
279
+ kindFilter = button.dataset.filterKind ?? "all";
280
+ document.querySelectorAll("[data-filter-kind]").forEach((item) => item.classList.toggle("active", item === button));
281
+ renderTasks();
282
+ }));
283
+ document.querySelectorAll("[data-filter-status]").forEach((button) => button.addEventListener("click", () => {
284
+ statusFilter = button.dataset.filterStatus ?? "active";
285
+ document.querySelectorAll("[data-filter-status]").forEach((item) => item.classList.toggle("active", item === button));
286
+ renderTasks();
287
+ }));
288
+ }
289
+ byId("refresh-button").addEventListener("click", () => void refreshSnapshot(true));
290
+ document.addEventListener("visibilitychange", () => {
291
+ if (document.hidden) {
292
+ if (refreshTimer !== null)
293
+ window.clearTimeout(refreshTimer);
294
+ activeRequest?.abort();
295
+ }
296
+ else
297
+ void refreshSnapshot(true);
298
+ });
299
+ document.addEventListener("keydown", (event) => {
300
+ if (event.key === "Escape")
301
+ byId("detail-pane").classList.remove("open");
302
+ });
303
+ bindFilters();
304
+ void refreshSnapshot(true);
305
+ export {};
@@ -0,0 +1,13 @@
1
+ import type { LocalSessionTask } from "../session-task-report.js";
2
+ import type { Mission, MissionStatus } from "../mission/types.js";
3
+ import type { WorkflowCheckpoint } from "../workflow-types.js";
4
+ import type { DashboardTask, DashboardTaskAction } from "./types.js";
5
+ export declare function getMissionActions(status: MissionStatus, daemonHealthy: boolean): DashboardTaskAction[];
6
+ export declare function buildDashboardTasks(options: {
7
+ missions: Mission[];
8
+ loops: LocalSessionTask[];
9
+ workflow: WorkflowCheckpoint | null;
10
+ daemonHealthy: boolean;
11
+ now?: number;
12
+ }): DashboardTask[];
13
+ //# sourceMappingURL=model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/dashboard/model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,KAAK,EACV,aAAa,EACb,mBAAmB,EAEpB,MAAM,YAAY,CAAC;AAiBpB,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,aAAa,EACrB,aAAa,EAAE,OAAO,GACrB,mBAAmB,EAAE,CASvB;AA+ED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAC3C,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACpC,aAAa,EAAE,OAAO,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GAAG,aAAa,EAAE,CAYlB"}
@@ -0,0 +1,112 @@
1
+ const ACTIVE_RANK = {
2
+ running: 0,
3
+ blocked: 1,
4
+ queued: 2,
5
+ paused: 3,
6
+ failed: 10,
7
+ canceled: 11,
8
+ succeeded: 12,
9
+ };
10
+ function summarize(value, limit = 240) {
11
+ const text = String(value ?? "").replace(/\s+/g, " ").trim();
12
+ return text.length <= limit ? text : `${text.slice(0, limit - 1)}…`;
13
+ }
14
+ export function getMissionActions(status, daemonHealthy) {
15
+ if (status === "running" || status === "blocked" || status === "queued") {
16
+ return ["pause", "cancel"];
17
+ }
18
+ if (status === "paused")
19
+ return ["resume", "cancel"];
20
+ if (daemonHealthy && ["succeeded", "failed", "canceled"].includes(status)) {
21
+ return ["retry"];
22
+ }
23
+ return [];
24
+ }
25
+ function missionTask(mission, daemonHealthy) {
26
+ return {
27
+ id: mission.id,
28
+ kind: "mission",
29
+ title: summarize(mission.name, 100) || "未命名使命",
30
+ description: summarize(mission.description),
31
+ status: mission.status,
32
+ rawStatus: mission.status,
33
+ source: daemonHealthy ? "daemon" : "local",
34
+ sessionId: mission.sessionId,
35
+ createdAt: mission.createdAt,
36
+ updatedAt: mission.updatedAt,
37
+ startedAt: mission.metrics.startTime,
38
+ endedAt: mission.metrics.endTime,
39
+ progress: {
40
+ turns: mission.metrics.totalTurns,
41
+ tokens: mission.metrics.totalTokens,
42
+ toolCalls: mission.metrics.totalToolCalls,
43
+ },
44
+ error: mission.error
45
+ ? { code: mission.error.code, message: summarize(mission.error.message, 300) }
46
+ : undefined,
47
+ actions: getMissionActions(mission.status, daemonHealthy),
48
+ };
49
+ }
50
+ function loopStatus(status) {
51
+ if (status === "active")
52
+ return "queued";
53
+ if (status === "completed")
54
+ return "succeeded";
55
+ return status;
56
+ }
57
+ function loopTask(task) {
58
+ const status = loopStatus(task.status);
59
+ return {
60
+ id: task.id,
61
+ kind: "loop",
62
+ title: summarize(task.prompt, 100) || "循环任务",
63
+ description: `每 ${Math.max(1, Math.round(task.intervalMs / 1000))} 秒执行 · ${task.mode}`,
64
+ status,
65
+ rawStatus: task.status,
66
+ source: task.runner === "daemon" ? "daemon" : "session",
67
+ sessionId: task.sessionId,
68
+ createdAt: task.createdAt,
69
+ updatedAt: task.updatedAt,
70
+ startedAt: task.lastRunAt,
71
+ nextRunAt: task.nextRunAt,
72
+ actions: status === "queued" || status === "running" ? ["cancel"] : [],
73
+ };
74
+ }
75
+ function workflowTask(checkpoint) {
76
+ const status = checkpoint.status === "completed"
77
+ ? "succeeded"
78
+ : checkpoint.status === "awaiting_approval"
79
+ ? "blocked"
80
+ : checkpoint.status;
81
+ return {
82
+ id: checkpoint.runId,
83
+ kind: "workflow",
84
+ title: `Workflow · ${summarize(checkpoint.currentState, 80)}`,
85
+ description: summarize(checkpoint.workflowDefinition?.description ?? checkpoint.workflowId),
86
+ status,
87
+ rawStatus: checkpoint.status,
88
+ source: "local",
89
+ sessionId: checkpoint.sessionId,
90
+ createdAt: checkpoint.history[0]?.timestamp ?? checkpoint.updatedAt,
91
+ updatedAt: checkpoint.updatedAt,
92
+ progress: { toolCalls: checkpoint.completedToolResults.length },
93
+ error: checkpoint.error
94
+ ? { message: summarize(checkpoint.error.message, 300) }
95
+ : undefined,
96
+ actions: [],
97
+ };
98
+ }
99
+ export function buildDashboardTasks(options) {
100
+ const tasks = [
101
+ ...options.missions.map((mission) => missionTask(mission, options.daemonHealthy)),
102
+ ...options.loops.map(loopTask),
103
+ ...(options.workflow ? [workflowTask(options.workflow)] : []),
104
+ ];
105
+ return tasks.sort((a, b) => {
106
+ const rank = ACTIVE_RANK[a.status] - ACTIVE_RANK[b.status];
107
+ if (rank !== 0)
108
+ return rank;
109
+ const updated = b.updatedAt - a.updatedAt;
110
+ return updated !== 0 ? updated : a.id.localeCompare(b.id);
111
+ });
112
+ }
@@ -0,0 +1,3 @@
1
+ export declare const DASHBOARD_HTML = "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"color-scheme\" content=\"dark\">\n <title>\u8F7B\u7075 \u00B7 \u4EFB\u52A1\u5DE5\u4F5C\u53F0</title>\n <link rel=\"stylesheet\" href=\"/assets/dashboard.css\">\n</head>\n<body>\n <div class=\"shell\">\n <header class=\"topbar\">\n <div class=\"brand-block\">\n <span class=\"brand-mark\" aria-hidden=\"true\">QL</span>\n <div><p class=\"eyebrow\">LOCAL AGENT CONTROL</p><h1>\u8F7B\u7075\u4EFB\u52A1\u5DE5\u4F5C\u53F0</h1></div>\n </div>\n <div class=\"runtime-strip\" aria-live=\"polite\">\n <span class=\"signal\" id=\"runtime-signal\"></span>\n <span id=\"runtime-label\">\u6B63\u5728\u8FDE\u63A5\u672C\u5730\u8FD0\u884C\u65F6</span>\n <span class=\"divider\"></span>\n <span id=\"source-label\">LOCAL</span>\n <button class=\"icon-button\" id=\"refresh-button\" type=\"button\">\u5237\u65B0</button>\n </div>\n </header>\n\n <section class=\"summary-band\" aria-label=\"\u4EFB\u52A1\u6458\u8981\">\n <div class=\"summary-lead\"><span>\u5F53\u524D\u6001\u52BF</span><strong id=\"summary-total\">\u2014</strong></div>\n <div class=\"summary-item\"><span>\u8FD0\u884C</span><strong id=\"summary-running\">\u2014</strong></div>\n <div class=\"summary-item\"><span>\u7B49\u5F85</span><strong id=\"summary-queued\">\u2014</strong></div>\n <div class=\"summary-item\"><span>\u963B\u585E</span><strong id=\"summary-blocked\">\u2014</strong></div>\n <div class=\"summary-item\"><span>\u5931\u8D25</span><strong id=\"summary-failed\">\u2014</strong></div>\n <time id=\"updated-at\">\u5C1A\u672A\u540C\u6B65</time>\n </section>\n\n <main class=\"workbench\">\n <section class=\"task-rail\" aria-labelledby=\"task-heading\">\n <div class=\"section-heading\">\n <div><p class=\"eyebrow\">TASK RAIL</p><h2 id=\"task-heading\">\u4EFB\u52A1\u8F68\u9053</h2></div>\n <span id=\"visible-count\">0 \u9879</span>\n </div>\n <label class=\"search-box\">\n <span aria-hidden=\"true\">\u2315</span>\n <input id=\"task-search\" type=\"search\" placeholder=\"\u641C\u7D22\u4EFB\u52A1\u3001\u4F1A\u8BDD\u6216\u72B6\u6001\" autocomplete=\"off\">\n </label>\n <div class=\"filter-row\" id=\"kind-filters\" aria-label=\"\u4EFB\u52A1\u7C7B\u578B\u7B5B\u9009\">\n <button class=\"filter active\" data-filter-kind=\"all\" type=\"button\">\u5168\u90E8</button>\n <button class=\"filter\" data-filter-kind=\"mission\" type=\"button\">\u4F7F\u547D</button>\n <button class=\"filter\" data-filter-kind=\"loop\" type=\"button\">\u5FAA\u73AF</button>\n <button class=\"filter\" data-filter-kind=\"workflow\" type=\"button\">\u5DE5\u4F5C\u6D41</button>\n </div>\n <div class=\"filter-row\" id=\"status-filters\" aria-label=\"\u4EFB\u52A1\u72B6\u6001\u7B5B\u9009\">\n <button class=\"filter active\" data-filter-status=\"all\" type=\"button\">\u6D3B\u8DC3\u4F18\u5148</button>\n <button class=\"filter\" data-filter-status=\"active\" type=\"button\">\u4EC5\u6D3B\u8DC3</button>\n <button class=\"filter\" data-filter-status=\"failed\" type=\"button\">\u4EC5\u5931\u8D25</button>\n </div>\n <div class=\"task-list skeleton-stack\" id=\"task-list\" aria-live=\"polite\" aria-busy=\"true\">\n <div class=\"skeleton\"></div><div class=\"skeleton\"></div><div class=\"skeleton\"></div>\n </div>\n </section>\n\n <aside class=\"detail-pane\" id=\"detail-pane\" aria-label=\"\u4EFB\u52A1\u8BE6\u60C5\">\n <div class=\"detail-empty\" id=\"detail-empty\">\n <span class=\"crosshair\" aria-hidden=\"true\">\uFF0B</span>\n <h2 id=\"detail-heading\">\u9009\u62E9\u4E00\u9879\u4EFB\u52A1</h2>\n <p>\u67E5\u770B\u6267\u884C\u6765\u6E90\u3001\u8FDB\u5EA6\u3001\u65E5\u5FD7\u548C\u5F53\u524D\u53EF\u7528\u64CD\u4F5C\u3002</p>\n </div>\n <div class=\"detail-content\" id=\"detail-content\" hidden></div>\n </aside>\n </main>\n\n <section class=\"activity-panel\" aria-labelledby=\"activity-heading\">\n <div class=\"section-heading\"><div><p class=\"eyebrow\">RECENT SIGNALS</p><h2 id=\"activity-heading\">\u6700\u8FD1\u6D3B\u52A8</h2></div><span id=\"activity-boundary\">\u6700\u591A 20 \u6761</span></div>\n <ol id=\"activity-list\" class=\"activity-list\"><li class=\"muted\">\u7B49\u5F85\u672C\u5730\u6307\u6807\u2026</li></ol>\n </section>\n\n <footer class=\"boundary-bar\">\n <span>\u4EC5\u76D1\u542C 127.0.0.1</span><span>\u4EFB\u52A1\u6B63\u6587\u4E0D\u5916\u4F20</span><span id=\"permission-label\">\u6743\u9650 \u2014</span>\n </footer>\n </div>\n <div id=\"toast\" class=\"toast\" role=\"status\" aria-live=\"polite\" hidden></div>\n <script type=\"module\" src=\"/assets/dashboard.js\"></script>\n</body>\n</html>";
2
+ export declare const DASHBOARD_CSS = "\n:root {\n --ink: #dce8e2; --muted: #81928b; --dim: #53635d;\n --ground: #0b100e; --surface: #111815; --raised: #17201c;\n --line: #28372f; --line-strong: #3b5046; --accent: #63d5a2;\n --running: #63d5a2; --queued: #d6b865; --blocked: #df9665;\n --failed: #e27676; --paused: #8ca7d8; --radius: 3px;\n}\n* { box-sizing: border-box; }\nbody { margin: 0; min-height: 100vh; color: var(--ink); background: var(--ground); font-family: \"Aptos\", \"Microsoft YaHei UI\", sans-serif; }\nbody::before { content: \"\"; position: fixed; inset: 0; pointer-events: none; opacity: .25; background-image: linear-gradient(var(--line) 1px, transparent 1px), linear-gradient(90deg, var(--line) 1px, transparent 1px); background-size: 48px 48px; mask-image: linear-gradient(to bottom, black, transparent 72%); }\nbutton, input { font: inherit; }\nbutton { color: inherit; }\n.shell { position: relative; width: min(1540px, 100%); margin: 0 auto; padding: clamp(18px, 3vw, 42px); }\n.topbar { display: flex; align-items: center; justify-content: space-between; gap: 24px; padding-bottom: 22px; border-bottom: 1px solid var(--line-strong); }\n.brand-block { display: flex; align-items: center; gap: 14px; }\n.brand-mark { display: grid; place-items: center; width: 44px; aspect-ratio: 1; border: 1px solid var(--accent); color: var(--accent); font: 700 13px/1 \"Cascadia Code\", monospace; letter-spacing: .12em; transform: rotate(-3deg); }\n.eyebrow { margin: 0 0 4px; color: var(--accent); font: 600 10px/1.2 \"Cascadia Code\", monospace; letter-spacing: .2em; }\nh1, h2, p { margin-top: 0; }\nh1 { margin-bottom: 0; font-size: clamp(22px, 3vw, 34px); font-weight: 650; letter-spacing: -.04em; }\nh2 { margin-bottom: 0; font-size: 18px; font-weight: 620; }\n.runtime-strip { display: flex; align-items: center; gap: 10px; color: var(--muted); font: 12px \"Cascadia Code\", monospace; }\n.signal { width: 8px; height: 8px; border-radius: 50%; background: var(--queued); box-shadow: 0 0 0 4px color-mix(in srgb, var(--queued) 14%, transparent); }\n.signal.ready { background: var(--running); box-shadow: 0 0 0 4px color-mix(in srgb, var(--running) 14%, transparent); }\n.divider { width: 1px; height: 18px; background: var(--line-strong); }\n.icon-button, .filter, .action-button { border: 1px solid var(--line-strong); border-radius: var(--radius); background: transparent; cursor: pointer; transition: border-color .18s ease, background .18s ease, transform .18s ease; }\n.icon-button { padding: 7px 11px; }\n.icon-button:hover, .filter:hover, .action-button:hover { border-color: var(--accent); }\n.icon-button:active, .action-button:active { transform: translateY(1px); }\nbutton:focus-visible, input:focus-visible, .task-row:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }\n.summary-band { display: grid; grid-template-columns: minmax(180px, 1.3fr) repeat(4, minmax(90px, .55fr)) minmax(170px, 1fr); border-bottom: 1px solid var(--line); }\n.summary-band > * { min-height: 88px; padding: 20px 18px; border-right: 1px solid var(--line); display: flex; flex-direction: column; justify-content: center; }\n.summary-band > *:last-child { border-right: 0; }\n.summary-band span, .summary-band time { color: var(--muted); font: 11px \"Cascadia Code\", monospace; }\n.summary-band strong { margin-top: 5px; font: 650 26px \"Cascadia Code\", monospace; }\n.summary-lead { background: var(--accent); color: #092016; }\n.summary-lead span { color: #244d3b; }\n.summary-band time { align-items: flex-end; text-align: right; }\n.workbench { display: grid; grid-template-columns: minmax(360px, .9fr) minmax(500px, 1.35fr); min-height: 560px; border-bottom: 1px solid var(--line-strong); }\n.task-rail { padding: 28px 26px 28px 0; border-right: 1px solid var(--line-strong); }\n.detail-pane { position: relative; padding: 32px 0 32px 34px; min-width: 0; }\n.section-heading { display: flex; align-items: end; justify-content: space-between; gap: 16px; margin-bottom: 20px; }\n.section-heading > span { color: var(--muted); font: 11px \"Cascadia Code\", monospace; }\n.search-box { height: 42px; display: flex; align-items: center; gap: 10px; padding: 0 12px; border: 1px solid var(--line-strong); background: var(--surface); }\n.search-box span { color: var(--accent); font-size: 20px; }\n.search-box input { width: 100%; border: 0; outline: 0; color: var(--ink); background: transparent; }\n.search-box input::placeholder { color: var(--dim); }\n.filter-row { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 10px; }\n.filter { padding: 6px 10px; color: var(--muted); font-size: 11px; }\n.filter.active { color: #092016; border-color: var(--accent); background: var(--accent); }\n.task-list { display: grid; gap: 1px; margin-top: 18px; background: var(--line); border: 1px solid var(--line); max-height: 620px; overflow: auto; }\n.task-row { position: relative; display: grid; grid-template-columns: 7px 1fr auto; gap: 13px; width: 100%; padding: 16px 14px 15px 0; border: 0; text-align: left; background: var(--surface); cursor: pointer; }\n.task-row:hover, .task-row.selected { background: var(--raised); }\n.task-row.selected::after { content: \"\"; position: absolute; inset: 0; border: 1px solid var(--accent); pointer-events: none; }\n.status-line { width: 3px; height: 100%; justify-self: center; background: var(--dim); }\n.status-line.running { background: var(--running); } .status-line.queued { background: var(--queued); } .status-line.blocked { background: var(--blocked); } .status-line.failed { background: var(--failed); } .status-line.paused { background: var(--paused); }\n.task-copy { min-width: 0; }\n.task-meta { display: flex; gap: 8px; margin-bottom: 7px; color: var(--muted); font: 10px \"Cascadia Code\", monospace; text-transform: uppercase; letter-spacing: .08em; }\n.task-title { display: block; margin-bottom: 6px; overflow: hidden; color: var(--ink); font-weight: 620; text-overflow: ellipsis; white-space: nowrap; }\n.task-description { display: block; overflow: hidden; color: var(--muted); font-size: 12px; line-height: 1.45; text-overflow: ellipsis; white-space: nowrap; }\n.task-time { color: var(--dim); font: 10px \"Cascadia Code\", monospace; white-space: nowrap; }\n.detail-empty { min-height: 420px; display: grid; place-content: center; justify-items: center; text-align: center; color: var(--muted); }\n.detail-empty p { max-width: 300px; margin-top: 10px; line-height: 1.6; }\n.crosshair { display: grid; place-items: center; width: 64px; height: 64px; margin-bottom: 20px; border: 1px dashed var(--line-strong); color: var(--accent); font: 28px \"Cascadia Code\", monospace; }\n.detail-content { position: relative; animation: reveal .28s cubic-bezier(.22,1,.36,1); }\n.detail-close { position: absolute; top: 0; right: 0; }\n@keyframes reveal { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }\n.detail-kicker { color: var(--accent); font: 11px \"Cascadia Code\", monospace; text-transform: uppercase; letter-spacing: .12em; }\n.detail-title { max-width: 760px; margin: 10px 0 14px; font-size: clamp(25px, 3vw, 40px); line-height: 1.05; letter-spacing: -.04em; }\n.detail-description { max-width: 760px; color: var(--muted); line-height: 1.7; }\n.detail-grid { display: grid; grid-template-columns: repeat(3, 1fr); margin: 28px 0; border-block: 1px solid var(--line); }\n.detail-stat { padding: 16px 12px 16px 0; }\n.detail-stat span { display: block; color: var(--dim); font: 10px \"Cascadia Code\", monospace; text-transform: uppercase; }\n.detail-stat strong { display: block; margin-top: 7px; font: 600 14px \"Cascadia Code\", monospace; }\n.detail-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 28px; }\n.action-button { padding: 9px 14px; }\n.action-button.primary { color: #092016; border-color: var(--accent); background: var(--accent); }\n.action-button.danger { color: var(--failed); border-color: color-mix(in srgb, var(--failed) 65%, var(--line)); }\n.event-log { display: grid; gap: 0; padding: 0; list-style: none; border-top: 1px solid var(--line); }\n.event-log li { display: grid; grid-template-columns: 90px 1fr; gap: 14px; padding: 11px 0; border-bottom: 1px solid var(--line); color: var(--muted); font-size: 12px; }\n.event-log time { color: var(--dim); font: 10px \"Cascadia Code\", monospace; }\n.activity-panel { padding: 28px 0; }\n.activity-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0 28px; padding: 0; list-style: none; }\n.activity-list li { display: grid; grid-template-columns: 88px 110px 1fr; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--line); color: var(--muted); font-size: 12px; }\n.activity-list time, .activity-type { color: var(--dim); font: 10px \"Cascadia Code\", monospace; }\n.boundary-bar { display: flex; flex-wrap: wrap; gap: 8px 24px; padding-top: 18px; border-top: 1px solid var(--line-strong); color: var(--dim); font: 10px \"Cascadia Code\", monospace; text-transform: uppercase; letter-spacing: .08em; }\n.boundary-bar span::before { content: \"\u25A1\"; margin-right: 7px; color: var(--accent); }\n.skeleton-stack { background: transparent; border: 0; }\n.skeleton { height: 78px; margin-bottom: 1px; background: linear-gradient(90deg, var(--surface), var(--raised), var(--surface)); background-size: 200% 100%; animation: scan 1.4s linear infinite; }\n@keyframes scan { to { background-position: -200% 0; } }\n.muted, .empty-copy { color: var(--muted); }\n.empty-copy { padding: 34px 20px; text-align: center; background: var(--surface); }\n.error-copy { color: var(--failed); }\n.toast { position: fixed; right: 24px; bottom: 24px; z-index: 10; max-width: min(420px, calc(100vw - 32px)); padding: 13px 16px; border: 1px solid var(--line-strong); background: var(--raised); box-shadow: 0 16px 50px #0008; font-size: 13px; }\n.toast.error { border-color: var(--failed); }\n@media (max-width: 880px) {\n .shell { padding: 18px; }\n .topbar { align-items: flex-start; }\n .runtime-strip { flex-wrap: wrap; justify-content: flex-end; }\n .summary-band { grid-template-columns: repeat(3, 1fr); }\n .summary-band > * { min-height: 68px; padding: 13px; }\n .summary-band time { align-items: flex-start; text-align: left; }\n .workbench { display: block; }\n .task-rail { padding-right: 0; border-right: 0; }\n .detail-pane { position: fixed; inset: auto 0 0; z-index: 8; max-height: 78vh; padding: 24px 20px; overflow: auto; border-top: 1px solid var(--accent); background: var(--ground); transform: translateY(105%); transition: transform .28s cubic-bezier(.22,1,.36,1); }\n .detail-pane.open { transform: none; }\n .detail-empty { display: none; }\n .detail-grid { grid-template-columns: repeat(2, 1fr); }\n .activity-list { grid-template-columns: 1fr; }\n}\n@media (max-width: 560px) {\n .brand-mark { width: 38px; }\n .runtime-strip .divider, #source-label { display: none; }\n .summary-band { grid-template-columns: repeat(2, 1fr); }\n .summary-lead { grid-column: span 2; }\n .summary-band time { grid-column: span 2; }\n .task-rail { padding-top: 22px; }\n .activity-list li { grid-template-columns: 72px 88px 1fr; }\n}\n@media (prefers-reduced-motion: reduce) { *, *::before, *::after { scroll-behavior: auto !important; animation: none !important; transition: none !important; } }\n";
3
+ //# sourceMappingURL=page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../src/dashboard/page.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,k7JAkFnB,CAAC;AAET,eAAO,MAAM,aAAa,8iWAwHzB,CAAC"}