opencode-supertask 0.1.28 → 0.1.30
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.
- package/README.md +3 -1
- package/dist/cli/index.js +1100 -563
- package/dist/cli/index.js.map +1 -1
- package/dist/gateway/index.js +1096 -562
- package/dist/gateway/index.js.map +1 -1
- package/dist/plugin/supertask.js.map +1 -1
- package/dist/web/index.js +3109 -2572
- package/dist/web/index.js.map +1 -1
- package/package.json +1 -1
package/dist/gateway/index.js
CHANGED
|
@@ -9486,8 +9486,8 @@ var require_luxon = __commonJS({
|
|
|
9486
9486
|
* Set whether Luxon will throw when it encounters invalid DateTimes, Durations, or Intervals
|
|
9487
9487
|
* @type {boolean}
|
|
9488
9488
|
*/
|
|
9489
|
-
static set throwOnInvalid(
|
|
9490
|
-
throwOnInvalid =
|
|
9489
|
+
static set throwOnInvalid(t2) {
|
|
9490
|
+
throwOnInvalid = t2;
|
|
9491
9491
|
}
|
|
9492
9492
|
/**
|
|
9493
9493
|
* Reset Luxon's global caches. Should only be necessary in testing scenarios.
|
|
@@ -10380,7 +10380,7 @@ var require_luxon = __commonJS({
|
|
|
10380
10380
|
}, tokens = _Formatter.parseFormat(fmt), realTokens = tokens.reduce((found, {
|
|
10381
10381
|
literal,
|
|
10382
10382
|
val
|
|
10383
|
-
}) => literal ? found : found.concat(val), []), collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter((
|
|
10383
|
+
}) => literal ? found : found.concat(val), []), collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter((t2) => t2)), durationInfo = {
|
|
10384
10384
|
isNegativeDuration: collapsed < 0,
|
|
10385
10385
|
// this relies on "collapsed" being based on "shiftTo", which builds up the object
|
|
10386
10386
|
// in order
|
|
@@ -12290,15 +12290,15 @@ var require_luxon = __commonJS({
|
|
|
12290
12290
|
return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
|
|
12291
12291
|
}
|
|
12292
12292
|
function unitForToken(token, loc) {
|
|
12293
|
-
const one = digitRegex(loc), two = digitRegex(loc, "{2}"), three = digitRegex(loc, "{3}"), four = digitRegex(loc, "{4}"), six = digitRegex(loc, "{6}"), oneOrTwo = digitRegex(loc, "{1,2}"), oneToThree = digitRegex(loc, "{1,3}"), oneToSix = digitRegex(loc, "{1,6}"), oneToNine = digitRegex(loc, "{1,9}"), twoToFour = digitRegex(loc, "{2,4}"), fourToSix = digitRegex(loc, "{4,6}"), literal = (
|
|
12294
|
-
regex: RegExp(escapeToken(
|
|
12293
|
+
const one = digitRegex(loc), two = digitRegex(loc, "{2}"), three = digitRegex(loc, "{3}"), four = digitRegex(loc, "{4}"), six = digitRegex(loc, "{6}"), oneOrTwo = digitRegex(loc, "{1,2}"), oneToThree = digitRegex(loc, "{1,3}"), oneToSix = digitRegex(loc, "{1,6}"), oneToNine = digitRegex(loc, "{1,9}"), twoToFour = digitRegex(loc, "{2,4}"), fourToSix = digitRegex(loc, "{4,6}"), literal = (t2) => ({
|
|
12294
|
+
regex: RegExp(escapeToken(t2.val)),
|
|
12295
12295
|
deser: ([s2]) => s2,
|
|
12296
12296
|
literal: true
|
|
12297
|
-
}), unitate = (
|
|
12297
|
+
}), unitate = (t2) => {
|
|
12298
12298
|
if (token.literal) {
|
|
12299
|
-
return literal(
|
|
12299
|
+
return literal(t2);
|
|
12300
12300
|
}
|
|
12301
|
-
switch (
|
|
12301
|
+
switch (t2.val) {
|
|
12302
12302
|
// era
|
|
12303
12303
|
case "G":
|
|
12304
12304
|
return oneOf(loc.eras("short"), 0);
|
|
@@ -12413,7 +12413,7 @@ var require_luxon = __commonJS({
|
|
|
12413
12413
|
case " ":
|
|
12414
12414
|
return simple(/[^\S\n\r]/);
|
|
12415
12415
|
default:
|
|
12416
|
-
return literal(
|
|
12416
|
+
return literal(t2);
|
|
12417
12417
|
}
|
|
12418
12418
|
};
|
|
12419
12419
|
const unit = unitate(token) || {
|
|
@@ -12615,15 +12615,15 @@ var require_luxon = __commonJS({
|
|
|
12615
12615
|
return tokens;
|
|
12616
12616
|
}
|
|
12617
12617
|
function expandMacroTokens(tokens, locale) {
|
|
12618
|
-
return Array.prototype.concat(...tokens.map((
|
|
12618
|
+
return Array.prototype.concat(...tokens.map((t2) => maybeExpandMacroToken(t2, locale)));
|
|
12619
12619
|
}
|
|
12620
12620
|
var TokenParser = class {
|
|
12621
12621
|
constructor(locale, format) {
|
|
12622
12622
|
this.locale = locale;
|
|
12623
12623
|
this.format = format;
|
|
12624
12624
|
this.tokens = expandMacroTokens(Formatter.parseFormat(format), locale);
|
|
12625
|
-
this.units = this.tokens.map((
|
|
12626
|
-
this.disqualifyingUnit = this.units.find((
|
|
12625
|
+
this.units = this.tokens.map((t2) => unitForToken(t2, locale));
|
|
12626
|
+
this.disqualifyingUnit = this.units.find((t2) => t2.invalidReason);
|
|
12627
12627
|
if (!this.disqualifyingUnit) {
|
|
12628
12628
|
const [regexString, handlers] = buildRegex(this.units);
|
|
12629
12629
|
this.regex = RegExp(regexString, "i");
|
|
@@ -13448,7 +13448,7 @@ var require_luxon = __commonJS({
|
|
|
13448
13448
|
*/
|
|
13449
13449
|
static parseFormatForOpts(formatOpts, localeOpts = {}) {
|
|
13450
13450
|
const tokenList = formatOptsToTokens(formatOpts, Locale.fromObject(localeOpts));
|
|
13451
|
-
return !tokenList ? null : tokenList.map((
|
|
13451
|
+
return !tokenList ? null : tokenList.map((t2) => t2 ? t2.val : null).join("");
|
|
13452
13452
|
}
|
|
13453
13453
|
/**
|
|
13454
13454
|
* Produce the the fully expanded format token for the locale
|
|
@@ -13459,7 +13459,7 @@ var require_luxon = __commonJS({
|
|
|
13459
13459
|
*/
|
|
13460
13460
|
static expandFormat(fmt, localeOpts = {}) {
|
|
13461
13461
|
const expanded = expandMacroTokens(Formatter.parseFormat(fmt), Locale.fromObject(localeOpts));
|
|
13462
|
-
return expanded.map((
|
|
13462
|
+
return expanded.map((t2) => t2.val).join("");
|
|
13463
13463
|
}
|
|
13464
13464
|
static resetCache() {
|
|
13465
13465
|
zoneOffsetTs = void 0;
|
|
@@ -16316,10 +16316,10 @@ var require_random = __commonJS({
|
|
|
16316
16316
|
}
|
|
16317
16317
|
function mulberry32(seed) {
|
|
16318
16318
|
return () => {
|
|
16319
|
-
let
|
|
16320
|
-
|
|
16321
|
-
|
|
16322
|
-
return ((
|
|
16319
|
+
let t2 = seed += 1831565813;
|
|
16320
|
+
t2 = Math.imul(t2 ^ t2 >>> 15, t2 | 1);
|
|
16321
|
+
t2 ^= t2 + Math.imul(t2 ^ t2 >>> 7, t2 | 61);
|
|
16322
|
+
return ((t2 ^ t2 >>> 14) >>> 0) / 4294967296;
|
|
16323
16323
|
};
|
|
16324
16324
|
}
|
|
16325
16325
|
function seededRandom(str) {
|
|
@@ -17790,7 +17790,7 @@ var init_request = __esm({
|
|
|
17790
17790
|
});
|
|
17791
17791
|
|
|
17792
17792
|
// node_modules/hono/dist/utils/html.js
|
|
17793
|
-
var HtmlEscapedCallbackPhase, raw,
|
|
17793
|
+
var HtmlEscapedCallbackPhase, raw, resolveCallback;
|
|
17794
17794
|
var init_html = __esm({
|
|
17795
17795
|
"node_modules/hono/dist/utils/html.js"() {
|
|
17796
17796
|
"use strict";
|
|
@@ -17805,80 +17805,6 @@ var init_html = __esm({
|
|
|
17805
17805
|
escapedString.callbacks = callbacks;
|
|
17806
17806
|
return escapedString;
|
|
17807
17807
|
};
|
|
17808
|
-
escapeRe = /[&<>'"]/;
|
|
17809
|
-
stringBufferToString = async (buffer, callbacks) => {
|
|
17810
|
-
let str = "";
|
|
17811
|
-
callbacks ||= [];
|
|
17812
|
-
const resolvedBuffer = await Promise.all(buffer);
|
|
17813
|
-
for (let i = resolvedBuffer.length - 1; ; i--) {
|
|
17814
|
-
str += resolvedBuffer[i];
|
|
17815
|
-
i--;
|
|
17816
|
-
if (i < 0) {
|
|
17817
|
-
break;
|
|
17818
|
-
}
|
|
17819
|
-
let r = resolvedBuffer[i];
|
|
17820
|
-
if (typeof r === "object") {
|
|
17821
|
-
callbacks.push(...r.callbacks || []);
|
|
17822
|
-
}
|
|
17823
|
-
const isEscaped = r.isEscaped;
|
|
17824
|
-
r = await (typeof r === "object" ? r.toString() : r);
|
|
17825
|
-
if (typeof r === "object") {
|
|
17826
|
-
callbacks.push(...r.callbacks || []);
|
|
17827
|
-
}
|
|
17828
|
-
if (r.isEscaped ?? isEscaped) {
|
|
17829
|
-
str += r;
|
|
17830
|
-
} else {
|
|
17831
|
-
const buf = [str];
|
|
17832
|
-
escapeToBuffer(r, buf);
|
|
17833
|
-
str = buf[0];
|
|
17834
|
-
}
|
|
17835
|
-
}
|
|
17836
|
-
return raw(str, callbacks);
|
|
17837
|
-
};
|
|
17838
|
-
escapeToBuffer = (str, buffer) => {
|
|
17839
|
-
const match2 = str.search(escapeRe);
|
|
17840
|
-
if (match2 === -1) {
|
|
17841
|
-
buffer[0] += str;
|
|
17842
|
-
return;
|
|
17843
|
-
}
|
|
17844
|
-
let escape;
|
|
17845
|
-
let index2;
|
|
17846
|
-
let lastIndex = 0;
|
|
17847
|
-
for (index2 = match2; index2 < str.length; index2++) {
|
|
17848
|
-
switch (str.charCodeAt(index2)) {
|
|
17849
|
-
case 34:
|
|
17850
|
-
escape = """;
|
|
17851
|
-
break;
|
|
17852
|
-
case 39:
|
|
17853
|
-
escape = "'";
|
|
17854
|
-
break;
|
|
17855
|
-
case 38:
|
|
17856
|
-
escape = "&";
|
|
17857
|
-
break;
|
|
17858
|
-
case 60:
|
|
17859
|
-
escape = "<";
|
|
17860
|
-
break;
|
|
17861
|
-
case 62:
|
|
17862
|
-
escape = ">";
|
|
17863
|
-
break;
|
|
17864
|
-
default:
|
|
17865
|
-
continue;
|
|
17866
|
-
}
|
|
17867
|
-
buffer[0] += str.substring(lastIndex, index2) + escape;
|
|
17868
|
-
lastIndex = index2 + 1;
|
|
17869
|
-
}
|
|
17870
|
-
buffer[0] += str.substring(lastIndex, index2);
|
|
17871
|
-
};
|
|
17872
|
-
resolveCallbackSync = (str) => {
|
|
17873
|
-
const callbacks = str.callbacks;
|
|
17874
|
-
if (!callbacks?.length) {
|
|
17875
|
-
return str;
|
|
17876
|
-
}
|
|
17877
|
-
const buffer = [str];
|
|
17878
|
-
const context = {};
|
|
17879
|
-
callbacks.forEach((c) => c({ phase: HtmlEscapedCallbackPhase.Stringify, buffer, context }));
|
|
17880
|
-
return buffer[0];
|
|
17881
|
-
};
|
|
17882
17808
|
resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => {
|
|
17883
17809
|
if (typeof str === "object" && !(str instanceof String)) {
|
|
17884
17810
|
if (!(str instanceof Promise)) {
|
|
@@ -18276,9 +18202,9 @@ var init_context = __esm({
|
|
|
18276
18202
|
setDefaultContentType("application/json", headers)
|
|
18277
18203
|
);
|
|
18278
18204
|
};
|
|
18279
|
-
html = (
|
|
18280
|
-
const res = (
|
|
18281
|
-
return typeof
|
|
18205
|
+
html = (html, arg, headers) => {
|
|
18206
|
+
const res = (html2) => this.#newResponse(html2, arg, setDefaultContentType("text/html; charset=UTF-8", headers));
|
|
18207
|
+
return typeof html === "object" ? resolveCallback(html, HtmlEscapedCallbackPhase.Stringify, false, {}).then(res) : res(html);
|
|
18282
18208
|
};
|
|
18283
18209
|
/**
|
|
18284
18210
|
* `.redirect()` can Redirect, default status code is 302.
|
|
@@ -19451,50 +19377,6 @@ var init_dist = __esm({
|
|
|
19451
19377
|
}
|
|
19452
19378
|
});
|
|
19453
19379
|
|
|
19454
|
-
// node_modules/hono/dist/helper/html/index.js
|
|
19455
|
-
var html;
|
|
19456
|
-
var init_html2 = __esm({
|
|
19457
|
-
"node_modules/hono/dist/helper/html/index.js"() {
|
|
19458
|
-
"use strict";
|
|
19459
|
-
init_html();
|
|
19460
|
-
html = (strings, ...values) => {
|
|
19461
|
-
const buffer = [""];
|
|
19462
|
-
for (let i = 0, len = strings.length - 1; i < len; i++) {
|
|
19463
|
-
buffer[0] += strings[i];
|
|
19464
|
-
const children = Array.isArray(values[i]) ? values[i].flat(Infinity) : [values[i]];
|
|
19465
|
-
for (let i2 = 0, len2 = children.length; i2 < len2; i2++) {
|
|
19466
|
-
const child = children[i2];
|
|
19467
|
-
if (typeof child === "string") {
|
|
19468
|
-
escapeToBuffer(child, buffer);
|
|
19469
|
-
} else if (typeof child === "number") {
|
|
19470
|
-
;
|
|
19471
|
-
buffer[0] += child;
|
|
19472
|
-
} else if (typeof child === "boolean" || child === null || child === void 0) {
|
|
19473
|
-
continue;
|
|
19474
|
-
} else if (typeof child === "object" && child.isEscaped) {
|
|
19475
|
-
if (child.callbacks) {
|
|
19476
|
-
buffer.unshift("", child);
|
|
19477
|
-
} else {
|
|
19478
|
-
const tmp = child.toString();
|
|
19479
|
-
if (tmp instanceof Promise) {
|
|
19480
|
-
buffer.unshift("", tmp);
|
|
19481
|
-
} else {
|
|
19482
|
-
buffer[0] += tmp;
|
|
19483
|
-
}
|
|
19484
|
-
}
|
|
19485
|
-
} else if (child instanceof Promise) {
|
|
19486
|
-
buffer.unshift("", child);
|
|
19487
|
-
} else {
|
|
19488
|
-
escapeToBuffer(child.toString(), buffer);
|
|
19489
|
-
}
|
|
19490
|
-
}
|
|
19491
|
-
}
|
|
19492
|
-
buffer[0] += strings.at(-1);
|
|
19493
|
-
return buffer.length === 1 ? "callbacks" in buffer ? raw(resolveCallbackSync(raw(buffer[0], buffer.callbacks))) : raw(buffer[0]) : stringBufferToString(buffer, buffer.callbacks);
|
|
19494
|
-
};
|
|
19495
|
-
}
|
|
19496
|
-
});
|
|
19497
|
-
|
|
19498
19380
|
// src/core/services/database-maintenance.service.ts
|
|
19499
19381
|
import { Database as Database3 } from "bun:sqlite";
|
|
19500
19382
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -19931,13 +19813,827 @@ var init_database_maintenance_service = __esm({
|
|
|
19931
19813
|
}
|
|
19932
19814
|
});
|
|
19933
19815
|
|
|
19816
|
+
// src/web/ui.ts
|
|
19817
|
+
function t(locale, key, values = {}) {
|
|
19818
|
+
const template = (locale === "en" ? EN : ZH)[key];
|
|
19819
|
+
return template.replace(/\{([a-zA-Z]+)\}/g, (_, name) => String(values[name] ?? `{${name}}`));
|
|
19820
|
+
}
|
|
19821
|
+
function statusText(locale, status) {
|
|
19822
|
+
const key = `status.${status}`;
|
|
19823
|
+
return key in ZH ? t(locale, key) : t(locale, "status.unknown");
|
|
19824
|
+
}
|
|
19825
|
+
function formatRelative(timestamp2, locale) {
|
|
19826
|
+
if (!timestamp2) return "\u2014";
|
|
19827
|
+
const deltaMs = timestamp2 - Date.now();
|
|
19828
|
+
const abs = Math.abs(deltaMs);
|
|
19829
|
+
const language = locale === "en" ? "en" : "zh-CN";
|
|
19830
|
+
const formatter = new Intl.RelativeTimeFormat(language, { numeric: "auto" });
|
|
19831
|
+
if (abs < 6e4) return formatter.format(Math.round(deltaMs / 1e3), "second");
|
|
19832
|
+
if (abs < 36e5) return formatter.format(Math.round(deltaMs / 6e4), "minute");
|
|
19833
|
+
if (abs < 864e5) return formatter.format(Math.round(deltaMs / 36e5), "hour");
|
|
19834
|
+
return formatter.format(Math.round(deltaMs / 864e5), "day");
|
|
19835
|
+
}
|
|
19836
|
+
function formatFuture(timestamp2, locale) {
|
|
19837
|
+
if (!timestamp2) return "\u2014";
|
|
19838
|
+
if (timestamp2 < Date.now()) return t(locale, "schedule.overdue");
|
|
19839
|
+
return formatRelative(timestamp2, locale);
|
|
19840
|
+
}
|
|
19841
|
+
function formatDateTime(value, locale) {
|
|
19842
|
+
if (!value) return "\u2014";
|
|
19843
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
19844
|
+
return new Intl.DateTimeFormat(locale === "en" ? "en-US" : "zh-CN", {
|
|
19845
|
+
month: "short",
|
|
19846
|
+
day: "2-digit",
|
|
19847
|
+
hour: "2-digit",
|
|
19848
|
+
minute: "2-digit"
|
|
19849
|
+
}).format(date);
|
|
19850
|
+
}
|
|
19851
|
+
function icon(name, className = "icon") {
|
|
19852
|
+
const paths = {
|
|
19853
|
+
brand: '<path d="M7 4.5h10a2.5 2.5 0 0 1 2.5 2.5v10a2.5 2.5 0 0 1-2.5 2.5H7A2.5 2.5 0 0 1 4.5 17V7A2.5 2.5 0 0 1 7 4.5Z"/><path d="m8 12 2.4 2.4L16.5 8.5"/>',
|
|
19854
|
+
tasks: '<path d="M9 6h11M9 12h11M9 18h11"/><path d="M4 6h.01M4 12h.01M4 18h.01"/>',
|
|
19855
|
+
templates: '<rect x="3" y="5" width="18" height="16" rx="2"/><path d="M16 3v4M8 3v4M3 10h18"/><path d="M12 14v3l2 1"/>',
|
|
19856
|
+
runs: '<path d="M4 19.5V4.5M4 19.5h16"/><path d="m7 15 3-4 3 2 5-6"/>',
|
|
19857
|
+
system: '<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .34 1.88l.06.06-1.42 1.42-.06-.06a1.7 1.7 0 0 0-1.88-.34 1.7 1.7 0 0 0-1.03 1.56V20h-2v-.48a1.7 1.7 0 0 0-1.03-1.56 1.7 1.7 0 0 0-1.88.34l-.06.06-1.42-1.42.06-.06A1.7 1.7 0 0 0 9.4 15a1.7 1.7 0 0 0-1.56-1.03H7.5v-2h.34A1.7 1.7 0 0 0 9.4 10a1.7 1.7 0 0 0-.34-1.88L9 8.06l1.42-1.42.06.06a1.7 1.7 0 0 0 1.88.34A1.7 1.7 0 0 0 13.4 5.5V5h2v.5a1.7 1.7 0 0 0 1.03 1.56 1.7 1.7 0 0 0 1.88-.34l.06-.06 1.42 1.42-.06.06A1.7 1.7 0 0 0 19.4 10a1.7 1.7 0 0 0 1.56 1.03h.54v2h-.54A1.7 1.7 0 0 0 19.4 15Z"/>',
|
|
19858
|
+
refresh: '<path d="M20 6v5h-5"/><path d="M18.5 15a7 7 0 1 1-.8-7.8L20 11"/>',
|
|
19859
|
+
search: '<circle cx="11" cy="11" r="7"/><path d="m20 20-4-4"/>',
|
|
19860
|
+
sun: '<circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.42 1.42M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.42-1.42M17.66 6.34l1.41-1.41"/>',
|
|
19861
|
+
globe: '<circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/>',
|
|
19862
|
+
chevronLeft: '<path d="m15 18-6-6 6-6"/>',
|
|
19863
|
+
chevronRight: '<path d="m9 18 6-6-6-6"/>',
|
|
19864
|
+
copy: '<rect x="8" y="8" width="12" height="12" rx="2"/><path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2"/>',
|
|
19865
|
+
close: '<path d="m6 6 12 12M18 6 6 18"/>',
|
|
19866
|
+
inbox: '<path d="M4 4h16l2 12h-6l-2 3h-4l-2-3H2L4 4Z"/><path d="M8 9h8"/>',
|
|
19867
|
+
activity: '<path d="M3 12h4l2-6 4 12 2-6h6"/>',
|
|
19868
|
+
check: '<path d="m5 12 4 4L19 6"/>',
|
|
19869
|
+
alert: '<path d="M12 3 2.8 19h18.4L12 3Z"/><path d="M12 9v4M12 17h.01"/>',
|
|
19870
|
+
clock: '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>',
|
|
19871
|
+
database: '<ellipse cx="12" cy="5" rx="8" ry="3"/><path d="M4 5v6c0 1.66 3.58 3 8 3s8-1.34 8-3V5M4 11v6c0 1.66 3.58 3 8 3s8-1.34 8-3v-6"/>'
|
|
19872
|
+
};
|
|
19873
|
+
return `<svg class="${className}" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">${paths[name]}</svg>`;
|
|
19874
|
+
}
|
|
19875
|
+
function clientMessages(locale) {
|
|
19876
|
+
const keys = [
|
|
19877
|
+
"action.cancel",
|
|
19878
|
+
"action.confirm",
|
|
19879
|
+
"action.copy",
|
|
19880
|
+
"action.delete",
|
|
19881
|
+
"action.refresh",
|
|
19882
|
+
"action.logs",
|
|
19883
|
+
"action.hideLogs",
|
|
19884
|
+
"details.copySuccess",
|
|
19885
|
+
"feedback.copyFailed",
|
|
19886
|
+
"dialog.cancelTask",
|
|
19887
|
+
"dialog.cancelTaskBody",
|
|
19888
|
+
"dialog.retryTask",
|
|
19889
|
+
"dialog.retryTaskBody",
|
|
19890
|
+
"dialog.deleteTask",
|
|
19891
|
+
"dialog.deleteTaskBody",
|
|
19892
|
+
"dialog.disableTemplate",
|
|
19893
|
+
"dialog.disableTemplateBody",
|
|
19894
|
+
"dialog.deleteTemplate",
|
|
19895
|
+
"dialog.deleteTemplateBody",
|
|
19896
|
+
"dialog.triggerTemplate",
|
|
19897
|
+
"dialog.triggerTemplateBody",
|
|
19898
|
+
"dialog.clearTitle",
|
|
19899
|
+
"dialog.clearBody",
|
|
19900
|
+
"dialog.clearInstruction",
|
|
19901
|
+
"feedback.retryFailed",
|
|
19902
|
+
"feedback.cancelFailed",
|
|
19903
|
+
"feedback.deleteFailed",
|
|
19904
|
+
"feedback.requestFailed",
|
|
19905
|
+
"feedback.triggered",
|
|
19906
|
+
"feedback.configSaved",
|
|
19907
|
+
"feedback.databaseCleared",
|
|
19908
|
+
"filter.noResults"
|
|
19909
|
+
];
|
|
19910
|
+
return Object.fromEntries(keys.map((key) => [key, t(locale, key)]));
|
|
19911
|
+
}
|
|
19912
|
+
function renderLayout(options) {
|
|
19913
|
+
const { locale, activeTab, body } = options;
|
|
19914
|
+
const page = PAGE_KEYS[activeTab];
|
|
19915
|
+
const nav = [
|
|
19916
|
+
{ id: "tasks", href: "/", icon: "tasks" },
|
|
19917
|
+
{ id: "templates", href: "/templates", icon: "templates" },
|
|
19918
|
+
{ id: "runs", href: "/runs", icon: "runs" },
|
|
19919
|
+
{ id: "system", href: "/system", icon: "system" }
|
|
19920
|
+
];
|
|
19921
|
+
const ui = JSON.stringify(clientMessages(locale)).replace(/</g, "\\u003c");
|
|
19922
|
+
const language = locale === "en" ? "en" : "zh-CN";
|
|
19923
|
+
return `<!DOCTYPE html>
|
|
19924
|
+
<html lang="${language}">
|
|
19925
|
+
<head>
|
|
19926
|
+
<meta charset="UTF-8">
|
|
19927
|
+
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
|
19928
|
+
<meta name="color-scheme" content="light dark">
|
|
19929
|
+
<meta name="theme-color" content="#f5f7fb">
|
|
19930
|
+
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Crect x='2' y='2' width='20' height='20' rx='6' fill='%235957d9'/%3E%3Cpath d='m7 12 3 3 7-7' fill='none' stroke='white' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E">
|
|
19931
|
+
<title>${t(locale, page.title)} \xB7 SuperTask</title>
|
|
19932
|
+
<script>(function(){try{var p=localStorage.getItem('supertask-theme')||'system';var d=p==='system'?(matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light'):p;document.documentElement.dataset.theme=d;document.documentElement.dataset.themePreference=p}catch(e){}})();</script>
|
|
19933
|
+
${STYLES}
|
|
19934
|
+
</head>
|
|
19935
|
+
<body>
|
|
19936
|
+
<a class="skip-link" href="#main">${t(locale, "a11y.skip")}</a>
|
|
19937
|
+
<div class="app-shell">
|
|
19938
|
+
<header class="topbar">
|
|
19939
|
+
<div class="brand">
|
|
19940
|
+
<div class="brand-mark">${icon("brand")}</div>
|
|
19941
|
+
<div>
|
|
19942
|
+
<div class="brand-name">${t(locale, "app.name")} <span>${t(locale, "app.dashboard")}</span></div>
|
|
19943
|
+
<div class="brand-tagline">${t(locale, "app.tagline")}</div>
|
|
19944
|
+
</div>
|
|
19945
|
+
</div>
|
|
19946
|
+
<div class="top-actions">
|
|
19947
|
+
<div class="local-chip"><span class="live-dot"></span>${t(locale, "app.local")}</div>
|
|
19948
|
+
<div class="control language-switch" role="group" aria-label="${t(locale, "language.label")}">
|
|
19949
|
+
<button type="button" class="${locale === "zh-CN" ? "active" : ""}" onclick="setLocale('zh-CN')" aria-pressed="${locale === "zh-CN"}">\u4E2D</button>
|
|
19950
|
+
<button type="button" class="${locale === "en" ? "active" : ""}" onclick="setLocale('en')" aria-pressed="${locale === "en"}">EN</button>
|
|
19951
|
+
</div>
|
|
19952
|
+
<button type="button" class="icon-button mobile-language" style="display:none" onclick="setLocale('${locale === "en" ? "zh-CN" : "en"}')" aria-label="${t(locale, "language.label")}" title="${t(locale, "language.label")}">${icon("globe")}</button>
|
|
19953
|
+
<label class="select-wrap" aria-label="${t(locale, "theme.label")}">
|
|
19954
|
+
${icon("sun")}
|
|
19955
|
+
<select id="theme-select" class="control" onchange="setTheme(this.value)" title="${t(locale, "theme.label")}">
|
|
19956
|
+
<option value="system">${t(locale, "theme.system")}</option>
|
|
19957
|
+
<option value="light">${t(locale, "theme.light")}</option>
|
|
19958
|
+
<option value="dark">${t(locale, "theme.dark")}</option>
|
|
19959
|
+
</select>
|
|
19960
|
+
</label>
|
|
19961
|
+
<button type="button" class="icon-button" onclick="refreshPage(this)" aria-label="${t(locale, "action.refresh")}" title="${t(locale, "action.refresh")}">${icon("refresh")}</button>
|
|
19962
|
+
</div>
|
|
19963
|
+
</header>
|
|
19964
|
+
<nav class="tabs" aria-label="Primary">
|
|
19965
|
+
${nav.map((item) => `<a href="${item.href}" class="${activeTab === item.id ? "active" : ""}" ${activeTab === item.id ? 'aria-current="page"' : ""}>${icon(item.icon)}<span>${t(locale, `nav.${item.id}`)}</span></a>`).join("")}
|
|
19966
|
+
</nav>
|
|
19967
|
+
<main id="main" tabindex="-1">
|
|
19968
|
+
<div class="page-heading reveal">
|
|
19969
|
+
<div><h1>${t(locale, page.title)}</h1><p>${t(locale, page.description)}</p></div>
|
|
19970
|
+
</div>
|
|
19971
|
+
${body}
|
|
19972
|
+
</main>
|
|
19973
|
+
<footer>${t(locale, "app.footer")}</footer>
|
|
19974
|
+
</div>
|
|
19975
|
+
<div id="toast-region" class="toast-region" role="status" aria-live="polite"></div>
|
|
19976
|
+
<dialog id="detail-dialog">
|
|
19977
|
+
<div class="dialog-head"><div><h2>${t(locale, "details.title")}</h2><p>${t(locale, "details.subtitle")}</p></div><button class="icon-button" onclick="document.getElementById('detail-dialog').close()" aria-label="${t(locale, "action.close")}">${icon("close")}</button></div>
|
|
19978
|
+
<div class="dialog-body"><pre id="detail-content" class="json-view"></pre></div>
|
|
19979
|
+
<div class="dialog-actions"><button class="btn" onclick="copyDetails()">${icon("copy")}${t(locale, "action.copy")}</button><button class="btn btn-primary" onclick="document.getElementById('detail-dialog').close()">${t(locale, "action.close")}</button></div>
|
|
19980
|
+
</dialog>
|
|
19981
|
+
<dialog id="confirm-dialog">
|
|
19982
|
+
<div class="dialog-head"><div><h2 id="confirm-title"></h2></div><button class="icon-button" onclick="document.getElementById('confirm-dialog').close('cancel')" aria-label="${t(locale, "action.close")}">${icon("close")}</button></div>
|
|
19983
|
+
<div class="dialog-body"><p id="confirm-body" class="confirm-copy"></p></div>
|
|
19984
|
+
<div class="dialog-actions"><button class="btn" onclick="document.getElementById('confirm-dialog').close('cancel')">${t(locale, "action.cancel")}</button><button id="confirm-ok" class="btn btn-primary" onclick="document.getElementById('confirm-dialog').close('confirm')">${t(locale, "action.confirm")}</button></div>
|
|
19985
|
+
</dialog>
|
|
19986
|
+
<dialog id="danger-dialog">
|
|
19987
|
+
<div class="dialog-head"><div><h2>${t(locale, "dialog.clearTitle")}</h2></div><button class="icon-button" onclick="document.getElementById('danger-dialog').close('cancel')" aria-label="${t(locale, "action.close")}">${icon("close")}</button></div>
|
|
19988
|
+
<div class="dialog-body"><p class="confirm-copy"><strong>${t(locale, "dialog.clearBody")}</strong>${t(locale, "dialog.clearInstruction")}</p><input id="danger-confirmation" class="danger-input" autocomplete="off" spellcheck="false" placeholder="CLEAR" oninput="document.getElementById('danger-ok').disabled=this.value!=='CLEAR'"></div>
|
|
19989
|
+
<div class="dialog-actions"><button class="btn" onclick="document.getElementById('danger-dialog').close('cancel')">${t(locale, "action.cancel")}</button><button id="danger-ok" class="btn btn-danger" disabled onclick="document.getElementById('danger-dialog').close('confirm')">${t(locale, "action.clearDatabase")}</button></div>
|
|
19990
|
+
</dialog>
|
|
19991
|
+
<script>
|
|
19992
|
+
const UI=${ui};
|
|
19993
|
+
const text=(key,values={})=>(UI[key]||key).replace(/{([a-zA-Z]+)}/g,(_,name)=>String(values[name]??'{'+name+'}'));
|
|
19994
|
+
const themeMedia=matchMedia('(prefers-color-scheme: dark)');
|
|
19995
|
+
function applyTheme(preference){const resolved=preference==='system'?(themeMedia.matches?'dark':'light'):preference;document.documentElement.dataset.theme=resolved;document.documentElement.dataset.themePreference=preference;const select=document.getElementById('theme-select');if(select)select.value=preference;document.querySelector('meta[name="theme-color"]').content=resolved==='dark'?'#090d15':'#f5f7fb';}
|
|
19996
|
+
function setTheme(preference){localStorage.setItem('supertask-theme',preference);applyTheme(preference);}
|
|
19997
|
+
function setLocale(value){document.cookie='supertask_locale='+encodeURIComponent(value)+'; Path=/; Max-Age=31536000; SameSite=Lax';location.reload();}
|
|
19998
|
+
themeMedia.addEventListener?.('change',()=>{if((localStorage.getItem('supertask-theme')||'system')==='system')applyTheme('system');});
|
|
19999
|
+
applyTheme(localStorage.getItem('supertask-theme')||'system');
|
|
20000
|
+
requestAnimationFrame(()=>document.documentElement.classList.add('ui-ready'));
|
|
20001
|
+
function refreshPage(button){button.classList.add('refreshing');button.setAttribute('aria-label','${t(locale, "a11y.refreshing")}');location.reload();}
|
|
20002
|
+
function showToast(message,type='ok'){const region=document.getElementById('toast-region');const node=document.createElement('div');node.className='toast '+type;node.innerHTML=(type==='error'?'${icon("alert")}':'${icon("check")}')+'<span></span>';node.querySelector('span').textContent=message;region.appendChild(node);setTimeout(()=>{node.classList.add('leaving');setTimeout(()=>node.remove(),220)},3600);}
|
|
20003
|
+
async function readJson(response){const data=await response.json().catch(()=>({}));if(!response.ok)throw new Error(data.error||text('feedback.requestFailed'));return data;}
|
|
20004
|
+
async function ask(title,body,danger=false){const dialog=document.getElementById('confirm-dialog');document.getElementById('confirm-title').textContent=title;document.getElementById('confirm-body').textContent=body;document.getElementById('confirm-ok').className='btn '+(danger?'btn-danger':'btn-primary');return new Promise(resolve=>{dialog.addEventListener('close',()=>resolve(dialog.returnValue==='confirm'),{once:true});dialog.showModal();});}
|
|
20005
|
+
async function askDanger(){const dialog=document.getElementById('danger-dialog');const input=document.getElementById('danger-confirmation');input.value='';document.getElementById('danger-ok').disabled=true;return new Promise(resolve=>{dialog.addEventListener('close',()=>resolve(dialog.returnValue==='confirm'),{once:true});dialog.showModal();setTimeout(()=>input.focus(),50);});}
|
|
20006
|
+
async function retryTask(id){if(!await ask(text('dialog.retryTask',{id}),text('dialog.retryTaskBody')))return;try{await readJson(await fetch('/api/tasks/'+id+'/retry',{method:'POST'}));location.reload()}catch(error){showToast(text('feedback.retryFailed')+': '+error.message,'error')}}
|
|
20007
|
+
async function cancelTask(id){if(!await ask(text('dialog.cancelTask',{id}),text('dialog.cancelTaskBody'),true))return;try{await readJson(await fetch('/api/tasks/'+id+'/cancel',{method:'POST'}));location.reload()}catch(error){showToast(text('feedback.cancelFailed')+': '+error.message,'error')}}
|
|
20008
|
+
async function deleteTask(id){if(!await ask(text('dialog.deleteTask',{id}),text('dialog.deleteTaskBody'),true))return;try{await readJson(await fetch('/api/tasks/'+id,{method:'DELETE'}));location.reload()}catch(error){showToast(text('feedback.deleteFailed')+': '+error.message,'error')}}
|
|
20009
|
+
async function showRecord(url){try{const data=await readJson(await fetch(url));document.getElementById('detail-content').textContent=JSON.stringify(data,null,2);document.getElementById('detail-dialog').showModal()}catch(error){showToast(error.message,'error')}}
|
|
20010
|
+
const showDetail=id=>showRecord('/api/tasks/'+id);const showRunDetail=id=>showRecord('/api/runs/'+id);const showTemplateDetail=id=>showRecord('/api/templates/'+id);
|
|
20011
|
+
async function copyDetails(){try{await navigator.clipboard.writeText(document.getElementById('detail-content').textContent);showToast(text('details.copySuccess'))}catch{showToast(text('feedback.copyFailed'),'error')}}
|
|
20012
|
+
async function enableTmpl(id){try{await readJson(await fetch('/api/templates/'+id+'/enable',{method:'POST'}));location.reload()}catch(error){showToast(error.message,'error')}}
|
|
20013
|
+
async function disableTmpl(id){if(!await ask(text('dialog.disableTemplate'),text('dialog.disableTemplateBody')))return;try{await readJson(await fetch('/api/templates/'+id+'/disable',{method:'POST'}));location.reload()}catch(error){showToast(error.message,'error')}}
|
|
20014
|
+
async function deleteTmpl(id){if(!await ask(text('dialog.deleteTemplate'),text('dialog.deleteTemplateBody'),true))return;try{await readJson(await fetch('/api/templates/'+id,{method:'DELETE'}));location.reload()}catch(error){showToast(error.message,'error')}}
|
|
20015
|
+
async function triggerTmpl(id){if(!await ask(text('dialog.triggerTemplate'),text('dialog.triggerTemplateBody')))return;try{const data=await readJson(await fetch('/api/templates/'+id+'/trigger',{method:'POST'}));showToast(text('feedback.triggered',{id:data.taskId}));setTimeout(()=>location.reload(),550)}catch(error){showToast(error.message,'error')}}
|
|
20016
|
+
function toggleLog(id,button){const panel=document.getElementById('log-'+id);const hidden=!panel.hidden;panel.hidden=hidden;button.setAttribute('aria-expanded',String(!hidden));button.textContent=text(hidden?'action.logs':'action.hideLogs');}
|
|
20017
|
+
function filterTasks(value){const query=value.trim().toLocaleLowerCase();let visible=0;document.querySelectorAll('[data-task-row]').forEach(row=>{const match=!query||row.dataset.search.toLocaleLowerCase().includes(query);row.hidden=!match;if(match)visible++});const empty=document.getElementById('search-empty');if(empty)empty.hidden=visible!==0;}
|
|
20018
|
+
async function clearDatabase(){if(!await askDanger())return;try{const data=await readJson(await fetch('/api/database/clear',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({confirmation:'CLEAR'})}));showToast(text('feedback.databaseCleared',{path:data.backupPath}));setTimeout(()=>location.reload(),1000)}catch(error){showToast(error.message,'error')}}
|
|
20019
|
+
async function saveConfig(){const form=document.getElementById('config-form');const data={worker:{maxConcurrency:Number(form.mc.value),pollIntervalMs:Number(form.pi.value),heartbeatIntervalMs:Number(form.hi.value)*1000,taskTimeoutMs:Number(form.to.value)*60000},scheduler:{enabled:form.se.checked,checkIntervalMs:Number(form.si.value)},watchdog:{heartbeatTimeoutMs:Number(form.wt.value)*1000,checkIntervalMs:Number(form.wci.value)*1000,cleanupIntervalMs:Number(form.wcl.value)*3600000,retentionDays:Number(form.rd.value)}};try{await readJson(await fetch('/api/config',{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(data)}));showToast(text('feedback.configSaved'))}catch(error){showToast(error.message,'error')}}
|
|
20020
|
+
</script>
|
|
20021
|
+
</body>
|
|
20022
|
+
</html>`;
|
|
20023
|
+
}
|
|
20024
|
+
var ZH, EN, STYLES, PAGE_KEYS;
|
|
20025
|
+
var init_ui = __esm({
|
|
20026
|
+
"src/web/ui.ts"() {
|
|
20027
|
+
"use strict";
|
|
20028
|
+
ZH = {
|
|
20029
|
+
"app.name": "SuperTask",
|
|
20030
|
+
"app.dashboard": "\u63A7\u5236\u53F0",
|
|
20031
|
+
"app.tagline": "\u53EF\u9760\u7684\u672C\u5730 Agent \u8C03\u5EA6\u4E2D\u5FC3",
|
|
20032
|
+
"app.local": "\u672C\u5730\u8FD0\u884C",
|
|
20033
|
+
"app.footer": "Local-first \xB7 \u6570\u636E\u7559\u5728\u4F60\u7684\u8BBE\u5907\u4E0A",
|
|
20034
|
+
"nav.tasks": "\u4EFB\u52A1",
|
|
20035
|
+
"nav.templates": "\u8C03\u5EA6",
|
|
20036
|
+
"nav.runs": "\u6267\u884C\u8BB0\u5F55",
|
|
20037
|
+
"nav.system": "\u7CFB\u7EDF",
|
|
20038
|
+
"page.tasks.title": "\u4EFB\u52A1\u961F\u5217",
|
|
20039
|
+
"page.tasks.description": "\u67E5\u770B\u4F18\u5148\u7EA7\u3001\u6267\u884C\u72B6\u6001\u4E0E\u91CD\u8BD5\u60C5\u51B5\uFF0C\u5FEB\u901F\u5904\u7406\u9700\u8981\u5173\u6CE8\u7684\u4EFB\u52A1\u3002",
|
|
20040
|
+
"page.templates.title": "\u8C03\u5EA6\u6A21\u677F",
|
|
20041
|
+
"page.templates.description": "\u7BA1\u7406 Cron\u3001\u5EF6\u8FDF\u548C\u5FAA\u73AF\u4EFB\u52A1\uFF0C\u8BA9\u91CD\u590D\u5DE5\u4F5C\u6309\u8BA1\u5212\u81EA\u52A8\u8FD0\u884C\u3002",
|
|
20042
|
+
"page.runs.title": "\u6267\u884C\u8BB0\u5F55",
|
|
20043
|
+
"page.runs.description": "\u8FFD\u8E2A\u6BCF\u6B21 Agent \u6267\u884C\u7684\u72B6\u6001\u3001\u8017\u65F6\u3001\u5FC3\u8DF3\u4E0E\u8F93\u51FA\u3002",
|
|
20044
|
+
"page.system.title": "\u7CFB\u7EDF\u8BBE\u7F6E",
|
|
20045
|
+
"page.system.description": "\u8C03\u6574 Gateway \u8FD0\u884C\u53C2\u6570\uFF0C\u68C0\u67E5\u5B9E\u65F6\u4EFB\u52A1\u5E76\u7BA1\u7406\u672C\u5730\u6570\u636E\u3002",
|
|
20046
|
+
"action.refresh": "\u5237\u65B0",
|
|
20047
|
+
"action.details": "\u8BE6\u60C5",
|
|
20048
|
+
"action.retry": "\u91CD\u8BD5",
|
|
20049
|
+
"action.cancel": "\u53D6\u6D88",
|
|
20050
|
+
"action.delete": "\u5220\u9664",
|
|
20051
|
+
"action.enable": "\u542F\u7528",
|
|
20052
|
+
"action.disable": "\u7981\u7528",
|
|
20053
|
+
"action.trigger": "\u7ACB\u5373\u89E6\u53D1",
|
|
20054
|
+
"action.logs": "\u67E5\u770B\u65E5\u5FD7",
|
|
20055
|
+
"action.hideLogs": "\u6536\u8D77\u65E5\u5FD7",
|
|
20056
|
+
"action.save": "\u4FDD\u5B58\u8BBE\u7F6E",
|
|
20057
|
+
"action.copy": "\u590D\u5236 JSON",
|
|
20058
|
+
"action.close": "\u5173\u95ED",
|
|
20059
|
+
"action.confirm": "\u786E\u8BA4",
|
|
20060
|
+
"action.clearDatabase": "\u6E05\u7A7A\u6570\u636E\u5E93",
|
|
20061
|
+
"status.pending": "\u5F85\u6267\u884C",
|
|
20062
|
+
"status.running": "\u8FD0\u884C\u4E2D",
|
|
20063
|
+
"status.done": "\u5DF2\u5B8C\u6210",
|
|
20064
|
+
"status.failed": "\u5931\u8D25",
|
|
20065
|
+
"status.dead_letter": "\u6B7B\u4FE1",
|
|
20066
|
+
"status.cancelled": "\u5DF2\u53D6\u6D88",
|
|
20067
|
+
"status.unknown": "\u672A\u77E5",
|
|
20068
|
+
"stats.total": "\u603B\u4EFB\u52A1",
|
|
20069
|
+
"stats.pending": "\u5F85\u6267\u884C",
|
|
20070
|
+
"stats.running": "\u8FD0\u884C\u4E2D",
|
|
20071
|
+
"stats.done": "\u5DF2\u5B8C\u6210",
|
|
20072
|
+
"stats.failedDead": "\u5931\u8D25\u4E0E\u6B7B\u4FE1",
|
|
20073
|
+
"stats.templates": "\u6A21\u677F\u603B\u6570",
|
|
20074
|
+
"stats.enabled": "\u5DF2\u542F\u7528",
|
|
20075
|
+
"stats.disabled": "\u5DF2\u7981\u7528",
|
|
20076
|
+
"stats.records": "\u6267\u884C\u603B\u6570",
|
|
20077
|
+
"stats.pageDone": "\u672C\u9875\u6210\u529F",
|
|
20078
|
+
"stats.pageFailed": "\u672C\u9875\u5931\u8D25",
|
|
20079
|
+
"stats.pageRunning": "\u672C\u9875\u8FD0\u884C\u4E2D",
|
|
20080
|
+
"filter.all": "\u5168\u90E8",
|
|
20081
|
+
"filter.searchTasks": "\u641C\u7D22\u5F53\u524D\u9875\u7684\u4EFB\u52A1\u3001Agent \u6216\u63D0\u793A\u8BCD",
|
|
20082
|
+
"filter.noResults": "\u6CA1\u6709\u7B26\u5408\u5F53\u524D\u641C\u7D22\u6761\u4EF6\u7684\u4EFB\u52A1",
|
|
20083
|
+
"table.id": "ID",
|
|
20084
|
+
"table.task": "\u4EFB\u52A1",
|
|
20085
|
+
"table.name": "\u540D\u79F0",
|
|
20086
|
+
"table.agent": "Agent",
|
|
20087
|
+
"table.status": "\u72B6\u6001",
|
|
20088
|
+
"table.duration": "\u8017\u65F6",
|
|
20089
|
+
"table.retries": "\u91CD\u8BD5",
|
|
20090
|
+
"table.actions": "\u64CD\u4F5C",
|
|
20091
|
+
"table.type": "\u7C7B\u578B",
|
|
20092
|
+
"table.rule": "\u89C4\u5219",
|
|
20093
|
+
"table.lastRun": "\u4E0A\u6B21\u6267\u884C",
|
|
20094
|
+
"table.nextRun": "\u4E0B\u6B21\u6267\u884C",
|
|
20095
|
+
"table.run": "Run",
|
|
20096
|
+
"table.heartbeat": "\u5FC3\u8DF3",
|
|
20097
|
+
"table.session": "Session",
|
|
20098
|
+
"table.model": "\u6A21\u578B",
|
|
20099
|
+
"table.startedAt": "\u542F\u52A8\u65F6\u95F4",
|
|
20100
|
+
"table.pid": "PID",
|
|
20101
|
+
"pagination.previous": "\u4E0A\u4E00\u9875",
|
|
20102
|
+
"pagination.next": "\u4E0B\u4E00\u9875",
|
|
20103
|
+
"pagination.summary": "\u7B2C {page} \u9875\uFF0C\u5171 {pages} \u9875 \xB7 {total} \u6761",
|
|
20104
|
+
"empty.tasks": "\u961F\u5217\u91CC\u8FD8\u6CA1\u6709\u4EFB\u52A1",
|
|
20105
|
+
"empty.tasksHint": "\u901A\u8FC7 OpenCode \u63D2\u4EF6\u6216 supertask add \u521B\u5EFA\u7B2C\u4E00\u4E2A\u4EFB\u52A1\u3002",
|
|
20106
|
+
"empty.templates": "\u8FD8\u6CA1\u6709\u8C03\u5EA6\u6A21\u677F",
|
|
20107
|
+
"empty.templatesHint": "\u4F7F\u7528 CLI \u521B\u5EFA\uFF1Asupertask template add",
|
|
20108
|
+
"empty.runs": "\u8FD8\u6CA1\u6709\u6267\u884C\u8BB0\u5F55",
|
|
20109
|
+
"empty.running": "\u5F53\u524D\u6CA1\u6709\u8FD0\u884C\u4E2D\u7684\u4EFB\u52A1",
|
|
20110
|
+
"schedule.cron": "Cron",
|
|
20111
|
+
"schedule.recurring": "\u5FAA\u73AF",
|
|
20112
|
+
"schedule.delayed": "\u5EF6\u8FDF",
|
|
20113
|
+
"schedule.unknown": "\u672A\u77E5",
|
|
20114
|
+
"schedule.enabled": "\u5DF2\u542F\u7528",
|
|
20115
|
+
"schedule.disabled": "\u5DF2\u7981\u7528",
|
|
20116
|
+
"schedule.minutes": "{count} \u5206\u949F",
|
|
20117
|
+
"schedule.seconds": "{count} \u79D2",
|
|
20118
|
+
"schedule.hours": "{count} \u5C0F\u65F6",
|
|
20119
|
+
"schedule.days": "{count} \u5929",
|
|
20120
|
+
"schedule.overdue": "\u5DF2\u5230\u671F",
|
|
20121
|
+
"system.worker": "Worker",
|
|
20122
|
+
"system.scheduler": "Scheduler",
|
|
20123
|
+
"system.watchdog": "Watchdog",
|
|
20124
|
+
"system.maxConcurrency": "\u6700\u5927\u5E76\u53D1",
|
|
20125
|
+
"system.pollInterval": "\u8F6E\u8BE2\u95F4\u9694",
|
|
20126
|
+
"system.heartbeatInterval": "\u5FC3\u8DF3\u95F4\u9694",
|
|
20127
|
+
"system.taskTimeout": "\u4EFB\u52A1\u8D85\u65F6",
|
|
20128
|
+
"system.schedulerEnabled": "\u542F\u7528\u8C03\u5EA6",
|
|
20129
|
+
"system.checkInterval": "\u68C0\u67E5\u95F4\u9694",
|
|
20130
|
+
"system.heartbeatTimeout": "\u5FC3\u8DF3\u8D85\u65F6",
|
|
20131
|
+
"system.cleanupInterval": "\u6E05\u7406\u95F4\u9694",
|
|
20132
|
+
"system.retentionDays": "\u6570\u636E\u4FDD\u7559",
|
|
20133
|
+
"system.milliseconds": "\u6BEB\u79D2",
|
|
20134
|
+
"system.seconds": "\u79D2",
|
|
20135
|
+
"system.minutes": "\u5206\u949F",
|
|
20136
|
+
"system.hours": "\u5C0F\u65F6",
|
|
20137
|
+
"system.days": "\u5929",
|
|
20138
|
+
"system.activeTemplates": "\u6D3B\u8DC3\u6A21\u677F",
|
|
20139
|
+
"system.saveHint": "\u4FDD\u5B58\u540E\u9700\u91CD\u542F Gateway \u751F\u6548",
|
|
20140
|
+
"system.runningTasks": "\u5F53\u524D\u8FD0\u884C\u4EFB\u52A1\uFF08{running} / {limit} \u5E76\u53D1\uFF09",
|
|
20141
|
+
"system.taskStats": "\u4EFB\u52A1\u6982\u89C8",
|
|
20142
|
+
"system.configFile": "\u914D\u7F6E\u6587\u4EF6",
|
|
20143
|
+
"system.path": "\u8DEF\u5F84",
|
|
20144
|
+
"system.fileExists": "\u6587\u4EF6\u5B58\u5728",
|
|
20145
|
+
"system.yes": "\u662F",
|
|
20146
|
+
"system.noDefault": "\u5426\uFF0C\u5F53\u524D\u4F7F\u7528\u9ED8\u8BA4\u503C",
|
|
20147
|
+
"system.danger": "\u5371\u9669\u64CD\u4F5C",
|
|
20148
|
+
"system.dangerDescription": "\u7CFB\u7EDF\u4F1A\u5148\u521B\u5EFA\u53EF\u6821\u9A8C\u5907\u4EFD\uFF0C\u518D\u4E8B\u52A1\u6027\u6E05\u7A7A\u4EFB\u52A1\u3001\u6267\u884C\u8BB0\u5F55\u548C\u8C03\u5EA6\u6A21\u677F\uFF1B\u5B58\u5728\u8FD0\u884C\u4EFB\u52A1\u65F6\u4F1A\u62D2\u7EDD\u64CD\u4F5C\u3002",
|
|
20149
|
+
"theme.label": "\u4E3B\u9898",
|
|
20150
|
+
"theme.system": "\u8DDF\u968F\u7CFB\u7EDF",
|
|
20151
|
+
"theme.light": "\u6D45\u8272",
|
|
20152
|
+
"theme.dark": "\u6DF1\u8272",
|
|
20153
|
+
"language.label": "\u8BED\u8A00",
|
|
20154
|
+
"details.title": "\u6570\u636E\u8BE6\u60C5",
|
|
20155
|
+
"details.subtitle": "\u539F\u59CB\u8BB0\u5F55\uFF08JSON\uFF09",
|
|
20156
|
+
"details.copySuccess": "JSON \u5DF2\u590D\u5236",
|
|
20157
|
+
"dialog.cancelTask": "\u53D6\u6D88\u4EFB\u52A1 #{id}\uFF1F",
|
|
20158
|
+
"dialog.cancelTaskBody": "\u8FD0\u884C\u4E2D\u7684\u4EFB\u52A1\u4F1A\u5728\u4E0B\u4E00\u4E2A\u8F6E\u8BE2\u5468\u671F\u7EC8\u6B62\u5BF9\u5E94\u8FDB\u7A0B\u6811\u3002",
|
|
20159
|
+
"dialog.retryTask": "\u91CD\u8BD5\u4EFB\u52A1 #{id}\uFF1F",
|
|
20160
|
+
"dialog.retryTaskBody": "\u4EFB\u52A1\u5C06\u56DE\u5230\u5F85\u6267\u884C\u72B6\u6001\uFF0C\u5E76\u91CD\u7F6E\u81EA\u52A8\u91CD\u8BD5\u9884\u7B97\u3002",
|
|
20161
|
+
"dialog.deleteTask": "\u5220\u9664\u4EFB\u52A1 #{id}\uFF1F",
|
|
20162
|
+
"dialog.deleteTaskBody": "\u4EFB\u52A1\u53CA\u5173\u8054\u6267\u884C\u8BB0\u5F55\u5C06\u6C38\u4E45\u5220\u9664\uFF0C\u6B64\u64CD\u4F5C\u65E0\u6CD5\u64A4\u9500\u3002",
|
|
20163
|
+
"dialog.disableTemplate": "\u7981\u7528\u8FD9\u4E2A\u8C03\u5EA6\u6A21\u677F\uFF1F",
|
|
20164
|
+
"dialog.disableTemplateBody": "\u6A21\u677F\u5C06\u505C\u6B62\u81EA\u52A8\u521B\u5EFA\u65B0\u4EFB\u52A1\uFF0C\u5DF2\u6709\u4EFB\u52A1\u4E0D\u53D7\u5F71\u54CD\u3002",
|
|
20165
|
+
"dialog.deleteTemplate": "\u5220\u9664\u8FD9\u4E2A\u8C03\u5EA6\u6A21\u677F\uFF1F",
|
|
20166
|
+
"dialog.deleteTemplateBody": "\u6A21\u677F\u914D\u7F6E\u5C06\u6C38\u4E45\u5220\u9664\uFF0C\u6B64\u64CD\u4F5C\u65E0\u6CD5\u64A4\u9500\u3002",
|
|
20167
|
+
"dialog.triggerTemplate": "\u7ACB\u5373\u89E6\u53D1\u4E00\u6B21\uFF1F",
|
|
20168
|
+
"dialog.triggerTemplateBody": "\u7CFB\u7EDF\u4F1A\u6309\u5F53\u524D\u6A21\u677F\u521B\u5EFA\u4E00\u4E2A\u65B0\u4EFB\u52A1\uFF0C\u5E76\u9075\u5B88\u6700\u5927\u5B9E\u4F8B\u9650\u5236\u3002",
|
|
20169
|
+
"dialog.clearTitle": "\u786E\u8BA4\u6E05\u7A7A\u6570\u636E\u5E93",
|
|
20170
|
+
"dialog.clearBody": "\u8FD9\u4F1A\u5220\u9664\u5168\u90E8\u4EFB\u52A1\u3001\u6267\u884C\u8BB0\u5F55\u548C\u8C03\u5EA6\u6A21\u677F\u3002\u7CFB\u7EDF\u4F1A\u5148\u81EA\u52A8\u5907\u4EFD\u3002",
|
|
20171
|
+
"dialog.clearInstruction": "\u8F93\u5165 CLEAR \u4EE5\u786E\u8BA4",
|
|
20172
|
+
"feedback.retryFailed": "\u91CD\u8BD5\u5931\u8D25",
|
|
20173
|
+
"feedback.cancelFailed": "\u53D6\u6D88\u5931\u8D25",
|
|
20174
|
+
"feedback.deleteFailed": "\u5220\u9664\u5931\u8D25",
|
|
20175
|
+
"feedback.requestFailed": "\u8BF7\u6C42\u5931\u8D25",
|
|
20176
|
+
"feedback.triggered": "\u5DF2\u521B\u5EFA\u4EFB\u52A1 #{id}",
|
|
20177
|
+
"feedback.configSaved": "\u8BBE\u7F6E\u5DF2\u4FDD\u5B58\uFF0C\u91CD\u542F Gateway \u540E\u751F\u6548",
|
|
20178
|
+
"feedback.databaseCleared": "\u6570\u636E\u5E93\u5DF2\u6E05\u7A7A\uFF0C\u5907\u4EFD\u4F4D\u4E8E\uFF1A{path}",
|
|
20179
|
+
"feedback.copyFailed": "\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u9009\u62E9\u5185\u5BB9",
|
|
20180
|
+
"a11y.skip": "\u8DF3\u5230\u4E3B\u8981\u5185\u5BB9",
|
|
20181
|
+
"a11y.refreshing": "\u6B63\u5728\u5237\u65B0"
|
|
20182
|
+
};
|
|
20183
|
+
EN = {
|
|
20184
|
+
"app.name": "SuperTask",
|
|
20185
|
+
"app.dashboard": "Dashboard",
|
|
20186
|
+
"app.tagline": "Reliable local agent orchestration",
|
|
20187
|
+
"app.local": "Running locally",
|
|
20188
|
+
"app.footer": "Local-first \xB7 Your data stays on this device",
|
|
20189
|
+
"nav.tasks": "Tasks",
|
|
20190
|
+
"nav.templates": "Schedules",
|
|
20191
|
+
"nav.runs": "Runs",
|
|
20192
|
+
"nav.system": "System",
|
|
20193
|
+
"page.tasks.title": "Task queue",
|
|
20194
|
+
"page.tasks.description": "Track priority, execution state, and retries, then act on tasks that need attention.",
|
|
20195
|
+
"page.templates.title": "Schedule templates",
|
|
20196
|
+
"page.templates.description": "Manage cron, delayed, and recurring work so repeated tasks run on time.",
|
|
20197
|
+
"page.runs.title": "Execution history",
|
|
20198
|
+
"page.runs.description": "Inspect the status, duration, heartbeat, and output of every agent run.",
|
|
20199
|
+
"page.system.title": "System settings",
|
|
20200
|
+
"page.system.description": "Tune Gateway behavior, inspect active work, and manage local data.",
|
|
20201
|
+
"action.refresh": "Refresh",
|
|
20202
|
+
"action.details": "Details",
|
|
20203
|
+
"action.retry": "Retry",
|
|
20204
|
+
"action.cancel": "Cancel",
|
|
20205
|
+
"action.delete": "Delete",
|
|
20206
|
+
"action.enable": "Enable",
|
|
20207
|
+
"action.disable": "Disable",
|
|
20208
|
+
"action.trigger": "Run now",
|
|
20209
|
+
"action.logs": "View log",
|
|
20210
|
+
"action.hideLogs": "Hide log",
|
|
20211
|
+
"action.save": "Save settings",
|
|
20212
|
+
"action.copy": "Copy JSON",
|
|
20213
|
+
"action.close": "Close",
|
|
20214
|
+
"action.confirm": "Confirm",
|
|
20215
|
+
"action.clearDatabase": "Clear database",
|
|
20216
|
+
"status.pending": "Pending",
|
|
20217
|
+
"status.running": "Running",
|
|
20218
|
+
"status.done": "Done",
|
|
20219
|
+
"status.failed": "Failed",
|
|
20220
|
+
"status.dead_letter": "Dead letter",
|
|
20221
|
+
"status.cancelled": "Cancelled",
|
|
20222
|
+
"status.unknown": "Unknown",
|
|
20223
|
+
"stats.total": "Total tasks",
|
|
20224
|
+
"stats.pending": "Pending",
|
|
20225
|
+
"stats.running": "Running",
|
|
20226
|
+
"stats.done": "Completed",
|
|
20227
|
+
"stats.failedDead": "Failed & dead",
|
|
20228
|
+
"stats.templates": "Templates",
|
|
20229
|
+
"stats.enabled": "Enabled",
|
|
20230
|
+
"stats.disabled": "Disabled",
|
|
20231
|
+
"stats.records": "Total runs",
|
|
20232
|
+
"stats.pageDone": "Succeeded here",
|
|
20233
|
+
"stats.pageFailed": "Failed here",
|
|
20234
|
+
"stats.pageRunning": "Running here",
|
|
20235
|
+
"filter.all": "All",
|
|
20236
|
+
"filter.searchTasks": "Search tasks, agents, or prompts on this page",
|
|
20237
|
+
"filter.noResults": "No tasks match this search",
|
|
20238
|
+
"table.id": "ID",
|
|
20239
|
+
"table.task": "Task",
|
|
20240
|
+
"table.name": "Name",
|
|
20241
|
+
"table.agent": "Agent",
|
|
20242
|
+
"table.status": "Status",
|
|
20243
|
+
"table.duration": "Duration",
|
|
20244
|
+
"table.retries": "Retries",
|
|
20245
|
+
"table.actions": "Actions",
|
|
20246
|
+
"table.type": "Type",
|
|
20247
|
+
"table.rule": "Rule",
|
|
20248
|
+
"table.lastRun": "Last run",
|
|
20249
|
+
"table.nextRun": "Next run",
|
|
20250
|
+
"table.run": "Run",
|
|
20251
|
+
"table.heartbeat": "Heartbeat",
|
|
20252
|
+
"table.session": "Session",
|
|
20253
|
+
"table.model": "Model",
|
|
20254
|
+
"table.startedAt": "Started",
|
|
20255
|
+
"table.pid": "PID",
|
|
20256
|
+
"pagination.previous": "Previous",
|
|
20257
|
+
"pagination.next": "Next",
|
|
20258
|
+
"pagination.summary": "Page {page} of {pages} \xB7 {total} items",
|
|
20259
|
+
"empty.tasks": "Your queue is empty",
|
|
20260
|
+
"empty.tasksHint": "Create the first task with the OpenCode plugin or supertask add.",
|
|
20261
|
+
"empty.templates": "No schedule templates yet",
|
|
20262
|
+
"empty.templatesHint": "Create one with: supertask template add",
|
|
20263
|
+
"empty.runs": "No execution history yet",
|
|
20264
|
+
"empty.running": "No tasks are running right now",
|
|
20265
|
+
"schedule.cron": "Cron",
|
|
20266
|
+
"schedule.recurring": "Recurring",
|
|
20267
|
+
"schedule.delayed": "Delayed",
|
|
20268
|
+
"schedule.unknown": "Unknown",
|
|
20269
|
+
"schedule.enabled": "Enabled",
|
|
20270
|
+
"schedule.disabled": "Disabled",
|
|
20271
|
+
"schedule.minutes": "{count} min",
|
|
20272
|
+
"schedule.seconds": "{count} sec",
|
|
20273
|
+
"schedule.hours": "{count} hr",
|
|
20274
|
+
"schedule.days": "{count} days",
|
|
20275
|
+
"schedule.overdue": "Overdue",
|
|
20276
|
+
"system.worker": "Worker",
|
|
20277
|
+
"system.scheduler": "Scheduler",
|
|
20278
|
+
"system.watchdog": "Watchdog",
|
|
20279
|
+
"system.maxConcurrency": "Max concurrency",
|
|
20280
|
+
"system.pollInterval": "Poll interval",
|
|
20281
|
+
"system.heartbeatInterval": "Heartbeat interval",
|
|
20282
|
+
"system.taskTimeout": "Task timeout",
|
|
20283
|
+
"system.schedulerEnabled": "Enable scheduler",
|
|
20284
|
+
"system.checkInterval": "Check interval",
|
|
20285
|
+
"system.heartbeatTimeout": "Heartbeat timeout",
|
|
20286
|
+
"system.cleanupInterval": "Cleanup interval",
|
|
20287
|
+
"system.retentionDays": "Data retention",
|
|
20288
|
+
"system.milliseconds": "ms",
|
|
20289
|
+
"system.seconds": "seconds",
|
|
20290
|
+
"system.minutes": "minutes",
|
|
20291
|
+
"system.hours": "hours",
|
|
20292
|
+
"system.days": "days",
|
|
20293
|
+
"system.activeTemplates": "Active templates",
|
|
20294
|
+
"system.saveHint": "Restart Gateway to apply saved settings",
|
|
20295
|
+
"system.runningTasks": "Active tasks ({running} / {limit} concurrent)",
|
|
20296
|
+
"system.taskStats": "Task overview",
|
|
20297
|
+
"system.configFile": "Configuration file",
|
|
20298
|
+
"system.path": "Path",
|
|
20299
|
+
"system.fileExists": "File exists",
|
|
20300
|
+
"system.yes": "Yes",
|
|
20301
|
+
"system.noDefault": "No, using defaults",
|
|
20302
|
+
"system.danger": "Danger zone",
|
|
20303
|
+
"system.dangerDescription": "A verified backup is created first, then tasks, runs, and templates are cleared transactionally. Active work blocks this operation.",
|
|
20304
|
+
"theme.label": "Theme",
|
|
20305
|
+
"theme.system": "System",
|
|
20306
|
+
"theme.light": "Light",
|
|
20307
|
+
"theme.dark": "Dark",
|
|
20308
|
+
"language.label": "Language",
|
|
20309
|
+
"details.title": "Data details",
|
|
20310
|
+
"details.subtitle": "Raw record (JSON)",
|
|
20311
|
+
"details.copySuccess": "JSON copied",
|
|
20312
|
+
"dialog.cancelTask": "Cancel task #{id}?",
|
|
20313
|
+
"dialog.cancelTaskBody": "A running task will terminate its process tree on the next worker poll.",
|
|
20314
|
+
"dialog.retryTask": "Retry task #{id}?",
|
|
20315
|
+
"dialog.retryTaskBody": "The task returns to pending and its automatic retry budget is reset.",
|
|
20316
|
+
"dialog.deleteTask": "Delete task #{id}?",
|
|
20317
|
+
"dialog.deleteTaskBody": "The task and its execution history will be permanently deleted.",
|
|
20318
|
+
"dialog.disableTemplate": "Disable this schedule?",
|
|
20319
|
+
"dialog.disableTemplateBody": "It will stop creating new tasks automatically. Existing tasks are unchanged.",
|
|
20320
|
+
"dialog.deleteTemplate": "Delete this schedule?",
|
|
20321
|
+
"dialog.deleteTemplateBody": "This template configuration will be permanently deleted.",
|
|
20322
|
+
"dialog.triggerTemplate": "Run this schedule now?",
|
|
20323
|
+
"dialog.triggerTemplateBody": "A new task is created from the template, subject to its instance limit.",
|
|
20324
|
+
"dialog.clearTitle": "Confirm database clear",
|
|
20325
|
+
"dialog.clearBody": "This deletes every task, run, and schedule template after creating a backup.",
|
|
20326
|
+
"dialog.clearInstruction": "Type CLEAR to confirm",
|
|
20327
|
+
"feedback.retryFailed": "Retry failed",
|
|
20328
|
+
"feedback.cancelFailed": "Cancellation failed",
|
|
20329
|
+
"feedback.deleteFailed": "Delete failed",
|
|
20330
|
+
"feedback.requestFailed": "Request failed",
|
|
20331
|
+
"feedback.triggered": "Task #{id} created",
|
|
20332
|
+
"feedback.configSaved": "Settings saved. Restart Gateway to apply them.",
|
|
20333
|
+
"feedback.databaseCleared": "Database cleared. Backup: {path}",
|
|
20334
|
+
"feedback.copyFailed": "Copy failed. Select the content manually.",
|
|
20335
|
+
"a11y.skip": "Skip to main content",
|
|
20336
|
+
"a11y.refreshing": "Refreshing"
|
|
20337
|
+
};
|
|
20338
|
+
STYLES = `
|
|
20339
|
+
<style>
|
|
20340
|
+
:root {
|
|
20341
|
+
color-scheme: light;
|
|
20342
|
+
--bg:#f5f7fb; --bg-glow:rgba(99,102,241,.12); --surface:#ffffff; --surface-2:#f8fafc;
|
|
20343
|
+
--surface-3:#eef2f7; --text:#172033; --text-2:#58657a; --text-3:#8792a5;
|
|
20344
|
+
--border:#dfe5ee; --border-strong:#cbd4e1; --primary:#5957d9; --primary-hover:#4846c7;
|
|
20345
|
+
--primary-soft:#eeedff; --green:#15805d; --green-soft:#e7f7f0; --red:#c63f4f; --red-soft:#fdecef;
|
|
20346
|
+
--yellow:#a66608; --yellow-soft:#fff4d9; --blue:#2563b8; --blue-soft:#e8f1ff; --purple:#7552c8;
|
|
20347
|
+
--shadow-sm:0 1px 2px rgba(16,24,40,.04); --shadow-md:0 12px 30px rgba(30,41,59,.08);
|
|
20348
|
+
--shadow-lg:0 24px 60px rgba(30,41,59,.18); --radius:14px; --radius-sm:9px;
|
|
20349
|
+
--focus:0 0 0 3px rgba(89,87,217,.22);
|
|
20350
|
+
}
|
|
20351
|
+
:root[data-theme="dark"] {
|
|
20352
|
+
color-scheme: dark;
|
|
20353
|
+
--bg:#090d15; --bg-glow:rgba(99,102,241,.18); --surface:#111722; --surface-2:#151d2a;
|
|
20354
|
+
--surface-3:#1c2635; --text:#edf2f8; --text-2:#a6b1c2; --text-3:#738095;
|
|
20355
|
+
--border:#273244; --border-strong:#344258; --primary:#8b87ff; --primary-hover:#a19eff;
|
|
20356
|
+
--primary-soft:#242347; --green:#48c78e; --green-soft:#16362b; --red:#ff7180; --red-soft:#3b1e27;
|
|
20357
|
+
--yellow:#f0b34b; --yellow-soft:#392d18; --blue:#6ea8ff; --blue-soft:#192d4b; --purple:#b89cff;
|
|
20358
|
+
--shadow-sm:0 1px 2px rgba(0,0,0,.25); --shadow-md:0 16px 36px rgba(0,0,0,.28);
|
|
20359
|
+
--shadow-lg:0 28px 70px rgba(0,0,0,.45); --focus:0 0 0 3px rgba(139,135,255,.25);
|
|
20360
|
+
}
|
|
20361
|
+
* { box-sizing:border-box; }
|
|
20362
|
+
html { min-height:100%; background:var(--bg); }
|
|
20363
|
+
body { min-height:100vh; margin:0; color:var(--text); background:
|
|
20364
|
+
radial-gradient(circle at 10% -10%,var(--bg-glow),transparent 34rem),var(--bg);
|
|
20365
|
+
font-family:Inter,ui-sans-serif,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
|
20366
|
+
font-size:14px; line-height:1.5; -webkit-font-smoothing:antialiased; }
|
|
20367
|
+
button,input,select { font:inherit; }
|
|
20368
|
+
button,a,select,input { -webkit-tap-highlight-color:transparent; }
|
|
20369
|
+
a { color:inherit; }
|
|
20370
|
+
code,.mono,.m { font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace; }
|
|
20371
|
+
.skip-link { position:fixed; left:16px; top:-60px; z-index:200; padding:10px 14px; border-radius:8px;
|
|
20372
|
+
color:#fff; background:var(--primary); transition:top .2s ease; }
|
|
20373
|
+
.skip-link:focus { top:16px; }
|
|
20374
|
+
.app-shell { width:min(1440px,100%); margin:0 auto; padding:0 28px 28px; }
|
|
20375
|
+
.topbar { min-height:80px; display:flex; align-items:center; justify-content:space-between; gap:20px;
|
|
20376
|
+
border-bottom:1px solid var(--border); }
|
|
20377
|
+
.brand { display:flex; align-items:center; gap:12px; min-width:0; }
|
|
20378
|
+
.brand-mark { width:38px; height:38px; display:grid; place-items:center; color:#fff; border-radius:11px;
|
|
20379
|
+
background:linear-gradient(145deg,#7773ff,#514ec8); box-shadow:0 9px 22px rgba(89,87,217,.28); }
|
|
20380
|
+
.brand-mark .icon { width:23px; height:23px; }
|
|
20381
|
+
.brand-name { display:flex; align-items:baseline; gap:7px; font-size:17px; font-weight:760; letter-spacing:-.025em; }
|
|
20382
|
+
.brand-name span { color:var(--text-3); font-size:12px; font-weight:650; letter-spacing:.02em; text-transform:uppercase; }
|
|
20383
|
+
.brand-tagline { color:var(--text-2); font-size:12px; margin-top:1px; }
|
|
20384
|
+
.top-actions { display:flex; align-items:center; justify-content:flex-end; gap:8px; }
|
|
20385
|
+
.local-chip { display:inline-flex; align-items:center; gap:7px; color:var(--text-2); font-size:12px; padding:7px 10px;
|
|
20386
|
+
border:1px solid var(--border); border-radius:999px; background:color-mix(in srgb,var(--surface) 78%,transparent); }
|
|
20387
|
+
.live-dot { width:7px; height:7px; border-radius:50%; background:var(--green); box-shadow:0 0 0 4px var(--green-soft); }
|
|
20388
|
+
.control { height:36px; border:1px solid var(--border); border-radius:9px; background:var(--surface);
|
|
20389
|
+
color:var(--text-2); box-shadow:var(--shadow-sm); }
|
|
20390
|
+
.select-wrap { position:relative; display:flex; align-items:center; }
|
|
20391
|
+
.select-wrap>.icon { width:15px; height:15px; position:absolute; left:10px; pointer-events:none; color:var(--text-3); }
|
|
20392
|
+
.select-wrap select { appearance:none; padding:0 29px 0 31px; cursor:pointer; outline:none; }
|
|
20393
|
+
.select-wrap::after { content:""; position:absolute; right:11px; width:6px; height:6px; border-right:1.5px solid currentColor;
|
|
20394
|
+
border-bottom:1.5px solid currentColor; transform:rotate(45deg) translateY(-2px); pointer-events:none; color:var(--text-3); }
|
|
20395
|
+
.language-switch { display:flex; padding:3px; gap:2px; }
|
|
20396
|
+
.language-switch button { height:28px; min-width:34px; padding:0 8px; border:0; border-radius:6px; color:var(--text-3);
|
|
20397
|
+
background:transparent; cursor:pointer; font-size:12px; font-weight:650; }
|
|
20398
|
+
.language-switch button.active { background:var(--surface-3); color:var(--text); }
|
|
20399
|
+
.icon-button { width:36px; height:36px; display:grid; place-items:center; border:1px solid var(--border); border-radius:9px;
|
|
20400
|
+
color:var(--text-2); background:var(--surface); box-shadow:var(--shadow-sm); cursor:pointer; }
|
|
20401
|
+
.icon-button .icon { width:17px; height:17px; }
|
|
20402
|
+
.icon-button:hover,.control:hover { border-color:var(--border-strong); color:var(--text); }
|
|
20403
|
+
.icon-button.refreshing .icon { animation:spin .7s linear infinite; }
|
|
20404
|
+
.tabs { display:flex; gap:6px; margin:18px 0 30px; padding:5px; width:max-content; max-width:100%; overflow-x:auto;
|
|
20405
|
+
border:1px solid var(--border); border-radius:12px; background:color-mix(in srgb,var(--surface) 82%,transparent); box-shadow:var(--shadow-sm); }
|
|
20406
|
+
.tabs a { display:flex; align-items:center; gap:8px; min-height:36px; padding:0 14px; border-radius:8px; color:var(--text-2);
|
|
20407
|
+
font-weight:650; font-size:13px; text-decoration:none; white-space:nowrap; transition:background .16s ease,color .16s ease,box-shadow .16s ease; }
|
|
20408
|
+
.tabs a .icon { width:16px; height:16px; }
|
|
20409
|
+
.tabs a:hover { color:var(--text); background:var(--surface-2); }
|
|
20410
|
+
.tabs a.active { color:var(--primary); background:var(--surface); box-shadow:0 1px 4px rgba(16,24,40,.08); }
|
|
20411
|
+
main { outline:none; }
|
|
20412
|
+
.page-heading { display:flex; justify-content:space-between; align-items:flex-end; gap:20px; margin-bottom:22px; }
|
|
20413
|
+
.page-heading h1 { margin:0; font-size:28px; line-height:1.2; letter-spacing:-.035em; }
|
|
20414
|
+
.page-heading p { margin:7px 0 0; color:var(--text-2); max-width:720px; }
|
|
20415
|
+
.stats-grid { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:14px; margin-bottom:20px; }
|
|
20416
|
+
.stats-grid.three { grid-template-columns:repeat(3,minmax(0,1fr)); }
|
|
20417
|
+
.stat-card { position:relative; min-height:118px; padding:19px; overflow:hidden; border:1px solid var(--border); border-radius:var(--radius);
|
|
20418
|
+
background:linear-gradient(145deg,var(--surface),color-mix(in srgb,var(--surface-2) 72%,var(--surface)));
|
|
20419
|
+
box-shadow:var(--shadow-sm); transition:transform .18s ease,border-color .18s ease,box-shadow .18s ease; }
|
|
20420
|
+
.stat-card:hover { transform:translateY(-2px); border-color:var(--border-strong); box-shadow:var(--shadow-md); }
|
|
20421
|
+
.stat-card::after { content:""; position:absolute; width:90px; height:90px; right:-38px; top:-45px; border-radius:50%; background:var(--tone-soft); }
|
|
20422
|
+
.stat-top { display:flex; justify-content:space-between; align-items:center; }
|
|
20423
|
+
.stat-icon { width:32px; height:32px; display:grid; place-items:center; border-radius:9px; color:var(--tone); background:var(--tone-soft); }
|
|
20424
|
+
.stat-icon .icon { width:17px; height:17px; }
|
|
20425
|
+
.stat-value { margin-top:13px; font-size:28px; font-weight:760; line-height:1; letter-spacing:-.04em; color:var(--tone); }
|
|
20426
|
+
.stat-label { margin-top:7px; color:var(--text-2); font-size:12px; font-weight:650; }
|
|
20427
|
+
.tone-neutral { --tone:var(--text-2); --tone-soft:var(--surface-3); }
|
|
20428
|
+
.tone-blue { --tone:var(--blue); --tone-soft:var(--blue-soft); }
|
|
20429
|
+
.tone-green { --tone:var(--green); --tone-soft:var(--green-soft); }
|
|
20430
|
+
.tone-red { --tone:var(--red); --tone-soft:var(--red-soft); }
|
|
20431
|
+
.tone-purple { --tone:var(--purple); --tone-soft:var(--primary-soft); }
|
|
20432
|
+
.toolbar { display:flex; justify-content:space-between; align-items:center; gap:14px; margin:0 0 12px; }
|
|
20433
|
+
.filters { display:flex; gap:6px; overflow-x:auto; padding:2px; }
|
|
20434
|
+
.filter-chip { display:inline-flex; align-items:center; min-height:34px; padding:0 12px; border:1px solid var(--border); border-radius:9px;
|
|
20435
|
+
color:var(--text-2); background:var(--surface); text-decoration:none; font-size:12px; font-weight:650; white-space:nowrap; }
|
|
20436
|
+
.filter-chip:hover { border-color:var(--border-strong); color:var(--text); }
|
|
20437
|
+
.filter-chip.active { border-color:color-mix(in srgb,var(--primary) 38%,var(--border)); color:var(--primary); background:var(--primary-soft); }
|
|
20438
|
+
.search-box { position:relative; width:min(320px,100%); flex:0 1 320px; }
|
|
20439
|
+
.search-box .icon { position:absolute; left:11px; top:50%; width:16px; height:16px; color:var(--text-3); transform:translateY(-50%); }
|
|
20440
|
+
.search-box input { width:100%; height:36px; padding:0 12px 0 36px; border:1px solid var(--border); border-radius:9px;
|
|
20441
|
+
outline:none; color:var(--text); background:var(--surface); box-shadow:var(--shadow-sm); }
|
|
20442
|
+
.search-box input::placeholder { color:var(--text-3); }
|
|
20443
|
+
.search-box input:focus { border-color:var(--primary); box-shadow:var(--focus); }
|
|
20444
|
+
.panel,.card { border:1px solid var(--border); border-radius:var(--radius); background:var(--surface); box-shadow:var(--shadow-sm); }
|
|
20445
|
+
.panel { overflow:hidden; margin-bottom:16px; }
|
|
20446
|
+
.panel-head { min-height:52px; display:flex; align-items:center; justify-content:space-between; gap:12px; padding:0 18px;
|
|
20447
|
+
border-bottom:1px solid var(--border); }
|
|
20448
|
+
.panel-head h2,.panel-head h3 { margin:0; font-size:14px; letter-spacing:-.01em; }
|
|
20449
|
+
.table-wrap { width:100%; overflow-x:auto; }
|
|
20450
|
+
table { width:100%; border-collapse:separate; border-spacing:0; font-size:13px; }
|
|
20451
|
+
th { height:42px; padding:0 13px; color:var(--text-3); background:var(--surface-2); border-bottom:1px solid var(--border);
|
|
20452
|
+
font-size:11px; font-weight:730; letter-spacing:.045em; text-align:left; text-transform:uppercase; white-space:nowrap; }
|
|
20453
|
+
td { padding:12px 13px; border-bottom:1px solid var(--border); vertical-align:middle; }
|
|
20454
|
+
tbody tr:last-child td { border-bottom:0; }
|
|
20455
|
+
tbody tr { transition:background .14s ease; }
|
|
20456
|
+
tbody tr:hover { background:color-mix(in srgb,var(--primary-soft) 30%,transparent); }
|
|
20457
|
+
.task-name { font-weight:680; color:var(--text); }
|
|
20458
|
+
.task-prompt { max-width:520px; margin-top:3px; color:var(--text-2); font-size:12px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
|
20459
|
+
.muted,.mu { color:var(--text-2); }
|
|
20460
|
+
.faint { color:var(--text-3); }
|
|
20461
|
+
.small,.sm { font-size:12px; }
|
|
20462
|
+
.tag { display:inline-flex; align-items:center; min-height:23px; max-width:180px; padding:0 8px; border:1px solid var(--border);
|
|
20463
|
+
border-radius:7px; color:var(--text-2); background:var(--surface-2); font-size:11px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
|
20464
|
+
.badge { display:inline-flex; align-items:center; gap:6px; min-height:24px; padding:0 9px; border-radius:999px; font-size:11px; font-weight:720; white-space:nowrap; }
|
|
20465
|
+
.badge::before { content:""; width:6px; height:6px; border-radius:50%; background:currentColor; }
|
|
20466
|
+
.b-pending { color:var(--text-2); background:var(--surface-3); }
|
|
20467
|
+
.b-running { color:var(--blue); background:var(--blue-soft); }
|
|
20468
|
+
.b-running::before { animation:pulse 1.7s ease-in-out infinite; }
|
|
20469
|
+
.b-done { color:var(--green); background:var(--green-soft); }
|
|
20470
|
+
.b-failed { color:var(--red); background:var(--red-soft); }
|
|
20471
|
+
.b-dead_letter { color:var(--yellow); background:var(--yellow-soft); }
|
|
20472
|
+
.b-cancelled,.b-unknown { color:var(--text-3); background:var(--surface-3); }
|
|
20473
|
+
.t-cron { color:var(--purple); } .t-recurring { color:var(--blue); } .t-delayed { color:var(--yellow); }
|
|
20474
|
+
.actions { display:flex; align-items:center; flex-wrap:wrap; gap:5px; }
|
|
20475
|
+
.btn { min-height:32px; display:inline-flex; align-items:center; justify-content:center; gap:6px; padding:0 11px; border:1px solid var(--border);
|
|
20476
|
+
border-radius:8px; color:var(--text-2); background:var(--surface); text-decoration:none; cursor:pointer; font-size:12px; font-weight:650;
|
|
20477
|
+
transition:transform .12s ease,background .15s ease,border-color .15s ease,color .15s ease,box-shadow .15s ease; }
|
|
20478
|
+
.btn:hover { color:var(--text); border-color:var(--border-strong); background:var(--surface-2); }
|
|
20479
|
+
.btn:active { transform:scale(.97); }
|
|
20480
|
+
.btn:disabled { opacity:.5; cursor:not-allowed; transform:none; }
|
|
20481
|
+
.btn-primary { color:#fff; border-color:var(--primary); background:var(--primary); }
|
|
20482
|
+
.btn-primary:hover { color:#fff; border-color:var(--primary-hover); background:var(--primary-hover); }
|
|
20483
|
+
.btn-danger { color:var(--red); }
|
|
20484
|
+
.btn-danger:hover { color:var(--red); border-color:color-mix(in srgb,var(--red) 55%,var(--border)); background:var(--red-soft); }
|
|
20485
|
+
.btn-warning:hover { color:var(--yellow); border-color:color-mix(in srgb,var(--yellow) 55%,var(--border)); background:var(--yellow-soft); }
|
|
20486
|
+
.btn .icon { width:14px; height:14px; }
|
|
20487
|
+
.pagination { display:flex; justify-content:center; align-items:center; gap:10px; margin:18px 0 4px; }
|
|
20488
|
+
.pagination .summary { color:var(--text-2); font-size:12px; }
|
|
20489
|
+
.empty-state { display:grid; place-items:center; min-height:230px; padding:36px; text-align:center; }
|
|
20490
|
+
.empty-icon { width:48px; height:48px; display:grid; place-items:center; border-radius:14px; color:var(--primary); background:var(--primary-soft); }
|
|
20491
|
+
.empty-icon .icon { width:23px; height:23px; }
|
|
20492
|
+
.empty-state h3 { margin:13px 0 4px; font-size:15px; }
|
|
20493
|
+
.empty-state p { margin:0; color:var(--text-2); font-size:12px; }
|
|
20494
|
+
.empty-state code { display:inline-block; margin-top:10px; padding:5px 8px; border-radius:6px; background:var(--surface-3); }
|
|
20495
|
+
.settings-grid { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:14px; margin-bottom:16px; }
|
|
20496
|
+
.settings-card { padding:18px; }
|
|
20497
|
+
.settings-title { display:flex; align-items:center; justify-content:space-between; margin:0 0 16px; font-size:14px; }
|
|
20498
|
+
.settings-title span:first-child { display:flex; align-items:center; gap:8px; }
|
|
20499
|
+
.settings-title .icon { width:17px; height:17px; color:var(--primary); }
|
|
20500
|
+
.field { display:grid; grid-template-columns:minmax(0,1fr) 112px; align-items:center; gap:12px; margin:11px 0; }
|
|
20501
|
+
.field label { color:var(--text-2); font-size:12px; }
|
|
20502
|
+
.input-unit { position:relative; }
|
|
20503
|
+
.input-unit input { width:100%; height:36px; padding:0 47px 0 10px; border:1px solid var(--border); border-radius:8px; outline:none;
|
|
20504
|
+
color:var(--text); background:var(--surface-2); }
|
|
20505
|
+
.input-unit span { position:absolute; right:9px; top:50%; transform:translateY(-50%); color:var(--text-3); font-size:10px; pointer-events:none; }
|
|
20506
|
+
.input-unit input:focus { border-color:var(--primary); box-shadow:var(--focus); background:var(--surface); }
|
|
20507
|
+
.switch-field { display:flex; align-items:center; justify-content:space-between; gap:12px; margin:11px 0; color:var(--text-2); font-size:12px; }
|
|
20508
|
+
.switch { position:relative; width:42px; height:24px; flex:0 0 auto; }
|
|
20509
|
+
.switch input { position:absolute; opacity:0; }
|
|
20510
|
+
.switch span { position:absolute; inset:0; border-radius:999px; background:var(--surface-3); border:1px solid var(--border-strong); cursor:pointer; transition:.2s ease; }
|
|
20511
|
+
.switch span::after { content:""; position:absolute; width:17px; height:17px; left:2px; top:2px; border-radius:50%; background:var(--surface);
|
|
20512
|
+
box-shadow:0 1px 3px rgba(0,0,0,.22); transition:transform .2s ease; }
|
|
20513
|
+
.switch input:checked+span { background:var(--primary); border-color:var(--primary); }
|
|
20514
|
+
.switch input:checked+span::after { transform:translateX(18px); }
|
|
20515
|
+
.save-row { display:flex; align-items:center; justify-content:flex-end; gap:12px; margin:0 0 24px; }
|
|
20516
|
+
.info-list { padding:4px 18px; }
|
|
20517
|
+
.info-row { display:flex; align-items:flex-start; justify-content:space-between; gap:20px; padding:13px 0; border-bottom:1px solid var(--border); }
|
|
20518
|
+
.info-row:last-child { border-bottom:0; }
|
|
20519
|
+
.info-key { color:var(--text-2); }
|
|
20520
|
+
.info-value { font-weight:650; text-align:right; overflow-wrap:anywhere; }
|
|
20521
|
+
.overview-grid { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:12px; padding:18px; }
|
|
20522
|
+
.overview-item { padding:13px; border-radius:10px; background:var(--surface-2); }
|
|
20523
|
+
.overview-item span { color:var(--text-2); font-size:11px; }
|
|
20524
|
+
.overview-item strong { display:block; margin-top:5px; font-size:19px; }
|
|
20525
|
+
.danger-card { margin-top:16px; padding:18px; border-color:color-mix(in srgb,var(--red) 40%,var(--border)); background:linear-gradient(145deg,var(--surface),var(--red-soft)); }
|
|
20526
|
+
.danger-card h2 { display:flex; align-items:center; gap:8px; margin:0 0 5px; color:var(--red); font-size:14px; }
|
|
20527
|
+
.danger-card h2 .icon { width:17px; height:17px; }
|
|
20528
|
+
.danger-card p { max-width:800px; margin:0 0 14px; color:var(--text-2); font-size:12px; }
|
|
20529
|
+
.log-panel { margin:12px 0; animation:reveal .18s ease both; }
|
|
20530
|
+
.log-box { max-height:360px; overflow:auto; padding:16px; color:var(--text-2); background:#0b1018; font-family:"SFMono-Regular",Consolas,monospace;
|
|
20531
|
+
font-size:12px; white-space:pre-wrap; overflow-wrap:anywhere; }
|
|
20532
|
+
:root[data-theme="light"] .log-box { color:#dbe5f3; }
|
|
20533
|
+
dialog { width:min(760px,calc(100% - 32px)); padding:0; border:1px solid var(--border); border-radius:16px; color:var(--text);
|
|
20534
|
+
background:var(--surface); box-shadow:var(--shadow-lg); }
|
|
20535
|
+
dialog[open] { animation:dialog-in .18s ease both; }
|
|
20536
|
+
dialog::backdrop { background:rgba(8,12,20,.62); backdrop-filter:blur(3px); animation:fade-in .18s ease both; }
|
|
20537
|
+
.dialog-head { display:flex; align-items:center; justify-content:space-between; gap:14px; padding:17px 18px; border-bottom:1px solid var(--border); }
|
|
20538
|
+
.dialog-head h2 { margin:0; font-size:15px; }
|
|
20539
|
+
.dialog-head p { margin:3px 0 0; color:var(--text-2); font-size:11px; }
|
|
20540
|
+
.dialog-body { max-height:70vh; overflow:auto; padding:18px; }
|
|
20541
|
+
.dialog-actions { display:flex; align-items:center; justify-content:flex-end; gap:8px; padding:14px 18px; border-top:1px solid var(--border); }
|
|
20542
|
+
.json-view { min-height:160px; margin:0; padding:15px; overflow:auto; border:1px solid var(--border); border-radius:10px; color:var(--text-2);
|
|
20543
|
+
background:var(--surface-2); font-size:12px; white-space:pre-wrap; overflow-wrap:anywhere; }
|
|
20544
|
+
.confirm-copy { color:var(--text-2); margin:0; }
|
|
20545
|
+
.confirm-copy strong { display:block; margin-bottom:5px; color:var(--text); font-size:15px; }
|
|
20546
|
+
.danger-input { width:100%; height:40px; margin-top:14px; padding:0 12px; border:1px solid var(--border); border-radius:9px; outline:none;
|
|
20547
|
+
color:var(--text); background:var(--surface-2); font-family:"SFMono-Regular",Consolas,monospace; text-transform:uppercase; }
|
|
20548
|
+
.danger-input:focus { border-color:var(--red); box-shadow:0 0 0 3px color-mix(in srgb,var(--red) 22%,transparent); }
|
|
20549
|
+
.toast-region { position:fixed; top:18px; right:18px; z-index:300; display:grid; gap:8px; pointer-events:none; }
|
|
20550
|
+
.toast { min-width:260px; max-width:min(420px,calc(100vw - 36px)); display:flex; align-items:flex-start; gap:10px; padding:12px 14px;
|
|
20551
|
+
border:1px solid var(--border); border-radius:11px; color:var(--text); background:var(--surface); box-shadow:var(--shadow-lg); animation:toast-in .22s ease both; }
|
|
20552
|
+
.toast .icon { width:18px; height:18px; flex:0 0 auto; margin-top:1px; }
|
|
20553
|
+
.toast.ok .icon { color:var(--green); } .toast.error .icon { color:var(--red); }
|
|
20554
|
+
.toast.leaving { animation:toast-out .18s ease both; }
|
|
20555
|
+
footer { display:flex; justify-content:center; padding:24px 0 4px; color:var(--text-3); font-size:11px; }
|
|
20556
|
+
[hidden] { display:none!important; }
|
|
20557
|
+
:focus-visible { outline:none; box-shadow:var(--focus); }
|
|
20558
|
+
.ui-ready,.ui-ready * { transition-property:background-color,border-color,color,box-shadow; transition-duration:.16s; transition-timing-function:ease; }
|
|
20559
|
+
.reveal { animation:reveal .28s ease both; }
|
|
20560
|
+
.reveal-delay-1 { animation-delay:.04s; } .reveal-delay-2 { animation-delay:.08s; }
|
|
20561
|
+
@keyframes spin { to { transform:rotate(360deg); } }
|
|
20562
|
+
@keyframes pulse { 0%,100% { opacity:1; box-shadow:0 0 0 0 currentColor; } 50% { opacity:.7; box-shadow:0 0 0 4px transparent; } }
|
|
20563
|
+
@keyframes reveal { from { opacity:0; transform:translateY(6px); } to { opacity:1; transform:none; } }
|
|
20564
|
+
@keyframes dialog-in { from { opacity:0; transform:translateY(8px) scale(.985); } to { opacity:1; transform:none; } }
|
|
20565
|
+
@keyframes fade-in { from { opacity:0; } to { opacity:1; } }
|
|
20566
|
+
@keyframes toast-in { from { opacity:0; transform:translateY(-8px) scale(.98); } to { opacity:1; transform:none; } }
|
|
20567
|
+
@keyframes toast-out { to { opacity:0; transform:translateY(-6px) scale(.98); } }
|
|
20568
|
+
@media (max-width:1000px) {
|
|
20569
|
+
.stats-grid { grid-template-columns:repeat(2,minmax(0,1fr)); }
|
|
20570
|
+
.settings-grid { grid-template-columns:1fr; }
|
|
20571
|
+
.local-chip { display:none; }
|
|
20572
|
+
}
|
|
20573
|
+
@media (max-width:720px) {
|
|
20574
|
+
.app-shell { padding:0 16px 20px; }
|
|
20575
|
+
.topbar { min-height:72px; }
|
|
20576
|
+
.brand-tagline,.brand-name span { display:none; }
|
|
20577
|
+
.top-actions { gap:5px; }
|
|
20578
|
+
.select-wrap select { width:42px; color:transparent; padding:0; }
|
|
20579
|
+
.select-wrap>.icon { left:12px; color:var(--text-2); }
|
|
20580
|
+
.select-wrap::after { display:none; }
|
|
20581
|
+
.language-switch { display:none; }
|
|
20582
|
+
.mobile-language { display:grid!important; }
|
|
20583
|
+
.tabs { width:100%; margin:14px 0 24px; }
|
|
20584
|
+
.tabs a { flex:1; justify-content:center; padding:0 10px; }
|
|
20585
|
+
.tabs a span { display:none; }
|
|
20586
|
+
.page-heading { align-items:flex-start; }
|
|
20587
|
+
.page-heading h1 { font-size:24px; }
|
|
20588
|
+
.page-heading p { font-size:13px; }
|
|
20589
|
+
.stats-grid,.stats-grid.three { grid-template-columns:repeat(2,minmax(0,1fr)); gap:10px; }
|
|
20590
|
+
.stat-card { min-height:105px; padding:15px; }
|
|
20591
|
+
.toolbar { align-items:stretch; flex-direction:column; }
|
|
20592
|
+
.search-box { width:100%; flex-basis:auto; }
|
|
20593
|
+
.filters { order:2; }
|
|
20594
|
+
.overview-grid { grid-template-columns:repeat(2,minmax(0,1fr)); }
|
|
20595
|
+
.save-row { align-items:flex-end; flex-direction:column-reverse; }
|
|
20596
|
+
.save-row .btn { width:100%; }
|
|
20597
|
+
.responsive-table { display:block; padding:10px; }
|
|
20598
|
+
.responsive-table thead { position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; }
|
|
20599
|
+
.responsive-table tbody { display:grid; gap:10px; }
|
|
20600
|
+
.responsive-table tr { display:grid; gap:0; padding:10px 12px; border:1px solid var(--border); border-radius:11px; background:var(--surface-2); }
|
|
20601
|
+
.responsive-table td { min-width:0; display:grid; grid-template-columns:88px minmax(0,1fr); align-items:start; gap:10px; padding:6px 0; border:0; }
|
|
20602
|
+
.responsive-table td::before { content:attr(data-label); color:var(--text-3); font-size:10px; font-weight:730; letter-spacing:.045em; text-transform:uppercase; }
|
|
20603
|
+
.responsive-table td[data-primary] { display:block; padding-bottom:10px; margin-bottom:4px; border-bottom:1px solid var(--border); }
|
|
20604
|
+
.responsive-table td[data-primary]::before { display:none; }
|
|
20605
|
+
.responsive-table .task-prompt { max-width:100%; }
|
|
20606
|
+
.responsive-table .actions { justify-content:flex-start; }
|
|
20607
|
+
}
|
|
20608
|
+
@media (max-width:520px) {
|
|
20609
|
+
.stats-grid,.stats-grid.three { grid-template-columns:1fr 1fr; }
|
|
20610
|
+
.stat-card { min-height:98px; }
|
|
20611
|
+
.stat-value { font-size:24px; }
|
|
20612
|
+
.page-heading p { max-width:95%; }
|
|
20613
|
+
.field { grid-template-columns:minmax(0,1fr) 105px; }
|
|
20614
|
+
.overview-grid { grid-template-columns:1fr 1fr; padding:12px; }
|
|
20615
|
+
.pagination .summary { max-width:150px; text-align:center; }
|
|
20616
|
+
}
|
|
20617
|
+
@media (prefers-reduced-motion:reduce) {
|
|
20618
|
+
*,*::before,*::after { scroll-behavior:auto!important; animation-duration:.01ms!important; animation-iteration-count:1!important; transition-duration:.01ms!important; }
|
|
20619
|
+
}
|
|
20620
|
+
</style>`;
|
|
20621
|
+
PAGE_KEYS = {
|
|
20622
|
+
tasks: { title: "page.tasks.title", description: "page.tasks.description" },
|
|
20623
|
+
templates: { title: "page.templates.title", description: "page.templates.description" },
|
|
20624
|
+
runs: { title: "page.runs.title", description: "page.runs.description" },
|
|
20625
|
+
system: { title: "page.system.title", description: "page.system.description" }
|
|
20626
|
+
};
|
|
20627
|
+
}
|
|
20628
|
+
});
|
|
20629
|
+
|
|
19934
20630
|
// src/web/index.tsx
|
|
19935
20631
|
var web_exports = {};
|
|
19936
20632
|
__export(web_exports, {
|
|
19937
20633
|
dashboardApp: () => dashboardApp,
|
|
19938
20634
|
default: () => web_default
|
|
19939
20635
|
});
|
|
19940
|
-
import { existsSync as existsSync6,
|
|
20636
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
19941
20637
|
import { dirname as dirname6 } from "path";
|
|
19942
20638
|
function parsePositiveInteger(value) {
|
|
19943
20639
|
if (!/^\d+$/.test(value)) return null;
|
|
@@ -19950,41 +20646,34 @@ function parseTaskStatus(value) {
|
|
|
19950
20646
|
function safeStatus(value) {
|
|
19951
20647
|
return value && TASK_STATUSES.has(value) ? value : "unknown";
|
|
19952
20648
|
}
|
|
20649
|
+
function resolveLocale(c) {
|
|
20650
|
+
const requested = c.req.query("lang");
|
|
20651
|
+
if (requested === "en" || requested === "zh-CN") return requested;
|
|
20652
|
+
const cookie = c.req.header("Cookie") ?? "";
|
|
20653
|
+
const match2 = /(?:^|;\s*)supertask_locale=([^;]+)/.exec(cookie);
|
|
20654
|
+
if (match2) {
|
|
20655
|
+
try {
|
|
20656
|
+
const saved = decodeURIComponent(match2[1]);
|
|
20657
|
+
if (saved === "en" || saved === "zh-CN") return saved;
|
|
20658
|
+
} catch {
|
|
20659
|
+
}
|
|
20660
|
+
}
|
|
20661
|
+
const accepted = c.req.header("Accept-Language")?.toLowerCase() ?? "";
|
|
20662
|
+
return accepted.startsWith("en") ? "en" : "zh-CN";
|
|
20663
|
+
}
|
|
19953
20664
|
function formatDuration(startAt, endAt) {
|
|
19954
|
-
if (!startAt) return "
|
|
20665
|
+
if (!startAt) return "\u2014";
|
|
19955
20666
|
const start = new Date(startAt).getTime();
|
|
19956
20667
|
const end = endAt ? new Date(endAt).getTime() : Date.now();
|
|
19957
20668
|
const seconds = Math.floor((end - start) / 1e3);
|
|
19958
20669
|
if (seconds < 0) return "0s";
|
|
19959
20670
|
if (seconds < 60) return `${seconds}s`;
|
|
19960
|
-
if (seconds < 3600) return `${Math.floor(seconds / 60)}m${seconds % 60}s`;
|
|
19961
|
-
return `${Math.floor(seconds / 3600)}h${Math.floor(seconds % 3600 / 60)}m`;
|
|
20671
|
+
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
|
|
20672
|
+
return `${Math.floor(seconds / 3600)}h ${Math.floor(seconds % 3600 / 60)}m`;
|
|
19962
20673
|
}
|
|
19963
|
-
function
|
|
19964
|
-
if (!
|
|
19965
|
-
|
|
19966
|
-
if (diff < 6e4) return `${Math.floor(diff / 1e3)}\u79D2\u524D`;
|
|
19967
|
-
if (diff < 36e5) return `${Math.floor(diff / 6e4)}\u5206\u949F\u524D`;
|
|
19968
|
-
if (diff < 864e5) return `${Math.floor(diff / 36e5)}\u5C0F\u65F6\u524D`;
|
|
19969
|
-
return `${Math.floor(diff / 864e5)}\u5929\u524D`;
|
|
19970
|
-
}
|
|
19971
|
-
function timeUntil(ms) {
|
|
19972
|
-
if (!ms) return "-";
|
|
19973
|
-
const diff = ms - Date.now();
|
|
19974
|
-
if (diff < 0) return "\u5DF2\u5230\u671F";
|
|
19975
|
-
if (diff < 6e4) return `${Math.floor(diff / 1e3)}\u79D2\u540E`;
|
|
19976
|
-
if (diff < 36e5) return `${Math.floor(diff / 6e4)}\u5206\u949F\u540E`;
|
|
19977
|
-
if (diff < 864e5) return `${Math.floor(diff / 36e5)}\u5C0F\u65F6\u540E`;
|
|
19978
|
-
return `${Math.floor(diff / 864e5)}\u5929\u540E`;
|
|
19979
|
-
}
|
|
19980
|
-
function formatDate(ts) {
|
|
19981
|
-
if (!ts) return "-";
|
|
19982
|
-
const d = ts instanceof Date ? ts : new Date(ts);
|
|
19983
|
-
return d.toLocaleString("zh-CN", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" });
|
|
19984
|
-
}
|
|
19985
|
-
function esc(s) {
|
|
19986
|
-
if (!s) return "";
|
|
19987
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
20674
|
+
function esc(value) {
|
|
20675
|
+
if (!value) return "";
|
|
20676
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
19988
20677
|
}
|
|
19989
20678
|
function readCurrentConfig() {
|
|
19990
20679
|
const configPath = getConfigPath();
|
|
@@ -20003,94 +20692,51 @@ function writeConfig(cfg) {
|
|
|
20003
20692
|
writeFileSync2(tempPath, JSON.stringify(cfg, null, 2) + "\n", { mode: 384 });
|
|
20004
20693
|
renameSync2(tempPath, configPath);
|
|
20005
20694
|
}
|
|
20006
|
-
function
|
|
20007
|
-
return
|
|
20008
|
-
<
|
|
20009
|
-
|
|
20010
|
-
|
|
20011
|
-
async function deleteTask(id){if(!confirm('\u786E\u5B9A\u5220\u9664\u4EFB\u52A1 #'+id+'?'))return;const r=await fetch('/api/tasks/'+id,{method:'DELETE'});const d=await r.json();if(!r.ok){alert('\u5220\u9664\u5931\u8D25: '+d.error);return;}location.reload();}
|
|
20012
|
-
async function showDetail(id){try{const r=await fetch('/api/tasks/'+id);const t=await r.json();document.getElementById('dc').textContent=JSON.stringify(t,null,2);document.getElementById('dd').showModal();}catch(e){alert('\u83B7\u53D6\u8BE6\u60C5\u5931\u8D25');}}
|
|
20013
|
-
async function showRunDetail(id){try{const r=await fetch('/api/runs/'+id);const t=await r.json();document.getElementById('dc').textContent=JSON.stringify(t,null,2);document.getElementById('dd').showModal();}catch(e){alert('\u83B7\u53D6\u8BE6\u60C5\u5931\u8D25');}}
|
|
20014
|
-
async function showTemplateDetail(id){try{const r=await fetch('/api/templates/'+id);const t=await r.json();document.getElementById('dc').textContent=JSON.stringify(t,null,2);document.getElementById('dd').showModal();}catch(e){alert('\u83B7\u53D6\u8BE6\u60C5\u5931\u8D25');}}
|
|
20015
|
-
async function enableTmpl(id){await fetch('/api/templates/'+id+'/enable',{method:'POST'});location.reload();}
|
|
20016
|
-
async function disableTmpl(id){if(!confirm('\u786E\u5B9A\u7981\u7528\u6B64\u6A21\u677F?'))return;await fetch('/api/templates/'+id+'/disable',{method:'POST'});location.reload();}
|
|
20017
|
-
async function deleteTmpl(id){if(!confirm('\u786E\u5B9A\u5220\u9664\u6B64\u6A21\u677F? \u6B64\u64CD\u4F5C\u4E0D\u53EF\u6062\u590D!'))return;await fetch('/api/templates/'+id,{method:'DELETE'});location.reload();}
|
|
20018
|
-
async function triggerTmpl(id){if(!confirm('\u7ACB\u5373\u89E6\u53D1\u4E00\u6B21?'))return;const r=await fetch('/api/templates/'+id+'/trigger',{method:'POST'});const d=await r.json();if(d.success){alert('\u5DF2\u521B\u5EFA\u4EFB\u52A1 #'+d.taskId);location.reload();}else{alert('\u89E6\u53D1\u5931\u8D25');}}
|
|
20019
|
-
function toggleLog(id){const el=document.getElementById('log-'+id);el.style.display=el.style.display==='none'?'block':'none';}
|
|
20020
|
-
|
|
20021
|
-
async function clearDatabase(){
|
|
20022
|
-
if(!confirm('\u786E\u5B9A\u6E05\u7A7A\u6240\u6709\u4EFB\u52A1\u6570\u636E\uFF1F\u7CFB\u7EDF\u4F1A\u5148\u81EA\u52A8\u521B\u5EFA\u5907\u4EFD\u3002'))return;
|
|
20023
|
-
if(!confirm('\u518D\u6B21\u786E\u8BA4\uFF1A\u5C06\u4E8B\u52A1\u6027\u5220\u9664\u6240\u6709\u4EFB\u52A1\u3001\u6267\u884C\u8BB0\u5F55\u548C\u8C03\u5EA6\u6A21\u677F\u3002'))return;
|
|
20024
|
-
try{
|
|
20025
|
-
const r=await fetch('/api/database/clear',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({confirmation:'CLEAR'})});
|
|
20026
|
-
const d=await r.json();
|
|
20027
|
-
if(d.success){alert('\u6570\u636E\u5E93\u5DF2\u6E05\u7A7A\uFF0C\u81EA\u52A8\u5907\u4EFD\uFF1A'+d.backupPath);location.reload();}
|
|
20028
|
-
else{alert('\u6E05\u7A7A\u5931\u8D25: '+d.error);}
|
|
20029
|
-
}catch(e){alert('\u6E05\u7A7A\u5931\u8D25: '+e.message);}
|
|
20695
|
+
function statCard(value, label, tone, cardIcon, delay = "") {
|
|
20696
|
+
return `<div class="stat-card ${tone} reveal ${delay}">
|
|
20697
|
+
<div class="stat-top"><div class="stat-icon">${cardIcon}</div></div>
|
|
20698
|
+
<div class="stat-value">${value}</div><div class="stat-label">${label}</div>
|
|
20699
|
+
</div>`;
|
|
20030
20700
|
}
|
|
20031
|
-
|
|
20032
|
-
|
|
20033
|
-
|
|
20034
|
-
const data={
|
|
20035
|
-
worker:{
|
|
20036
|
-
maxConcurrency:Number(form.mc.value),
|
|
20037
|
-
pollIntervalMs:Number(form.pi.value),
|
|
20038
|
-
heartbeatIntervalMs:Number(form.hi.value)*1000,
|
|
20039
|
-
taskTimeoutMs:Number(form.to.value)*60000,
|
|
20040
|
-
},
|
|
20041
|
-
scheduler:{
|
|
20042
|
-
enabled:form.se.checked,
|
|
20043
|
-
checkIntervalMs:Number(form.si.value),
|
|
20044
|
-
},
|
|
20045
|
-
watchdog:{
|
|
20046
|
-
heartbeatTimeoutMs:Number(form.wt.value)*1000,
|
|
20047
|
-
checkIntervalMs:Number(form.wci.value)*1000,
|
|
20048
|
-
cleanupIntervalMs:Number(form.wcl.value)*3600000,
|
|
20049
|
-
retentionDays:Number(form.rd.value),
|
|
20050
|
-
}
|
|
20051
|
-
};
|
|
20052
|
-
try{
|
|
20053
|
-
const r=await fetch('/api/config',{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(data)});
|
|
20054
|
-
const d=await r.json();
|
|
20055
|
-
if(d.success){document.getElementById('toast').textContent='\u914D\u7F6E\u5DF2\u4FDD\u5B58\uFF0C\u91CD\u542F Gateway \u540E\u751F\u6548';document.getElementById('toast').style.display='block';setTimeout(()=>document.getElementById('toast').style.display='none',3000);}
|
|
20056
|
-
else{alert('\u4FDD\u5B58\u5931\u8D25: '+d.error);}
|
|
20057
|
-
}catch(e){alert('\u4FDD\u5B58\u5931\u8D25: '+e.message);}
|
|
20701
|
+
function emptyState(title, hint, code = "") {
|
|
20702
|
+
return `<div class="empty-state"><div><div class="empty-icon">${icon("inbox")}</div>
|
|
20703
|
+
<h3>${title}</h3><p>${hint}</p>${code ? `<code>${code}</code>` : ""}</div></div>`;
|
|
20058
20704
|
}
|
|
20059
|
-
|
|
20060
|
-
|
|
20061
|
-
|
|
20062
|
-
|
|
20063
|
-
<
|
|
20064
|
-
|
|
20065
|
-
|
|
20066
|
-
|
|
20067
|
-
|
|
20068
|
-
|
|
20069
|
-
|
|
20070
|
-
|
|
20071
|
-
|
|
20072
|
-
|
|
20073
|
-
</nav>
|
|
20074
|
-
${body}
|
|
20075
|
-
</div>
|
|
20076
|
-
<dialog id="dd"><div class="dh"><h3 style="margin:0">\u8BE6\u60C5</h3><button class="cb" onclick="document.getElementById('dd').close()">×</button></div><div class="db"><pre id="dc"></pre></div></dialog>
|
|
20077
|
-
</body></html>`;
|
|
20705
|
+
function formatInterval(milliseconds, locale) {
|
|
20706
|
+
const formatter = new Intl.NumberFormat(locale === "en" ? "en-US" : "zh-CN", {
|
|
20707
|
+
maximumFractionDigits: 1
|
|
20708
|
+
});
|
|
20709
|
+
if (milliseconds < 6e4) {
|
|
20710
|
+
return t(locale, "schedule.seconds", { count: formatter.format(milliseconds / 1e3) });
|
|
20711
|
+
}
|
|
20712
|
+
if (milliseconds < 36e5) {
|
|
20713
|
+
return t(locale, "schedule.minutes", { count: formatter.format(milliseconds / 6e4) });
|
|
20714
|
+
}
|
|
20715
|
+
if (milliseconds < 864e5) {
|
|
20716
|
+
return t(locale, "schedule.hours", { count: formatter.format(milliseconds / 36e5) });
|
|
20717
|
+
}
|
|
20718
|
+
return t(locale, "schedule.days", { count: formatter.format(milliseconds / 864e5) });
|
|
20078
20719
|
}
|
|
20079
|
-
|
|
20720
|
+
function pagination(locale, basePath, page, pages, total, suffix = "") {
|
|
20721
|
+
const previous = page > 1 ? `<a class="btn" href="${basePath}?page=${page - 1}${suffix}">${icon("chevronLeft")}${t(locale, "pagination.previous")}</a>` : "";
|
|
20722
|
+
const next = page < pages ? `<a class="btn" href="${basePath}?page=${page + 1}${suffix}">${t(locale, "pagination.next")}${icon("chevronRight")}</a>` : "";
|
|
20723
|
+
return `<div class="pagination">${previous}<span class="summary">${t(locale, "pagination.summary", { page, pages, total })}</span>${next}</div>`;
|
|
20724
|
+
}
|
|
20725
|
+
var app, TASK_STATUSES, dashboardApp, web_default;
|
|
20080
20726
|
var init_web = __esm({
|
|
20081
20727
|
"src/web/index.tsx"() {
|
|
20082
20728
|
"use strict";
|
|
20083
20729
|
init_dist();
|
|
20084
|
-
init_html2();
|
|
20085
|
-
init_task_service();
|
|
20086
|
-
init_task_run_service();
|
|
20087
|
-
init_task_template_service();
|
|
20088
|
-
init_database_maintenance_service();
|
|
20089
20730
|
init_drizzle_orm();
|
|
20090
20731
|
init_db2();
|
|
20732
|
+
init_database_maintenance_service();
|
|
20733
|
+
init_task_run_service();
|
|
20734
|
+
init_task_service();
|
|
20735
|
+
init_task_template_service();
|
|
20091
20736
|
init_config();
|
|
20092
20737
|
init_health();
|
|
20093
20738
|
init_job_templates();
|
|
20739
|
+
init_ui();
|
|
20094
20740
|
app = new Hono2();
|
|
20095
20741
|
TASK_STATUSES = /* @__PURE__ */ new Set([
|
|
20096
20742
|
"pending",
|
|
@@ -20131,84 +20777,9 @@ var init_web = __esm({
|
|
|
20131
20777
|
const health = getGatewayHealth();
|
|
20132
20778
|
return c.json(health, health.status === "ok" ? 200 : 503);
|
|
20133
20779
|
});
|
|
20134
|
-
SHARED_STYLES = html`
|
|
20135
|
-
<style>
|
|
20136
|
-
:root { --bg:#0d1117; --card:#161b22; --border:#30363d; --t1:#c9d1d9; --t2:#8b949e; --green:#238636; --red:#da3633; --yellow:#d29922; --blue:#1f6feb; --purple:#8957e5; }
|
|
20137
|
-
* { box-sizing:border-box; }
|
|
20138
|
-
body { font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif; background:var(--bg); color:var(--t1); margin:0; line-height:1.5; }
|
|
20139
|
-
.c { max-width:1280px; margin:0 auto; padding:20px; }
|
|
20140
|
-
header { display:flex; justify-content:space-between; align-items:center; margin-bottom:20px; border-bottom:1px solid var(--border); padding-bottom:16px; }
|
|
20141
|
-
h1 { font-size:22px; margin:0; color:#fff; }
|
|
20142
|
-
nav.tabs { display:flex; gap:0; margin-bottom:20px; border-bottom:1px solid var(--border); }
|
|
20143
|
-
nav.tabs a { display:block; padding:10px 20px; color:var(--t2); text-decoration:none; font-size:14px; font-weight:500; border-bottom:2px solid transparent; }
|
|
20144
|
-
nav.tabs a:hover { color:var(--t1); }
|
|
20145
|
-
nav.tabs a.active { color:#fff; border-bottom-color:var(--blue); }
|
|
20146
|
-
.g4 { display:grid; grid-template-columns:repeat(auto-fit,minmax(160px,1fr)); gap:16px; margin-bottom:24px; }
|
|
20147
|
-
.g3 { display:grid; grid-template-columns:repeat(auto-fit,minmax(280px,1fr)); gap:16px; margin-bottom:24px; }
|
|
20148
|
-
.card { background:var(--card); border:1px solid var(--border); border-radius:8px; padding:16px; }
|
|
20149
|
-
.sv { font-size:28px; font-weight:bold; }
|
|
20150
|
-
.sl { color:var(--t2); font-size:12px; text-transform:uppercase; margin-top:4px; }
|
|
20151
|
-
.panel { background:var(--card); border:1px solid var(--border); border-radius:8px; overflow:hidden; margin-bottom:16px; }
|
|
20152
|
-
.ph { padding:12px 16px; border-bottom:1px solid var(--border); display:flex; justify-content:space-between; align-items:center; }
|
|
20153
|
-
.ph h3 { margin:0; font-size:14px; }
|
|
20154
|
-
table { width:100%; border-collapse:collapse; font-size:13px; }
|
|
20155
|
-
th { background:#21262d; color:var(--t2); font-weight:600; text-align:left; padding:8px 12px; white-space:nowrap; }
|
|
20156
|
-
td { padding:8px 12px; border-bottom:1px solid var(--border); vertical-align:top; }
|
|
20157
|
-
tr:last-child td { border-bottom:none; }
|
|
20158
|
-
tr:hover { background:rgba(255,255,255,0.02); }
|
|
20159
|
-
.badge { display:inline-block; padding:2px 8px; border-radius:12px; font-size:11px; font-weight:600; }
|
|
20160
|
-
.b-pending { background:rgba(110,118,129,0.3); color:#8b949e; }
|
|
20161
|
-
.b-running { background:rgba(56,139,253,0.15); color:#58a6ff; }
|
|
20162
|
-
.b-done { background:rgba(46,160,67,0.15); color:#3fb950; }
|
|
20163
|
-
.b-failed { background:rgba(248,81,73,0.15); color:#f85149; }
|
|
20164
|
-
.b-dead_letter { background:rgba(210,153,34,0.15); color:#d29922; }
|
|
20165
|
-
.b-cancelled { background:rgba(110,118,129,0.2); color:#6e7681; }
|
|
20166
|
-
.btn { appearance:none; background:transparent; border:1px solid var(--border); color:var(--t2); padding:4px 10px; border-radius:4px; cursor:pointer; font-size:12px; margin-right:4px; text-decoration:none; }
|
|
20167
|
-
.btn:hover { background:#30363d; color:#fff; }
|
|
20168
|
-
.btn-sm { padding:2px 6px; font-size:11px; }
|
|
20169
|
-
.btn-primary { background:var(--green); border-color:var(--green); color:#fff; }
|
|
20170
|
-
.btn-primary:hover { opacity:0.85; color:#fff; }
|
|
20171
|
-
.btn-danger:hover { color:var(--red); border-color:var(--red); }
|
|
20172
|
-
.btn-warn:hover { color:var(--yellow); border-color:var(--yellow); }
|
|
20173
|
-
.rf { background:var(--green); color:white; border:none; padding:6px 16px; border-radius:6px; font-weight:600; cursor:pointer; text-decoration:none; }
|
|
20174
|
-
.rf:hover { opacity:0.9; }
|
|
20175
|
-
.m { font-family:monospace; font-size:12px; }
|
|
20176
|
-
.mu { color:var(--t2); }
|
|
20177
|
-
.sm { font-size:12px; }
|
|
20178
|
-
.el { max-width:400px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; display:inline-block; vertical-align:middle; }
|
|
20179
|
-
.tag { display:inline-block; background:#21262d; padding:1px 6px; border-radius:4px; font-size:11px; }
|
|
20180
|
-
.t-cron { color:var(--purple); }
|
|
20181
|
-
.t-recurring { color:var(--blue); }
|
|
20182
|
-
.t-delayed { color:var(--yellow); }
|
|
20183
|
-
.pn { margin-top:16px; display:flex; justify-content:center; gap:10px; align-items:center; }
|
|
20184
|
-
.ir { display:flex; justify-content:space-between; padding:6px 0; border-bottom:1px solid var(--border); font-size:13px; }
|
|
20185
|
-
.ir:last-child { border-bottom:none; }
|
|
20186
|
-
.ik { color:var(--t2); }
|
|
20187
|
-
.iv { font-weight:500; }
|
|
20188
|
-
.form-row { display:flex; gap:12px; align-items:center; margin-bottom:12px; }
|
|
20189
|
-
.form-row label { color:var(--t2); font-size:13px; min-width:100px; }
|
|
20190
|
-
.form-row input, .form-row select { background:#0d1117; border:1px solid var(--border); color:var(--t1); padding:6px 10px; border-radius:4px; font-size:13px; }
|
|
20191
|
-
.form-row input:focus, .form-row select:focus { outline:none; border-color:var(--blue); }
|
|
20192
|
-
.log-box { background:#0d1117; border:1px solid var(--border); border-radius:4px; padding:12px; font-family:monospace; font-size:12px; max-height:300px; overflow-y:auto; white-space:pre-wrap; color:var(--t1); margin-top:8px; }
|
|
20193
|
-
dialog { background:var(--card); color:var(--t1); border:1px solid var(--border); border-radius:8px; padding:0; max-width:900px; width:90%; }
|
|
20194
|
-
dialog::backdrop { background:rgba(0,0,0,0.6); }
|
|
20195
|
-
.dh { padding:16px; border-bottom:1px solid var(--border); display:flex; justify-content:space-between; align-items:center; }
|
|
20196
|
-
.db { padding:16px; max-height:70vh; overflow-y:auto; }
|
|
20197
|
-
pre { margin:0; white-space:pre-wrap; font-size:12px; }
|
|
20198
|
-
.cb { background:transparent; border:none; color:var(--t2); cursor:pointer; font-size:20px; }
|
|
20199
|
-
.mt8 { margin-top:8px; }
|
|
20200
|
-
.mt16 { margin-top:16px; }
|
|
20201
|
-
.mb0 { margin-bottom:0; }
|
|
20202
|
-
.ta-center { text-align:center; }
|
|
20203
|
-
.p30 { padding:30px; }
|
|
20204
|
-
.toast { position:fixed; top:20px; right:20px; padding:12px 20px; border-radius:6px; color:#fff; font-size:14px; z-index:9999; display:none; }
|
|
20205
|
-
.toast-ok { background:var(--green); }
|
|
20206
|
-
.toast-err { background:var(--red); }
|
|
20207
|
-
</style>
|
|
20208
|
-
`;
|
|
20209
20780
|
app.get("/", async (c) => {
|
|
20210
|
-
const
|
|
20211
|
-
const page = parsePositiveInteger(
|
|
20781
|
+
const locale = resolveLocale(c);
|
|
20782
|
+
const page = parsePositiveInteger(c.req.query("page") || "1");
|
|
20212
20783
|
if (page === null) return c.text("invalid page", 400);
|
|
20213
20784
|
const statusFilter = c.req.query("status") || "";
|
|
20214
20785
|
const parsedStatus = statusFilter ? parseTaskStatus(statusFilter) : null;
|
|
@@ -20219,8 +20790,7 @@ var init_web = __esm({
|
|
|
20219
20790
|
TaskService.list({ limit, offset, ...parsedStatus ? { status: parsedStatus } : {} }),
|
|
20220
20791
|
TaskService.stats({})
|
|
20221
20792
|
]);
|
|
20222
|
-
const
|
|
20223
|
-
const latestRuns = await TaskRunService.getLatestByTaskIds(taskIds);
|
|
20793
|
+
const latestRuns = await TaskRunService.getLatestByTaskIds(tasks4.map((task) => task.id));
|
|
20224
20794
|
const counts = {
|
|
20225
20795
|
pending: statsData.pending || 0,
|
|
20226
20796
|
running: statsData.running || 0,
|
|
@@ -20228,253 +20798,211 @@ var init_web = __esm({
|
|
|
20228
20798
|
failed: (statsData.failed || 0) + (statsData.dead_letter || 0),
|
|
20229
20799
|
total: statsData.total || 0
|
|
20230
20800
|
};
|
|
20231
|
-
const
|
|
20232
|
-
|
|
20233
|
-
|
|
20234
|
-
|
|
20235
|
-
|
|
20236
|
-
|
|
20237
|
-
|
|
20238
|
-
|
|
20239
|
-
|
|
20240
|
-
|
|
20241
|
-
|
|
20801
|
+
const filteredTotal = parsedStatus ? Number(statsData[parsedStatus] ?? 0) : counts.total;
|
|
20802
|
+
const totalPages = Math.max(1, Math.ceil(filteredTotal / limit));
|
|
20803
|
+
const filterItems = [
|
|
20804
|
+
{ status: "", label: t(locale, "filter.all") },
|
|
20805
|
+
{ status: "pending", label: statusText(locale, "pending") },
|
|
20806
|
+
{ status: "running", label: statusText(locale, "running") },
|
|
20807
|
+
{ status: "done", label: statusText(locale, "done") },
|
|
20808
|
+
{ status: "failed", label: statusText(locale, "failed") },
|
|
20809
|
+
{ status: "dead_letter", label: statusText(locale, "dead_letter") },
|
|
20810
|
+
{ status: "cancelled", label: statusText(locale, "cancelled") }
|
|
20811
|
+
];
|
|
20812
|
+
const filters = filterItems.map(({ status, label }) => {
|
|
20813
|
+
const href = status ? `/?status=${status}` : "/";
|
|
20814
|
+
return `<a href="${href}" class="filter-chip ${statusFilter === status ? "active" : ""}">${label}</a>`;
|
|
20815
|
+
}).join("");
|
|
20816
|
+
const rows = tasks4.map((task) => {
|
|
20242
20817
|
const status = safeStatus(task.status);
|
|
20243
|
-
const st = status.toUpperCase();
|
|
20244
20818
|
const executionActive = latestRuns.get(task.id)?.status === "running";
|
|
20245
|
-
|
|
20246
|
-
|
|
20247
|
-
<td
|
|
20248
|
-
<td><
|
|
20249
|
-
<td><span class="
|
|
20250
|
-
<td
|
|
20251
|
-
<td class="
|
|
20252
|
-
<td>
|
|
20253
|
-
|
|
20254
|
-
|
|
20255
|
-
${
|
|
20256
|
-
${
|
|
20257
|
-
|
|
20258
|
-
|
|
20259
|
-
|
|
20260
|
-
|
|
20261
|
-
|
|
20262
|
-
|
|
20263
|
-
|
|
20264
|
-
|
|
20819
|
+
const searchable = esc(`${task.name} ${task.agent} ${task.prompt}`);
|
|
20820
|
+
return `<tr data-task-row data-search="${searchable}">
|
|
20821
|
+
<td class="faint" data-label="${t(locale, "table.id")}">#${task.id}</td>
|
|
20822
|
+
<td data-primary data-label="${t(locale, "table.task")}"><div class="task-name">${esc(task.name)}</div><div class="task-prompt" title="${esc(task.prompt)}">${esc(task.prompt.substring(0, 160))}</div></td>
|
|
20823
|
+
<td data-label="${t(locale, "table.agent")}"><span class="tag">${esc(task.agent)}</span></td>
|
|
20824
|
+
<td data-label="${t(locale, "table.status")}"><span class="badge b-${status}">${statusText(locale, status)}</span></td>
|
|
20825
|
+
<td data-label="${t(locale, "table.duration")}" class="small ${task.status === "running" ? "" : "muted"}">${formatDuration(task.startedAt, task.finishedAt)}</td>
|
|
20826
|
+
<td data-label="${t(locale, "table.retries")}" class="muted small">${(task.retryCount ?? 0) > 0 ? task.retryCount : "\u2014"}</td>
|
|
20827
|
+
<td data-label="${t(locale, "table.actions")}"><div class="actions">
|
|
20828
|
+
<button type="button" class="btn" onclick="showDetail(${task.id})">${t(locale, "action.details")}</button>
|
|
20829
|
+
${task.status === "failed" || task.status === "dead_letter" ? `<button type="button" class="btn btn-warning" onclick="retryTask(${task.id})">${t(locale, "action.retry")}</button>` : ""}
|
|
20830
|
+
${["pending", "running", "failed"].includes(task.status ?? "") ? `<button type="button" class="btn btn-warning" onclick="cancelTask(${task.id})">${t(locale, "action.cancel")}</button>` : ""}
|
|
20831
|
+
${task.status === "running" || executionActive ? "" : `<button type="button" class="btn btn-danger" onclick="deleteTask(${task.id})">${t(locale, "action.delete")}</button>`}
|
|
20832
|
+
</div></td>
|
|
20833
|
+
</tr>`;
|
|
20834
|
+
}).join("");
|
|
20835
|
+
const table = tasks4.length === 0 ? emptyState(t(locale, "empty.tasks"), t(locale, "empty.tasksHint")) : `<div class="table-wrap"><table class="responsive-table">
|
|
20836
|
+
<thead><tr><th>ID</th><th>${t(locale, "table.task")}</th><th>${t(locale, "table.agent")}</th><th>${t(locale, "table.status")}</th><th>${t(locale, "table.duration")}</th><th>${t(locale, "table.retries")}</th><th>${t(locale, "table.actions")}</th></tr></thead>
|
|
20837
|
+
<tbody>${rows}</tbody>
|
|
20838
|
+
</table></div>
|
|
20839
|
+
<div id="search-empty" hidden>${emptyState(t(locale, "filter.noResults"), "")}</div>`;
|
|
20840
|
+
const suffix = statusFilter ? `&status=${statusFilter}` : "";
|
|
20265
20841
|
const body = `
|
|
20266
|
-
<div class="
|
|
20267
|
-
|
|
20268
|
-
|
|
20269
|
-
|
|
20270
|
-
|
|
20842
|
+
<div class="stats-grid">
|
|
20843
|
+
${statCard(counts.pending, t(locale, "stats.pending"), "tone-neutral", icon("clock"))}
|
|
20844
|
+
${statCard(counts.running, t(locale, "stats.running"), "tone-blue", icon("activity"), "reveal-delay-1")}
|
|
20845
|
+
${statCard(counts.done, t(locale, "stats.done"), "tone-green", icon("check"), "reveal-delay-1")}
|
|
20846
|
+
${statCard(counts.failed, t(locale, "stats.failedDead"), "tone-red", icon("alert"), "reveal-delay-2")}
|
|
20847
|
+
</div>
|
|
20848
|
+
<div class="toolbar reveal reveal-delay-1">
|
|
20849
|
+
<div class="filters">${filters}</div>
|
|
20850
|
+
<label class="search-box">${icon("search")}<input type="search" oninput="filterTasks(this.value)" placeholder="${t(locale, "filter.searchTasks")}" aria-label="${t(locale, "filter.searchTasks")}"></label>
|
|
20271
20851
|
</div>
|
|
20272
|
-
|
|
20273
|
-
|
|
20274
|
-
|
|
20275
|
-
<tbody>${rows}</tbody>
|
|
20276
|
-
</table></div>
|
|
20277
|
-
${paging}`;
|
|
20278
|
-
return c.html(renderLayout("\u4EFB\u52A1\u961F\u5217", "tasks", body));
|
|
20852
|
+
<section class="panel reveal reveal-delay-2">${table}</section>
|
|
20853
|
+
${pagination(locale, "/", page, totalPages, filteredTotal, suffix)}`;
|
|
20854
|
+
return c.html(renderLayout({ locale, activeTab: "tasks", body }));
|
|
20279
20855
|
});
|
|
20280
20856
|
app.get("/templates", async (c) => {
|
|
20857
|
+
const locale = resolveLocale(c);
|
|
20281
20858
|
const templates = await TaskTemplateService.list(100);
|
|
20282
|
-
const enabled = templates.filter((
|
|
20859
|
+
const enabled = templates.filter((template) => template.enabled).length;
|
|
20283
20860
|
const disabled = templates.length - enabled;
|
|
20284
|
-
|
|
20285
|
-
|
|
20286
|
-
const
|
|
20287
|
-
|
|
20288
|
-
|
|
20289
|
-
|
|
20290
|
-
if (
|
|
20291
|
-
|
|
20292
|
-
|
|
20293
|
-
|
|
20294
|
-
|
|
20295
|
-
|
|
20296
|
-
<td
|
|
20297
|
-
<td
|
|
20298
|
-
|
|
20299
|
-
<td
|
|
20300
|
-
<td
|
|
20301
|
-
<td>${
|
|
20302
|
-
|
|
20303
|
-
|
|
20304
|
-
|
|
20305
|
-
|
|
20306
|
-
<button class="btn btn-sm btn-primary" onclick="triggerTmpl(${t.id})">\u89E6\u53D1</button>
|
|
20307
|
-
${toggleBtn}
|
|
20308
|
-
<button class="btn btn-sm btn-danger" onclick="deleteTmpl(${t.id})">\u5220\u9664</button>
|
|
20309
|
-
</td></tr>`;
|
|
20310
|
-
}
|
|
20311
|
-
const emptyRow = templates.length === 0 ? `<tr><td colspan="8" class="ta-center mu p30">\u6682\u65E0\u5B9A\u65F6\u4EFB\u52A1\u6A21\u677F\u3002\u4F7F\u7528 CLI \u521B\u5EFA\uFF1A<code>supertask template add</code></td></tr>` : "";
|
|
20861
|
+
const rows = templates.map((template) => {
|
|
20862
|
+
const scheduleType = ["cron", "recurring", "delayed"].includes(template.scheduleType) ? template.scheduleType : "unknown";
|
|
20863
|
+
const typeLabel = scheduleType === "cron" ? t(locale, "schedule.cron") : scheduleType === "recurring" ? t(locale, "schedule.recurring") : scheduleType === "delayed" ? t(locale, "schedule.delayed") : t(locale, "schedule.unknown");
|
|
20864
|
+
let rule = "\u2014";
|
|
20865
|
+
if (template.scheduleType === "cron") rule = template.cronExpr || "\u2014";
|
|
20866
|
+
if (template.scheduleType === "recurring" && template.intervalMs) rule = formatInterval(template.intervalMs, locale);
|
|
20867
|
+
if (template.scheduleType === "delayed") rule = formatDateTime(template.runAt, locale);
|
|
20868
|
+
const toggle = template.enabled ? `<button type="button" class="btn btn-warning" onclick="disableTmpl(${template.id})">${t(locale, "action.disable")}</button>` : `<button type="button" class="btn" onclick="enableTmpl(${template.id})">${t(locale, "action.enable")}</button>`;
|
|
20869
|
+
return `<tr>
|
|
20870
|
+
<td class="faint" data-label="${t(locale, "table.id")}">#${template.id}</td>
|
|
20871
|
+
<td data-primary data-label="${t(locale, "table.name")}"><div class="task-name">${esc(template.name)}</div><div class="task-prompt" title="${esc(template.prompt)}">${esc(template.prompt.substring(0, 140))}</div>
|
|
20872
|
+
<div class="actions" style="margin-top:5px"><span class="tag">${esc(template.agent)}</span>${template.model && template.model !== "default" ? `<span class="tag">${esc(template.model)}</span>` : ""}</div></td>
|
|
20873
|
+
<td data-label="${t(locale, "table.type")}"><span class="tag t-${scheduleType}">${typeLabel}</span></td>
|
|
20874
|
+
<td data-label="${t(locale, "table.rule")}" class="m small">${esc(rule)}</td>
|
|
20875
|
+
<td data-label="${t(locale, "table.status")}"><span class="badge ${template.enabled ? "b-done" : "b-cancelled"}">${t(locale, template.enabled ? "schedule.enabled" : "schedule.disabled")}</span></td>
|
|
20876
|
+
<td data-label="${t(locale, "table.lastRun")}" class="small muted">${formatRelative(template.lastRunAt, locale)}</td>
|
|
20877
|
+
<td data-label="${t(locale, "table.nextRun")}" class="small">${formatFuture(template.nextRunAt, locale)}</td>
|
|
20878
|
+
<td data-label="${t(locale, "table.actions")}"><div class="actions"><button type="button" class="btn" onclick="showTemplateDetail(${template.id})">${t(locale, "action.details")}</button>
|
|
20879
|
+
<button type="button" class="btn btn-primary" onclick="triggerTmpl(${template.id})">${t(locale, "action.trigger")}</button>${toggle}
|
|
20880
|
+
<button type="button" class="btn btn-danger" onclick="deleteTmpl(${template.id})">${t(locale, "action.delete")}</button></div></td>
|
|
20881
|
+
</tr>`;
|
|
20882
|
+
}).join("");
|
|
20312
20883
|
const body = `
|
|
20313
|
-
<div class="
|
|
20314
|
-
|
|
20315
|
-
|
|
20316
|
-
|
|
20884
|
+
<div class="stats-grid three">
|
|
20885
|
+
${statCard(templates.length, t(locale, "stats.templates"), "tone-purple", icon("templates"))}
|
|
20886
|
+
${statCard(enabled, t(locale, "stats.enabled"), "tone-green", icon("check"), "reveal-delay-1")}
|
|
20887
|
+
${statCard(disabled, t(locale, "stats.disabled"), "tone-neutral", icon("clock"), "reveal-delay-2")}
|
|
20317
20888
|
</div>
|
|
20318
|
-
<
|
|
20319
|
-
<div class="
|
|
20320
|
-
|
|
20321
|
-
|
|
20322
|
-
|
|
20323
|
-
</table>
|
|
20324
|
-
</div>`;
|
|
20325
|
-
return c.html(renderLayout("\u5B9A\u65F6\u4EFB\u52A1", "templates", body));
|
|
20889
|
+
<section class="panel reveal reveal-delay-2">
|
|
20890
|
+
<div class="panel-head"><h2>${t(locale, "page.templates.title")}</h2></div>
|
|
20891
|
+
${templates.length === 0 ? emptyState(t(locale, "empty.templates"), t(locale, "empty.templatesHint"), "supertask template add") : `<div class="table-wrap"><table class="responsive-table"><thead><tr><th>ID</th><th>${t(locale, "table.name")}</th><th>${t(locale, "table.type")}</th><th>${t(locale, "table.rule")}</th><th>${t(locale, "table.status")}</th><th>${t(locale, "table.lastRun")}</th><th>${t(locale, "table.nextRun")}</th><th>${t(locale, "table.actions")}</th></tr></thead><tbody>${rows}</tbody></table></div>`}
|
|
20892
|
+
</section>`;
|
|
20893
|
+
return c.html(renderLayout({ locale, activeTab: "templates", body }));
|
|
20326
20894
|
});
|
|
20327
20895
|
app.get("/runs", async (c) => {
|
|
20896
|
+
const locale = resolveLocale(c);
|
|
20328
20897
|
const page = parsePositiveInteger(c.req.query("page") || "1");
|
|
20329
20898
|
if (page === null) return c.text("invalid page", 400);
|
|
20330
20899
|
const limit = 50;
|
|
20331
20900
|
const offset = (page - 1) * limit;
|
|
20332
|
-
const { taskRuns:
|
|
20901
|
+
const { taskRuns: taskRuns4, tasks: tasks4 } = schema_exports;
|
|
20333
20902
|
const runs = await db.select({
|
|
20334
|
-
id:
|
|
20335
|
-
taskId:
|
|
20336
|
-
sessionId:
|
|
20337
|
-
model:
|
|
20338
|
-
status:
|
|
20339
|
-
startedAt:
|
|
20340
|
-
finishedAt:
|
|
20341
|
-
log:
|
|
20342
|
-
heartbeatAt:
|
|
20343
|
-
workerPid:
|
|
20344
|
-
childPid:
|
|
20345
|
-
taskName:
|
|
20346
|
-
taskAgent:
|
|
20347
|
-
}).from(
|
|
20348
|
-
const totalResult = await db.select({ count: sql`count(*)` }).from(
|
|
20903
|
+
id: taskRuns4.id,
|
|
20904
|
+
taskId: taskRuns4.taskId,
|
|
20905
|
+
sessionId: taskRuns4.sessionId,
|
|
20906
|
+
model: taskRuns4.model,
|
|
20907
|
+
status: taskRuns4.status,
|
|
20908
|
+
startedAt: taskRuns4.startedAt,
|
|
20909
|
+
finishedAt: taskRuns4.finishedAt,
|
|
20910
|
+
log: taskRuns4.log,
|
|
20911
|
+
heartbeatAt: taskRuns4.heartbeatAt,
|
|
20912
|
+
workerPid: taskRuns4.workerPid,
|
|
20913
|
+
childPid: taskRuns4.childPid,
|
|
20914
|
+
taskName: tasks4.name,
|
|
20915
|
+
taskAgent: tasks4.agent
|
|
20916
|
+
}).from(taskRuns4).innerJoin(tasks4, eq(taskRuns4.taskId, tasks4.id)).orderBy(desc(taskRuns4.startedAt), desc(taskRuns4.id)).limit(limit).offset(offset);
|
|
20917
|
+
const totalResult = await db.select({ count: sql`count(*)` }).from(taskRuns4);
|
|
20349
20918
|
const total = Number(totalResult[0]?.count ?? 0);
|
|
20350
|
-
const totalPages = Math.ceil(total / limit);
|
|
20351
|
-
|
|
20352
|
-
const
|
|
20353
|
-
for (const run of runs) {
|
|
20919
|
+
const totalPages = Math.max(1, Math.ceil(total / limit));
|
|
20920
|
+
const logs = [];
|
|
20921
|
+
const rows = runs.map((run) => {
|
|
20354
20922
|
const status = safeStatus(run.status);
|
|
20355
|
-
const shortSession = run.sessionId ? run.sessionId.slice(4, 7) + "***" + run.sessionId.slice(-3) : "-";
|
|
20356
|
-
const logBtn = run.log ? `<button class="btn btn-sm" onclick="toggleLog(${run.id})">\u65E5\u5FD7</button>` : "";
|
|
20357
|
-
rows += `<tr>
|
|
20358
|
-
<td class="mu">#${run.id}</td>
|
|
20359
|
-
<td><div style="font-weight:500">${esc(run.taskName)} <span class="mu">(#${run.taskId})</span></div>
|
|
20360
|
-
${run.model ? `<div class="sm"><span class="tag">${esc(run.model)}</span></div>` : ""}</td>
|
|
20361
|
-
<td><span class="tag">${esc(run.taskAgent)}</span></td>
|
|
20362
|
-
<td><span class="badge b-${status}">${status.toUpperCase()}</span></td>
|
|
20363
|
-
<td class="sm">${formatDuration(run.startedAt, run.finishedAt)}</td>
|
|
20364
|
-
<td class="sm mu">${run.heartbeatAt ? timeAgo(run.heartbeatAt) : "-"}</td>
|
|
20365
|
-
<td><button class="btn btn-sm" onclick="showRunDetail(${run.id})">\u8BE6\u60C5</button>${logBtn}</td>
|
|
20366
|
-
</tr>`;
|
|
20367
20923
|
if (run.log) {
|
|
20368
|
-
|
|
20369
|
-
|
|
20370
|
-
|
|
20371
|
-
|
|
20372
|
-
|
|
20373
|
-
|
|
20374
|
-
|
|
20375
|
-
|
|
20376
|
-
|
|
20377
|
-
|
|
20378
|
-
|
|
20924
|
+
logs.push(`<section id="log-${run.id}" class="panel log-panel" hidden><div class="panel-head"><h3>Run #${run.id} \xB7 ${esc(run.taskName)}</h3></div><div class="log-box">${esc(run.log)}</div></section>`);
|
|
20925
|
+
}
|
|
20926
|
+
return `<tr>
|
|
20927
|
+
<td class="faint" data-label="${t(locale, "table.run")}">#${run.id}</td>
|
|
20928
|
+
<td data-primary data-label="${t(locale, "table.task")}"><div class="task-name">${esc(run.taskName)} <span class="faint">#${run.taskId}</span></div>${run.model ? `<div style="margin-top:4px"><span class="tag">${esc(run.model)}</span></div>` : ""}</td>
|
|
20929
|
+
<td data-label="${t(locale, "table.agent")}"><span class="tag">${esc(run.taskAgent)}</span></td>
|
|
20930
|
+
<td data-label="${t(locale, "table.status")}"><span class="badge b-${status}">${statusText(locale, status)}</span></td>
|
|
20931
|
+
<td data-label="${t(locale, "table.duration")}" class="small">${formatDuration(run.startedAt, run.finishedAt)}</td>
|
|
20932
|
+
<td data-label="${t(locale, "table.heartbeat")}" class="small muted">${formatRelative(run.heartbeatAt, locale)}</td>
|
|
20933
|
+
<td data-label="${t(locale, "table.actions")}"><div class="actions"><button type="button" class="btn" onclick="showRunDetail(${run.id})">${t(locale, "action.details")}</button>
|
|
20934
|
+
${run.log ? `<button type="button" class="btn" aria-expanded="false" onclick="toggleLog(${run.id},this)">${t(locale, "action.logs")}</button>` : ""}</div></td>
|
|
20935
|
+
</tr>`;
|
|
20936
|
+
}).join("");
|
|
20379
20937
|
const body = `
|
|
20380
|
-
<div class="
|
|
20381
|
-
|
|
20382
|
-
|
|
20383
|
-
|
|
20384
|
-
|
|
20938
|
+
<div class="stats-grid">
|
|
20939
|
+
${statCard(total, t(locale, "stats.records"), "tone-purple", icon("runs"))}
|
|
20940
|
+
${statCard(runs.filter((run) => run.status === "done").length, t(locale, "stats.pageDone"), "tone-green", icon("check"), "reveal-delay-1")}
|
|
20941
|
+
${statCard(runs.filter((run) => run.status === "failed").length, t(locale, "stats.pageFailed"), "tone-red", icon("alert"), "reveal-delay-1")}
|
|
20942
|
+
${statCard(runs.filter((run) => run.status === "running").length, t(locale, "stats.pageRunning"), "tone-blue", icon("activity"), "reveal-delay-2")}
|
|
20385
20943
|
</div>
|
|
20386
|
-
<
|
|
20387
|
-
|
|
20388
|
-
|
|
20389
|
-
</table></div>
|
|
20390
|
-
${logsHtml.join("")}
|
|
20391
|
-
${paging}`;
|
|
20392
|
-
return c.html(renderLayout("\u6267\u884C\u65E5\u5FD7", "runs", body));
|
|
20944
|
+
<section class="panel reveal reveal-delay-2">${runs.length === 0 ? emptyState(t(locale, "empty.runs"), "") : `<div class="table-wrap"><table class="responsive-table"><thead><tr><th>${t(locale, "table.run")}</th><th>${t(locale, "table.task")}</th><th>${t(locale, "table.agent")}</th><th>${t(locale, "table.status")}</th><th>${t(locale, "table.duration")}</th><th>${t(locale, "table.heartbeat")}</th><th>${t(locale, "table.actions")}</th></tr></thead><tbody>${rows}</tbody></table></div>`}</section>
|
|
20945
|
+
${logs.join("")}${pagination(locale, "/runs", page, totalPages, total)}`;
|
|
20946
|
+
return c.html(renderLayout({ locale, activeTab: "runs", body }));
|
|
20393
20947
|
});
|
|
20394
20948
|
app.get("/system", async (c) => {
|
|
20949
|
+
const locale = resolveLocale(c);
|
|
20395
20950
|
const config = loadConfig();
|
|
20396
20951
|
const configPath = getConfigPath();
|
|
20397
|
-
const stats = await
|
|
20398
|
-
|
|
20399
|
-
|
|
20952
|
+
const [stats, runningRuns, templates] = await Promise.all([
|
|
20953
|
+
TaskService.stats({}),
|
|
20954
|
+
TaskRunService.getAllRunningRuns(),
|
|
20955
|
+
TaskTemplateService.list(100)
|
|
20956
|
+
]);
|
|
20400
20957
|
const configExists = existsSync6(configPath);
|
|
20401
|
-
|
|
20402
|
-
|
|
20403
|
-
|
|
20404
|
-
|
|
20405
|
-
|
|
20406
|
-
|
|
20407
|
-
|
|
20408
|
-
|
|
20409
|
-
<td class="sm">${formatDate(run.startedAt)}</td>
|
|
20410
|
-
<td class="sm">${run.heartbeatAt ? timeAgo(run.heartbeatAt) : "-"}</td>
|
|
20411
|
-
<td class="m sm">W:${run.workerPid ?? "-"} C:${run.childPid ?? "-"}</td>
|
|
20412
|
-
<td class="sm">${formatDuration(run.startedAt, null)}</td>
|
|
20413
|
-
</tr>`;
|
|
20414
|
-
}
|
|
20415
|
-
}
|
|
20416
|
-
const schedulerStatus = config.scheduler.enabled ? '<span class="badge b-done">\u5DF2\u542F\u7528</span>' : '<span class="badge b-cancelled">\u5DF2\u7981\u7528</span>';
|
|
20417
|
-
const configFileStatus = configExists ? '<span class="badge b-done">\u662F</span>' : '<span class="badge b-cancelled">\u5426 (\u4F7F\u7528\u9ED8\u8BA4\u503C)</span>';
|
|
20958
|
+
const runRows = runningRuns.map((run) => {
|
|
20959
|
+
const session = run.sessionId ? `${run.sessionId.slice(4, 7)}***${run.sessionId.slice(-3)}` : "\u2014";
|
|
20960
|
+
return `<tr><td class="faint" data-label="${t(locale, "table.run")}">#${run.id}</td><td data-primary data-label="${t(locale, "table.task")}">#${run.taskId}</td><td data-label="${t(locale, "table.session")}" class="m small">${esc(session)}</td>
|
|
20961
|
+
<td data-label="${t(locale, "table.model")}" class="small">${esc(run.model) || "\u2014"}</td><td data-label="${t(locale, "table.startedAt")}" class="small">${formatDateTime(run.startedAt, locale)}</td>
|
|
20962
|
+
<td data-label="${t(locale, "table.heartbeat")}" class="small muted">${formatRelative(run.heartbeatAt, locale)}</td><td data-label="${t(locale, "table.pid")}" class="m small">W:${run.workerPid ?? "\u2014"} C:${run.childPid ?? "\u2014"}</td>
|
|
20963
|
+
<td data-label="${t(locale, "table.duration")}" class="small">${formatDuration(run.startedAt, null)}</td></tr>`;
|
|
20964
|
+
}).join("");
|
|
20965
|
+
const unitInput = (name, value, min, unit, max) => `<div class="input-unit"><input id="${name}" type="number" name="${name}" value="${value}" min="${min}" ${max ? `max="${max}"` : ""}><span>${unit}</span></div>`;
|
|
20418
20966
|
const body = `
|
|
20419
|
-
<form id="config-form" onsubmit="event.preventDefault();saveConfig()
|
|
20420
|
-
|
|
20421
|
-
|
|
20422
|
-
|
|
20423
|
-
|
|
20424
|
-
|
|
20425
|
-
|
|
20426
|
-
|
|
20427
|
-
|
|
20428
|
-
|
|
20429
|
-
|
|
20430
|
-
|
|
20431
|
-
|
|
20432
|
-
<
|
|
20967
|
+
<form id="config-form" onsubmit="event.preventDefault();saveConfig()">
|
|
20968
|
+
<div class="settings-grid reveal">
|
|
20969
|
+
<section class="card settings-card"><h2 class="settings-title"><span>${icon("activity")}${t(locale, "system.worker")}</span></h2>
|
|
20970
|
+
<div class="field"><label for="mc">${t(locale, "system.maxConcurrency")}</label>${unitInput("mc", config.worker.maxConcurrency, 1, "\xD7", 20)}</div>
|
|
20971
|
+
<div class="field"><label for="pi">${t(locale, "system.pollInterval")}</label>${unitInput("pi", config.worker.pollIntervalMs, 100, t(locale, "system.milliseconds"))}</div>
|
|
20972
|
+
<div class="field"><label for="hi">${t(locale, "system.heartbeatInterval")}</label>${unitInput("hi", config.worker.heartbeatIntervalMs / 1e3, 5, t(locale, "system.seconds"))}</div>
|
|
20973
|
+
<div class="field"><label for="to">${t(locale, "system.taskTimeout")}</label>${unitInput("to", config.worker.taskTimeoutMs / 6e4, 1, t(locale, "system.minutes"))}</div>
|
|
20974
|
+
</section>
|
|
20975
|
+
<section class="card settings-card reveal-delay-1"><h2 class="settings-title"><span>${icon("templates")}${t(locale, "system.scheduler")}</span><span class="badge ${config.scheduler.enabled ? "b-done" : "b-cancelled"}">${t(locale, config.scheduler.enabled ? "schedule.enabled" : "schedule.disabled")}</span></h2>
|
|
20976
|
+
<div class="switch-field"><label for="se">${t(locale, "system.schedulerEnabled")}</label><label class="switch"><input id="se" type="checkbox" name="se" ${config.scheduler.enabled ? "checked" : ""}><span></span></label></div>
|
|
20977
|
+
<div class="field"><label for="si">${t(locale, "system.checkInterval")}</label>${unitInput("si", config.scheduler.checkIntervalMs, 100, t(locale, "system.milliseconds"))}</div>
|
|
20978
|
+
<div class="info-row"><span class="info-key">${t(locale, "system.activeTemplates")}</span><span class="info-value">${templates.filter((template) => template.enabled).length} / ${templates.length}</span></div>
|
|
20979
|
+
</section>
|
|
20980
|
+
<section class="card settings-card reveal-delay-2"><h2 class="settings-title"><span>${icon("system")}${t(locale, "system.watchdog")}</span></h2>
|
|
20981
|
+
<div class="field"><label for="wt">${t(locale, "system.heartbeatTimeout")}</label>${unitInput("wt", config.watchdog.heartbeatTimeoutMs / 1e3, 10, t(locale, "system.seconds"))}</div>
|
|
20982
|
+
<div class="field"><label for="wci">${t(locale, "system.checkInterval")}</label>${unitInput("wci", config.watchdog.checkIntervalMs / 1e3, 1, t(locale, "system.seconds"))}</div>
|
|
20983
|
+
<div class="field"><label for="wcl">${t(locale, "system.cleanupInterval")}</label>${unitInput("wcl", config.watchdog.cleanupIntervalMs / 36e5, 1, t(locale, "system.hours"))}</div>
|
|
20984
|
+
<div class="field"><label for="rd">${t(locale, "system.retentionDays")}</label>${unitInput("rd", config.watchdog.retentionDays, 1, t(locale, "system.days"))}</div>
|
|
20985
|
+
</section>
|
|
20433
20986
|
</div>
|
|
20434
|
-
<div class="
|
|
20435
|
-
<h3 style="margin:0 0 12px;font-size:14px">Watchdog \u914D\u7F6E</h3>
|
|
20436
|
-
<div class="form-row"><label>\u5FC3\u8DF3\u8D85\u65F6(\u79D2)</label><input type="number" name="wt" value="${config.watchdog.heartbeatTimeoutMs / 1e3}" min="10" style="width:100px"></div>
|
|
20437
|
-
<div class="form-row"><label>\u68C0\u67E5\u95F4\u9694(\u79D2)</label><input type="number" name="wci" value="${config.watchdog.checkIntervalMs / 1e3}" min="1" style="width:100px"></div>
|
|
20438
|
-
<div class="form-row"><label>\u6E05\u7406\u95F4\u9694(\u5C0F\u65F6)</label><input type="number" name="wcl" value="${config.watchdog.cleanupIntervalMs / 36e5}" min="1" style="width:100px"></div>
|
|
20439
|
-
<div class="form-row"><label>\u6570\u636E\u4FDD\u7559(\u5929)</label><input type="number" name="rd" value="${config.watchdog.retentionDays}" min="1" style="width:100px"></div>
|
|
20440
|
-
</div>
|
|
20441
|
-
</div>
|
|
20442
|
-
<div style="text-align:center;margin-bottom:24px">
|
|
20443
|
-
<button type="submit" class="rf" style="font-size:14px;padding:10px 30px">\u4FDD\u5B58\u914D\u7F6E</button>
|
|
20444
|
-
<span class="mu sm" style="margin-left:12px">\u4FDD\u5B58\u540E\u9700\u91CD\u542F Gateway \u751F\u6548</span>
|
|
20445
|
-
</div>
|
|
20987
|
+
<div class="save-row"><span class="muted small">${t(locale, "system.saveHint")}</span><button type="submit" class="btn btn-primary">${t(locale, "action.save")}</button></div>
|
|
20446
20988
|
</form>
|
|
20447
|
-
|
|
20448
|
-
|
|
20449
|
-
|
|
20450
|
-
|
|
20451
|
-
|
|
20452
|
-
|
|
20453
|
-
|
|
20454
|
-
|
|
20455
|
-
|
|
20456
|
-
|
|
20457
|
-
|
|
20458
|
-
<div class="
|
|
20459
|
-
|
|
20460
|
-
|
|
20461
|
-
|
|
20462
|
-
|
|
20463
|
-
|
|
20464
|
-
</div>
|
|
20465
|
-
|
|
20466
|
-
<div class="card mt16">
|
|
20467
|
-
<h3 style="margin:0 0 12px;font-size:14px">\u914D\u7F6E\u6587\u4EF6</h3>
|
|
20468
|
-
<div class="ir"><span class="ik">\u8DEF\u5F84</span><span class="iv m sm">${esc(configPath)}</span></div>
|
|
20469
|
-
<div class="ir"><span class="ik">\u6587\u4EF6\u5B58\u5728</span><span class="iv">${configFileStatus}</span></div>
|
|
20470
|
-
</div>
|
|
20471
|
-
|
|
20472
|
-
<div class="card mt16" style="border-color:var(--red)">
|
|
20473
|
-
<h3 style="margin:0 0 12px;font-size:14px;color:var(--red)">\u5371\u9669\u64CD\u4F5C</h3>
|
|
20474
|
-
<p class="sm mu" style="margin:0 0 12px">\u81EA\u52A8\u5907\u4EFD\u540E\u4E8B\u52A1\u6027\u6E05\u7A7A tasks\u3001task_runs \u548C task_templates\uFF1B\u5B58\u5728\u8FD0\u884C\u4E2D\u4EFB\u52A1\u65F6\u4F1A\u62D2\u7EDD\u6267\u884C\u3002</p>
|
|
20475
|
-
<button class="btn btn-danger" style="border-color:var(--red);color:var(--red);padding:6px 16px" onclick="clearDatabase()">\u6E05\u7A7A\u6570\u636E\u5E93</button>
|
|
20476
|
-
</div>`;
|
|
20477
|
-
return c.html(renderLayout("\u7CFB\u7EDF\u72B6\u6001", "system", body));
|
|
20989
|
+
<section class="panel reveal">
|
|
20990
|
+
<div class="panel-head"><h2>${t(locale, "system.runningTasks", { running: runningRuns.length, limit: config.worker.maxConcurrency })}</h2></div>
|
|
20991
|
+
${runningRuns.length === 0 ? emptyState(t(locale, "empty.running"), "") : `<div class="table-wrap"><table class="responsive-table"><thead><tr><th>${t(locale, "table.run")}</th><th>${t(locale, "table.task")}</th><th>${t(locale, "table.session")}</th><th>${t(locale, "table.model")}</th><th>${t(locale, "table.startedAt")}</th><th>${t(locale, "table.heartbeat")}</th><th>${t(locale, "table.pid")}</th><th>${t(locale, "table.duration")}</th></tr></thead><tbody>${runRows}</tbody></table></div>`}
|
|
20992
|
+
</section>
|
|
20993
|
+
<section class="panel reveal reveal-delay-1"><div class="panel-head"><h2>${t(locale, "system.taskStats")}</h2></div><div class="overview-grid">
|
|
20994
|
+
<div class="overview-item"><span>${statusText(locale, "pending")}</span><strong>${stats.pending || 0}</strong></div>
|
|
20995
|
+
<div class="overview-item"><span>${statusText(locale, "running")}</span><strong style="color:var(--blue)">${stats.running || 0}</strong></div>
|
|
20996
|
+
<div class="overview-item"><span>${statusText(locale, "done")}</span><strong style="color:var(--green)">${stats.done || 0}</strong></div>
|
|
20997
|
+
<div class="overview-item"><span>${t(locale, "stats.failedDead")}</span><strong style="color:var(--red)">${(stats.failed || 0) + (stats.dead_letter || 0)}</strong></div>
|
|
20998
|
+
</div></section>
|
|
20999
|
+
<section class="panel reveal reveal-delay-1"><div class="panel-head"><h2>${t(locale, "system.configFile")}</h2></div><div class="info-list">
|
|
21000
|
+
<div class="info-row"><span class="info-key">${t(locale, "system.path")}</span><span class="info-value m small">${esc(configPath)}</span></div>
|
|
21001
|
+
<div class="info-row"><span class="info-key">${t(locale, "system.fileExists")}</span><span class="badge ${configExists ? "b-done" : "b-cancelled"}">${t(locale, configExists ? "system.yes" : "system.noDefault")}</span></div>
|
|
21002
|
+
</div></section>
|
|
21003
|
+
<section class="card danger-card reveal reveal-delay-2"><h2>${icon("alert")}${t(locale, "system.danger")}</h2><p>${t(locale, "system.dangerDescription")}</p>
|
|
21004
|
+
<button type="button" class="btn btn-danger" onclick="clearDatabase()">${icon("database")}${t(locale, "action.clearDatabase")}</button></section>`;
|
|
21005
|
+
return c.html(renderLayout({ locale, activeTab: "system", body }));
|
|
20478
21006
|
});
|
|
20479
21007
|
app.get("/api/tasks/:id", async (c) => {
|
|
20480
21008
|
const id = parsePositiveInteger(c.req.param("id"));
|
|
@@ -20494,9 +21022,9 @@ var init_web = __esm({
|
|
|
20494
21022
|
app.get("/api/templates/:id", async (c) => {
|
|
20495
21023
|
const id = parsePositiveInteger(c.req.param("id"));
|
|
20496
21024
|
if (id === null) return c.json({ error: "invalid id" }, 400);
|
|
20497
|
-
const
|
|
20498
|
-
if (!
|
|
20499
|
-
return c.json(
|
|
21025
|
+
const template = await TaskTemplateService.getById(id);
|
|
21026
|
+
if (!template) return c.json({ error: "not found" }, 404);
|
|
21027
|
+
return c.json(template);
|
|
20500
21028
|
});
|
|
20501
21029
|
app.post("/api/tasks/:id/retry", async (c) => {
|
|
20502
21030
|
const id = parsePositiveInteger(c.req.param("id"));
|
|
@@ -20540,14 +21068,14 @@ var init_web = __esm({
|
|
|
20540
21068
|
app.delete("/api/templates/:id", async (c) => {
|
|
20541
21069
|
const id = parsePositiveInteger(c.req.param("id"));
|
|
20542
21070
|
if (id === null) return c.json({ error: "invalid id" }, 400);
|
|
20543
|
-
const
|
|
20544
|
-
return
|
|
21071
|
+
const deleted = await TaskTemplateService.delete(id);
|
|
21072
|
+
return deleted ? c.json({ success: true }) : c.json({ error: "not found" }, 404);
|
|
20545
21073
|
});
|
|
20546
21074
|
app.post("/api/templates/:id/trigger", async (c) => {
|
|
20547
21075
|
const id = parsePositiveInteger(c.req.param("id"));
|
|
20548
21076
|
if (id === null) return c.json({ error: "invalid id" }, 400);
|
|
20549
|
-
const
|
|
20550
|
-
if (!
|
|
21077
|
+
const template = await TaskTemplateService.getById(id);
|
|
21078
|
+
if (!template) return c.json({ error: "not found" }, 404);
|
|
20551
21079
|
const task = await triggerTaskFromTemplate(id);
|
|
20552
21080
|
if (!task) return c.json({ error: "maxInstances reached" }, 409);
|
|
20553
21081
|
return c.json({ success: true, taskId: task.id });
|
|
@@ -20556,24 +21084,27 @@ var init_web = __esm({
|
|
|
20556
21084
|
try {
|
|
20557
21085
|
const body = await c.req.json();
|
|
20558
21086
|
const current = readCurrentConfig();
|
|
20559
|
-
const
|
|
20560
|
-
const
|
|
20561
|
-
const
|
|
20562
|
-
const
|
|
20563
|
-
const
|
|
20564
|
-
const
|
|
21087
|
+
const currentWorker = current.worker ?? {};
|
|
21088
|
+
const currentScheduler = current.scheduler ?? {};
|
|
21089
|
+
const currentWatchdog = current.watchdog ?? {};
|
|
21090
|
+
const bodyWorker = body.worker ?? {};
|
|
21091
|
+
const bodyScheduler = body.scheduler ?? {};
|
|
21092
|
+
const bodyWatchdog = body.watchdog ?? {};
|
|
20565
21093
|
const merged = {
|
|
20566
21094
|
...current,
|
|
20567
21095
|
...body,
|
|
20568
21096
|
configVersion: 2,
|
|
20569
|
-
worker: { ...
|
|
20570
|
-
scheduler: { ...
|
|
20571
|
-
watchdog: { ...
|
|
21097
|
+
worker: { ...currentWorker, ...bodyWorker },
|
|
21098
|
+
scheduler: { ...currentScheduler, ...bodyScheduler },
|
|
21099
|
+
watchdog: { ...currentWatchdog, ...bodyWatchdog }
|
|
20572
21100
|
};
|
|
20573
21101
|
writeConfig(validateConfig(merged));
|
|
20574
21102
|
return c.json({ success: true });
|
|
20575
|
-
} catch (
|
|
20576
|
-
return c.json({
|
|
21103
|
+
} catch (error) {
|
|
21104
|
+
return c.json({
|
|
21105
|
+
success: false,
|
|
21106
|
+
error: error instanceof Error ? error.message : String(error)
|
|
21107
|
+
}, 400);
|
|
20577
21108
|
}
|
|
20578
21109
|
});
|
|
20579
21110
|
app.post("/api/database/clear", async (c) => {
|
|
@@ -20584,9 +21115,12 @@ var init_web = __esm({
|
|
|
20584
21115
|
try {
|
|
20585
21116
|
const result = DatabaseMaintenanceService.clear({ allowCurrentGateway: true });
|
|
20586
21117
|
return c.json({ success: true, ...result });
|
|
20587
|
-
} catch (
|
|
20588
|
-
const status =
|
|
20589
|
-
return c.json({
|
|
21118
|
+
} catch (error) {
|
|
21119
|
+
const status = error instanceof DatabaseMaintenanceConflictError ? 409 : 500;
|
|
21120
|
+
return c.json({
|
|
21121
|
+
success: false,
|
|
21122
|
+
error: error instanceof Error ? error.message : String(error)
|
|
21123
|
+
}, status);
|
|
20590
21124
|
}
|
|
20591
21125
|
});
|
|
20592
21126
|
dashboardApp = app;
|