hypercore-cli 1.1.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 (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +110 -0
  3. package/dist/api-XGC7D5AW.js +162 -0
  4. package/dist/auth-DNQWYQKT.js +21 -0
  5. package/dist/background-2EGCAAQH.js +14 -0
  6. package/dist/backlog-Q2NZCLNY.js +24 -0
  7. package/dist/chunk-2CMSCWQW.js +162 -0
  8. package/dist/chunk-2LJ2DVEB.js +167 -0
  9. package/dist/chunk-3RPFCQKJ.js +288 -0
  10. package/dist/chunk-43OLRXM5.js +263 -0
  11. package/dist/chunk-4DVYJAJL.js +57 -0
  12. package/dist/chunk-6OL3GA3P.js +173 -0
  13. package/dist/chunk-AUHU7ALH.js +2023 -0
  14. package/dist/chunk-B6A2AKLN.js +139 -0
  15. package/dist/chunk-BE46C7JW.js +46 -0
  16. package/dist/chunk-CUVAUOXL.js +58 -0
  17. package/dist/chunk-GH7E2OJE.js +223 -0
  18. package/dist/chunk-GOOTEPBK.js +271 -0
  19. package/dist/chunk-GPPMJYSM.js +133 -0
  20. package/dist/chunk-GU2FZQ6A.js +69 -0
  21. package/dist/chunk-IOPKN5GD.js +190 -0
  22. package/dist/chunk-IXOIOGR5.js +1505 -0
  23. package/dist/chunk-KRPOPWGA.js +251 -0
  24. package/dist/chunk-MGLJ53QN.js +219 -0
  25. package/dist/chunk-MV4TTRYX.js +533 -0
  26. package/dist/chunk-OPZYEVYR.js +150 -0
  27. package/dist/chunk-QTSLP47C.js +166 -0
  28. package/dist/chunk-R3GPQC7I.js +393 -0
  29. package/dist/chunk-RKB2JOV2.js +43 -0
  30. package/dist/chunk-RNG3K465.js +80 -0
  31. package/dist/chunk-TGTYKBGC.js +86 -0
  32. package/dist/chunk-U5SGAIMM.js +681 -0
  33. package/dist/chunk-V5UHPPSY.js +140 -0
  34. package/dist/chunk-WHLVZCQY.js +245 -0
  35. package/dist/chunk-XDRCBMZZ.js +66 -0
  36. package/dist/chunk-XOS6HPEF.js +134 -0
  37. package/dist/chunk-ZSBHUGWR.js +262 -0
  38. package/dist/claude-NSQ442XD.js +12 -0
  39. package/dist/commands-CK3WFAGI.js +128 -0
  40. package/dist/commands-U63OEO5J.js +1044 -0
  41. package/dist/commands-ZE6GD3WC.js +232 -0
  42. package/dist/config-4EW42BSF.js +8 -0
  43. package/dist/config-loader-SXO674TF.js +24 -0
  44. package/dist/diagnose-AFW3ZTZ4.js +12 -0
  45. package/dist/display-IIUBEYWN.js +58 -0
  46. package/dist/extractor-QV53W2YJ.js +129 -0
  47. package/dist/history-WMSCHERZ.js +180 -0
  48. package/dist/index.d.ts +1 -0
  49. package/dist/index.js +406 -0
  50. package/dist/instance-registry-YSIJXSO7.js +15 -0
  51. package/dist/keybindings-JAAMLH3G.js +15 -0
  52. package/dist/loader-WHNTZTLP.js +58 -0
  53. package/dist/network-MM6YWPGO.js +279 -0
  54. package/dist/notify-HPTALZDC.js +14 -0
  55. package/dist/openai-compat-UQWJXBEK.js +12 -0
  56. package/dist/permissions-JUKXMNDH.js +10 -0
  57. package/dist/prompt-QV45TXRL.js +166 -0
  58. package/dist/quality-ST7PPNFR.js +16 -0
  59. package/dist/repl-RT3AHL7M.js +3375 -0
  60. package/dist/roadmap-5OBEKROY.js +17 -0
  61. package/dist/server-PORT7OEG.js +57 -0
  62. package/dist/session-4VUNDWLH.js +21 -0
  63. package/dist/skills-V4A35XKG.js +175 -0
  64. package/dist/store-Y4LU5QTO.js +25 -0
  65. package/dist/team-HO7Z4SIM.js +385 -0
  66. package/dist/telemetry-6R4EIE6O.js +30 -0
  67. package/dist/test-runner-ZQH5Y6OJ.js +619 -0
  68. package/dist/theme-3SYJ3UQA.js +14 -0
  69. package/dist/upgrade-7TGI3SXO.js +83 -0
  70. package/dist/verify-JUDKTPKZ.js +14 -0
  71. package/dist/web/static/app.js +562 -0
  72. package/dist/web/static/index.html +132 -0
  73. package/dist/web/static/mirror.css +1001 -0
  74. package/dist/web/static/mirror.html +184 -0
  75. package/dist/web/static/mirror.js +1125 -0
  76. package/dist/web/static/onboard.css +302 -0
  77. package/dist/web/static/onboard.html +140 -0
  78. package/dist/web/static/onboard.js +260 -0
  79. package/dist/web/static/style.css +602 -0
  80. package/dist/web/static/workspace.css +1568 -0
  81. package/dist/web/static/workspace.html +408 -0
  82. package/dist/web/static/workspace.js +1683 -0
  83. package/dist/web-Z5HSCQHW.js +39 -0
  84. package/package.json +67 -0
@@ -0,0 +1,30 @@
1
+ import {
2
+ getEventsFilePath,
3
+ getEventsSummary,
4
+ readEvents,
5
+ setTelemetryEnabled,
6
+ trackCmdExec,
7
+ trackError,
8
+ trackEvent,
9
+ trackLineRun,
10
+ trackModelSwitch,
11
+ trackSessionEnd,
12
+ trackSessionStart,
13
+ trackTeamAction,
14
+ trackWebStart
15
+ } from "./chunk-2CMSCWQW.js";
16
+ export {
17
+ getEventsFilePath,
18
+ getEventsSummary,
19
+ readEvents,
20
+ setTelemetryEnabled,
21
+ trackCmdExec,
22
+ trackError,
23
+ trackEvent,
24
+ trackLineRun,
25
+ trackModelSwitch,
26
+ trackSessionEnd,
27
+ trackSessionStart,
28
+ trackTeamAction,
29
+ trackWebStart
30
+ };
@@ -0,0 +1,619 @@
1
+ import {
2
+ HYPERCORE_DIR
3
+ } from "./chunk-V5UHPPSY.js";
4
+
5
+ // src/admin/test-runner.ts
6
+ import { spawn } from "child_process";
7
+ import { existsSync } from "fs";
8
+ import { mkdir, readFile, readdir, unlink, writeFile } from "fs/promises";
9
+ import { join } from "path";
10
+ var DEFAULT_TIMEOUT_MS = 3 * 60 * 1e3;
11
+ var MAX_OUTPUT_CHARS = 2e4;
12
+ var MAX_OUTPUT_LOG_CHARS = 5e5;
13
+ var MAX_HISTORY_RUNS = 30;
14
+ var HISTORY_PREVIEW_RUNS = 10;
15
+ var DEFAULT_AUTO_INTERVAL_MINUTES = 30;
16
+ var MIN_AUTO_INTERVAL_MINUTES = 5;
17
+ var MAX_AUTO_INTERVAL_MINUTES = 24 * 60;
18
+ var DEFAULT_BASE_URL = "http://127.0.0.1:3210";
19
+ var activeRun = null;
20
+ var latestRun = null;
21
+ var runHistoryLoaded = false;
22
+ var runHistory = [];
23
+ var autoConfigLoaded = false;
24
+ var autoConfig = null;
25
+ var autoSchedulerTimer = null;
26
+ var autoNextRunAt = null;
27
+ var autoLastAttemptAt = null;
28
+ var autoLastRunId = null;
29
+ var autoLastError = null;
30
+ var autoContext = {
31
+ projectRoot: process.cwd(),
32
+ baseUrl: DEFAULT_BASE_URL
33
+ };
34
+ function nowISO() {
35
+ return (/* @__PURE__ */ new Date()).toISOString();
36
+ }
37
+ function trimTail(text) {
38
+ if (text.length <= MAX_OUTPUT_CHARS) return text;
39
+ return text.slice(text.length - MAX_OUTPUT_CHARS);
40
+ }
41
+ function trimLog(text) {
42
+ if (text.length <= MAX_OUTPUT_LOG_CHARS) return text;
43
+ return text.slice(text.length - MAX_OUTPUT_LOG_CHARS);
44
+ }
45
+ function historyFilePath() {
46
+ return join(HYPERCORE_DIR, "admin", "test-runs.json");
47
+ }
48
+ function logsDirPath() {
49
+ return join(HYPERCORE_DIR, "admin", "test-run-logs");
50
+ }
51
+ function runLogFilePath(runId) {
52
+ return join(logsDirPath(), `${runId}.log`);
53
+ }
54
+ function autoConfigFilePath() {
55
+ return join(HYPERCORE_DIR, "admin", "test-auto.json");
56
+ }
57
+ function sanitizeFileToken(value) {
58
+ const normalized = value.trim().replace(/[^a-zA-Z0-9._-]+/g, "_");
59
+ return normalized.replace(/^_+|_+$/g, "") || "unknown";
60
+ }
61
+ function getTestRunDownloadFileName(info, ext = "log") {
62
+ const suiteId = sanitizeFileToken(info.suiteId);
63
+ const version = sanitizeFileToken(info.suiteVersion);
64
+ const runId = sanitizeFileToken(info.runId);
65
+ return `hypermirror-${suiteId}-v${version}-${runId}.${ext}`;
66
+ }
67
+ function formatTestRunDownloadText(detail) {
68
+ const info = detail.info;
69
+ const headerLines = [
70
+ "# HyperMirror Test Run Log",
71
+ `runId: ${info.runId}`,
72
+ `suiteId: ${info.suiteId}`,
73
+ `suiteName: ${info.suiteName}`,
74
+ `suiteVersion: ${info.suiteVersion}`,
75
+ `status: ${info.status}`,
76
+ `startedAt: ${info.startedAt}`,
77
+ `endedAt: ${info.endedAt || "-"}`,
78
+ `durationMs: ${info.durationMs ?? "-"}`,
79
+ `exitCode: ${info.exitCode ?? "-"}`,
80
+ `signal: ${info.signal ?? "-"}`,
81
+ `isRunning: ${detail.isRunning ? "true" : "false"}`,
82
+ "---"
83
+ ];
84
+ const output = detail.output || "";
85
+ return `${headerLines.join("\n")}
86
+ ${output}`;
87
+ }
88
+ function normalizeExec(exec) {
89
+ if (!Array.isArray(exec)) return [];
90
+ return exec.filter((v) => typeof v === "string" && v.trim().length > 0);
91
+ }
92
+ function sanitizeSuite(raw) {
93
+ if (!raw || typeof raw !== "object") return null;
94
+ const obj = raw;
95
+ const id = typeof obj.id === "string" ? obj.id.trim() : "";
96
+ const name = typeof obj.name === "string" ? obj.name.trim() : "";
97
+ const version = typeof obj.version === "string" ? obj.version.trim() : "";
98
+ const description = typeof obj.description === "string" ? obj.description : void 0;
99
+ const exec = normalizeExec(obj.exec);
100
+ const timeoutMs = typeof obj.timeoutMs === "number" && Number.isFinite(obj.timeoutMs) && obj.timeoutMs > 0 ? Math.floor(obj.timeoutMs) : void 0;
101
+ if (!id || !name || !version || exec.length === 0) return null;
102
+ return { id, name, version, description, exec, timeoutMs };
103
+ }
104
+ function defaultManifest() {
105
+ return {
106
+ schemaVersion: 1,
107
+ activeSuiteId: "api-regression",
108
+ suites: [
109
+ {
110
+ id: "api-regression",
111
+ name: "API Regression",
112
+ version: "1.0.0",
113
+ description: "\u8986\u76D6\u6838\u5FC3 Web/API/WS \u4E3B\u94FE\u8DEF",
114
+ exec: ["node", "tests/e2e/run-api-regression.mjs"],
115
+ timeoutMs: DEFAULT_TIMEOUT_MS
116
+ }
117
+ ]
118
+ };
119
+ }
120
+ function normalizeManifest(raw) {
121
+ const base = defaultManifest();
122
+ if (!raw || typeof raw !== "object") return base;
123
+ const obj = raw;
124
+ const suites = Array.isArray(obj.suites) ? obj.suites.map(sanitizeSuite).filter((s) => !!s) : [];
125
+ const schemaVersion = typeof obj.schemaVersion === "number" && Number.isFinite(obj.schemaVersion) ? Math.floor(obj.schemaVersion) : 1;
126
+ const activeSuiteId = typeof obj.activeSuiteId === "string" && obj.activeSuiteId.trim() ? obj.activeSuiteId.trim() : suites[0]?.id || base.activeSuiteId;
127
+ return {
128
+ schemaVersion,
129
+ activeSuiteId,
130
+ suites: suites.length > 0 ? suites : base.suites
131
+ };
132
+ }
133
+ function resolveRunnerContext(projectRootOrOpts) {
134
+ const envBaseUrl = process.env.HYPER_E2E_BASE_URL || DEFAULT_BASE_URL;
135
+ if (typeof projectRootOrOpts === "string") {
136
+ return {
137
+ projectRoot: projectRootOrOpts || process.cwd(),
138
+ baseUrl: envBaseUrl
139
+ };
140
+ }
141
+ const opts = projectRootOrOpts || {};
142
+ return {
143
+ projectRoot: opts.projectRoot || process.cwd(),
144
+ baseUrl: opts.baseUrl?.trim() || envBaseUrl
145
+ };
146
+ }
147
+ function pickDefaultSuiteId(manifest) {
148
+ if (manifest.activeSuiteId && manifest.suites.some((s) => s.id === manifest.activeSuiteId)) {
149
+ return manifest.activeSuiteId;
150
+ }
151
+ return manifest.suites[0]?.id || "";
152
+ }
153
+ function clampAutoIntervalMinutes(value) {
154
+ return Math.min(MAX_AUTO_INTERVAL_MINUTES, Math.max(MIN_AUTO_INTERVAL_MINUTES, Math.floor(value)));
155
+ }
156
+ function parseIntervalMinutes(value) {
157
+ if (typeof value === "number" && Number.isFinite(value)) {
158
+ return clampAutoIntervalMinutes(value);
159
+ }
160
+ if (typeof value === "string" && value.trim()) {
161
+ const num = Number(value);
162
+ if (Number.isFinite(num)) {
163
+ return clampAutoIntervalMinutes(num);
164
+ }
165
+ }
166
+ return null;
167
+ }
168
+ function normalizeAutoRegressionConfig(raw, manifest) {
169
+ const obj = raw && typeof raw === "object" ? raw : {};
170
+ const suiteSet = new Set(manifest.suites.map((s) => s.id));
171
+ const defaultSuiteId = pickDefaultSuiteId(manifest);
172
+ const intervalMinutes = parseIntervalMinutes(obj.intervalMinutes) ?? DEFAULT_AUTO_INTERVAL_MINUTES;
173
+ const suiteCandidate = typeof obj.suiteId === "string" ? obj.suiteId.trim() : "";
174
+ const suiteId = suiteSet.has(suiteCandidate) ? suiteCandidate : defaultSuiteId;
175
+ return {
176
+ enabled: typeof obj.enabled === "boolean" ? obj.enabled : false,
177
+ suiteId,
178
+ intervalMinutes,
179
+ updatedAt: typeof obj.updatedAt === "string" && obj.updatedAt ? obj.updatedAt : nowISO()
180
+ };
181
+ }
182
+ function autoRuntimeSnapshot(config) {
183
+ return {
184
+ nextRunAt: autoNextRunAt,
185
+ lastAttemptAt: autoLastAttemptAt,
186
+ lastRunId: autoLastRunId,
187
+ lastError: autoLastError,
188
+ schedulerState: config.enabled && !!autoSchedulerTimer ? "active" : "idle"
189
+ };
190
+ }
191
+ function autoSnapshot(config) {
192
+ return {
193
+ config,
194
+ runtime: autoRuntimeSnapshot(config)
195
+ };
196
+ }
197
+ function clearAutoSchedulerTimer() {
198
+ if (autoSchedulerTimer) {
199
+ clearTimeout(autoSchedulerTimer);
200
+ autoSchedulerTimer = null;
201
+ }
202
+ autoNextRunAt = null;
203
+ }
204
+ function applyAutoContext(context) {
205
+ autoContext = context;
206
+ }
207
+ function sanitizeRunInfo(raw) {
208
+ if (!raw || typeof raw !== "object") return null;
209
+ const obj = raw;
210
+ const runId = typeof obj.runId === "string" ? obj.runId : "";
211
+ const suiteId = typeof obj.suiteId === "string" ? obj.suiteId : "";
212
+ const suiteName = typeof obj.suiteName === "string" ? obj.suiteName : "";
213
+ const suiteVersion = typeof obj.suiteVersion === "string" ? obj.suiteVersion : "";
214
+ const startedAt = typeof obj.startedAt === "string" ? obj.startedAt : "";
215
+ const endedAt = typeof obj.endedAt === "string" ? obj.endedAt : void 0;
216
+ const durationMs = typeof obj.durationMs === "number" && Number.isFinite(obj.durationMs) ? Math.floor(obj.durationMs) : void 0;
217
+ const status = typeof obj.status === "string" ? obj.status : "";
218
+ const outputTail = typeof obj.outputTail === "string" ? trimTail(obj.outputTail) : "";
219
+ const exitCode = typeof obj.exitCode === "number" || obj.exitCode === null ? obj.exitCode : void 0;
220
+ const signal = typeof obj.signal === "string" || obj.signal === null ? obj.signal : void 0;
221
+ if (!runId || !suiteId || !suiteName || !suiteVersion || !startedAt || !status) {
222
+ return null;
223
+ }
224
+ if (!["running", "passed", "failed", "timeout"].includes(status)) {
225
+ return null;
226
+ }
227
+ return {
228
+ runId,
229
+ suiteId,
230
+ suiteName,
231
+ suiteVersion,
232
+ startedAt,
233
+ endedAt,
234
+ durationMs,
235
+ status,
236
+ exitCode,
237
+ signal,
238
+ outputTail
239
+ };
240
+ }
241
+ async function loadRunHistory() {
242
+ if (runHistoryLoaded) return;
243
+ runHistoryLoaded = true;
244
+ const file = historyFilePath();
245
+ if (!existsSync(file)) {
246
+ runHistory = [];
247
+ return;
248
+ }
249
+ try {
250
+ const content = await readFile(file, "utf-8");
251
+ const parsed = JSON.parse(content);
252
+ if (!Array.isArray(parsed)) {
253
+ runHistory = [];
254
+ return;
255
+ }
256
+ runHistory = parsed.map(sanitizeRunInfo).filter((v) => !!v).slice(0, MAX_HISTORY_RUNS);
257
+ } catch {
258
+ runHistory = [];
259
+ }
260
+ }
261
+ async function persistRunHistory() {
262
+ const file = historyFilePath();
263
+ await mkdir(join(HYPERCORE_DIR, "admin"), { recursive: true });
264
+ await writeFile(file, JSON.stringify(runHistory.slice(0, MAX_HISTORY_RUNS), null, 2), "utf-8");
265
+ }
266
+ async function writeRunLog(runId, output) {
267
+ await mkdir(logsDirPath(), { recursive: true });
268
+ await writeFile(runLogFilePath(runId), trimLog(output), "utf-8");
269
+ }
270
+ async function readRunLog(runId) {
271
+ try {
272
+ const file = runLogFilePath(runId);
273
+ if (!existsSync(file)) return null;
274
+ return await readFile(file, "utf-8");
275
+ } catch {
276
+ return null;
277
+ }
278
+ }
279
+ async function cleanupRunLogs() {
280
+ const dir = logsDirPath();
281
+ if (!existsSync(dir)) return;
282
+ const keep = new Set(runHistory.map((r) => `${r.runId}.log`));
283
+ try {
284
+ const files = await readdir(dir);
285
+ await Promise.all(
286
+ files.filter((file) => file.endsWith(".log") && !keep.has(file)).map((file) => unlink(join(dir, file)).catch(() => {
287
+ }))
288
+ );
289
+ } catch {
290
+ }
291
+ }
292
+ async function appendHistory(info) {
293
+ await loadRunHistory();
294
+ runHistory = [info, ...runHistory.filter((r) => r.runId !== info.runId)].slice(0, MAX_HISTORY_RUNS);
295
+ await persistRunHistory();
296
+ await cleanupRunLogs();
297
+ }
298
+ async function loadAutoRegressionConfig(manifest) {
299
+ if (autoConfigLoaded && autoConfig) {
300
+ autoConfig = normalizeAutoRegressionConfig(autoConfig, manifest);
301
+ return autoConfig;
302
+ }
303
+ autoConfigLoaded = true;
304
+ const file = autoConfigFilePath();
305
+ if (!existsSync(file)) {
306
+ autoConfig = normalizeAutoRegressionConfig({}, manifest);
307
+ return autoConfig;
308
+ }
309
+ try {
310
+ const content = await readFile(file, "utf-8");
311
+ const parsed = JSON.parse(content);
312
+ autoConfig = normalizeAutoRegressionConfig(parsed, manifest);
313
+ return autoConfig;
314
+ } catch {
315
+ autoConfig = normalizeAutoRegressionConfig({}, manifest);
316
+ return autoConfig;
317
+ }
318
+ }
319
+ async function persistAutoRegressionConfig(config) {
320
+ const file = autoConfigFilePath();
321
+ await mkdir(join(HYPERCORE_DIR, "admin"), { recursive: true });
322
+ await writeFile(file, JSON.stringify(config, null, 2), "utf-8");
323
+ }
324
+ function scheduleNextAutoRegressionRun(config) {
325
+ clearAutoSchedulerTimer();
326
+ if (!config.enabled) return;
327
+ const delayMs = config.intervalMinutes * 60 * 1e3;
328
+ autoNextRunAt = new Date(Date.now() + delayMs).toISOString();
329
+ autoSchedulerTimer = setTimeout(() => {
330
+ void executeAutoRegressionRun();
331
+ }, delayMs);
332
+ if (typeof autoSchedulerTimer.unref === "function") {
333
+ autoSchedulerTimer.unref();
334
+ }
335
+ }
336
+ async function executeAutoRegressionRun() {
337
+ autoSchedulerTimer = null;
338
+ autoNextRunAt = null;
339
+ const config = autoConfig;
340
+ if (!config || !config.enabled) return;
341
+ autoLastAttemptAt = nowISO();
342
+ try {
343
+ const snapshot = await startTestSuiteRun({
344
+ projectRoot: autoContext.projectRoot,
345
+ baseUrl: autoContext.baseUrl,
346
+ suiteId: config.suiteId,
347
+ trigger: "auto"
348
+ });
349
+ autoLastRunId = snapshot.running?.runId || snapshot.latest?.runId || autoLastRunId;
350
+ autoLastError = null;
351
+ } catch (err) {
352
+ if (err && typeof err === "object" && "statusCode" in err && err.statusCode === 409) {
353
+ autoLastError = "\u8DF3\u8FC7\u81EA\u52A8\u56DE\u5F52\uFF1A\u5DF2\u6709\u6D4B\u8BD5\u8FD0\u884C\u4E2D";
354
+ } else {
355
+ autoLastError = err instanceof Error ? err.message : String(err);
356
+ }
357
+ } finally {
358
+ if (autoConfig?.enabled) {
359
+ scheduleNextAutoRegressionRun(autoConfig);
360
+ }
361
+ }
362
+ }
363
+ function ensureAutoScheduler(config) {
364
+ if (!config.enabled) {
365
+ clearAutoSchedulerTimer();
366
+ return;
367
+ }
368
+ if (!autoSchedulerTimer) {
369
+ scheduleNextAutoRegressionRun(config);
370
+ }
371
+ }
372
+ async function ensureAutoConfig(context, manifest) {
373
+ applyAutoContext(context);
374
+ const config = await loadAutoRegressionConfig(manifest);
375
+ autoConfig = config;
376
+ return config;
377
+ }
378
+ async function getAutoRegressionSnapshot(opts = process.cwd()) {
379
+ const context = resolveRunnerContext(opts);
380
+ const manifest = await loadTestSuiteManifest(context.projectRoot);
381
+ const config = await ensureAutoConfig(context, manifest);
382
+ ensureAutoScheduler(config);
383
+ return autoSnapshot(config);
384
+ }
385
+ async function updateAutoRegressionConfig(update, opts = process.cwd()) {
386
+ const context = resolveRunnerContext(opts);
387
+ const manifest = await loadTestSuiteManifest(context.projectRoot);
388
+ const current = await ensureAutoConfig(context, manifest);
389
+ const suiteSet = new Set(manifest.suites.map((s) => s.id));
390
+ let enabled = current.enabled;
391
+ if (Object.prototype.hasOwnProperty.call(update, "enabled")) {
392
+ if (typeof update.enabled !== "boolean") {
393
+ throw new TestRunnerError("enabled \u5FC5\u987B\u662F boolean", 400);
394
+ }
395
+ enabled = update.enabled;
396
+ }
397
+ let suiteId = current.suiteId;
398
+ if (Object.prototype.hasOwnProperty.call(update, "suiteId")) {
399
+ if (typeof update.suiteId !== "string" || !update.suiteId.trim()) {
400
+ throw new TestRunnerError("suiteId \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32", 400);
401
+ }
402
+ const candidate = update.suiteId.trim();
403
+ if (!suiteSet.has(candidate)) {
404
+ throw new TestRunnerError(`\u672A\u77E5\u6D4B\u8BD5\u5957\u4EF6: ${candidate}`, 400);
405
+ }
406
+ suiteId = candidate;
407
+ }
408
+ let intervalMinutes = current.intervalMinutes;
409
+ if (Object.prototype.hasOwnProperty.call(update, "intervalMinutes")) {
410
+ if (typeof update.intervalMinutes !== "number" || !Number.isFinite(update.intervalMinutes)) {
411
+ throw new TestRunnerError("intervalMinutes \u5FC5\u987B\u662F\u6570\u5B57", 400);
412
+ }
413
+ const value = Math.floor(update.intervalMinutes);
414
+ if (value < MIN_AUTO_INTERVAL_MINUTES || value > MAX_AUTO_INTERVAL_MINUTES) {
415
+ throw new TestRunnerError(`intervalMinutes \u5FC5\u987B\u5728 ${MIN_AUTO_INTERVAL_MINUTES}-${MAX_AUTO_INTERVAL_MINUTES} \u4E4B\u95F4`, 400);
416
+ }
417
+ intervalMinutes = value;
418
+ }
419
+ const nextConfig = {
420
+ enabled,
421
+ suiteId,
422
+ intervalMinutes,
423
+ updatedAt: nowISO()
424
+ };
425
+ autoConfig = nextConfig;
426
+ await persistAutoRegressionConfig(nextConfig);
427
+ if (nextConfig.enabled) {
428
+ autoLastError = null;
429
+ scheduleNextAutoRegressionRun(nextConfig);
430
+ } else {
431
+ clearAutoSchedulerTimer();
432
+ }
433
+ return autoSnapshot(nextConfig);
434
+ }
435
+ async function loadTestSuiteManifest(projectRoot = process.cwd()) {
436
+ const manifestPath = join(projectRoot, "tests", "e2e", "manifest.json");
437
+ if (!existsSync(manifestPath)) {
438
+ return defaultManifest();
439
+ }
440
+ try {
441
+ const content = await readFile(manifestPath, "utf-8");
442
+ return normalizeManifest(JSON.parse(content));
443
+ } catch {
444
+ return defaultManifest();
445
+ }
446
+ }
447
+ function materializeRunningInfo(run) {
448
+ return {
449
+ ...run.info,
450
+ durationMs: Date.now() - run.startedMs,
451
+ outputTail: trimTail(run.output)
452
+ };
453
+ }
454
+ async function getTestRunnerSnapshot(opts = process.cwd()) {
455
+ const context = resolveRunnerContext(opts);
456
+ await loadRunHistory();
457
+ const manifest = await loadTestSuiteManifest(context.projectRoot);
458
+ const automationConfig = await ensureAutoConfig(context, manifest);
459
+ ensureAutoScheduler(automationConfig);
460
+ return {
461
+ manifest,
462
+ running: activeRun ? materializeRunningInfo(activeRun) : null,
463
+ latest: latestRun,
464
+ history: runHistory.slice(0, HISTORY_PREVIEW_RUNS),
465
+ automation: autoSnapshot(automationConfig)
466
+ };
467
+ }
468
+ async function getTestRunDetail(runId) {
469
+ await loadRunHistory();
470
+ if (activeRun && activeRun.info.runId === runId) {
471
+ return {
472
+ info: materializeRunningInfo(activeRun),
473
+ output: trimLog(activeRun.output),
474
+ isRunning: true
475
+ };
476
+ }
477
+ const info = runHistory.find((r) => r.runId === runId);
478
+ if (!info) {
479
+ throw new TestRunnerError(`\u672A\u627E\u5230\u6D4B\u8BD5\u8FD0\u884C\u8BB0\u5F55: ${runId}`, 404);
480
+ }
481
+ const output = await readRunLog(runId);
482
+ return {
483
+ info,
484
+ output: output || info.outputTail || "",
485
+ isRunning: false
486
+ };
487
+ }
488
+ var TestRunnerError = class extends Error {
489
+ statusCode;
490
+ constructor(message, statusCode = 400) {
491
+ super(message);
492
+ this.statusCode = statusCode;
493
+ }
494
+ };
495
+ async function startTestSuiteRun(opts = {}) {
496
+ if (activeRun) {
497
+ throw new TestRunnerError("\u5DF2\u6709\u6D4B\u8BD5\u4EFB\u52A1\u5728\u8FD0\u884C\u4E2D\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5", 409);
498
+ }
499
+ await loadRunHistory();
500
+ const context = resolveRunnerContext(opts);
501
+ const projectRoot = context.projectRoot;
502
+ const manifest = await loadTestSuiteManifest(projectRoot);
503
+ const automationConfig = await ensureAutoConfig(context, manifest);
504
+ const targetSuiteId = opts.suiteId || manifest.activeSuiteId || manifest.suites[0]?.id;
505
+ const suite = manifest.suites.find((s) => s.id === targetSuiteId) || manifest.suites[0];
506
+ if (!suite) {
507
+ throw new TestRunnerError("\u672A\u627E\u5230\u53EF\u6267\u884C\u6D4B\u8BD5\u5957\u4EF6", 404);
508
+ }
509
+ if (suite.exec.length === 0) {
510
+ throw new TestRunnerError(`\u6D4B\u8BD5\u5957\u4EF6 ${suite.id} \u672A\u914D\u7F6E exec`, 400);
511
+ }
512
+ const runId = `test_${Date.now().toString(36)}`;
513
+ const startedAt = nowISO();
514
+ const command = suite.exec[0];
515
+ const args = suite.exec.slice(1);
516
+ const env = {
517
+ ...process.env,
518
+ HYPER_E2E_BASE_URL: context.baseUrl || process.env.HYPER_E2E_BASE_URL || DEFAULT_BASE_URL,
519
+ HYPER_E2E_SUITE_ID: suite.id,
520
+ HYPER_E2E_SUITE_VERSION: suite.version
521
+ };
522
+ const child = spawn(command, args, {
523
+ cwd: projectRoot,
524
+ env,
525
+ stdio: ["ignore", "pipe", "pipe"]
526
+ });
527
+ const timeoutMs = suite.timeoutMs || DEFAULT_TIMEOUT_MS;
528
+ const info = {
529
+ runId,
530
+ suiteId: suite.id,
531
+ suiteName: suite.name,
532
+ suiteVersion: suite.version,
533
+ startedAt,
534
+ status: "running",
535
+ outputTail: ""
536
+ };
537
+ activeRun = {
538
+ child,
539
+ startedMs: Date.now(),
540
+ timer: setTimeout(() => {
541
+ if (!activeRun || activeRun.info.runId !== runId) return;
542
+ activeRun.timedOut = true;
543
+ activeRun.info.status = "timeout";
544
+ child.kill("SIGTERM");
545
+ }, timeoutMs),
546
+ info,
547
+ output: "",
548
+ timedOut: false
549
+ };
550
+ const appendOutput = (chunk) => {
551
+ if (!activeRun || activeRun.info.runId !== runId) return;
552
+ activeRun.output = trimLog(`${activeRun.output}${chunk.toString("utf-8")}`);
553
+ };
554
+ child.stdout.on("data", appendOutput);
555
+ child.stderr.on("data", appendOutput);
556
+ child.on("error", (err) => {
557
+ if (!activeRun || activeRun.info.runId !== runId) return;
558
+ clearTimeout(activeRun.timer);
559
+ const endedAt = nowISO();
560
+ latestRun = {
561
+ ...activeRun.info,
562
+ endedAt,
563
+ durationMs: Date.now() - activeRun.startedMs,
564
+ status: "failed",
565
+ exitCode: null,
566
+ signal: null,
567
+ outputTail: trimTail(`${activeRun.output}
568
+ [runner error] ${err.message}`)
569
+ };
570
+ writeRunLog(latestRun.runId, `${activeRun.output}
571
+ [runner error] ${err.message}`).catch(() => {
572
+ });
573
+ appendHistory(latestRun).catch(() => {
574
+ });
575
+ activeRun = null;
576
+ });
577
+ child.on("exit", (code, signal) => {
578
+ if (!activeRun || activeRun.info.runId !== runId) return;
579
+ clearTimeout(activeRun.timer);
580
+ const endedAt = nowISO();
581
+ const status = activeRun.timedOut ? "timeout" : code === 0 ? "passed" : "failed";
582
+ latestRun = {
583
+ ...activeRun.info,
584
+ endedAt,
585
+ durationMs: Date.now() - activeRun.startedMs,
586
+ status,
587
+ exitCode: code,
588
+ signal,
589
+ outputTail: trimTail(activeRun.output)
590
+ };
591
+ writeRunLog(latestRun.runId, activeRun.output).catch(() => {
592
+ });
593
+ appendHistory(latestRun).catch(() => {
594
+ });
595
+ activeRun = null;
596
+ });
597
+ const running = activeRun ? materializeRunningInfo(activeRun) : null;
598
+ if (opts.trigger !== "auto" && automationConfig.enabled) {
599
+ scheduleNextAutoRegressionRun(automationConfig);
600
+ }
601
+ return {
602
+ manifest,
603
+ running,
604
+ latest: latestRun,
605
+ history: runHistory.slice(0, HISTORY_PREVIEW_RUNS),
606
+ automation: autoSnapshot(automationConfig)
607
+ };
608
+ }
609
+ export {
610
+ TestRunnerError,
611
+ formatTestRunDownloadText,
612
+ getAutoRegressionSnapshot,
613
+ getTestRunDetail,
614
+ getTestRunDownloadFileName,
615
+ getTestRunnerSnapshot,
616
+ loadTestSuiteManifest,
617
+ startTestSuiteRun,
618
+ updateAutoRegressionConfig
619
+ };
@@ -0,0 +1,14 @@
1
+ import {
2
+ THEME_LABELS,
3
+ getAvailableThemes,
4
+ getTheme,
5
+ getThemeName,
6
+ setTheme
7
+ } from "./chunk-RNG3K465.js";
8
+ export {
9
+ THEME_LABELS,
10
+ getAvailableThemes,
11
+ getTheme,
12
+ getThemeName,
13
+ setTheme
14
+ };