dsh-eval-guard 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/README.md +91 -13
  2. package/assets/bomberman/workspace.zip +0 -0
  3. package/assets/commit0/instance.json +30 -0
  4. package/assets/commit0/workspace.zip +0 -0
  5. package/assets/ctf/workspace.zip +0 -0
  6. package/assets/featurebench/hidden/test_regression.py +690 -0
  7. package/assets/featurebench/instance.json +16 -0
  8. package/assets/featurebench/workspace.zip +0 -0
  9. package/assets/swe-bench-pro/hidden/test.patch +36 -0
  10. package/assets/swe-bench-pro/instance.json +10 -0
  11. package/assets/swe-bench-pro/workspace.zip +0 -0
  12. package/assets/tank/workspace.zip +0 -0
  13. package/eval/README.md +18 -0
  14. package/eval/arena/arena/README.md +21 -0
  15. package/eval/arena/arena/bt.py +104 -0
  16. package/eval/arena/arena/clock.py +15 -0
  17. package/eval/arena/arena/core.py +265 -0
  18. package/eval/arena/arena/run.cmd +24 -0
  19. package/eval/arena/arena/run.sh +11 -0
  20. package/eval/arena/arena/schedule.py +79 -0
  21. package/eval/arena/arena/tournament.py +183 -0
  22. package/eval/arena/bomberman/bots/README.md +16 -0
  23. package/eval/arena/bomberman/bots/deepseek-v4.1-flash.cpp +447 -0
  24. package/eval/arena/bomberman/bots/doubao-seed-2.1.cpp +542 -0
  25. package/eval/arena/bomberman/bots/glm-5.3.cpp +406 -0
  26. package/eval/arena/bomberman/bots/gpt-6-astra.cpp +768 -0
  27. package/eval/arena/bomberman/bots/gpt-6-sol.cpp +444 -0
  28. package/eval/arena/bomberman/bots/kimi-k3.cpp +432 -0
  29. package/eval/arena/bomberman/bots/mimo-v2.6-pro.cpp +529 -0
  30. package/eval/arena/bomberman/bots/opus-5.5.cpp +548 -0
  31. package/eval/arena/bomberman/bots/qwen-3.8-max.cpp +836 -0
  32. package/eval/arena/bomberman/tournament/README.md +11 -0
  33. package/eval/arena/bomberman/tournament/bt.py +56 -0
  34. package/eval/arena/bomberman/tournament/evaluate.py +199 -0
  35. package/eval/arena/bomberman/tournament/run.py +79 -0
  36. package/eval/arena/bomberman/tournament/schedule-2to10.json.gz +0 -0
  37. package/eval/arena/bomberman/workspace/MANIFEST.json +13 -0
  38. package/eval/arena/bomberman/workspace/README.md +29 -0
  39. package/eval/arena/bomberman/workspace/RULES.md +100 -0
  40. package/eval/arena/bomberman/workspace/baseline.cpp +240 -0
  41. package/eval/arena/bomberman/workspace/clock.py +25 -0
  42. package/eval/arena/bomberman/workspace/engine/arena.py +198 -0
  43. package/eval/arena/bomberman/workspace/engine/build.py +35 -0
  44. package/eval/arena/bomberman/workspace/engine/engine.py +256 -0
  45. package/eval/arena/bomberman/workspace/engine/verify_replay.py +24 -0
  46. package/eval/arena/bomberman/workspace/run.cmd +28 -0
  47. package/eval/arena/bomberman/workspace/run.sh +16 -0
  48. package/eval/arena/bomberman/workspace/viewer/index.html +42 -0
  49. package/eval/arena/ctf/bots/README.md +18 -0
  50. package/eval/arena/ctf/bots/deepseek-v4.1-flash.cpp +601 -0
  51. package/eval/arena/ctf/bots/doubao-seed-2.1-turbo.cpp +320 -0
  52. package/eval/arena/ctf/bots/fable-5.1.cpp +634 -0
  53. package/eval/arena/ctf/bots/glm-5.3.cpp +689 -0
  54. package/eval/arena/ctf/bots/gpt-6-astra.cpp +657 -0
  55. package/eval/arena/ctf/bots/gpt-6.1-sol.cpp +725 -0
  56. package/eval/arena/ctf/bots/kimi-k3.cpp +667 -0
  57. package/eval/arena/ctf/bots/mimo-v2.6-pro.cpp +533 -0
  58. package/eval/arena/ctf/bots/opus-5.5.cpp +645 -0
  59. package/eval/arena/ctf/bots/qwen-3.8-max.cpp +726 -0
  60. package/eval/arena/ctf/bots/sonnet-5.5.cpp +569 -0
  61. package/eval/arena/ctf/tournament/README.md +15 -0
  62. package/eval/arena/ctf/tournament/formal-2to8.json +31 -0
  63. package/eval/arena/ctf/tournament/formal-9to15.json +31 -0
  64. package/eval/arena/ctf/workspace/README.md +41 -0
  65. package/eval/arena/ctf/workspace/RULES.md +119 -0
  66. package/eval/arena/ctf/workspace/arena.py +186 -0
  67. package/eval/arena/ctf/workspace/baseline.cpp +257 -0
  68. package/eval/arena/ctf/workspace/clock.py +15 -0
  69. package/eval/arena/ctf/workspace/core.py +265 -0
  70. package/eval/arena/ctf/workspace/engine.py +389 -0
  71. package/eval/arena/ctf/workspace/run.cmd +24 -0
  72. package/eval/arena/ctf/workspace/run.sh +11 -0
  73. package/eval/arena/ctf/workspace/viewer.html +71 -0
  74. package/eval/arena/land/bots/README.md +17 -0
  75. package/eval/arena/land/bots/deepseek-4.1f@workbuddy#1/bot.cpp +608 -0
  76. package/eval/arena/land/bots/glm-5.3@workbuddy#1/bot.cpp +521 -0
  77. package/eval/arena/land/bots/gpt-6-astra@codex#1/bot.cpp +373 -0
  78. package/eval/arena/land/bots/gpt-6-sol@codex#1/bot.cpp +341 -0
  79. package/eval/arena/land/bots/kimi-k3@workbuddy#1/bot.cpp +492 -0
  80. package/eval/arena/land/bots/mimo-2.6-pro@zcode#1/bot.cpp +652 -0
  81. package/eval/arena/land/bots/mystery-model@zcode#1/bot.cpp +466 -0
  82. package/eval/arena/land/bots/opus-5.5@claude-code#1/bot.cpp +630 -0
  83. package/eval/arena/land/bots/sonnet-5@claude-code#1/bot.cpp +619 -0
  84. package/eval/arena/land/tournament/make_replay_page.py +17 -0
  85. package/eval/arena/land/tournament/rate.py +290 -0
  86. package/eval/arena/land/tournament/round_robin.py +172 -0
  87. package/eval/arena/land/tournament/sparring.cpp +356 -0
  88. package/eval/arena/land/workspace/RULES.md +114 -0
  89. package/eval/arena/land/workspace/TASK.md +46 -0
  90. package/eval/arena/land/workspace/anchors/.build/greedy_f2e6d2dea5ca +0 -0
  91. package/eval/arena/land/workspace/anchors/greedy.cpp +194 -0
  92. package/eval/arena/land/workspace/anchors/random.cpp +80 -0
  93. package/eval/arena/land/workspace/clock.py +64 -0
  94. package/eval/arena/land/workspace/engine/build.py +33 -0
  95. package/eval/arena/land/workspace/engine/game.py +273 -0
  96. package/eval/arena/land/workspace/engine/referee.py +308 -0
  97. package/eval/arena/land/workspace/starter/.build/bot_be32b45b4699 +0 -0
  98. package/eval/arena/land/workspace/starter/bot.cpp +82 -0
  99. package/eval/arena/land/workspace/viewer/viewer.html +210 -0
  100. package/eval/arena/tank/bots/README.md +24 -0
  101. package/eval/arena/tank/bots/half1/deepseek-v4.1-flash.cpp +367 -0
  102. package/eval/arena/tank/bots/half1/doubao-seed-evolving.cpp +402 -0
  103. package/eval/arena/tank/bots/half1/gemini-4-argon.cpp +1132 -0
  104. package/eval/arena/tank/bots/half1/glm-5.3.cpp +551 -0
  105. package/eval/arena/tank/bots/half1/gpt-6-astra.cpp +233 -0
  106. package/eval/arena/tank/bots/half1/gpt-6.1-sol-ultrafast.cpp +350 -0
  107. package/eval/arena/tank/bots/half1/gpt-6.1-sol.cpp +78 -0
  108. package/eval/arena/tank/bots/half1/grok-4.7.cpp +1099 -0
  109. package/eval/arena/tank/bots/half1/haiku-5.5.cpp +210 -0
  110. package/eval/arena/tank/bots/half1/kimi-k3.cpp +353 -0
  111. package/eval/arena/tank/bots/half1/mimo-v2.6-pro.cpp +336 -0
  112. package/eval/arena/tank/bots/half1/muse-wonder-full.cpp +560 -0
  113. package/eval/arena/tank/bots/half1/opus-5.5.cpp +435 -0
  114. package/eval/arena/tank/bots/half1/qwen-3.8-max.cpp +537 -0
  115. package/eval/arena/tank/bots/half2/deepseek-v4.1-flash.cpp +425 -0
  116. package/eval/arena/tank/bots/half2/doubao-seed-evolving.cpp +308 -0
  117. package/eval/arena/tank/bots/half2/gemini-4-argon.cpp +1390 -0
  118. package/eval/arena/tank/bots/half2/glm-5.3.cpp +651 -0
  119. package/eval/arena/tank/bots/half2/gpt-6-astra.cpp +241 -0
  120. package/eval/arena/tank/bots/half2/gpt-6.1-sol-ultrafast.cpp +356 -0
  121. package/eval/arena/tank/bots/half2/gpt-6.1-sol.cpp +87 -0
  122. package/eval/arena/tank/bots/half2/grok-4.7.cpp +1205 -0
  123. package/eval/arena/tank/bots/half2/haiku-5.5.cpp +210 -0
  124. package/eval/arena/tank/bots/half2/kimi-k3.cpp +487 -0
  125. package/eval/arena/tank/bots/half2/mimo-v2.6-pro.cpp +336 -0
  126. package/eval/arena/tank/bots/half2/muse-wonder-full.cpp +560 -0
  127. package/eval/arena/tank/bots/half2/opus-5.5.cpp +476 -0
  128. package/eval/arena/tank/bots/half2/qwen-3.8-max.cpp +392 -0
  129. package/eval/arena/tank/tournament/README.md +24 -0
  130. package/eval/arena/tank/tournament/bt.py +218 -0
  131. package/eval/arena/tank/tournament/final.json +202 -0
  132. package/eval/arena/tank/tournament/halftime.json +146 -0
  133. package/eval/arena/tank/tournament/progress.json +911 -0
  134. package/eval/arena/tank/tournament/schedule.py +100 -0
  135. package/eval/arena/tank/tournament/sparring.cpp +928 -0
  136. package/eval/arena/tank/tournament/tournament.py +1426 -0
  137. package/eval/arena/tank/workspace/README.md +169 -0
  138. package/eval/arena/tank/workspace/RULES.md +443 -0
  139. package/eval/arena/tank/workspace/arena.py +862 -0
  140. package/eval/arena/tank/workspace/baseline.cpp +259 -0
  141. package/eval/arena/tank/workspace/clock.py +103 -0
  142. package/eval/arena/tank/workspace/core.py +712 -0
  143. package/eval/arena/tank/workspace/engine.py +573 -0
  144. package/eval/arena/tank/workspace/history/.lock +0 -0
  145. package/eval/arena/tank/workspace/history/20261017-193610-08715287f91f-bot.cpp +109 -0
  146. package/eval/arena/tank/workspace/history/20261017-193619-f00b2d5635fa-gpt-6-astra.cpp +241 -0
  147. package/eval/arena/tank/workspace/history/20261017-193643-63319e0a2b1a-gpt-6_1-sol-ultrafast.cpp +356 -0
  148. package/eval/arena/tank/workspace/history/20261017-193647-41c1341d0a8a-gemini-4-argon.cpp +1390 -0
  149. package/eval/arena/tank/workspace/history/20261017-193652-e34c0f45539a-gpt-6_1-sol.cpp +87 -0
  150. package/eval/arena/tank/workspace/history/20261017-193654-c026348ce96e-opus-5_5.cpp +476 -0
  151. package/eval/arena/tank/workspace/history/20261017-193655-4a21366e94d1-muse-wonder-full.cpp +560 -0
  152. package/eval/arena/tank/workspace/history/20261017-193657-dbc4e9969c52-mimo-v2_6-pro.cpp +336 -0
  153. package/eval/arena/tank/workspace/history/20261017-193703-723cd036eb31-grok-4_7.cpp +1205 -0
  154. package/eval/arena/tank/workspace/history/20261017-193707-1ebbb227be68-deepseek-v4_1-flash.cpp +425 -0
  155. package/eval/arena/tank/workspace/history/20261017-193709-aeb0e7202ad5-doubao-seed-evolving.cpp +308 -0
  156. package/eval/arena/tank/workspace/history/20261017-193710-b21ebeb9bc76-kimi-k3.cpp +487 -0
  157. package/eval/arena/tank/workspace/history/20261017-193712-54cd86531905-haiku-5_5.cpp +210 -0
  158. package/eval/arena/tank/workspace/history/20261017-193714-efdb8e35c0bf-qwen-3_8-max.cpp +392 -0
  159. package/eval/arena/tank/workspace/history/20261017-193715-681423c23fdb-glm-5_3.cpp +651 -0
  160. package/eval/arena/tank/workspace/history/20261017-193717-1a8b3c75181d-sparring.cpp +928 -0
  161. package/eval/arena/tank/workspace/history/20261017-193720-1e0803a541e8-baseline.cpp +259 -0
  162. package/eval/arena/tank/workspace/history/log.jsonl +103 -0
  163. package/eval/arena/tank/workspace/judge/cycles.hpp +53 -0
  164. package/eval/arena/tank/workspace/judge/fast.cpp +337 -0
  165. package/eval/arena/tank/workspace/judge/referee.cpp +125 -0
  166. package/eval/arena/tank/workspace/judge/speed.cpp +99 -0
  167. package/eval/arena/tank/workspace/run.cmd +31 -0
  168. package/eval/arena/tank/workspace/run.sh +23 -0
  169. package/eval/arena/tank/workspace/tank.hpp +1762 -0
  170. package/eval/arena/tank/workspace/tank_main.cpp +215 -0
  171. package/eval/arena/tank/workspace/viewer.html +1150 -0
  172. package/eval/arena/warehouse/workspace/SPEC.md +130 -0
  173. package/eval/arena/warehouse/workspace/TASK.md +71 -0
  174. package/eval/arena/warehouse/workspace/baseline/common.h +88 -0
  175. package/eval/arena/warehouse/workspace/baseline/main.cpp +27 -0
  176. package/eval/arena/warehouse/workspace/baseline/old_stuff.cpp +87 -0
  177. package/eval/arena/warehouse/workspace/baseline/sim.cpp +427 -0
  178. package/eval/arena/warehouse/workspace/baseline/util.cpp +220 -0
  179. package/eval/arena/warehouse/workspace/clock.py +42 -0
  180. package/eval/arena/warehouse/workspace/src/common.h +88 -0
  181. package/eval/arena/warehouse/workspace/src/main.cpp +27 -0
  182. package/eval/arena/warehouse/workspace/src/old_stuff.cpp +87 -0
  183. package/eval/arena/warehouse/workspace/src/sim.cpp +427 -0
  184. package/eval/arena/warehouse/workspace/src/util.cpp +220 -0
  185. package/eval/arena/warehouse/workspace/tools/bench.py +119 -0
  186. package/eval/arena/warehouse/workspace/tools/check_input.py +161 -0
  187. package/eval/arena/warehouse/workspace/tools/gen.py +132 -0
  188. package/eval/arena/warehouse/workspace/tools/measure.py +107 -0
  189. package/eval/arena/warehouse/workspace/workloads/public/pub_crowded.in +1420 -0
  190. package/eval/arena/warehouse/workspace/workloads/public/pub_crowded.out +9162 -0
  191. package/eval/arena/warehouse/workspace/workloads/public/pub_dynamic.in +1465 -0
  192. package/eval/arena/warehouse/workspace/workloads/public/pub_dynamic.out +3439 -0
  193. package/eval/arena/warehouse/workspace/workloads/public/pub_flood.in +2757 -0
  194. package/eval/arena/warehouse/workspace/workloads/public/pub_flood.out +3126 -0
  195. package/eval/arena/warehouse/workspace/workloads/public/pub_small.in +731 -0
  196. package/eval/arena/warehouse/workspace/workloads/public/pub_small.out +2578 -0
  197. package/eval/arena/warehouse/workspace/workloads/public/pub_wide.in +528 -0
  198. package/eval/arena/warehouse/workspace/workloads/public/pub_wide.out +2026 -0
  199. package/eval/bomberman.py +20 -0
  200. package/eval/commit0.py +21 -0
  201. package/eval/common.py +61 -0
  202. package/eval/ctf.py +20 -0
  203. package/eval/featurebench.py +25 -0
  204. package/eval/land.py +20 -0
  205. package/eval/legacy/bomberman-prices.json +4 -0
  206. package/eval/legacy/bomberman.py +161 -0
  207. package/eval/legacy/commit0.py +91 -0
  208. package/eval/legacy/ctf-prices.json +4 -0
  209. package/eval/legacy/ctf.py +178 -0
  210. package/eval/legacy/featurebench.py +103 -0
  211. package/eval/legacy/land-prices.json +4 -0
  212. package/eval/legacy/land.py +177 -0
  213. package/eval/legacy/swe_bench_pro.py +116 -0
  214. package/eval/legacy/tank-prices.json +3 -0
  215. package/eval/legacy/tank.py +408 -0
  216. package/eval/legacy/warehouse-prices.json +4 -0
  217. package/eval/legacy/warehouse.py +365 -0
  218. package/eval/swe-bench-pro.py +25 -0
  219. package/eval/tank.py +20 -0
  220. package/eval/warehouse.py +20 -0
  221. package/lib/client/index.js +376 -63
  222. package/lib/client/index.js.map +1 -1
  223. package/lib/index.js +684 -27
  224. package/package.json +2 -1
@@ -20,6 +20,41 @@ window.__ModuleLoader__.load({
20
20
  const INSTALL_ROUTE = "/api/eval-guard/install";
21
21
  const EVENTS_ROUTE = "/api/eval-guard/events";
22
22
  const REVEAL_ROUTE = "/api/eval-guard/reveal";
23
+ const JOB_CANCEL_ROUTE = "/api/eval-guard/job/cancel";
24
+ const EVAL_ROUTE = "/api/eval-guard/evaluate";
25
+ /** Common pip indexes offered by the settings page. */
26
+ const PIP_MIRRORS = [
27
+ {
28
+ id: "pypi",
29
+ label: "PyPI",
30
+ url: "https://pypi.org/simple"
31
+ },
32
+ {
33
+ id: "tsinghua",
34
+ label: "清华 TUNA",
35
+ url: "https://pypi.tuna.tsinghua.edu.cn/simple"
36
+ },
37
+ {
38
+ id: "aliyun",
39
+ label: "阿里云",
40
+ url: "https://mirrors.aliyun.com/pypi/simple"
41
+ },
42
+ {
43
+ id: "tencent",
44
+ label: "腾讯云",
45
+ url: "https://mirrors.cloud.tencent.com/pypi/simple"
46
+ },
47
+ {
48
+ id: "bfsu",
49
+ label: "北外",
50
+ url: "https://mirrors.bfsu.edu.cn/pypi/web/simple"
51
+ },
52
+ {
53
+ id: "sjtu",
54
+ label: "上海交大",
55
+ url: "https://mirror.sjtu.edu.cn/pypi/web/simple"
56
+ }
57
+ ];
23
58
  //#endregion
24
59
  //#region src/client/bridge.ts
25
60
  let current;
@@ -41,7 +76,13 @@ window.__ModuleLoader__.load({
41
76
  timeLimitMinutes: 90,
42
77
  forbiddenPaths: "",
43
78
  toolBlacklistUseDefault: true,
44
- toolBlacklist: ""
79
+ toolBlacklist: "",
80
+ cloneSource: "github",
81
+ customCloneUrl: "",
82
+ pipIndex: "pypi",
83
+ customPipUrl: "",
84
+ gitProxyUrl: "",
85
+ pipProxyUrl: ""
45
86
  };
46
87
  const box = {
47
88
  border: "1px solid var(--vscode-panel-border, #3c3c3c)",
@@ -97,7 +138,13 @@ window.__ModuleLoader__.load({
97
138
  timeLimitMinutes: typeof value?.timeLimitMinutes === "number" ? value.timeLimitMinutes : 90,
98
139
  forbiddenPaths: typeof value?.forbiddenPaths === "string" ? value.forbiddenPaths : "",
99
140
  toolBlacklistUseDefault: value?.toolBlacklistUseDefault !== false,
100
- toolBlacklist: typeof value?.toolBlacklist === "string" ? value.toolBlacklist : ""
141
+ toolBlacklist: typeof value?.toolBlacklist === "string" ? value.toolBlacklist : "",
142
+ cloneSource: typeof value?.cloneSource === "string" && value.cloneSource !== "" ? value.cloneSource : "github",
143
+ customCloneUrl: typeof value?.customCloneUrl === "string" ? value.customCloneUrl : "",
144
+ pipIndex: typeof value?.pipIndex === "string" && value.pipIndex !== "" ? value.pipIndex : "pypi",
145
+ customPipUrl: typeof value?.customPipUrl === "string" ? value.customPipUrl : "",
146
+ gitProxyUrl: typeof value?.gitProxyUrl === "string" ? value.gitProxyUrl : "",
147
+ pipProxyUrl: typeof value?.pipProxyUrl === "string" ? value.pipProxyUrl : ""
101
148
  };
102
149
  }
103
150
  /** The settings card. */
@@ -110,13 +157,46 @@ window.__ModuleLoader__.load({
110
157
  const [pendingEnable, setPendingEnable] = (0, react.useState)(false);
111
158
  const [notice, setNotice] = (0, react.useState)("");
112
159
  const [error, setError] = (0, react.useState)("");
113
- const [workspaceId, setWorkspaceId] = (0, react.useState)("");
114
- const [taskId, setTaskId] = (0, react.useState)("");
160
+ const [workspaceId, setWorkspaceId] = (0, react.useState)(() => typeof localStorage === "undefined" ? "" : localStorage.getItem("evalGuard.workspaceId") ?? "");
161
+ const [taskId, setTaskId] = (0, react.useState)(() => typeof localStorage === "undefined" ? "" : localStorage.getItem("evalGuard.taskId") ?? "");
115
162
  const [check, setCheck] = (0, react.useState)(null);
116
163
  const [manual, setManual] = (0, react.useState)(null);
117
164
  const [prompt, setPrompt] = (0, react.useState)("");
118
165
  const [copied, setCopied] = (0, react.useState)("");
119
166
  const workspaces = evalBridge()?.workspaces() ?? [];
167
+ const [job, setJob] = (0, react.useState)(null);
168
+ const [jobNotice, setJobNotice] = (0, react.useState)("");
169
+ const pollJob = (id) => {
170
+ fetch("/api/eval-guard/job?id=" + encodeURIComponent(id)).then(readJson).then(setJob).catch(() => void 0);
171
+ };
172
+ const loadLatestJob = (workspace, task) => {
173
+ if (workspace === "" || task === "") {
174
+ setJob(null);
175
+ return;
176
+ }
177
+ fetch("/api/eval-guard/jobs?workspaceId=" + encodeURIComponent(workspace) + "&task=" + encodeURIComponent(task)).then(readJson).then((items) => {
178
+ const latest = items[0];
179
+ if (latest !== void 0) pollJob(latest.id);
180
+ else setJob(null);
181
+ }).catch(() => void 0);
182
+ };
183
+ (0, react.useEffect)(() => {
184
+ if (job === null || job.status !== "running" && job.status !== "queued") return;
185
+ const timer = setInterval(() => {
186
+ pollJob(job.id);
187
+ }, 800);
188
+ return () => clearInterval(timer);
189
+ }, [job]);
190
+ (0, react.useEffect)(() => {
191
+ if (job?.status === "succeeded" && job.kind === "install" && job.prompt !== void 0) {
192
+ setPrompt(job.prompt);
193
+ setJobNotice(t("settings.installOk"));
194
+ loadState();
195
+ }
196
+ }, [job]);
197
+ (0, react.useEffect)(() => {
198
+ loadLatestJob(workspaceId, taskId);
199
+ }, [workspaceId, taskId]);
120
200
  (0, react.useEffect)(() => {
121
201
  if (snapshot.status !== "ready" || snapshot.value === void 0 || dirty) return;
122
202
  setDraft(normalize(snapshot.value));
@@ -137,7 +217,7 @@ window.__ModuleLoader__.load({
137
217
  return () => clearInterval(timer);
138
218
  }, []);
139
219
  (0, react.useEffect)(() => {
140
- if (taskId === "" && state !== null && state.tasks.length > 0) setTaskId(state.tasks[0].id);
220
+ if (state !== null && state.tasks.length > 0 && (taskId === "" || !state.tasks.some((task) => task.id === taskId))) setTaskId(state.tasks[0].id);
141
221
  }, [state, taskId]);
142
222
  const ready = snapshot.status === "ready" && snapshot.writable;
143
223
  const save = async (patch) => {
@@ -172,6 +252,34 @@ window.__ModuleLoader__.load({
172
252
  }));
173
253
  setDirty(true);
174
254
  };
255
+ const selectTask = (next) => {
256
+ setTaskId(next);
257
+ try {
258
+ localStorage.setItem("evalGuard.taskId", next);
259
+ } catch {}
260
+ setCheck(null);
261
+ setManual(null);
262
+ setPrompt("");
263
+ setJob(null);
264
+ setJobNotice("");
265
+ if (workspaceId !== "") loadManual(workspaceId, next);
266
+ const task = state?.tasks.find((item) => item.id === next);
267
+ if (task === void 0) return;
268
+ const remotes = task.remotes ?? [];
269
+ const cloneSource = draft.cloneSource === "custom" || remotes.some((remote) => remote.id === draft.cloneSource) ? draft.cloneSource : remotes[0]?.id ?? "github";
270
+ setDraft((current) => ({
271
+ ...current,
272
+ timeLimitMinutes: task.timeLimitMinutes,
273
+ timeLimitEnabled: task.timeLimitEnabled,
274
+ cloneSource
275
+ }));
276
+ setNotice(t("settings.taskDefaults").replace("{minutes}", String(task.timeLimitMinutes)).replace("{state}", task.timeLimitEnabled ? t("settings.on") : t("settings.off")));
277
+ save({
278
+ timeLimitMinutes: task.timeLimitMinutes,
279
+ timeLimitEnabled: task.timeLimitEnabled,
280
+ cloneSource
281
+ });
282
+ };
175
283
  const toggleEnabled = (next) => {
176
284
  if (!next) {
177
285
  save({ enabled: false });
@@ -206,6 +314,7 @@ window.__ModuleLoader__.load({
206
314
  if (workspaceId === "" || taskId === "") return;
207
315
  setError("");
208
316
  setNotice("");
317
+ setJobNotice("");
209
318
  const checked = await fetch(CHECK_ROUTE + "?workspaceId=" + encodeURIComponent(workspaceId)).then(readJson);
210
319
  setCheck(checked);
211
320
  if (!checked.ok || !checked.empty) {
@@ -217,17 +326,48 @@ window.__ModuleLoader__.load({
217
326
  headers: { "content-type": "application/json" },
218
327
  body: JSON.stringify({
219
328
  workspaceId,
220
- task: taskId
329
+ task: taskId,
330
+ cloneSource: draft.cloneSource,
331
+ customCloneUrl: draft.customCloneUrl,
332
+ pipIndex: draft.pipIndex,
333
+ customPipUrl: draft.customPipUrl,
334
+ gitProxyUrl: draft.gitProxyUrl,
335
+ pipProxyUrl: draft.pipProxyUrl
221
336
  })
222
337
  }).then(readJson);
223
- if (!result.ok || result.prompt === void 0) {
338
+ if (!result.ok || result.jobId === void 0) {
224
339
  setError(result.error ?? "安装失败");
225
340
  return;
226
341
  }
227
- setPrompt(result.prompt);
228
- setNotice(t("settings.installOk"));
229
- evalBridge()?.startSession(workspaceId, result.prompt);
230
- loadState();
342
+ pollJob(result.jobId);
343
+ };
344
+ const evaluate = async () => {
345
+ if (workspaceId === "" || taskId === "") return;
346
+ setError("");
347
+ setNotice("");
348
+ setJobNotice("");
349
+ const result = await fetch(EVAL_ROUTE, {
350
+ method: "POST",
351
+ headers: { "content-type": "application/json" },
352
+ body: JSON.stringify({
353
+ workspaceId,
354
+ task: taskId
355
+ })
356
+ }).then(readJson);
357
+ if (!result.ok || result.jobId === void 0) {
358
+ setError(result.error ?? "评测启动失败");
359
+ return;
360
+ }
361
+ pollJob(result.jobId);
362
+ };
363
+ const cancelJob = async () => {
364
+ if (job === null) return;
365
+ await fetch(JOB_CANCEL_ROUTE, {
366
+ method: "POST",
367
+ headers: { "content-type": "application/json" },
368
+ body: JSON.stringify({ id: job.id })
369
+ });
370
+ pollJob(job.id);
231
371
  };
232
372
  const copy = async (label, text) => {
233
373
  try {
@@ -310,41 +450,6 @@ window.__ModuleLoader__.load({
310
450
  children: t("settings.enabledHint")
311
451
  })]
312
452
  }),
313
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
314
- style: box,
315
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
316
- style: row,
317
- children: [
318
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [
319
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
320
- type: "checkbox",
321
- checked: draft.timeLimitEnabled,
322
- disabled: !ready,
323
- onChange: (event) => void save({ timeLimitEnabled: event.target.checked })
324
- }),
325
- " ",
326
- t("settings.timeLimit")
327
- ] }),
328
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
329
- type: "number",
330
- min: 1,
331
- max: 1440,
332
- value: draft.timeLimitMinutes,
333
- disabled: !ready,
334
- style: { width: 90 },
335
- onChange: (event) => edit({ timeLimitMinutes: Number(event.target.value) }),
336
- onBlur: () => void save({ timeLimitMinutes: draft.timeLimitMinutes })
337
- }),
338
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
339
- style: note,
340
- children: t("settings.minutes")
341
- })
342
- ]
343
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
344
- style: note,
345
- children: t("settings.timeLimitHint")
346
- })]
347
- }),
348
453
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
349
454
  style: box,
350
455
  children: [
@@ -413,14 +518,7 @@ window.__ModuleLoader__.load({
413
518
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [t("settings.taskLabel"), ":"] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
414
519
  value: taskId,
415
520
  disabled: state === null || state.tasks.length === 0,
416
- onChange: (event) => {
417
- const next = event.target.value;
418
- setTaskId(next);
419
- setCheck(null);
420
- setManual(null);
421
- setPrompt("");
422
- if (workspaceId !== "") loadManual(workspaceId, next);
423
- },
521
+ onChange: (event) => selectTask(event.target.value),
424
522
  children: (state?.tasks ?? []).map((task) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
425
523
  value: task.id,
426
524
  children: task.label
@@ -431,6 +529,86 @@ window.__ModuleLoader__.load({
431
529
  style: note,
432
530
  children: state.tasks.find((task) => task.id === taskId)?.description ?? ""
433
531
  }),
532
+ (() => {
533
+ const selected = state?.tasks.find((item) => item.id === taskId);
534
+ if (selected?.needsSetup !== true) return null;
535
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
536
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
537
+ style: row,
538
+ children: [
539
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [t("settings.cloneSource"), ":"] }),
540
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
541
+ value: draft.cloneSource,
542
+ disabled: !ready,
543
+ onChange: (event) => void save({ cloneSource: event.target.value }),
544
+ children: [(selected.remotes ?? []).map((remote) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
545
+ value: remote.id,
546
+ children: remote.label
547
+ }, remote.id)), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
548
+ value: "custom",
549
+ children: t("settings.custom")
550
+ })]
551
+ }),
552
+ draft.cloneSource === "custom" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
553
+ style: { width: 260 },
554
+ disabled: !ready,
555
+ value: draft.customCloneUrl,
556
+ onChange: (event) => edit({ customCloneUrl: event.target.value }),
557
+ onBlur: () => void save({ customCloneUrl: draft.customCloneUrl }),
558
+ placeholder: "https://git.example.com"
559
+ })
560
+ ]
561
+ }),
562
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
563
+ style: row,
564
+ children: [
565
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [t("settings.pipIndex"), ":"] }),
566
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
567
+ value: draft.pipIndex,
568
+ disabled: !ready,
569
+ onChange: (event) => void save({ pipIndex: event.target.value }),
570
+ children: [PIP_MIRRORS.map((mirror) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
571
+ value: mirror.id,
572
+ children: mirror.label
573
+ }, mirror.id)), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
574
+ value: "custom",
575
+ children: t("settings.custom")
576
+ })]
577
+ }),
578
+ draft.pipIndex === "custom" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
579
+ style: { width: 260 },
580
+ disabled: !ready,
581
+ value: draft.customPipUrl,
582
+ onChange: (event) => edit({ customPipUrl: event.target.value }),
583
+ onBlur: () => void save({ customPipUrl: draft.customPipUrl }),
584
+ placeholder: "https://pypi.example.com/simple"
585
+ })
586
+ ]
587
+ }),
588
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
589
+ style: row,
590
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [t("settings.gitProxy"), ":"] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
591
+ style: { width: 260 },
592
+ disabled: !ready,
593
+ value: draft.gitProxyUrl,
594
+ onChange: (event) => edit({ gitProxyUrl: event.target.value }),
595
+ onBlur: () => void save({ gitProxyUrl: draft.gitProxyUrl }),
596
+ placeholder: "127.0.0.1:7897"
597
+ })]
598
+ }),
599
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
600
+ style: row,
601
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [t("settings.pipProxy"), ":"] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
602
+ style: { width: 260 },
603
+ disabled: !ready,
604
+ value: draft.pipProxyUrl,
605
+ onChange: (event) => edit({ pipProxyUrl: event.target.value }),
606
+ onBlur: () => void save({ pipProxyUrl: draft.pipProxyUrl }),
607
+ placeholder: "127.0.0.1:7897"
608
+ })]
609
+ })
610
+ ] });
611
+ })(),
434
612
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
435
613
  style: row,
436
614
  children: [
@@ -438,8 +616,13 @@ window.__ModuleLoader__.load({
438
616
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
439
617
  value: workspaceId,
440
618
  onChange: (event) => {
441
- setWorkspaceId(event.target.value);
442
- loadManual(event.target.value, taskId);
619
+ const next = event.target.value;
620
+ setWorkspaceId(next);
621
+ try {
622
+ localStorage.setItem("evalGuard.workspaceId", next);
623
+ } catch {}
624
+ loadManual(next, taskId);
625
+ loadLatestJob(next, taskId);
443
626
  },
444
627
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
445
628
  value: "",
@@ -451,12 +634,22 @@ window.__ModuleLoader__.load({
451
634
  }),
452
635
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
453
636
  style: primary,
454
- disabled: workspaceId === "" || taskId === "",
637
+ disabled: workspaceId === "" || taskId === "" || job?.status === "running",
455
638
  onClick: () => void install(),
456
639
  children: t("settings.check")
640
+ }),
641
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
642
+ style: button,
643
+ disabled: workspaceId === "" || taskId === "" || job?.status === "running",
644
+ onClick: () => void evaluate(),
645
+ children: t("settings.evaluate")
457
646
  })
458
647
  ]
459
648
  }),
649
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
650
+ style: note,
651
+ children: t("settings.evalHint")
652
+ }),
460
653
  workspaces.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
461
654
  style: note,
462
655
  children: t("settings.noWorkspaces")
@@ -486,9 +679,99 @@ window.__ModuleLoader__.load({
486
679
  color: "#d77"
487
680
  },
488
681
  children: error
682
+ }),
683
+ jobNotice !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
684
+ style: {
685
+ ...note,
686
+ color: "#7c7"
687
+ },
688
+ children: jobNotice
689
+ }),
690
+ job !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
691
+ style: {
692
+ ...box,
693
+ borderColor: job.status === "failed" ? "#a55" : job.status === "succeeded" ? "#4a7" : "#3a6"
694
+ },
695
+ children: [
696
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
697
+ style: row,
698
+ children: [
699
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: job.kind === "install" ? t("settings.installJob") : t("settings.evalJob") }),
700
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: job.status }),
701
+ job.status === "running" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
702
+ style: danger,
703
+ onClick: () => void cancelJob(),
704
+ children: t("settings.cancel")
705
+ }),
706
+ job.kind === "install" && job.status === "succeeded" && job.prompt !== void 0 && workspaceId !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
707
+ style: primary,
708
+ onClick: () => evalBridge()?.startSession(workspaceId, job.prompt),
709
+ children: t("settings.startSession")
710
+ })
711
+ ]
712
+ }),
713
+ job.error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
714
+ style: {
715
+ ...note,
716
+ color: "#d77"
717
+ },
718
+ children: job.error
719
+ }),
720
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
721
+ style: {
722
+ ...textarea,
723
+ maxHeight: 260,
724
+ overflow: "auto"
725
+ },
726
+ children: job.log || t("settings.waiting")
727
+ }),
728
+ job.result !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
729
+ style: {
730
+ ...textarea,
731
+ maxHeight: 260,
732
+ overflow: "auto"
733
+ },
734
+ children: JSON.stringify(job.result, void 0, 2)
735
+ })
736
+ ]
489
737
  })
490
738
  ]
491
739
  }),
740
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
741
+ style: box,
742
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
743
+ style: row,
744
+ children: [
745
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [
746
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
747
+ type: "checkbox",
748
+ checked: draft.timeLimitEnabled,
749
+ disabled: !ready,
750
+ onChange: (event) => void save({ timeLimitEnabled: event.target.checked })
751
+ }),
752
+ " ",
753
+ t("settings.timeLimit")
754
+ ] }),
755
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
756
+ type: "number",
757
+ min: 1,
758
+ max: 1440,
759
+ value: draft.timeLimitMinutes,
760
+ disabled: !ready,
761
+ style: { width: 90 },
762
+ onChange: (event) => edit({ timeLimitMinutes: Number(event.target.value) }),
763
+ onBlur: () => void save({ timeLimitMinutes: draft.timeLimitMinutes })
764
+ }),
765
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
766
+ style: note,
767
+ children: t("settings.minutes")
768
+ })
769
+ ]
770
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
771
+ style: note,
772
+ children: t("settings.timeLimitHint")
773
+ })]
774
+ }),
492
775
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
493
776
  style: box,
494
777
  children: [
@@ -660,12 +943,12 @@ window.__ModuleLoader__.load({
660
943
  "settings.blacklistHint": "每行一个工具名;默认内容按当前已安装工具生成,不存在的工具会被忽略。",
661
944
  "settings.reset": "恢复默认黑名单",
662
945
  "settings.editingDefault": "编辑后将以自定义黑名单保存。",
663
- "settings.task": "安装评测任务",
664
- "settings.taskHint": "选择一个空的工作区,复制坦克大战素材并把初始提示词填入新会话输入框。",
946
+ "settings.task": "安装 / 评测任务",
947
+ "settings.taskHint": "选择任务和工作区;安装与评测共用这组选择。安装会写入素材并准备环境,评测只对当前工作区运行一次。",
665
948
  "settings.taskLabel": "任务",
666
949
  "settings.workspace": "工作区",
667
950
  "settings.pickWorkspace": "请选择工作区",
668
- "settings.check": "检测并安装",
951
+ "settings.check": "安装任务",
669
952
  "settings.nonEmpty": "工作区不是空的;未执行任何操作。",
670
953
  "settings.manual": "手动通道",
671
954
  "settings.manualHint": "手动复制素材:自己把素材目录内容复制到工作区根目录;手动填充提示词:复制下面的提示词。",
@@ -677,6 +960,21 @@ window.__ModuleLoader__.load({
677
960
  "settings.reveal": "在文件管理器中打开",
678
961
  "settings.noWorkspaces": "没有可选的工作区。",
679
962
  "settings.installOk": "任务素材已安装,提示词已填入新会话,请自行发送。",
963
+ "settings.taskDefaults": "已按任务默认值设置限时:{minutes} 分钟,限时开关 {state}。你可以随后手动修改。",
964
+ "settings.on": "打开",
965
+ "settings.off": "关闭",
966
+ "settings.evaluate": "评测当前工作区",
967
+ "settings.evalJob": "评测任务",
968
+ "settings.installJob": "安装任务",
969
+ "settings.cloneSource": "仓库来源",
970
+ "settings.pipIndex": "pip 镜像",
971
+ "settings.custom": "自定义",
972
+ "settings.cancel": "取消",
973
+ "settings.waiting": "正在准备…",
974
+ "settings.evalHint": "只评测单个工作区,不做排名。评测任务与安装共用上面的工作区和任务选择。",
975
+ "settings.startSession": "填入新会话",
976
+ "settings.gitProxy": "Git 代理",
977
+ "settings.pipProxy": "pip 代理",
680
978
  "settings.loading": "加载中…",
681
979
  "settings.recent": "最近事件",
682
980
  "settings.none": "暂无。",
@@ -700,12 +998,12 @@ window.__ModuleLoader__.load({
700
998
  "settings.blacklistHint": "One tool name per line; the default is generated from the tools installed now and missing tools are ignored.",
701
999
  "settings.reset": "Reset default blacklist",
702
1000
  "settings.editingDefault": "Editing switches this to a custom blacklist.",
703
- "settings.task": "Install evaluation task",
704
- "settings.taskHint": "Pick an empty workspace, copy the tank pack in, and seed the initial prompt into a new conversation.",
1001
+ "settings.task": "Install / evaluate task",
1002
+ "settings.taskHint": "Pick a task and workspace; install and evaluation share this selection. Install writes the task and prepares the environment; evaluation runs once on the current workspace.",
705
1003
  "settings.taskLabel": "Task",
706
1004
  "settings.workspace": "Workspace",
707
1005
  "settings.pickWorkspace": "Pick a workspace",
708
- "settings.check": "Check and install",
1006
+ "settings.check": "Install task",
709
1007
  "settings.nonEmpty": "The workspace is not empty; nothing was changed.",
710
1008
  "settings.manual": "Manual channel",
711
1009
  "settings.manualHint": "Manual material copy: copy the source folder contents into the workspace root yourself. Manual prompt: copy the text below.",
@@ -717,6 +1015,21 @@ window.__ModuleLoader__.load({
717
1015
  "settings.reveal": "Open in file manager",
718
1016
  "settings.noWorkspaces": "No workspace is available.",
719
1017
  "settings.installOk": "Task installed and the prompt was seeded into a new conversation; send it yourself.",
1018
+ "settings.taskDefaults": "Applied task defaults: {minutes} minutes, time limit {state}. You can change them afterwards.",
1019
+ "settings.on": "on",
1020
+ "settings.off": "off",
1021
+ "settings.evaluate": "Evaluate current workspace",
1022
+ "settings.evalJob": "Evaluation job",
1023
+ "settings.installJob": "Install job",
1024
+ "settings.cloneSource": "Clone source",
1025
+ "settings.pipIndex": "pip index",
1026
+ "settings.custom": "Custom",
1027
+ "settings.cancel": "Cancel",
1028
+ "settings.waiting": "Preparing…",
1029
+ "settings.evalHint": "Evaluates one workspace only; no ranking. Uses the same workspace/task selection as install.",
1030
+ "settings.startSession": "Open in new conversation",
1031
+ "settings.gitProxy": "Git proxy",
1032
+ "settings.pipProxy": "pip proxy",
720
1033
  "settings.loading": "Loading…",
721
1034
  "settings.recent": "Recent events",
722
1035
  "settings.none": "None.",