opencode-supertask 0.1.28 → 0.1.29
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 +1094 -560
- 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/web/index.js +3109 -2572
- package/dist/web/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -12547,8 +12547,8 @@ var require_luxon = __commonJS({
|
|
|
12547
12547
|
* Set whether Luxon will throw when it encounters invalid DateTimes, Durations, or Intervals
|
|
12548
12548
|
* @type {boolean}
|
|
12549
12549
|
*/
|
|
12550
|
-
static set throwOnInvalid(
|
|
12551
|
-
throwOnInvalid =
|
|
12550
|
+
static set throwOnInvalid(t2) {
|
|
12551
|
+
throwOnInvalid = t2;
|
|
12552
12552
|
}
|
|
12553
12553
|
/**
|
|
12554
12554
|
* Reset Luxon's global caches. Should only be necessary in testing scenarios.
|
|
@@ -13441,7 +13441,7 @@ var require_luxon = __commonJS({
|
|
|
13441
13441
|
}, tokens = _Formatter.parseFormat(fmt), realTokens = tokens.reduce((found, {
|
|
13442
13442
|
literal,
|
|
13443
13443
|
val
|
|
13444
|
-
}) => literal ? found : found.concat(val), []), collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter((
|
|
13444
|
+
}) => literal ? found : found.concat(val), []), collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter((t2) => t2)), durationInfo = {
|
|
13445
13445
|
isNegativeDuration: collapsed < 0,
|
|
13446
13446
|
// this relies on "collapsed" being based on "shiftTo", which builds up the object
|
|
13447
13447
|
// in order
|
|
@@ -15351,15 +15351,15 @@ var require_luxon = __commonJS({
|
|
|
15351
15351
|
return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
|
|
15352
15352
|
}
|
|
15353
15353
|
function unitForToken(token, loc) {
|
|
15354
|
-
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 = (
|
|
15355
|
-
regex: RegExp(escapeToken(
|
|
15354
|
+
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) => ({
|
|
15355
|
+
regex: RegExp(escapeToken(t2.val)),
|
|
15356
15356
|
deser: ([s2]) => s2,
|
|
15357
15357
|
literal: true
|
|
15358
|
-
}), unitate = (
|
|
15358
|
+
}), unitate = (t2) => {
|
|
15359
15359
|
if (token.literal) {
|
|
15360
|
-
return literal(
|
|
15360
|
+
return literal(t2);
|
|
15361
15361
|
}
|
|
15362
|
-
switch (
|
|
15362
|
+
switch (t2.val) {
|
|
15363
15363
|
// era
|
|
15364
15364
|
case "G":
|
|
15365
15365
|
return oneOf(loc.eras("short"), 0);
|
|
@@ -15474,7 +15474,7 @@ var require_luxon = __commonJS({
|
|
|
15474
15474
|
case " ":
|
|
15475
15475
|
return simple(/[^\S\n\r]/);
|
|
15476
15476
|
default:
|
|
15477
|
-
return literal(
|
|
15477
|
+
return literal(t2);
|
|
15478
15478
|
}
|
|
15479
15479
|
};
|
|
15480
15480
|
const unit = unitate(token) || {
|
|
@@ -15676,15 +15676,15 @@ var require_luxon = __commonJS({
|
|
|
15676
15676
|
return tokens;
|
|
15677
15677
|
}
|
|
15678
15678
|
function expandMacroTokens(tokens, locale) {
|
|
15679
|
-
return Array.prototype.concat(...tokens.map((
|
|
15679
|
+
return Array.prototype.concat(...tokens.map((t2) => maybeExpandMacroToken(t2, locale)));
|
|
15680
15680
|
}
|
|
15681
15681
|
var TokenParser = class {
|
|
15682
15682
|
constructor(locale, format) {
|
|
15683
15683
|
this.locale = locale;
|
|
15684
15684
|
this.format = format;
|
|
15685
15685
|
this.tokens = expandMacroTokens(Formatter.parseFormat(format), locale);
|
|
15686
|
-
this.units = this.tokens.map((
|
|
15687
|
-
this.disqualifyingUnit = this.units.find((
|
|
15686
|
+
this.units = this.tokens.map((t2) => unitForToken(t2, locale));
|
|
15687
|
+
this.disqualifyingUnit = this.units.find((t2) => t2.invalidReason);
|
|
15688
15688
|
if (!this.disqualifyingUnit) {
|
|
15689
15689
|
const [regexString, handlers] = buildRegex(this.units);
|
|
15690
15690
|
this.regex = RegExp(regexString, "i");
|
|
@@ -16509,7 +16509,7 @@ var require_luxon = __commonJS({
|
|
|
16509
16509
|
*/
|
|
16510
16510
|
static parseFormatForOpts(formatOpts, localeOpts = {}) {
|
|
16511
16511
|
const tokenList = formatOptsToTokens(formatOpts, Locale.fromObject(localeOpts));
|
|
16512
|
-
return !tokenList ? null : tokenList.map((
|
|
16512
|
+
return !tokenList ? null : tokenList.map((t2) => t2 ? t2.val : null).join("");
|
|
16513
16513
|
}
|
|
16514
16514
|
/**
|
|
16515
16515
|
* Produce the the fully expanded format token for the locale
|
|
@@ -16520,7 +16520,7 @@ var require_luxon = __commonJS({
|
|
|
16520
16520
|
*/
|
|
16521
16521
|
static expandFormat(fmt, localeOpts = {}) {
|
|
16522
16522
|
const expanded = expandMacroTokens(Formatter.parseFormat(fmt), Locale.fromObject(localeOpts));
|
|
16523
|
-
return expanded.map((
|
|
16523
|
+
return expanded.map((t2) => t2.val).join("");
|
|
16524
16524
|
}
|
|
16525
16525
|
static resetCache() {
|
|
16526
16526
|
zoneOffsetTs = void 0;
|
|
@@ -19377,10 +19377,10 @@ var require_random = __commonJS({
|
|
|
19377
19377
|
}
|
|
19378
19378
|
function mulberry32(seed) {
|
|
19379
19379
|
return () => {
|
|
19380
|
-
let
|
|
19381
|
-
|
|
19382
|
-
|
|
19383
|
-
return ((
|
|
19380
|
+
let t2 = seed += 1831565813;
|
|
19381
|
+
t2 = Math.imul(t2 ^ t2 >>> 15, t2 | 1);
|
|
19382
|
+
t2 ^= t2 + Math.imul(t2 ^ t2 >>> 7, t2 | 61);
|
|
19383
|
+
return ((t2 ^ t2 >>> 14) >>> 0) / 4294967296;
|
|
19384
19384
|
};
|
|
19385
19385
|
}
|
|
19386
19386
|
function seededRandom(str) {
|
|
@@ -23688,7 +23688,7 @@ var init_request = __esm({
|
|
|
23688
23688
|
});
|
|
23689
23689
|
|
|
23690
23690
|
// node_modules/hono/dist/utils/html.js
|
|
23691
|
-
var HtmlEscapedCallbackPhase, raw,
|
|
23691
|
+
var HtmlEscapedCallbackPhase, raw, resolveCallback;
|
|
23692
23692
|
var init_html = __esm({
|
|
23693
23693
|
"node_modules/hono/dist/utils/html.js"() {
|
|
23694
23694
|
"use strict";
|
|
@@ -23703,80 +23703,6 @@ var init_html = __esm({
|
|
|
23703
23703
|
escapedString.callbacks = callbacks;
|
|
23704
23704
|
return escapedString;
|
|
23705
23705
|
};
|
|
23706
|
-
escapeRe = /[&<>'"]/;
|
|
23707
|
-
stringBufferToString = async (buffer, callbacks) => {
|
|
23708
|
-
let str = "";
|
|
23709
|
-
callbacks ||= [];
|
|
23710
|
-
const resolvedBuffer = await Promise.all(buffer);
|
|
23711
|
-
for (let i = resolvedBuffer.length - 1; ; i--) {
|
|
23712
|
-
str += resolvedBuffer[i];
|
|
23713
|
-
i--;
|
|
23714
|
-
if (i < 0) {
|
|
23715
|
-
break;
|
|
23716
|
-
}
|
|
23717
|
-
let r = resolvedBuffer[i];
|
|
23718
|
-
if (typeof r === "object") {
|
|
23719
|
-
callbacks.push(...r.callbacks || []);
|
|
23720
|
-
}
|
|
23721
|
-
const isEscaped = r.isEscaped;
|
|
23722
|
-
r = await (typeof r === "object" ? r.toString() : r);
|
|
23723
|
-
if (typeof r === "object") {
|
|
23724
|
-
callbacks.push(...r.callbacks || []);
|
|
23725
|
-
}
|
|
23726
|
-
if (r.isEscaped ?? isEscaped) {
|
|
23727
|
-
str += r;
|
|
23728
|
-
} else {
|
|
23729
|
-
const buf = [str];
|
|
23730
|
-
escapeToBuffer(r, buf);
|
|
23731
|
-
str = buf[0];
|
|
23732
|
-
}
|
|
23733
|
-
}
|
|
23734
|
-
return raw(str, callbacks);
|
|
23735
|
-
};
|
|
23736
|
-
escapeToBuffer = (str, buffer) => {
|
|
23737
|
-
const match2 = str.search(escapeRe);
|
|
23738
|
-
if (match2 === -1) {
|
|
23739
|
-
buffer[0] += str;
|
|
23740
|
-
return;
|
|
23741
|
-
}
|
|
23742
|
-
let escape;
|
|
23743
|
-
let index2;
|
|
23744
|
-
let lastIndex = 0;
|
|
23745
|
-
for (index2 = match2; index2 < str.length; index2++) {
|
|
23746
|
-
switch (str.charCodeAt(index2)) {
|
|
23747
|
-
case 34:
|
|
23748
|
-
escape = """;
|
|
23749
|
-
break;
|
|
23750
|
-
case 39:
|
|
23751
|
-
escape = "'";
|
|
23752
|
-
break;
|
|
23753
|
-
case 38:
|
|
23754
|
-
escape = "&";
|
|
23755
|
-
break;
|
|
23756
|
-
case 60:
|
|
23757
|
-
escape = "<";
|
|
23758
|
-
break;
|
|
23759
|
-
case 62:
|
|
23760
|
-
escape = ">";
|
|
23761
|
-
break;
|
|
23762
|
-
default:
|
|
23763
|
-
continue;
|
|
23764
|
-
}
|
|
23765
|
-
buffer[0] += str.substring(lastIndex, index2) + escape;
|
|
23766
|
-
lastIndex = index2 + 1;
|
|
23767
|
-
}
|
|
23768
|
-
buffer[0] += str.substring(lastIndex, index2);
|
|
23769
|
-
};
|
|
23770
|
-
resolveCallbackSync = (str) => {
|
|
23771
|
-
const callbacks = str.callbacks;
|
|
23772
|
-
if (!callbacks?.length) {
|
|
23773
|
-
return str;
|
|
23774
|
-
}
|
|
23775
|
-
const buffer = [str];
|
|
23776
|
-
const context = {};
|
|
23777
|
-
callbacks.forEach((c) => c({ phase: HtmlEscapedCallbackPhase.Stringify, buffer, context }));
|
|
23778
|
-
return buffer[0];
|
|
23779
|
-
};
|
|
23780
23706
|
resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => {
|
|
23781
23707
|
if (typeof str === "object" && !(str instanceof String)) {
|
|
23782
23708
|
if (!(str instanceof Promise)) {
|
|
@@ -24174,9 +24100,9 @@ var init_context = __esm({
|
|
|
24174
24100
|
setDefaultContentType("application/json", headers)
|
|
24175
24101
|
);
|
|
24176
24102
|
};
|
|
24177
|
-
html = (
|
|
24178
|
-
const res = (
|
|
24179
|
-
return typeof
|
|
24103
|
+
html = (html, arg, headers) => {
|
|
24104
|
+
const res = (html2) => this.#newResponse(html2, arg, setDefaultContentType("text/html; charset=UTF-8", headers));
|
|
24105
|
+
return typeof html === "object" ? resolveCallback(html, HtmlEscapedCallbackPhase.Stringify, false, {}).then(res) : res(html);
|
|
24180
24106
|
};
|
|
24181
24107
|
/**
|
|
24182
24108
|
* `.redirect()` can Redirect, default status code is 302.
|
|
@@ -25349,46 +25275,816 @@ var init_dist = __esm({
|
|
|
25349
25275
|
}
|
|
25350
25276
|
});
|
|
25351
25277
|
|
|
25352
|
-
//
|
|
25353
|
-
|
|
25354
|
-
|
|
25355
|
-
|
|
25278
|
+
// src/web/ui.ts
|
|
25279
|
+
function t(locale, key, values = {}) {
|
|
25280
|
+
const template = (locale === "en" ? EN : ZH)[key];
|
|
25281
|
+
return template.replace(/\{([a-zA-Z]+)\}/g, (_, name) => String(values[name] ?? `{${name}}`));
|
|
25282
|
+
}
|
|
25283
|
+
function statusText(locale, status) {
|
|
25284
|
+
const key = `status.${status}`;
|
|
25285
|
+
return key in ZH ? t(locale, key) : t(locale, "status.unknown");
|
|
25286
|
+
}
|
|
25287
|
+
function formatRelative(timestamp2, locale) {
|
|
25288
|
+
if (!timestamp2) return "\u2014";
|
|
25289
|
+
const deltaMs = timestamp2 - Date.now();
|
|
25290
|
+
const abs = Math.abs(deltaMs);
|
|
25291
|
+
const language = locale === "en" ? "en" : "zh-CN";
|
|
25292
|
+
const formatter = new Intl.RelativeTimeFormat(language, { numeric: "auto" });
|
|
25293
|
+
if (abs < 6e4) return formatter.format(Math.round(deltaMs / 1e3), "second");
|
|
25294
|
+
if (abs < 36e5) return formatter.format(Math.round(deltaMs / 6e4), "minute");
|
|
25295
|
+
if (abs < 864e5) return formatter.format(Math.round(deltaMs / 36e5), "hour");
|
|
25296
|
+
return formatter.format(Math.round(deltaMs / 864e5), "day");
|
|
25297
|
+
}
|
|
25298
|
+
function formatFuture(timestamp2, locale) {
|
|
25299
|
+
if (!timestamp2) return "\u2014";
|
|
25300
|
+
if (timestamp2 < Date.now()) return t(locale, "schedule.overdue");
|
|
25301
|
+
return formatRelative(timestamp2, locale);
|
|
25302
|
+
}
|
|
25303
|
+
function formatDateTime(value, locale) {
|
|
25304
|
+
if (!value) return "\u2014";
|
|
25305
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
25306
|
+
return new Intl.DateTimeFormat(locale === "en" ? "en-US" : "zh-CN", {
|
|
25307
|
+
month: "short",
|
|
25308
|
+
day: "2-digit",
|
|
25309
|
+
hour: "2-digit",
|
|
25310
|
+
minute: "2-digit"
|
|
25311
|
+
}).format(date);
|
|
25312
|
+
}
|
|
25313
|
+
function icon(name, className = "icon") {
|
|
25314
|
+
const paths = {
|
|
25315
|
+
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"/>',
|
|
25316
|
+
tasks: '<path d="M9 6h11M9 12h11M9 18h11"/><path d="M4 6h.01M4 12h.01M4 18h.01"/>',
|
|
25317
|
+
templates: '<rect x="3" y="5" width="18" height="16" rx="2"/><path d="M16 3v4M8 3v4M3 10h18"/><path d="M12 14v3l2 1"/>',
|
|
25318
|
+
runs: '<path d="M4 19.5V4.5M4 19.5h16"/><path d="m7 15 3-4 3 2 5-6"/>',
|
|
25319
|
+
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"/>',
|
|
25320
|
+
refresh: '<path d="M20 6v5h-5"/><path d="M18.5 15a7 7 0 1 1-.8-7.8L20 11"/>',
|
|
25321
|
+
search: '<circle cx="11" cy="11" r="7"/><path d="m20 20-4-4"/>',
|
|
25322
|
+
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"/>',
|
|
25323
|
+
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"/>',
|
|
25324
|
+
chevronLeft: '<path d="m15 18-6-6 6-6"/>',
|
|
25325
|
+
chevronRight: '<path d="m9 18 6-6-6-6"/>',
|
|
25326
|
+
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"/>',
|
|
25327
|
+
close: '<path d="m6 6 12 12M18 6 6 18"/>',
|
|
25328
|
+
inbox: '<path d="M4 4h16l2 12h-6l-2 3h-4l-2-3H2L4 4Z"/><path d="M8 9h8"/>',
|
|
25329
|
+
activity: '<path d="M3 12h4l2-6 4 12 2-6h6"/>',
|
|
25330
|
+
check: '<path d="m5 12 4 4L19 6"/>',
|
|
25331
|
+
alert: '<path d="M12 3 2.8 19h18.4L12 3Z"/><path d="M12 9v4M12 17h.01"/>',
|
|
25332
|
+
clock: '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>',
|
|
25333
|
+
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"/>'
|
|
25334
|
+
};
|
|
25335
|
+
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>`;
|
|
25336
|
+
}
|
|
25337
|
+
function clientMessages(locale) {
|
|
25338
|
+
const keys = [
|
|
25339
|
+
"action.cancel",
|
|
25340
|
+
"action.confirm",
|
|
25341
|
+
"action.copy",
|
|
25342
|
+
"action.delete",
|
|
25343
|
+
"action.refresh",
|
|
25344
|
+
"action.logs",
|
|
25345
|
+
"action.hideLogs",
|
|
25346
|
+
"details.copySuccess",
|
|
25347
|
+
"feedback.copyFailed",
|
|
25348
|
+
"dialog.cancelTask",
|
|
25349
|
+
"dialog.cancelTaskBody",
|
|
25350
|
+
"dialog.retryTask",
|
|
25351
|
+
"dialog.retryTaskBody",
|
|
25352
|
+
"dialog.deleteTask",
|
|
25353
|
+
"dialog.deleteTaskBody",
|
|
25354
|
+
"dialog.disableTemplate",
|
|
25355
|
+
"dialog.disableTemplateBody",
|
|
25356
|
+
"dialog.deleteTemplate",
|
|
25357
|
+
"dialog.deleteTemplateBody",
|
|
25358
|
+
"dialog.triggerTemplate",
|
|
25359
|
+
"dialog.triggerTemplateBody",
|
|
25360
|
+
"dialog.clearTitle",
|
|
25361
|
+
"dialog.clearBody",
|
|
25362
|
+
"dialog.clearInstruction",
|
|
25363
|
+
"feedback.retryFailed",
|
|
25364
|
+
"feedback.cancelFailed",
|
|
25365
|
+
"feedback.deleteFailed",
|
|
25366
|
+
"feedback.requestFailed",
|
|
25367
|
+
"feedback.triggered",
|
|
25368
|
+
"feedback.configSaved",
|
|
25369
|
+
"feedback.databaseCleared",
|
|
25370
|
+
"filter.noResults"
|
|
25371
|
+
];
|
|
25372
|
+
return Object.fromEntries(keys.map((key) => [key, t(locale, key)]));
|
|
25373
|
+
}
|
|
25374
|
+
function renderLayout(options) {
|
|
25375
|
+
const { locale, activeTab, body } = options;
|
|
25376
|
+
const page = PAGE_KEYS[activeTab];
|
|
25377
|
+
const nav = [
|
|
25378
|
+
{ id: "tasks", href: "/", icon: "tasks" },
|
|
25379
|
+
{ id: "templates", href: "/templates", icon: "templates" },
|
|
25380
|
+
{ id: "runs", href: "/runs", icon: "runs" },
|
|
25381
|
+
{ id: "system", href: "/system", icon: "system" }
|
|
25382
|
+
];
|
|
25383
|
+
const ui = JSON.stringify(clientMessages(locale)).replace(/</g, "\\u003c");
|
|
25384
|
+
const language = locale === "en" ? "en" : "zh-CN";
|
|
25385
|
+
return `<!DOCTYPE html>
|
|
25386
|
+
<html lang="${language}">
|
|
25387
|
+
<head>
|
|
25388
|
+
<meta charset="UTF-8">
|
|
25389
|
+
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
|
25390
|
+
<meta name="color-scheme" content="light dark">
|
|
25391
|
+
<meta name="theme-color" content="#f5f7fb">
|
|
25392
|
+
<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">
|
|
25393
|
+
<title>${t(locale, page.title)} \xB7 SuperTask</title>
|
|
25394
|
+
<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>
|
|
25395
|
+
${STYLES}
|
|
25396
|
+
</head>
|
|
25397
|
+
<body>
|
|
25398
|
+
<a class="skip-link" href="#main">${t(locale, "a11y.skip")}</a>
|
|
25399
|
+
<div class="app-shell">
|
|
25400
|
+
<header class="topbar">
|
|
25401
|
+
<div class="brand">
|
|
25402
|
+
<div class="brand-mark">${icon("brand")}</div>
|
|
25403
|
+
<div>
|
|
25404
|
+
<div class="brand-name">${t(locale, "app.name")} <span>${t(locale, "app.dashboard")}</span></div>
|
|
25405
|
+
<div class="brand-tagline">${t(locale, "app.tagline")}</div>
|
|
25406
|
+
</div>
|
|
25407
|
+
</div>
|
|
25408
|
+
<div class="top-actions">
|
|
25409
|
+
<div class="local-chip"><span class="live-dot"></span>${t(locale, "app.local")}</div>
|
|
25410
|
+
<div class="control language-switch" role="group" aria-label="${t(locale, "language.label")}">
|
|
25411
|
+
<button type="button" class="${locale === "zh-CN" ? "active" : ""}" onclick="setLocale('zh-CN')" aria-pressed="${locale === "zh-CN"}">\u4E2D</button>
|
|
25412
|
+
<button type="button" class="${locale === "en" ? "active" : ""}" onclick="setLocale('en')" aria-pressed="${locale === "en"}">EN</button>
|
|
25413
|
+
</div>
|
|
25414
|
+
<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>
|
|
25415
|
+
<label class="select-wrap" aria-label="${t(locale, "theme.label")}">
|
|
25416
|
+
${icon("sun")}
|
|
25417
|
+
<select id="theme-select" class="control" onchange="setTheme(this.value)" title="${t(locale, "theme.label")}">
|
|
25418
|
+
<option value="system">${t(locale, "theme.system")}</option>
|
|
25419
|
+
<option value="light">${t(locale, "theme.light")}</option>
|
|
25420
|
+
<option value="dark">${t(locale, "theme.dark")}</option>
|
|
25421
|
+
</select>
|
|
25422
|
+
</label>
|
|
25423
|
+
<button type="button" class="icon-button" onclick="refreshPage(this)" aria-label="${t(locale, "action.refresh")}" title="${t(locale, "action.refresh")}">${icon("refresh")}</button>
|
|
25424
|
+
</div>
|
|
25425
|
+
</header>
|
|
25426
|
+
<nav class="tabs" aria-label="Primary">
|
|
25427
|
+
${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("")}
|
|
25428
|
+
</nav>
|
|
25429
|
+
<main id="main" tabindex="-1">
|
|
25430
|
+
<div class="page-heading reveal">
|
|
25431
|
+
<div><h1>${t(locale, page.title)}</h1><p>${t(locale, page.description)}</p></div>
|
|
25432
|
+
</div>
|
|
25433
|
+
${body}
|
|
25434
|
+
</main>
|
|
25435
|
+
<footer>${t(locale, "app.footer")}</footer>
|
|
25436
|
+
</div>
|
|
25437
|
+
<div id="toast-region" class="toast-region" role="status" aria-live="polite"></div>
|
|
25438
|
+
<dialog id="detail-dialog">
|
|
25439
|
+
<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>
|
|
25440
|
+
<div class="dialog-body"><pre id="detail-content" class="json-view"></pre></div>
|
|
25441
|
+
<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>
|
|
25442
|
+
</dialog>
|
|
25443
|
+
<dialog id="confirm-dialog">
|
|
25444
|
+
<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>
|
|
25445
|
+
<div class="dialog-body"><p id="confirm-body" class="confirm-copy"></p></div>
|
|
25446
|
+
<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>
|
|
25447
|
+
</dialog>
|
|
25448
|
+
<dialog id="danger-dialog">
|
|
25449
|
+
<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>
|
|
25450
|
+
<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>
|
|
25451
|
+
<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>
|
|
25452
|
+
</dialog>
|
|
25453
|
+
<script>
|
|
25454
|
+
const UI=${ui};
|
|
25455
|
+
const text=(key,values={})=>(UI[key]||key).replace(/{([a-zA-Z]+)}/g,(_,name)=>String(values[name]??'{'+name+'}'));
|
|
25456
|
+
const themeMedia=matchMedia('(prefers-color-scheme: dark)');
|
|
25457
|
+
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';}
|
|
25458
|
+
function setTheme(preference){localStorage.setItem('supertask-theme',preference);applyTheme(preference);}
|
|
25459
|
+
function setLocale(value){document.cookie='supertask_locale='+encodeURIComponent(value)+'; Path=/; Max-Age=31536000; SameSite=Lax';location.reload();}
|
|
25460
|
+
themeMedia.addEventListener?.('change',()=>{if((localStorage.getItem('supertask-theme')||'system')==='system')applyTheme('system');});
|
|
25461
|
+
applyTheme(localStorage.getItem('supertask-theme')||'system');
|
|
25462
|
+
requestAnimationFrame(()=>document.documentElement.classList.add('ui-ready'));
|
|
25463
|
+
function refreshPage(button){button.classList.add('refreshing');button.setAttribute('aria-label','${t(locale, "a11y.refreshing")}');location.reload();}
|
|
25464
|
+
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);}
|
|
25465
|
+
async function readJson(response){const data=await response.json().catch(()=>({}));if(!response.ok)throw new Error(data.error||text('feedback.requestFailed'));return data;}
|
|
25466
|
+
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();});}
|
|
25467
|
+
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);});}
|
|
25468
|
+
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')}}
|
|
25469
|
+
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')}}
|
|
25470
|
+
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')}}
|
|
25471
|
+
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')}}
|
|
25472
|
+
const showDetail=id=>showRecord('/api/tasks/'+id);const showRunDetail=id=>showRecord('/api/runs/'+id);const showTemplateDetail=id=>showRecord('/api/templates/'+id);
|
|
25473
|
+
async function copyDetails(){try{await navigator.clipboard.writeText(document.getElementById('detail-content').textContent);showToast(text('details.copySuccess'))}catch{showToast(text('feedback.copyFailed'),'error')}}
|
|
25474
|
+
async function enableTmpl(id){try{await readJson(await fetch('/api/templates/'+id+'/enable',{method:'POST'}));location.reload()}catch(error){showToast(error.message,'error')}}
|
|
25475
|
+
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')}}
|
|
25476
|
+
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')}}
|
|
25477
|
+
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')}}
|
|
25478
|
+
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');}
|
|
25479
|
+
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;}
|
|
25480
|
+
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')}}
|
|
25481
|
+
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')}}
|
|
25482
|
+
</script>
|
|
25483
|
+
</body>
|
|
25484
|
+
</html>`;
|
|
25485
|
+
}
|
|
25486
|
+
var ZH, EN, STYLES, PAGE_KEYS;
|
|
25487
|
+
var init_ui = __esm({
|
|
25488
|
+
"src/web/ui.ts"() {
|
|
25356
25489
|
"use strict";
|
|
25357
|
-
|
|
25358
|
-
|
|
25359
|
-
|
|
25360
|
-
|
|
25361
|
-
|
|
25362
|
-
|
|
25363
|
-
|
|
25364
|
-
|
|
25365
|
-
|
|
25366
|
-
|
|
25367
|
-
|
|
25368
|
-
|
|
25369
|
-
|
|
25370
|
-
|
|
25371
|
-
|
|
25372
|
-
|
|
25373
|
-
|
|
25374
|
-
|
|
25375
|
-
|
|
25376
|
-
|
|
25377
|
-
|
|
25378
|
-
|
|
25379
|
-
|
|
25380
|
-
|
|
25381
|
-
|
|
25382
|
-
|
|
25383
|
-
|
|
25384
|
-
|
|
25385
|
-
|
|
25386
|
-
|
|
25387
|
-
|
|
25388
|
-
|
|
25389
|
-
|
|
25390
|
-
|
|
25391
|
-
|
|
25490
|
+
ZH = {
|
|
25491
|
+
"app.name": "SuperTask",
|
|
25492
|
+
"app.dashboard": "\u63A7\u5236\u53F0",
|
|
25493
|
+
"app.tagline": "\u53EF\u9760\u7684\u672C\u5730 Agent \u8C03\u5EA6\u4E2D\u5FC3",
|
|
25494
|
+
"app.local": "\u672C\u5730\u8FD0\u884C",
|
|
25495
|
+
"app.footer": "Local-first \xB7 \u6570\u636E\u7559\u5728\u4F60\u7684\u8BBE\u5907\u4E0A",
|
|
25496
|
+
"nav.tasks": "\u4EFB\u52A1",
|
|
25497
|
+
"nav.templates": "\u8C03\u5EA6",
|
|
25498
|
+
"nav.runs": "\u6267\u884C\u8BB0\u5F55",
|
|
25499
|
+
"nav.system": "\u7CFB\u7EDF",
|
|
25500
|
+
"page.tasks.title": "\u4EFB\u52A1\u961F\u5217",
|
|
25501
|
+
"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",
|
|
25502
|
+
"page.templates.title": "\u8C03\u5EA6\u6A21\u677F",
|
|
25503
|
+
"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",
|
|
25504
|
+
"page.runs.title": "\u6267\u884C\u8BB0\u5F55",
|
|
25505
|
+
"page.runs.description": "\u8FFD\u8E2A\u6BCF\u6B21 Agent \u6267\u884C\u7684\u72B6\u6001\u3001\u8017\u65F6\u3001\u5FC3\u8DF3\u4E0E\u8F93\u51FA\u3002",
|
|
25506
|
+
"page.system.title": "\u7CFB\u7EDF\u8BBE\u7F6E",
|
|
25507
|
+
"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",
|
|
25508
|
+
"action.refresh": "\u5237\u65B0",
|
|
25509
|
+
"action.details": "\u8BE6\u60C5",
|
|
25510
|
+
"action.retry": "\u91CD\u8BD5",
|
|
25511
|
+
"action.cancel": "\u53D6\u6D88",
|
|
25512
|
+
"action.delete": "\u5220\u9664",
|
|
25513
|
+
"action.enable": "\u542F\u7528",
|
|
25514
|
+
"action.disable": "\u7981\u7528",
|
|
25515
|
+
"action.trigger": "\u7ACB\u5373\u89E6\u53D1",
|
|
25516
|
+
"action.logs": "\u67E5\u770B\u65E5\u5FD7",
|
|
25517
|
+
"action.hideLogs": "\u6536\u8D77\u65E5\u5FD7",
|
|
25518
|
+
"action.save": "\u4FDD\u5B58\u8BBE\u7F6E",
|
|
25519
|
+
"action.copy": "\u590D\u5236 JSON",
|
|
25520
|
+
"action.close": "\u5173\u95ED",
|
|
25521
|
+
"action.confirm": "\u786E\u8BA4",
|
|
25522
|
+
"action.clearDatabase": "\u6E05\u7A7A\u6570\u636E\u5E93",
|
|
25523
|
+
"status.pending": "\u5F85\u6267\u884C",
|
|
25524
|
+
"status.running": "\u8FD0\u884C\u4E2D",
|
|
25525
|
+
"status.done": "\u5DF2\u5B8C\u6210",
|
|
25526
|
+
"status.failed": "\u5931\u8D25",
|
|
25527
|
+
"status.dead_letter": "\u6B7B\u4FE1",
|
|
25528
|
+
"status.cancelled": "\u5DF2\u53D6\u6D88",
|
|
25529
|
+
"status.unknown": "\u672A\u77E5",
|
|
25530
|
+
"stats.total": "\u603B\u4EFB\u52A1",
|
|
25531
|
+
"stats.pending": "\u5F85\u6267\u884C",
|
|
25532
|
+
"stats.running": "\u8FD0\u884C\u4E2D",
|
|
25533
|
+
"stats.done": "\u5DF2\u5B8C\u6210",
|
|
25534
|
+
"stats.failedDead": "\u5931\u8D25\u4E0E\u6B7B\u4FE1",
|
|
25535
|
+
"stats.templates": "\u6A21\u677F\u603B\u6570",
|
|
25536
|
+
"stats.enabled": "\u5DF2\u542F\u7528",
|
|
25537
|
+
"stats.disabled": "\u5DF2\u7981\u7528",
|
|
25538
|
+
"stats.records": "\u6267\u884C\u603B\u6570",
|
|
25539
|
+
"stats.pageDone": "\u672C\u9875\u6210\u529F",
|
|
25540
|
+
"stats.pageFailed": "\u672C\u9875\u5931\u8D25",
|
|
25541
|
+
"stats.pageRunning": "\u672C\u9875\u8FD0\u884C\u4E2D",
|
|
25542
|
+
"filter.all": "\u5168\u90E8",
|
|
25543
|
+
"filter.searchTasks": "\u641C\u7D22\u5F53\u524D\u9875\u7684\u4EFB\u52A1\u3001Agent \u6216\u63D0\u793A\u8BCD",
|
|
25544
|
+
"filter.noResults": "\u6CA1\u6709\u7B26\u5408\u5F53\u524D\u641C\u7D22\u6761\u4EF6\u7684\u4EFB\u52A1",
|
|
25545
|
+
"table.id": "ID",
|
|
25546
|
+
"table.task": "\u4EFB\u52A1",
|
|
25547
|
+
"table.name": "\u540D\u79F0",
|
|
25548
|
+
"table.agent": "Agent",
|
|
25549
|
+
"table.status": "\u72B6\u6001",
|
|
25550
|
+
"table.duration": "\u8017\u65F6",
|
|
25551
|
+
"table.retries": "\u91CD\u8BD5",
|
|
25552
|
+
"table.actions": "\u64CD\u4F5C",
|
|
25553
|
+
"table.type": "\u7C7B\u578B",
|
|
25554
|
+
"table.rule": "\u89C4\u5219",
|
|
25555
|
+
"table.lastRun": "\u4E0A\u6B21\u6267\u884C",
|
|
25556
|
+
"table.nextRun": "\u4E0B\u6B21\u6267\u884C",
|
|
25557
|
+
"table.run": "Run",
|
|
25558
|
+
"table.heartbeat": "\u5FC3\u8DF3",
|
|
25559
|
+
"table.session": "Session",
|
|
25560
|
+
"table.model": "\u6A21\u578B",
|
|
25561
|
+
"table.startedAt": "\u542F\u52A8\u65F6\u95F4",
|
|
25562
|
+
"table.pid": "PID",
|
|
25563
|
+
"pagination.previous": "\u4E0A\u4E00\u9875",
|
|
25564
|
+
"pagination.next": "\u4E0B\u4E00\u9875",
|
|
25565
|
+
"pagination.summary": "\u7B2C {page} \u9875\uFF0C\u5171 {pages} \u9875 \xB7 {total} \u6761",
|
|
25566
|
+
"empty.tasks": "\u961F\u5217\u91CC\u8FD8\u6CA1\u6709\u4EFB\u52A1",
|
|
25567
|
+
"empty.tasksHint": "\u901A\u8FC7 OpenCode \u63D2\u4EF6\u6216 supertask add \u521B\u5EFA\u7B2C\u4E00\u4E2A\u4EFB\u52A1\u3002",
|
|
25568
|
+
"empty.templates": "\u8FD8\u6CA1\u6709\u8C03\u5EA6\u6A21\u677F",
|
|
25569
|
+
"empty.templatesHint": "\u4F7F\u7528 CLI \u521B\u5EFA\uFF1Asupertask template add",
|
|
25570
|
+
"empty.runs": "\u8FD8\u6CA1\u6709\u6267\u884C\u8BB0\u5F55",
|
|
25571
|
+
"empty.running": "\u5F53\u524D\u6CA1\u6709\u8FD0\u884C\u4E2D\u7684\u4EFB\u52A1",
|
|
25572
|
+
"schedule.cron": "Cron",
|
|
25573
|
+
"schedule.recurring": "\u5FAA\u73AF",
|
|
25574
|
+
"schedule.delayed": "\u5EF6\u8FDF",
|
|
25575
|
+
"schedule.unknown": "\u672A\u77E5",
|
|
25576
|
+
"schedule.enabled": "\u5DF2\u542F\u7528",
|
|
25577
|
+
"schedule.disabled": "\u5DF2\u7981\u7528",
|
|
25578
|
+
"schedule.minutes": "{count} \u5206\u949F",
|
|
25579
|
+
"schedule.seconds": "{count} \u79D2",
|
|
25580
|
+
"schedule.hours": "{count} \u5C0F\u65F6",
|
|
25581
|
+
"schedule.days": "{count} \u5929",
|
|
25582
|
+
"schedule.overdue": "\u5DF2\u5230\u671F",
|
|
25583
|
+
"system.worker": "Worker",
|
|
25584
|
+
"system.scheduler": "Scheduler",
|
|
25585
|
+
"system.watchdog": "Watchdog",
|
|
25586
|
+
"system.maxConcurrency": "\u6700\u5927\u5E76\u53D1",
|
|
25587
|
+
"system.pollInterval": "\u8F6E\u8BE2\u95F4\u9694",
|
|
25588
|
+
"system.heartbeatInterval": "\u5FC3\u8DF3\u95F4\u9694",
|
|
25589
|
+
"system.taskTimeout": "\u4EFB\u52A1\u8D85\u65F6",
|
|
25590
|
+
"system.schedulerEnabled": "\u542F\u7528\u8C03\u5EA6",
|
|
25591
|
+
"system.checkInterval": "\u68C0\u67E5\u95F4\u9694",
|
|
25592
|
+
"system.heartbeatTimeout": "\u5FC3\u8DF3\u8D85\u65F6",
|
|
25593
|
+
"system.cleanupInterval": "\u6E05\u7406\u95F4\u9694",
|
|
25594
|
+
"system.retentionDays": "\u6570\u636E\u4FDD\u7559",
|
|
25595
|
+
"system.milliseconds": "\u6BEB\u79D2",
|
|
25596
|
+
"system.seconds": "\u79D2",
|
|
25597
|
+
"system.minutes": "\u5206\u949F",
|
|
25598
|
+
"system.hours": "\u5C0F\u65F6",
|
|
25599
|
+
"system.days": "\u5929",
|
|
25600
|
+
"system.activeTemplates": "\u6D3B\u8DC3\u6A21\u677F",
|
|
25601
|
+
"system.saveHint": "\u4FDD\u5B58\u540E\u9700\u91CD\u542F Gateway \u751F\u6548",
|
|
25602
|
+
"system.runningTasks": "\u5F53\u524D\u8FD0\u884C\u4EFB\u52A1\uFF08{running} / {limit} \u5E76\u53D1\uFF09",
|
|
25603
|
+
"system.taskStats": "\u4EFB\u52A1\u6982\u89C8",
|
|
25604
|
+
"system.configFile": "\u914D\u7F6E\u6587\u4EF6",
|
|
25605
|
+
"system.path": "\u8DEF\u5F84",
|
|
25606
|
+
"system.fileExists": "\u6587\u4EF6\u5B58\u5728",
|
|
25607
|
+
"system.yes": "\u662F",
|
|
25608
|
+
"system.noDefault": "\u5426\uFF0C\u5F53\u524D\u4F7F\u7528\u9ED8\u8BA4\u503C",
|
|
25609
|
+
"system.danger": "\u5371\u9669\u64CD\u4F5C",
|
|
25610
|
+
"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",
|
|
25611
|
+
"theme.label": "\u4E3B\u9898",
|
|
25612
|
+
"theme.system": "\u8DDF\u968F\u7CFB\u7EDF",
|
|
25613
|
+
"theme.light": "\u6D45\u8272",
|
|
25614
|
+
"theme.dark": "\u6DF1\u8272",
|
|
25615
|
+
"language.label": "\u8BED\u8A00",
|
|
25616
|
+
"details.title": "\u6570\u636E\u8BE6\u60C5",
|
|
25617
|
+
"details.subtitle": "\u539F\u59CB\u8BB0\u5F55\uFF08JSON\uFF09",
|
|
25618
|
+
"details.copySuccess": "JSON \u5DF2\u590D\u5236",
|
|
25619
|
+
"dialog.cancelTask": "\u53D6\u6D88\u4EFB\u52A1 #{id}\uFF1F",
|
|
25620
|
+
"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",
|
|
25621
|
+
"dialog.retryTask": "\u91CD\u8BD5\u4EFB\u52A1 #{id}\uFF1F",
|
|
25622
|
+
"dialog.retryTaskBody": "\u4EFB\u52A1\u5C06\u56DE\u5230\u5F85\u6267\u884C\u72B6\u6001\uFF0C\u5E76\u91CD\u7F6E\u81EA\u52A8\u91CD\u8BD5\u9884\u7B97\u3002",
|
|
25623
|
+
"dialog.deleteTask": "\u5220\u9664\u4EFB\u52A1 #{id}\uFF1F",
|
|
25624
|
+
"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",
|
|
25625
|
+
"dialog.disableTemplate": "\u7981\u7528\u8FD9\u4E2A\u8C03\u5EA6\u6A21\u677F\uFF1F",
|
|
25626
|
+
"dialog.disableTemplateBody": "\u6A21\u677F\u5C06\u505C\u6B62\u81EA\u52A8\u521B\u5EFA\u65B0\u4EFB\u52A1\uFF0C\u5DF2\u6709\u4EFB\u52A1\u4E0D\u53D7\u5F71\u54CD\u3002",
|
|
25627
|
+
"dialog.deleteTemplate": "\u5220\u9664\u8FD9\u4E2A\u8C03\u5EA6\u6A21\u677F\uFF1F",
|
|
25628
|
+
"dialog.deleteTemplateBody": "\u6A21\u677F\u914D\u7F6E\u5C06\u6C38\u4E45\u5220\u9664\uFF0C\u6B64\u64CD\u4F5C\u65E0\u6CD5\u64A4\u9500\u3002",
|
|
25629
|
+
"dialog.triggerTemplate": "\u7ACB\u5373\u89E6\u53D1\u4E00\u6B21\uFF1F",
|
|
25630
|
+
"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",
|
|
25631
|
+
"dialog.clearTitle": "\u786E\u8BA4\u6E05\u7A7A\u6570\u636E\u5E93",
|
|
25632
|
+
"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",
|
|
25633
|
+
"dialog.clearInstruction": "\u8F93\u5165 CLEAR \u4EE5\u786E\u8BA4",
|
|
25634
|
+
"feedback.retryFailed": "\u91CD\u8BD5\u5931\u8D25",
|
|
25635
|
+
"feedback.cancelFailed": "\u53D6\u6D88\u5931\u8D25",
|
|
25636
|
+
"feedback.deleteFailed": "\u5220\u9664\u5931\u8D25",
|
|
25637
|
+
"feedback.requestFailed": "\u8BF7\u6C42\u5931\u8D25",
|
|
25638
|
+
"feedback.triggered": "\u5DF2\u521B\u5EFA\u4EFB\u52A1 #{id}",
|
|
25639
|
+
"feedback.configSaved": "\u8BBE\u7F6E\u5DF2\u4FDD\u5B58\uFF0C\u91CD\u542F Gateway \u540E\u751F\u6548",
|
|
25640
|
+
"feedback.databaseCleared": "\u6570\u636E\u5E93\u5DF2\u6E05\u7A7A\uFF0C\u5907\u4EFD\u4F4D\u4E8E\uFF1A{path}",
|
|
25641
|
+
"feedback.copyFailed": "\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u9009\u62E9\u5185\u5BB9",
|
|
25642
|
+
"a11y.skip": "\u8DF3\u5230\u4E3B\u8981\u5185\u5BB9",
|
|
25643
|
+
"a11y.refreshing": "\u6B63\u5728\u5237\u65B0"
|
|
25644
|
+
};
|
|
25645
|
+
EN = {
|
|
25646
|
+
"app.name": "SuperTask",
|
|
25647
|
+
"app.dashboard": "Dashboard",
|
|
25648
|
+
"app.tagline": "Reliable local agent orchestration",
|
|
25649
|
+
"app.local": "Running locally",
|
|
25650
|
+
"app.footer": "Local-first \xB7 Your data stays on this device",
|
|
25651
|
+
"nav.tasks": "Tasks",
|
|
25652
|
+
"nav.templates": "Schedules",
|
|
25653
|
+
"nav.runs": "Runs",
|
|
25654
|
+
"nav.system": "System",
|
|
25655
|
+
"page.tasks.title": "Task queue",
|
|
25656
|
+
"page.tasks.description": "Track priority, execution state, and retries, then act on tasks that need attention.",
|
|
25657
|
+
"page.templates.title": "Schedule templates",
|
|
25658
|
+
"page.templates.description": "Manage cron, delayed, and recurring work so repeated tasks run on time.",
|
|
25659
|
+
"page.runs.title": "Execution history",
|
|
25660
|
+
"page.runs.description": "Inspect the status, duration, heartbeat, and output of every agent run.",
|
|
25661
|
+
"page.system.title": "System settings",
|
|
25662
|
+
"page.system.description": "Tune Gateway behavior, inspect active work, and manage local data.",
|
|
25663
|
+
"action.refresh": "Refresh",
|
|
25664
|
+
"action.details": "Details",
|
|
25665
|
+
"action.retry": "Retry",
|
|
25666
|
+
"action.cancel": "Cancel",
|
|
25667
|
+
"action.delete": "Delete",
|
|
25668
|
+
"action.enable": "Enable",
|
|
25669
|
+
"action.disable": "Disable",
|
|
25670
|
+
"action.trigger": "Run now",
|
|
25671
|
+
"action.logs": "View log",
|
|
25672
|
+
"action.hideLogs": "Hide log",
|
|
25673
|
+
"action.save": "Save settings",
|
|
25674
|
+
"action.copy": "Copy JSON",
|
|
25675
|
+
"action.close": "Close",
|
|
25676
|
+
"action.confirm": "Confirm",
|
|
25677
|
+
"action.clearDatabase": "Clear database",
|
|
25678
|
+
"status.pending": "Pending",
|
|
25679
|
+
"status.running": "Running",
|
|
25680
|
+
"status.done": "Done",
|
|
25681
|
+
"status.failed": "Failed",
|
|
25682
|
+
"status.dead_letter": "Dead letter",
|
|
25683
|
+
"status.cancelled": "Cancelled",
|
|
25684
|
+
"status.unknown": "Unknown",
|
|
25685
|
+
"stats.total": "Total tasks",
|
|
25686
|
+
"stats.pending": "Pending",
|
|
25687
|
+
"stats.running": "Running",
|
|
25688
|
+
"stats.done": "Completed",
|
|
25689
|
+
"stats.failedDead": "Failed & dead",
|
|
25690
|
+
"stats.templates": "Templates",
|
|
25691
|
+
"stats.enabled": "Enabled",
|
|
25692
|
+
"stats.disabled": "Disabled",
|
|
25693
|
+
"stats.records": "Total runs",
|
|
25694
|
+
"stats.pageDone": "Succeeded here",
|
|
25695
|
+
"stats.pageFailed": "Failed here",
|
|
25696
|
+
"stats.pageRunning": "Running here",
|
|
25697
|
+
"filter.all": "All",
|
|
25698
|
+
"filter.searchTasks": "Search tasks, agents, or prompts on this page",
|
|
25699
|
+
"filter.noResults": "No tasks match this search",
|
|
25700
|
+
"table.id": "ID",
|
|
25701
|
+
"table.task": "Task",
|
|
25702
|
+
"table.name": "Name",
|
|
25703
|
+
"table.agent": "Agent",
|
|
25704
|
+
"table.status": "Status",
|
|
25705
|
+
"table.duration": "Duration",
|
|
25706
|
+
"table.retries": "Retries",
|
|
25707
|
+
"table.actions": "Actions",
|
|
25708
|
+
"table.type": "Type",
|
|
25709
|
+
"table.rule": "Rule",
|
|
25710
|
+
"table.lastRun": "Last run",
|
|
25711
|
+
"table.nextRun": "Next run",
|
|
25712
|
+
"table.run": "Run",
|
|
25713
|
+
"table.heartbeat": "Heartbeat",
|
|
25714
|
+
"table.session": "Session",
|
|
25715
|
+
"table.model": "Model",
|
|
25716
|
+
"table.startedAt": "Started",
|
|
25717
|
+
"table.pid": "PID",
|
|
25718
|
+
"pagination.previous": "Previous",
|
|
25719
|
+
"pagination.next": "Next",
|
|
25720
|
+
"pagination.summary": "Page {page} of {pages} \xB7 {total} items",
|
|
25721
|
+
"empty.tasks": "Your queue is empty",
|
|
25722
|
+
"empty.tasksHint": "Create the first task with the OpenCode plugin or supertask add.",
|
|
25723
|
+
"empty.templates": "No schedule templates yet",
|
|
25724
|
+
"empty.templatesHint": "Create one with: supertask template add",
|
|
25725
|
+
"empty.runs": "No execution history yet",
|
|
25726
|
+
"empty.running": "No tasks are running right now",
|
|
25727
|
+
"schedule.cron": "Cron",
|
|
25728
|
+
"schedule.recurring": "Recurring",
|
|
25729
|
+
"schedule.delayed": "Delayed",
|
|
25730
|
+
"schedule.unknown": "Unknown",
|
|
25731
|
+
"schedule.enabled": "Enabled",
|
|
25732
|
+
"schedule.disabled": "Disabled",
|
|
25733
|
+
"schedule.minutes": "{count} min",
|
|
25734
|
+
"schedule.seconds": "{count} sec",
|
|
25735
|
+
"schedule.hours": "{count} hr",
|
|
25736
|
+
"schedule.days": "{count} days",
|
|
25737
|
+
"schedule.overdue": "Overdue",
|
|
25738
|
+
"system.worker": "Worker",
|
|
25739
|
+
"system.scheduler": "Scheduler",
|
|
25740
|
+
"system.watchdog": "Watchdog",
|
|
25741
|
+
"system.maxConcurrency": "Max concurrency",
|
|
25742
|
+
"system.pollInterval": "Poll interval",
|
|
25743
|
+
"system.heartbeatInterval": "Heartbeat interval",
|
|
25744
|
+
"system.taskTimeout": "Task timeout",
|
|
25745
|
+
"system.schedulerEnabled": "Enable scheduler",
|
|
25746
|
+
"system.checkInterval": "Check interval",
|
|
25747
|
+
"system.heartbeatTimeout": "Heartbeat timeout",
|
|
25748
|
+
"system.cleanupInterval": "Cleanup interval",
|
|
25749
|
+
"system.retentionDays": "Data retention",
|
|
25750
|
+
"system.milliseconds": "ms",
|
|
25751
|
+
"system.seconds": "seconds",
|
|
25752
|
+
"system.minutes": "minutes",
|
|
25753
|
+
"system.hours": "hours",
|
|
25754
|
+
"system.days": "days",
|
|
25755
|
+
"system.activeTemplates": "Active templates",
|
|
25756
|
+
"system.saveHint": "Restart Gateway to apply saved settings",
|
|
25757
|
+
"system.runningTasks": "Active tasks ({running} / {limit} concurrent)",
|
|
25758
|
+
"system.taskStats": "Task overview",
|
|
25759
|
+
"system.configFile": "Configuration file",
|
|
25760
|
+
"system.path": "Path",
|
|
25761
|
+
"system.fileExists": "File exists",
|
|
25762
|
+
"system.yes": "Yes",
|
|
25763
|
+
"system.noDefault": "No, using defaults",
|
|
25764
|
+
"system.danger": "Danger zone",
|
|
25765
|
+
"system.dangerDescription": "A verified backup is created first, then tasks, runs, and templates are cleared transactionally. Active work blocks this operation.",
|
|
25766
|
+
"theme.label": "Theme",
|
|
25767
|
+
"theme.system": "System",
|
|
25768
|
+
"theme.light": "Light",
|
|
25769
|
+
"theme.dark": "Dark",
|
|
25770
|
+
"language.label": "Language",
|
|
25771
|
+
"details.title": "Data details",
|
|
25772
|
+
"details.subtitle": "Raw record (JSON)",
|
|
25773
|
+
"details.copySuccess": "JSON copied",
|
|
25774
|
+
"dialog.cancelTask": "Cancel task #{id}?",
|
|
25775
|
+
"dialog.cancelTaskBody": "A running task will terminate its process tree on the next worker poll.",
|
|
25776
|
+
"dialog.retryTask": "Retry task #{id}?",
|
|
25777
|
+
"dialog.retryTaskBody": "The task returns to pending and its automatic retry budget is reset.",
|
|
25778
|
+
"dialog.deleteTask": "Delete task #{id}?",
|
|
25779
|
+
"dialog.deleteTaskBody": "The task and its execution history will be permanently deleted.",
|
|
25780
|
+
"dialog.disableTemplate": "Disable this schedule?",
|
|
25781
|
+
"dialog.disableTemplateBody": "It will stop creating new tasks automatically. Existing tasks are unchanged.",
|
|
25782
|
+
"dialog.deleteTemplate": "Delete this schedule?",
|
|
25783
|
+
"dialog.deleteTemplateBody": "This template configuration will be permanently deleted.",
|
|
25784
|
+
"dialog.triggerTemplate": "Run this schedule now?",
|
|
25785
|
+
"dialog.triggerTemplateBody": "A new task is created from the template, subject to its instance limit.",
|
|
25786
|
+
"dialog.clearTitle": "Confirm database clear",
|
|
25787
|
+
"dialog.clearBody": "This deletes every task, run, and schedule template after creating a backup.",
|
|
25788
|
+
"dialog.clearInstruction": "Type CLEAR to confirm",
|
|
25789
|
+
"feedback.retryFailed": "Retry failed",
|
|
25790
|
+
"feedback.cancelFailed": "Cancellation failed",
|
|
25791
|
+
"feedback.deleteFailed": "Delete failed",
|
|
25792
|
+
"feedback.requestFailed": "Request failed",
|
|
25793
|
+
"feedback.triggered": "Task #{id} created",
|
|
25794
|
+
"feedback.configSaved": "Settings saved. Restart Gateway to apply them.",
|
|
25795
|
+
"feedback.databaseCleared": "Database cleared. Backup: {path}",
|
|
25796
|
+
"feedback.copyFailed": "Copy failed. Select the content manually.",
|
|
25797
|
+
"a11y.skip": "Skip to main content",
|
|
25798
|
+
"a11y.refreshing": "Refreshing"
|
|
25799
|
+
};
|
|
25800
|
+
STYLES = `
|
|
25801
|
+
<style>
|
|
25802
|
+
:root {
|
|
25803
|
+
color-scheme: light;
|
|
25804
|
+
--bg:#f5f7fb; --bg-glow:rgba(99,102,241,.12); --surface:#ffffff; --surface-2:#f8fafc;
|
|
25805
|
+
--surface-3:#eef2f7; --text:#172033; --text-2:#58657a; --text-3:#8792a5;
|
|
25806
|
+
--border:#dfe5ee; --border-strong:#cbd4e1; --primary:#5957d9; --primary-hover:#4846c7;
|
|
25807
|
+
--primary-soft:#eeedff; --green:#15805d; --green-soft:#e7f7f0; --red:#c63f4f; --red-soft:#fdecef;
|
|
25808
|
+
--yellow:#a66608; --yellow-soft:#fff4d9; --blue:#2563b8; --blue-soft:#e8f1ff; --purple:#7552c8;
|
|
25809
|
+
--shadow-sm:0 1px 2px rgba(16,24,40,.04); --shadow-md:0 12px 30px rgba(30,41,59,.08);
|
|
25810
|
+
--shadow-lg:0 24px 60px rgba(30,41,59,.18); --radius:14px; --radius-sm:9px;
|
|
25811
|
+
--focus:0 0 0 3px rgba(89,87,217,.22);
|
|
25812
|
+
}
|
|
25813
|
+
:root[data-theme="dark"] {
|
|
25814
|
+
color-scheme: dark;
|
|
25815
|
+
--bg:#090d15; --bg-glow:rgba(99,102,241,.18); --surface:#111722; --surface-2:#151d2a;
|
|
25816
|
+
--surface-3:#1c2635; --text:#edf2f8; --text-2:#a6b1c2; --text-3:#738095;
|
|
25817
|
+
--border:#273244; --border-strong:#344258; --primary:#8b87ff; --primary-hover:#a19eff;
|
|
25818
|
+
--primary-soft:#242347; --green:#48c78e; --green-soft:#16362b; --red:#ff7180; --red-soft:#3b1e27;
|
|
25819
|
+
--yellow:#f0b34b; --yellow-soft:#392d18; --blue:#6ea8ff; --blue-soft:#192d4b; --purple:#b89cff;
|
|
25820
|
+
--shadow-sm:0 1px 2px rgba(0,0,0,.25); --shadow-md:0 16px 36px rgba(0,0,0,.28);
|
|
25821
|
+
--shadow-lg:0 28px 70px rgba(0,0,0,.45); --focus:0 0 0 3px rgba(139,135,255,.25);
|
|
25822
|
+
}
|
|
25823
|
+
* { box-sizing:border-box; }
|
|
25824
|
+
html { min-height:100%; background:var(--bg); }
|
|
25825
|
+
body { min-height:100vh; margin:0; color:var(--text); background:
|
|
25826
|
+
radial-gradient(circle at 10% -10%,var(--bg-glow),transparent 34rem),var(--bg);
|
|
25827
|
+
font-family:Inter,ui-sans-serif,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
|
25828
|
+
font-size:14px; line-height:1.5; -webkit-font-smoothing:antialiased; }
|
|
25829
|
+
button,input,select { font:inherit; }
|
|
25830
|
+
button,a,select,input { -webkit-tap-highlight-color:transparent; }
|
|
25831
|
+
a { color:inherit; }
|
|
25832
|
+
code,.mono,.m { font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace; }
|
|
25833
|
+
.skip-link { position:fixed; left:16px; top:-60px; z-index:200; padding:10px 14px; border-radius:8px;
|
|
25834
|
+
color:#fff; background:var(--primary); transition:top .2s ease; }
|
|
25835
|
+
.skip-link:focus { top:16px; }
|
|
25836
|
+
.app-shell { width:min(1440px,100%); margin:0 auto; padding:0 28px 28px; }
|
|
25837
|
+
.topbar { min-height:80px; display:flex; align-items:center; justify-content:space-between; gap:20px;
|
|
25838
|
+
border-bottom:1px solid var(--border); }
|
|
25839
|
+
.brand { display:flex; align-items:center; gap:12px; min-width:0; }
|
|
25840
|
+
.brand-mark { width:38px; height:38px; display:grid; place-items:center; color:#fff; border-radius:11px;
|
|
25841
|
+
background:linear-gradient(145deg,#7773ff,#514ec8); box-shadow:0 9px 22px rgba(89,87,217,.28); }
|
|
25842
|
+
.brand-mark .icon { width:23px; height:23px; }
|
|
25843
|
+
.brand-name { display:flex; align-items:baseline; gap:7px; font-size:17px; font-weight:760; letter-spacing:-.025em; }
|
|
25844
|
+
.brand-name span { color:var(--text-3); font-size:12px; font-weight:650; letter-spacing:.02em; text-transform:uppercase; }
|
|
25845
|
+
.brand-tagline { color:var(--text-2); font-size:12px; margin-top:1px; }
|
|
25846
|
+
.top-actions { display:flex; align-items:center; justify-content:flex-end; gap:8px; }
|
|
25847
|
+
.local-chip { display:inline-flex; align-items:center; gap:7px; color:var(--text-2); font-size:12px; padding:7px 10px;
|
|
25848
|
+
border:1px solid var(--border); border-radius:999px; background:color-mix(in srgb,var(--surface) 78%,transparent); }
|
|
25849
|
+
.live-dot { width:7px; height:7px; border-radius:50%; background:var(--green); box-shadow:0 0 0 4px var(--green-soft); }
|
|
25850
|
+
.control { height:36px; border:1px solid var(--border); border-radius:9px; background:var(--surface);
|
|
25851
|
+
color:var(--text-2); box-shadow:var(--shadow-sm); }
|
|
25852
|
+
.select-wrap { position:relative; display:flex; align-items:center; }
|
|
25853
|
+
.select-wrap>.icon { width:15px; height:15px; position:absolute; left:10px; pointer-events:none; color:var(--text-3); }
|
|
25854
|
+
.select-wrap select { appearance:none; padding:0 29px 0 31px; cursor:pointer; outline:none; }
|
|
25855
|
+
.select-wrap::after { content:""; position:absolute; right:11px; width:6px; height:6px; border-right:1.5px solid currentColor;
|
|
25856
|
+
border-bottom:1.5px solid currentColor; transform:rotate(45deg) translateY(-2px); pointer-events:none; color:var(--text-3); }
|
|
25857
|
+
.language-switch { display:flex; padding:3px; gap:2px; }
|
|
25858
|
+
.language-switch button { height:28px; min-width:34px; padding:0 8px; border:0; border-radius:6px; color:var(--text-3);
|
|
25859
|
+
background:transparent; cursor:pointer; font-size:12px; font-weight:650; }
|
|
25860
|
+
.language-switch button.active { background:var(--surface-3); color:var(--text); }
|
|
25861
|
+
.icon-button { width:36px; height:36px; display:grid; place-items:center; border:1px solid var(--border); border-radius:9px;
|
|
25862
|
+
color:var(--text-2); background:var(--surface); box-shadow:var(--shadow-sm); cursor:pointer; }
|
|
25863
|
+
.icon-button .icon { width:17px; height:17px; }
|
|
25864
|
+
.icon-button:hover,.control:hover { border-color:var(--border-strong); color:var(--text); }
|
|
25865
|
+
.icon-button.refreshing .icon { animation:spin .7s linear infinite; }
|
|
25866
|
+
.tabs { display:flex; gap:6px; margin:18px 0 30px; padding:5px; width:max-content; max-width:100%; overflow-x:auto;
|
|
25867
|
+
border:1px solid var(--border); border-radius:12px; background:color-mix(in srgb,var(--surface) 82%,transparent); box-shadow:var(--shadow-sm); }
|
|
25868
|
+
.tabs a { display:flex; align-items:center; gap:8px; min-height:36px; padding:0 14px; border-radius:8px; color:var(--text-2);
|
|
25869
|
+
font-weight:650; font-size:13px; text-decoration:none; white-space:nowrap; transition:background .16s ease,color .16s ease,box-shadow .16s ease; }
|
|
25870
|
+
.tabs a .icon { width:16px; height:16px; }
|
|
25871
|
+
.tabs a:hover { color:var(--text); background:var(--surface-2); }
|
|
25872
|
+
.tabs a.active { color:var(--primary); background:var(--surface); box-shadow:0 1px 4px rgba(16,24,40,.08); }
|
|
25873
|
+
main { outline:none; }
|
|
25874
|
+
.page-heading { display:flex; justify-content:space-between; align-items:flex-end; gap:20px; margin-bottom:22px; }
|
|
25875
|
+
.page-heading h1 { margin:0; font-size:28px; line-height:1.2; letter-spacing:-.035em; }
|
|
25876
|
+
.page-heading p { margin:7px 0 0; color:var(--text-2); max-width:720px; }
|
|
25877
|
+
.stats-grid { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:14px; margin-bottom:20px; }
|
|
25878
|
+
.stats-grid.three { grid-template-columns:repeat(3,minmax(0,1fr)); }
|
|
25879
|
+
.stat-card { position:relative; min-height:118px; padding:19px; overflow:hidden; border:1px solid var(--border); border-radius:var(--radius);
|
|
25880
|
+
background:linear-gradient(145deg,var(--surface),color-mix(in srgb,var(--surface-2) 72%,var(--surface)));
|
|
25881
|
+
box-shadow:var(--shadow-sm); transition:transform .18s ease,border-color .18s ease,box-shadow .18s ease; }
|
|
25882
|
+
.stat-card:hover { transform:translateY(-2px); border-color:var(--border-strong); box-shadow:var(--shadow-md); }
|
|
25883
|
+
.stat-card::after { content:""; position:absolute; width:90px; height:90px; right:-38px; top:-45px; border-radius:50%; background:var(--tone-soft); }
|
|
25884
|
+
.stat-top { display:flex; justify-content:space-between; align-items:center; }
|
|
25885
|
+
.stat-icon { width:32px; height:32px; display:grid; place-items:center; border-radius:9px; color:var(--tone); background:var(--tone-soft); }
|
|
25886
|
+
.stat-icon .icon { width:17px; height:17px; }
|
|
25887
|
+
.stat-value { margin-top:13px; font-size:28px; font-weight:760; line-height:1; letter-spacing:-.04em; color:var(--tone); }
|
|
25888
|
+
.stat-label { margin-top:7px; color:var(--text-2); font-size:12px; font-weight:650; }
|
|
25889
|
+
.tone-neutral { --tone:var(--text-2); --tone-soft:var(--surface-3); }
|
|
25890
|
+
.tone-blue { --tone:var(--blue); --tone-soft:var(--blue-soft); }
|
|
25891
|
+
.tone-green { --tone:var(--green); --tone-soft:var(--green-soft); }
|
|
25892
|
+
.tone-red { --tone:var(--red); --tone-soft:var(--red-soft); }
|
|
25893
|
+
.tone-purple { --tone:var(--purple); --tone-soft:var(--primary-soft); }
|
|
25894
|
+
.toolbar { display:flex; justify-content:space-between; align-items:center; gap:14px; margin:0 0 12px; }
|
|
25895
|
+
.filters { display:flex; gap:6px; overflow-x:auto; padding:2px; }
|
|
25896
|
+
.filter-chip { display:inline-flex; align-items:center; min-height:34px; padding:0 12px; border:1px solid var(--border); border-radius:9px;
|
|
25897
|
+
color:var(--text-2); background:var(--surface); text-decoration:none; font-size:12px; font-weight:650; white-space:nowrap; }
|
|
25898
|
+
.filter-chip:hover { border-color:var(--border-strong); color:var(--text); }
|
|
25899
|
+
.filter-chip.active { border-color:color-mix(in srgb,var(--primary) 38%,var(--border)); color:var(--primary); background:var(--primary-soft); }
|
|
25900
|
+
.search-box { position:relative; width:min(320px,100%); flex:0 1 320px; }
|
|
25901
|
+
.search-box .icon { position:absolute; left:11px; top:50%; width:16px; height:16px; color:var(--text-3); transform:translateY(-50%); }
|
|
25902
|
+
.search-box input { width:100%; height:36px; padding:0 12px 0 36px; border:1px solid var(--border); border-radius:9px;
|
|
25903
|
+
outline:none; color:var(--text); background:var(--surface); box-shadow:var(--shadow-sm); }
|
|
25904
|
+
.search-box input::placeholder { color:var(--text-3); }
|
|
25905
|
+
.search-box input:focus { border-color:var(--primary); box-shadow:var(--focus); }
|
|
25906
|
+
.panel,.card { border:1px solid var(--border); border-radius:var(--radius); background:var(--surface); box-shadow:var(--shadow-sm); }
|
|
25907
|
+
.panel { overflow:hidden; margin-bottom:16px; }
|
|
25908
|
+
.panel-head { min-height:52px; display:flex; align-items:center; justify-content:space-between; gap:12px; padding:0 18px;
|
|
25909
|
+
border-bottom:1px solid var(--border); }
|
|
25910
|
+
.panel-head h2,.panel-head h3 { margin:0; font-size:14px; letter-spacing:-.01em; }
|
|
25911
|
+
.table-wrap { width:100%; overflow-x:auto; }
|
|
25912
|
+
table { width:100%; border-collapse:separate; border-spacing:0; font-size:13px; }
|
|
25913
|
+
th { height:42px; padding:0 13px; color:var(--text-3); background:var(--surface-2); border-bottom:1px solid var(--border);
|
|
25914
|
+
font-size:11px; font-weight:730; letter-spacing:.045em; text-align:left; text-transform:uppercase; white-space:nowrap; }
|
|
25915
|
+
td { padding:12px 13px; border-bottom:1px solid var(--border); vertical-align:middle; }
|
|
25916
|
+
tbody tr:last-child td { border-bottom:0; }
|
|
25917
|
+
tbody tr { transition:background .14s ease; }
|
|
25918
|
+
tbody tr:hover { background:color-mix(in srgb,var(--primary-soft) 30%,transparent); }
|
|
25919
|
+
.task-name { font-weight:680; color:var(--text); }
|
|
25920
|
+
.task-prompt { max-width:520px; margin-top:3px; color:var(--text-2); font-size:12px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
|
25921
|
+
.muted,.mu { color:var(--text-2); }
|
|
25922
|
+
.faint { color:var(--text-3); }
|
|
25923
|
+
.small,.sm { font-size:12px; }
|
|
25924
|
+
.tag { display:inline-flex; align-items:center; min-height:23px; max-width:180px; padding:0 8px; border:1px solid var(--border);
|
|
25925
|
+
border-radius:7px; color:var(--text-2); background:var(--surface-2); font-size:11px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
|
25926
|
+
.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; }
|
|
25927
|
+
.badge::before { content:""; width:6px; height:6px; border-radius:50%; background:currentColor; }
|
|
25928
|
+
.b-pending { color:var(--text-2); background:var(--surface-3); }
|
|
25929
|
+
.b-running { color:var(--blue); background:var(--blue-soft); }
|
|
25930
|
+
.b-running::before { animation:pulse 1.7s ease-in-out infinite; }
|
|
25931
|
+
.b-done { color:var(--green); background:var(--green-soft); }
|
|
25932
|
+
.b-failed { color:var(--red); background:var(--red-soft); }
|
|
25933
|
+
.b-dead_letter { color:var(--yellow); background:var(--yellow-soft); }
|
|
25934
|
+
.b-cancelled,.b-unknown { color:var(--text-3); background:var(--surface-3); }
|
|
25935
|
+
.t-cron { color:var(--purple); } .t-recurring { color:var(--blue); } .t-delayed { color:var(--yellow); }
|
|
25936
|
+
.actions { display:flex; align-items:center; flex-wrap:wrap; gap:5px; }
|
|
25937
|
+
.btn { min-height:32px; display:inline-flex; align-items:center; justify-content:center; gap:6px; padding:0 11px; border:1px solid var(--border);
|
|
25938
|
+
border-radius:8px; color:var(--text-2); background:var(--surface); text-decoration:none; cursor:pointer; font-size:12px; font-weight:650;
|
|
25939
|
+
transition:transform .12s ease,background .15s ease,border-color .15s ease,color .15s ease,box-shadow .15s ease; }
|
|
25940
|
+
.btn:hover { color:var(--text); border-color:var(--border-strong); background:var(--surface-2); }
|
|
25941
|
+
.btn:active { transform:scale(.97); }
|
|
25942
|
+
.btn:disabled { opacity:.5; cursor:not-allowed; transform:none; }
|
|
25943
|
+
.btn-primary { color:#fff; border-color:var(--primary); background:var(--primary); }
|
|
25944
|
+
.btn-primary:hover { color:#fff; border-color:var(--primary-hover); background:var(--primary-hover); }
|
|
25945
|
+
.btn-danger { color:var(--red); }
|
|
25946
|
+
.btn-danger:hover { color:var(--red); border-color:color-mix(in srgb,var(--red) 55%,var(--border)); background:var(--red-soft); }
|
|
25947
|
+
.btn-warning:hover { color:var(--yellow); border-color:color-mix(in srgb,var(--yellow) 55%,var(--border)); background:var(--yellow-soft); }
|
|
25948
|
+
.btn .icon { width:14px; height:14px; }
|
|
25949
|
+
.pagination { display:flex; justify-content:center; align-items:center; gap:10px; margin:18px 0 4px; }
|
|
25950
|
+
.pagination .summary { color:var(--text-2); font-size:12px; }
|
|
25951
|
+
.empty-state { display:grid; place-items:center; min-height:230px; padding:36px; text-align:center; }
|
|
25952
|
+
.empty-icon { width:48px; height:48px; display:grid; place-items:center; border-radius:14px; color:var(--primary); background:var(--primary-soft); }
|
|
25953
|
+
.empty-icon .icon { width:23px; height:23px; }
|
|
25954
|
+
.empty-state h3 { margin:13px 0 4px; font-size:15px; }
|
|
25955
|
+
.empty-state p { margin:0; color:var(--text-2); font-size:12px; }
|
|
25956
|
+
.empty-state code { display:inline-block; margin-top:10px; padding:5px 8px; border-radius:6px; background:var(--surface-3); }
|
|
25957
|
+
.settings-grid { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:14px; margin-bottom:16px; }
|
|
25958
|
+
.settings-card { padding:18px; }
|
|
25959
|
+
.settings-title { display:flex; align-items:center; justify-content:space-between; margin:0 0 16px; font-size:14px; }
|
|
25960
|
+
.settings-title span:first-child { display:flex; align-items:center; gap:8px; }
|
|
25961
|
+
.settings-title .icon { width:17px; height:17px; color:var(--primary); }
|
|
25962
|
+
.field { display:grid; grid-template-columns:minmax(0,1fr) 112px; align-items:center; gap:12px; margin:11px 0; }
|
|
25963
|
+
.field label { color:var(--text-2); font-size:12px; }
|
|
25964
|
+
.input-unit { position:relative; }
|
|
25965
|
+
.input-unit input { width:100%; height:36px; padding:0 47px 0 10px; border:1px solid var(--border); border-radius:8px; outline:none;
|
|
25966
|
+
color:var(--text); background:var(--surface-2); }
|
|
25967
|
+
.input-unit span { position:absolute; right:9px; top:50%; transform:translateY(-50%); color:var(--text-3); font-size:10px; pointer-events:none; }
|
|
25968
|
+
.input-unit input:focus { border-color:var(--primary); box-shadow:var(--focus); background:var(--surface); }
|
|
25969
|
+
.switch-field { display:flex; align-items:center; justify-content:space-between; gap:12px; margin:11px 0; color:var(--text-2); font-size:12px; }
|
|
25970
|
+
.switch { position:relative; width:42px; height:24px; flex:0 0 auto; }
|
|
25971
|
+
.switch input { position:absolute; opacity:0; }
|
|
25972
|
+
.switch span { position:absolute; inset:0; border-radius:999px; background:var(--surface-3); border:1px solid var(--border-strong); cursor:pointer; transition:.2s ease; }
|
|
25973
|
+
.switch span::after { content:""; position:absolute; width:17px; height:17px; left:2px; top:2px; border-radius:50%; background:var(--surface);
|
|
25974
|
+
box-shadow:0 1px 3px rgba(0,0,0,.22); transition:transform .2s ease; }
|
|
25975
|
+
.switch input:checked+span { background:var(--primary); border-color:var(--primary); }
|
|
25976
|
+
.switch input:checked+span::after { transform:translateX(18px); }
|
|
25977
|
+
.save-row { display:flex; align-items:center; justify-content:flex-end; gap:12px; margin:0 0 24px; }
|
|
25978
|
+
.info-list { padding:4px 18px; }
|
|
25979
|
+
.info-row { display:flex; align-items:flex-start; justify-content:space-between; gap:20px; padding:13px 0; border-bottom:1px solid var(--border); }
|
|
25980
|
+
.info-row:last-child { border-bottom:0; }
|
|
25981
|
+
.info-key { color:var(--text-2); }
|
|
25982
|
+
.info-value { font-weight:650; text-align:right; overflow-wrap:anywhere; }
|
|
25983
|
+
.overview-grid { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:12px; padding:18px; }
|
|
25984
|
+
.overview-item { padding:13px; border-radius:10px; background:var(--surface-2); }
|
|
25985
|
+
.overview-item span { color:var(--text-2); font-size:11px; }
|
|
25986
|
+
.overview-item strong { display:block; margin-top:5px; font-size:19px; }
|
|
25987
|
+
.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)); }
|
|
25988
|
+
.danger-card h2 { display:flex; align-items:center; gap:8px; margin:0 0 5px; color:var(--red); font-size:14px; }
|
|
25989
|
+
.danger-card h2 .icon { width:17px; height:17px; }
|
|
25990
|
+
.danger-card p { max-width:800px; margin:0 0 14px; color:var(--text-2); font-size:12px; }
|
|
25991
|
+
.log-panel { margin:12px 0; animation:reveal .18s ease both; }
|
|
25992
|
+
.log-box { max-height:360px; overflow:auto; padding:16px; color:var(--text-2); background:#0b1018; font-family:"SFMono-Regular",Consolas,monospace;
|
|
25993
|
+
font-size:12px; white-space:pre-wrap; overflow-wrap:anywhere; }
|
|
25994
|
+
:root[data-theme="light"] .log-box { color:#dbe5f3; }
|
|
25995
|
+
dialog { width:min(760px,calc(100% - 32px)); padding:0; border:1px solid var(--border); border-radius:16px; color:var(--text);
|
|
25996
|
+
background:var(--surface); box-shadow:var(--shadow-lg); }
|
|
25997
|
+
dialog[open] { animation:dialog-in .18s ease both; }
|
|
25998
|
+
dialog::backdrop { background:rgba(8,12,20,.62); backdrop-filter:blur(3px); animation:fade-in .18s ease both; }
|
|
25999
|
+
.dialog-head { display:flex; align-items:center; justify-content:space-between; gap:14px; padding:17px 18px; border-bottom:1px solid var(--border); }
|
|
26000
|
+
.dialog-head h2 { margin:0; font-size:15px; }
|
|
26001
|
+
.dialog-head p { margin:3px 0 0; color:var(--text-2); font-size:11px; }
|
|
26002
|
+
.dialog-body { max-height:70vh; overflow:auto; padding:18px; }
|
|
26003
|
+
.dialog-actions { display:flex; align-items:center; justify-content:flex-end; gap:8px; padding:14px 18px; border-top:1px solid var(--border); }
|
|
26004
|
+
.json-view { min-height:160px; margin:0; padding:15px; overflow:auto; border:1px solid var(--border); border-radius:10px; color:var(--text-2);
|
|
26005
|
+
background:var(--surface-2); font-size:12px; white-space:pre-wrap; overflow-wrap:anywhere; }
|
|
26006
|
+
.confirm-copy { color:var(--text-2); margin:0; }
|
|
26007
|
+
.confirm-copy strong { display:block; margin-bottom:5px; color:var(--text); font-size:15px; }
|
|
26008
|
+
.danger-input { width:100%; height:40px; margin-top:14px; padding:0 12px; border:1px solid var(--border); border-radius:9px; outline:none;
|
|
26009
|
+
color:var(--text); background:var(--surface-2); font-family:"SFMono-Regular",Consolas,monospace; text-transform:uppercase; }
|
|
26010
|
+
.danger-input:focus { border-color:var(--red); box-shadow:0 0 0 3px color-mix(in srgb,var(--red) 22%,transparent); }
|
|
26011
|
+
.toast-region { position:fixed; top:18px; right:18px; z-index:300; display:grid; gap:8px; pointer-events:none; }
|
|
26012
|
+
.toast { min-width:260px; max-width:min(420px,calc(100vw - 36px)); display:flex; align-items:flex-start; gap:10px; padding:12px 14px;
|
|
26013
|
+
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; }
|
|
26014
|
+
.toast .icon { width:18px; height:18px; flex:0 0 auto; margin-top:1px; }
|
|
26015
|
+
.toast.ok .icon { color:var(--green); } .toast.error .icon { color:var(--red); }
|
|
26016
|
+
.toast.leaving { animation:toast-out .18s ease both; }
|
|
26017
|
+
footer { display:flex; justify-content:center; padding:24px 0 4px; color:var(--text-3); font-size:11px; }
|
|
26018
|
+
[hidden] { display:none!important; }
|
|
26019
|
+
:focus-visible { outline:none; box-shadow:var(--focus); }
|
|
26020
|
+
.ui-ready,.ui-ready * { transition-property:background-color,border-color,color,box-shadow; transition-duration:.16s; transition-timing-function:ease; }
|
|
26021
|
+
.reveal { animation:reveal .28s ease both; }
|
|
26022
|
+
.reveal-delay-1 { animation-delay:.04s; } .reveal-delay-2 { animation-delay:.08s; }
|
|
26023
|
+
@keyframes spin { to { transform:rotate(360deg); } }
|
|
26024
|
+
@keyframes pulse { 0%,100% { opacity:1; box-shadow:0 0 0 0 currentColor; } 50% { opacity:.7; box-shadow:0 0 0 4px transparent; } }
|
|
26025
|
+
@keyframes reveal { from { opacity:0; transform:translateY(6px); } to { opacity:1; transform:none; } }
|
|
26026
|
+
@keyframes dialog-in { from { opacity:0; transform:translateY(8px) scale(.985); } to { opacity:1; transform:none; } }
|
|
26027
|
+
@keyframes fade-in { from { opacity:0; } to { opacity:1; } }
|
|
26028
|
+
@keyframes toast-in { from { opacity:0; transform:translateY(-8px) scale(.98); } to { opacity:1; transform:none; } }
|
|
26029
|
+
@keyframes toast-out { to { opacity:0; transform:translateY(-6px) scale(.98); } }
|
|
26030
|
+
@media (max-width:1000px) {
|
|
26031
|
+
.stats-grid { grid-template-columns:repeat(2,minmax(0,1fr)); }
|
|
26032
|
+
.settings-grid { grid-template-columns:1fr; }
|
|
26033
|
+
.local-chip { display:none; }
|
|
26034
|
+
}
|
|
26035
|
+
@media (max-width:720px) {
|
|
26036
|
+
.app-shell { padding:0 16px 20px; }
|
|
26037
|
+
.topbar { min-height:72px; }
|
|
26038
|
+
.brand-tagline,.brand-name span { display:none; }
|
|
26039
|
+
.top-actions { gap:5px; }
|
|
26040
|
+
.select-wrap select { width:42px; color:transparent; padding:0; }
|
|
26041
|
+
.select-wrap>.icon { left:12px; color:var(--text-2); }
|
|
26042
|
+
.select-wrap::after { display:none; }
|
|
26043
|
+
.language-switch { display:none; }
|
|
26044
|
+
.mobile-language { display:grid!important; }
|
|
26045
|
+
.tabs { width:100%; margin:14px 0 24px; }
|
|
26046
|
+
.tabs a { flex:1; justify-content:center; padding:0 10px; }
|
|
26047
|
+
.tabs a span { display:none; }
|
|
26048
|
+
.page-heading { align-items:flex-start; }
|
|
26049
|
+
.page-heading h1 { font-size:24px; }
|
|
26050
|
+
.page-heading p { font-size:13px; }
|
|
26051
|
+
.stats-grid,.stats-grid.three { grid-template-columns:repeat(2,minmax(0,1fr)); gap:10px; }
|
|
26052
|
+
.stat-card { min-height:105px; padding:15px; }
|
|
26053
|
+
.toolbar { align-items:stretch; flex-direction:column; }
|
|
26054
|
+
.search-box { width:100%; flex-basis:auto; }
|
|
26055
|
+
.filters { order:2; }
|
|
26056
|
+
.overview-grid { grid-template-columns:repeat(2,minmax(0,1fr)); }
|
|
26057
|
+
.save-row { align-items:flex-end; flex-direction:column-reverse; }
|
|
26058
|
+
.save-row .btn { width:100%; }
|
|
26059
|
+
.responsive-table { display:block; padding:10px; }
|
|
26060
|
+
.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; }
|
|
26061
|
+
.responsive-table tbody { display:grid; gap:10px; }
|
|
26062
|
+
.responsive-table tr { display:grid; gap:0; padding:10px 12px; border:1px solid var(--border); border-radius:11px; background:var(--surface-2); }
|
|
26063
|
+
.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; }
|
|
26064
|
+
.responsive-table td::before { content:attr(data-label); color:var(--text-3); font-size:10px; font-weight:730; letter-spacing:.045em; text-transform:uppercase; }
|
|
26065
|
+
.responsive-table td[data-primary] { display:block; padding-bottom:10px; margin-bottom:4px; border-bottom:1px solid var(--border); }
|
|
26066
|
+
.responsive-table td[data-primary]::before { display:none; }
|
|
26067
|
+
.responsive-table .task-prompt { max-width:100%; }
|
|
26068
|
+
.responsive-table .actions { justify-content:flex-start; }
|
|
26069
|
+
}
|
|
26070
|
+
@media (max-width:520px) {
|
|
26071
|
+
.stats-grid,.stats-grid.three { grid-template-columns:1fr 1fr; }
|
|
26072
|
+
.stat-card { min-height:98px; }
|
|
26073
|
+
.stat-value { font-size:24px; }
|
|
26074
|
+
.page-heading p { max-width:95%; }
|
|
26075
|
+
.field { grid-template-columns:minmax(0,1fr) 105px; }
|
|
26076
|
+
.overview-grid { grid-template-columns:1fr 1fr; padding:12px; }
|
|
26077
|
+
.pagination .summary { max-width:150px; text-align:center; }
|
|
26078
|
+
}
|
|
26079
|
+
@media (prefers-reduced-motion:reduce) {
|
|
26080
|
+
*,*::before,*::after { scroll-behavior:auto!important; animation-duration:.01ms!important; animation-iteration-count:1!important; transition-duration:.01ms!important; }
|
|
26081
|
+
}
|
|
26082
|
+
</style>`;
|
|
26083
|
+
PAGE_KEYS = {
|
|
26084
|
+
tasks: { title: "page.tasks.title", description: "page.tasks.description" },
|
|
26085
|
+
templates: { title: "page.templates.title", description: "page.templates.description" },
|
|
26086
|
+
runs: { title: "page.runs.title", description: "page.runs.description" },
|
|
26087
|
+
system: { title: "page.system.title", description: "page.system.description" }
|
|
25392
26088
|
};
|
|
25393
26089
|
}
|
|
25394
26090
|
});
|
|
@@ -25399,7 +26095,7 @@ __export(web_exports, {
|
|
|
25399
26095
|
dashboardApp: () => dashboardApp,
|
|
25400
26096
|
default: () => web_default
|
|
25401
26097
|
});
|
|
25402
|
-
import { existsSync as existsSync7,
|
|
26098
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync4, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
25403
26099
|
import { dirname as dirname8 } from "path";
|
|
25404
26100
|
function parsePositiveInteger2(value) {
|
|
25405
26101
|
if (!/^\d+$/.test(value)) return null;
|
|
@@ -25412,41 +26108,34 @@ function parseTaskStatus2(value) {
|
|
|
25412
26108
|
function safeStatus(value) {
|
|
25413
26109
|
return value && TASK_STATUSES2.has(value) ? value : "unknown";
|
|
25414
26110
|
}
|
|
26111
|
+
function resolveLocale(c) {
|
|
26112
|
+
const requested = c.req.query("lang");
|
|
26113
|
+
if (requested === "en" || requested === "zh-CN") return requested;
|
|
26114
|
+
const cookie = c.req.header("Cookie") ?? "";
|
|
26115
|
+
const match2 = /(?:^|;\s*)supertask_locale=([^;]+)/.exec(cookie);
|
|
26116
|
+
if (match2) {
|
|
26117
|
+
try {
|
|
26118
|
+
const saved = decodeURIComponent(match2[1]);
|
|
26119
|
+
if (saved === "en" || saved === "zh-CN") return saved;
|
|
26120
|
+
} catch {
|
|
26121
|
+
}
|
|
26122
|
+
}
|
|
26123
|
+
const accepted = c.req.header("Accept-Language")?.toLowerCase() ?? "";
|
|
26124
|
+
return accepted.startsWith("en") ? "en" : "zh-CN";
|
|
26125
|
+
}
|
|
25415
26126
|
function formatDuration(startAt, endAt) {
|
|
25416
|
-
if (!startAt) return "
|
|
26127
|
+
if (!startAt) return "\u2014";
|
|
25417
26128
|
const start = new Date(startAt).getTime();
|
|
25418
26129
|
const end = endAt ? new Date(endAt).getTime() : Date.now();
|
|
25419
26130
|
const seconds = Math.floor((end - start) / 1e3);
|
|
25420
26131
|
if (seconds < 0) return "0s";
|
|
25421
26132
|
if (seconds < 60) return `${seconds}s`;
|
|
25422
|
-
if (seconds < 3600) return `${Math.floor(seconds / 60)}m${seconds % 60}s`;
|
|
25423
|
-
return `${Math.floor(seconds / 3600)}h${Math.floor(seconds % 3600 / 60)}m`;
|
|
25424
|
-
}
|
|
25425
|
-
function
|
|
25426
|
-
if (!
|
|
25427
|
-
|
|
25428
|
-
if (diff < 6e4) return `${Math.floor(diff / 1e3)}\u79D2\u524D`;
|
|
25429
|
-
if (diff < 36e5) return `${Math.floor(diff / 6e4)}\u5206\u949F\u524D`;
|
|
25430
|
-
if (diff < 864e5) return `${Math.floor(diff / 36e5)}\u5C0F\u65F6\u524D`;
|
|
25431
|
-
return `${Math.floor(diff / 864e5)}\u5929\u524D`;
|
|
25432
|
-
}
|
|
25433
|
-
function timeUntil(ms) {
|
|
25434
|
-
if (!ms) return "-";
|
|
25435
|
-
const diff = ms - Date.now();
|
|
25436
|
-
if (diff < 0) return "\u5DF2\u5230\u671F";
|
|
25437
|
-
if (diff < 6e4) return `${Math.floor(diff / 1e3)}\u79D2\u540E`;
|
|
25438
|
-
if (diff < 36e5) return `${Math.floor(diff / 6e4)}\u5206\u949F\u540E`;
|
|
25439
|
-
if (diff < 864e5) return `${Math.floor(diff / 36e5)}\u5C0F\u65F6\u540E`;
|
|
25440
|
-
return `${Math.floor(diff / 864e5)}\u5929\u540E`;
|
|
25441
|
-
}
|
|
25442
|
-
function formatDate(ts) {
|
|
25443
|
-
if (!ts) return "-";
|
|
25444
|
-
const d = ts instanceof Date ? ts : new Date(ts);
|
|
25445
|
-
return d.toLocaleString("zh-CN", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" });
|
|
25446
|
-
}
|
|
25447
|
-
function esc(s) {
|
|
25448
|
-
if (!s) return "";
|
|
25449
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
26133
|
+
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
|
|
26134
|
+
return `${Math.floor(seconds / 3600)}h ${Math.floor(seconds % 3600 / 60)}m`;
|
|
26135
|
+
}
|
|
26136
|
+
function esc(value) {
|
|
26137
|
+
if (!value) return "";
|
|
26138
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
25450
26139
|
}
|
|
25451
26140
|
function readCurrentConfig() {
|
|
25452
26141
|
const configPath = getConfigPath();
|
|
@@ -25465,94 +26154,51 @@ function writeConfig(cfg) {
|
|
|
25465
26154
|
writeFileSync3(tempPath, JSON.stringify(cfg, null, 2) + "\n", { mode: 384 });
|
|
25466
26155
|
renameSync2(tempPath, configPath);
|
|
25467
26156
|
}
|
|
25468
|
-
function
|
|
25469
|
-
return
|
|
25470
|
-
<
|
|
25471
|
-
|
|
25472
|
-
|
|
25473
|
-
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();}
|
|
25474
|
-
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');}}
|
|
25475
|
-
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');}}
|
|
25476
|
-
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');}}
|
|
25477
|
-
async function enableTmpl(id){await fetch('/api/templates/'+id+'/enable',{method:'POST'});location.reload();}
|
|
25478
|
-
async function disableTmpl(id){if(!confirm('\u786E\u5B9A\u7981\u7528\u6B64\u6A21\u677F?'))return;await fetch('/api/templates/'+id+'/disable',{method:'POST'});location.reload();}
|
|
25479
|
-
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();}
|
|
25480
|
-
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');}}
|
|
25481
|
-
function toggleLog(id){const el=document.getElementById('log-'+id);el.style.display=el.style.display==='none'?'block':'none';}
|
|
25482
|
-
|
|
25483
|
-
async function clearDatabase(){
|
|
25484
|
-
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;
|
|
25485
|
-
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;
|
|
25486
|
-
try{
|
|
25487
|
-
const r=await fetch('/api/database/clear',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({confirmation:'CLEAR'})});
|
|
25488
|
-
const d=await r.json();
|
|
25489
|
-
if(d.success){alert('\u6570\u636E\u5E93\u5DF2\u6E05\u7A7A\uFF0C\u81EA\u52A8\u5907\u4EFD\uFF1A'+d.backupPath);location.reload();}
|
|
25490
|
-
else{alert('\u6E05\u7A7A\u5931\u8D25: '+d.error);}
|
|
25491
|
-
}catch(e){alert('\u6E05\u7A7A\u5931\u8D25: '+e.message);}
|
|
26157
|
+
function statCard(value, label, tone, cardIcon, delay = "") {
|
|
26158
|
+
return `<div class="stat-card ${tone} reveal ${delay}">
|
|
26159
|
+
<div class="stat-top"><div class="stat-icon">${cardIcon}</div></div>
|
|
26160
|
+
<div class="stat-value">${value}</div><div class="stat-label">${label}</div>
|
|
26161
|
+
</div>`;
|
|
25492
26162
|
}
|
|
25493
|
-
|
|
25494
|
-
|
|
25495
|
-
|
|
25496
|
-
|
|
25497
|
-
|
|
25498
|
-
|
|
25499
|
-
|
|
25500
|
-
|
|
25501
|
-
|
|
25502
|
-
}
|
|
25503
|
-
|
|
25504
|
-
|
|
25505
|
-
|
|
25506
|
-
|
|
25507
|
-
|
|
25508
|
-
|
|
25509
|
-
|
|
25510
|
-
|
|
25511
|
-
|
|
25512
|
-
|
|
25513
|
-
};
|
|
25514
|
-
|
|
25515
|
-
|
|
25516
|
-
|
|
25517
|
-
|
|
25518
|
-
else{alert('\u4FDD\u5B58\u5931\u8D25: '+d.error);}
|
|
25519
|
-
}catch(e){alert('\u4FDD\u5B58\u5931\u8D25: '+e.message);}
|
|
25520
|
-
}
|
|
25521
|
-
</script>
|
|
25522
|
-
</head>
|
|
25523
|
-
<body>
|
|
25524
|
-
<div id="toast" class="toast toast-ok"></div>
|
|
25525
|
-
<div class="c">
|
|
25526
|
-
<header>
|
|
25527
|
-
<div><h1>SuperTask Dashboard</h1><span class="mu sm">\u4EFB\u52A1\u8C03\u5EA6\u7BA1\u7406\u4E2D\u5FC3</span></div>
|
|
25528
|
-
<div><a href="${activeTab === "tasks" ? "/" : "/" + activeTab}" class="rf">\u5237\u65B0</a></div>
|
|
25529
|
-
</header>
|
|
25530
|
-
<nav class="tabs">
|
|
25531
|
-
<a href="/" class="${activeTab === "tasks" ? "active" : ""}">\u4EFB\u52A1\u961F\u5217</a>
|
|
25532
|
-
<a href="/templates" class="${activeTab === "templates" ? "active" : ""}">\u5B9A\u65F6\u4EFB\u52A1</a>
|
|
25533
|
-
<a href="/runs" class="${activeTab === "runs" ? "active" : ""}">\u6267\u884C\u65E5\u5FD7</a>
|
|
25534
|
-
<a href="/system" class="${activeTab === "system" ? "active" : ""}">\u7CFB\u7EDF\u72B6\u6001</a>
|
|
25535
|
-
</nav>
|
|
25536
|
-
${body}
|
|
25537
|
-
</div>
|
|
25538
|
-
<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>
|
|
25539
|
-
</body></html>`;
|
|
25540
|
-
}
|
|
25541
|
-
var app, TASK_STATUSES2, SHARED_STYLES, dashboardApp, web_default;
|
|
26163
|
+
function emptyState(title, hint, code = "") {
|
|
26164
|
+
return `<div class="empty-state"><div><div class="empty-icon">${icon("inbox")}</div>
|
|
26165
|
+
<h3>${title}</h3><p>${hint}</p>${code ? `<code>${code}</code>` : ""}</div></div>`;
|
|
26166
|
+
}
|
|
26167
|
+
function formatInterval(milliseconds, locale) {
|
|
26168
|
+
const formatter = new Intl.NumberFormat(locale === "en" ? "en-US" : "zh-CN", {
|
|
26169
|
+
maximumFractionDigits: 1
|
|
26170
|
+
});
|
|
26171
|
+
if (milliseconds < 6e4) {
|
|
26172
|
+
return t(locale, "schedule.seconds", { count: formatter.format(milliseconds / 1e3) });
|
|
26173
|
+
}
|
|
26174
|
+
if (milliseconds < 36e5) {
|
|
26175
|
+
return t(locale, "schedule.minutes", { count: formatter.format(milliseconds / 6e4) });
|
|
26176
|
+
}
|
|
26177
|
+
if (milliseconds < 864e5) {
|
|
26178
|
+
return t(locale, "schedule.hours", { count: formatter.format(milliseconds / 36e5) });
|
|
26179
|
+
}
|
|
26180
|
+
return t(locale, "schedule.days", { count: formatter.format(milliseconds / 864e5) });
|
|
26181
|
+
}
|
|
26182
|
+
function pagination(locale, basePath, page, pages, total, suffix = "") {
|
|
26183
|
+
const previous = page > 1 ? `<a class="btn" href="${basePath}?page=${page - 1}${suffix}">${icon("chevronLeft")}${t(locale, "pagination.previous")}</a>` : "";
|
|
26184
|
+
const next = page < pages ? `<a class="btn" href="${basePath}?page=${page + 1}${suffix}">${t(locale, "pagination.next")}${icon("chevronRight")}</a>` : "";
|
|
26185
|
+
return `<div class="pagination">${previous}<span class="summary">${t(locale, "pagination.summary", { page, pages, total })}</span>${next}</div>`;
|
|
26186
|
+
}
|
|
26187
|
+
var app, TASK_STATUSES2, dashboardApp, web_default;
|
|
25542
26188
|
var init_web = __esm({
|
|
25543
26189
|
"src/web/index.tsx"() {
|
|
25544
26190
|
"use strict";
|
|
25545
26191
|
init_dist();
|
|
25546
|
-
init_html2();
|
|
25547
|
-
init_task_service();
|
|
25548
|
-
init_task_run_service();
|
|
25549
|
-
init_task_template_service();
|
|
25550
|
-
init_database_maintenance_service();
|
|
25551
26192
|
init_drizzle_orm();
|
|
25552
26193
|
init_db2();
|
|
26194
|
+
init_database_maintenance_service();
|
|
26195
|
+
init_task_run_service();
|
|
26196
|
+
init_task_service();
|
|
26197
|
+
init_task_template_service();
|
|
25553
26198
|
init_config();
|
|
25554
26199
|
init_health();
|
|
25555
26200
|
init_job_templates();
|
|
26201
|
+
init_ui();
|
|
25556
26202
|
app = new Hono2();
|
|
25557
26203
|
TASK_STATUSES2 = /* @__PURE__ */ new Set([
|
|
25558
26204
|
"pending",
|
|
@@ -25593,84 +26239,9 @@ var init_web = __esm({
|
|
|
25593
26239
|
const health = getGatewayHealth();
|
|
25594
26240
|
return c.json(health, health.status === "ok" ? 200 : 503);
|
|
25595
26241
|
});
|
|
25596
|
-
SHARED_STYLES = html`
|
|
25597
|
-
<style>
|
|
25598
|
-
:root { --bg:#0d1117; --card:#161b22; --border:#30363d; --t1:#c9d1d9; --t2:#8b949e; --green:#238636; --red:#da3633; --yellow:#d29922; --blue:#1f6feb; --purple:#8957e5; }
|
|
25599
|
-
* { box-sizing:border-box; }
|
|
25600
|
-
body { font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif; background:var(--bg); color:var(--t1); margin:0; line-height:1.5; }
|
|
25601
|
-
.c { max-width:1280px; margin:0 auto; padding:20px; }
|
|
25602
|
-
header { display:flex; justify-content:space-between; align-items:center; margin-bottom:20px; border-bottom:1px solid var(--border); padding-bottom:16px; }
|
|
25603
|
-
h1 { font-size:22px; margin:0; color:#fff; }
|
|
25604
|
-
nav.tabs { display:flex; gap:0; margin-bottom:20px; border-bottom:1px solid var(--border); }
|
|
25605
|
-
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; }
|
|
25606
|
-
nav.tabs a:hover { color:var(--t1); }
|
|
25607
|
-
nav.tabs a.active { color:#fff; border-bottom-color:var(--blue); }
|
|
25608
|
-
.g4 { display:grid; grid-template-columns:repeat(auto-fit,minmax(160px,1fr)); gap:16px; margin-bottom:24px; }
|
|
25609
|
-
.g3 { display:grid; grid-template-columns:repeat(auto-fit,minmax(280px,1fr)); gap:16px; margin-bottom:24px; }
|
|
25610
|
-
.card { background:var(--card); border:1px solid var(--border); border-radius:8px; padding:16px; }
|
|
25611
|
-
.sv { font-size:28px; font-weight:bold; }
|
|
25612
|
-
.sl { color:var(--t2); font-size:12px; text-transform:uppercase; margin-top:4px; }
|
|
25613
|
-
.panel { background:var(--card); border:1px solid var(--border); border-radius:8px; overflow:hidden; margin-bottom:16px; }
|
|
25614
|
-
.ph { padding:12px 16px; border-bottom:1px solid var(--border); display:flex; justify-content:space-between; align-items:center; }
|
|
25615
|
-
.ph h3 { margin:0; font-size:14px; }
|
|
25616
|
-
table { width:100%; border-collapse:collapse; font-size:13px; }
|
|
25617
|
-
th { background:#21262d; color:var(--t2); font-weight:600; text-align:left; padding:8px 12px; white-space:nowrap; }
|
|
25618
|
-
td { padding:8px 12px; border-bottom:1px solid var(--border); vertical-align:top; }
|
|
25619
|
-
tr:last-child td { border-bottom:none; }
|
|
25620
|
-
tr:hover { background:rgba(255,255,255,0.02); }
|
|
25621
|
-
.badge { display:inline-block; padding:2px 8px; border-radius:12px; font-size:11px; font-weight:600; }
|
|
25622
|
-
.b-pending { background:rgba(110,118,129,0.3); color:#8b949e; }
|
|
25623
|
-
.b-running { background:rgba(56,139,253,0.15); color:#58a6ff; }
|
|
25624
|
-
.b-done { background:rgba(46,160,67,0.15); color:#3fb950; }
|
|
25625
|
-
.b-failed { background:rgba(248,81,73,0.15); color:#f85149; }
|
|
25626
|
-
.b-dead_letter { background:rgba(210,153,34,0.15); color:#d29922; }
|
|
25627
|
-
.b-cancelled { background:rgba(110,118,129,0.2); color:#6e7681; }
|
|
25628
|
-
.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; }
|
|
25629
|
-
.btn:hover { background:#30363d; color:#fff; }
|
|
25630
|
-
.btn-sm { padding:2px 6px; font-size:11px; }
|
|
25631
|
-
.btn-primary { background:var(--green); border-color:var(--green); color:#fff; }
|
|
25632
|
-
.btn-primary:hover { opacity:0.85; color:#fff; }
|
|
25633
|
-
.btn-danger:hover { color:var(--red); border-color:var(--red); }
|
|
25634
|
-
.btn-warn:hover { color:var(--yellow); border-color:var(--yellow); }
|
|
25635
|
-
.rf { background:var(--green); color:white; border:none; padding:6px 16px; border-radius:6px; font-weight:600; cursor:pointer; text-decoration:none; }
|
|
25636
|
-
.rf:hover { opacity:0.9; }
|
|
25637
|
-
.m { font-family:monospace; font-size:12px; }
|
|
25638
|
-
.mu { color:var(--t2); }
|
|
25639
|
-
.sm { font-size:12px; }
|
|
25640
|
-
.el { max-width:400px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; display:inline-block; vertical-align:middle; }
|
|
25641
|
-
.tag { display:inline-block; background:#21262d; padding:1px 6px; border-radius:4px; font-size:11px; }
|
|
25642
|
-
.t-cron { color:var(--purple); }
|
|
25643
|
-
.t-recurring { color:var(--blue); }
|
|
25644
|
-
.t-delayed { color:var(--yellow); }
|
|
25645
|
-
.pn { margin-top:16px; display:flex; justify-content:center; gap:10px; align-items:center; }
|
|
25646
|
-
.ir { display:flex; justify-content:space-between; padding:6px 0; border-bottom:1px solid var(--border); font-size:13px; }
|
|
25647
|
-
.ir:last-child { border-bottom:none; }
|
|
25648
|
-
.ik { color:var(--t2); }
|
|
25649
|
-
.iv { font-weight:500; }
|
|
25650
|
-
.form-row { display:flex; gap:12px; align-items:center; margin-bottom:12px; }
|
|
25651
|
-
.form-row label { color:var(--t2); font-size:13px; min-width:100px; }
|
|
25652
|
-
.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; }
|
|
25653
|
-
.form-row input:focus, .form-row select:focus { outline:none; border-color:var(--blue); }
|
|
25654
|
-
.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; }
|
|
25655
|
-
dialog { background:var(--card); color:var(--t1); border:1px solid var(--border); border-radius:8px; padding:0; max-width:900px; width:90%; }
|
|
25656
|
-
dialog::backdrop { background:rgba(0,0,0,0.6); }
|
|
25657
|
-
.dh { padding:16px; border-bottom:1px solid var(--border); display:flex; justify-content:space-between; align-items:center; }
|
|
25658
|
-
.db { padding:16px; max-height:70vh; overflow-y:auto; }
|
|
25659
|
-
pre { margin:0; white-space:pre-wrap; font-size:12px; }
|
|
25660
|
-
.cb { background:transparent; border:none; color:var(--t2); cursor:pointer; font-size:20px; }
|
|
25661
|
-
.mt8 { margin-top:8px; }
|
|
25662
|
-
.mt16 { margin-top:16px; }
|
|
25663
|
-
.mb0 { margin-bottom:0; }
|
|
25664
|
-
.ta-center { text-align:center; }
|
|
25665
|
-
.p30 { padding:30px; }
|
|
25666
|
-
.toast { position:fixed; top:20px; right:20px; padding:12px 20px; border-radius:6px; color:#fff; font-size:14px; z-index:9999; display:none; }
|
|
25667
|
-
.toast-ok { background:var(--green); }
|
|
25668
|
-
.toast-err { background:var(--red); }
|
|
25669
|
-
</style>
|
|
25670
|
-
`;
|
|
25671
26242
|
app.get("/", async (c) => {
|
|
25672
|
-
const
|
|
25673
|
-
const page = parsePositiveInteger2(
|
|
26243
|
+
const locale = resolveLocale(c);
|
|
26244
|
+
const page = parsePositiveInteger2(c.req.query("page") || "1");
|
|
25674
26245
|
if (page === null) return c.text("invalid page", 400);
|
|
25675
26246
|
const statusFilter = c.req.query("status") || "";
|
|
25676
26247
|
const parsedStatus = statusFilter ? parseTaskStatus2(statusFilter) : null;
|
|
@@ -25681,8 +26252,7 @@ var init_web = __esm({
|
|
|
25681
26252
|
TaskService.list({ limit, offset, ...parsedStatus ? { status: parsedStatus } : {} }),
|
|
25682
26253
|
TaskService.stats({})
|
|
25683
26254
|
]);
|
|
25684
|
-
const
|
|
25685
|
-
const latestRuns = await TaskRunService.getLatestByTaskIds(taskIds);
|
|
26255
|
+
const latestRuns = await TaskRunService.getLatestByTaskIds(tasks4.map((task) => task.id));
|
|
25686
26256
|
const counts = {
|
|
25687
26257
|
pending: statsData.pending || 0,
|
|
25688
26258
|
running: statsData.running || 0,
|
|
@@ -25690,253 +26260,211 @@ var init_web = __esm({
|
|
|
25690
26260
|
failed: (statsData.failed || 0) + (statsData.dead_letter || 0),
|
|
25691
26261
|
total: statsData.total || 0
|
|
25692
26262
|
};
|
|
25693
|
-
const
|
|
25694
|
-
|
|
25695
|
-
|
|
25696
|
-
|
|
25697
|
-
|
|
25698
|
-
|
|
25699
|
-
|
|
25700
|
-
|
|
25701
|
-
|
|
25702
|
-
|
|
25703
|
-
|
|
26263
|
+
const filteredTotal = parsedStatus ? Number(statsData[parsedStatus] ?? 0) : counts.total;
|
|
26264
|
+
const totalPages = Math.max(1, Math.ceil(filteredTotal / limit));
|
|
26265
|
+
const filterItems = [
|
|
26266
|
+
{ status: "", label: t(locale, "filter.all") },
|
|
26267
|
+
{ status: "pending", label: statusText(locale, "pending") },
|
|
26268
|
+
{ status: "running", label: statusText(locale, "running") },
|
|
26269
|
+
{ status: "done", label: statusText(locale, "done") },
|
|
26270
|
+
{ status: "failed", label: statusText(locale, "failed") },
|
|
26271
|
+
{ status: "dead_letter", label: statusText(locale, "dead_letter") },
|
|
26272
|
+
{ status: "cancelled", label: statusText(locale, "cancelled") }
|
|
26273
|
+
];
|
|
26274
|
+
const filters = filterItems.map(({ status, label }) => {
|
|
26275
|
+
const href = status ? `/?status=${status}` : "/";
|
|
26276
|
+
return `<a href="${href}" class="filter-chip ${statusFilter === status ? "active" : ""}">${label}</a>`;
|
|
26277
|
+
}).join("");
|
|
26278
|
+
const rows = tasks4.map((task) => {
|
|
25704
26279
|
const status = safeStatus(task.status);
|
|
25705
|
-
const st = status.toUpperCase();
|
|
25706
26280
|
const executionActive = latestRuns.get(task.id)?.status === "running";
|
|
25707
|
-
|
|
25708
|
-
|
|
25709
|
-
<td
|
|
25710
|
-
<td><
|
|
25711
|
-
<td><span class="
|
|
25712
|
-
<td
|
|
25713
|
-
<td class="
|
|
25714
|
-
<td>
|
|
25715
|
-
|
|
25716
|
-
|
|
25717
|
-
${
|
|
25718
|
-
${
|
|
25719
|
-
|
|
25720
|
-
|
|
25721
|
-
|
|
25722
|
-
|
|
25723
|
-
|
|
25724
|
-
|
|
25725
|
-
|
|
25726
|
-
|
|
26281
|
+
const searchable = esc(`${task.name} ${task.agent} ${task.prompt}`);
|
|
26282
|
+
return `<tr data-task-row data-search="${searchable}">
|
|
26283
|
+
<td class="faint" data-label="${t(locale, "table.id")}">#${task.id}</td>
|
|
26284
|
+
<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>
|
|
26285
|
+
<td data-label="${t(locale, "table.agent")}"><span class="tag">${esc(task.agent)}</span></td>
|
|
26286
|
+
<td data-label="${t(locale, "table.status")}"><span class="badge b-${status}">${statusText(locale, status)}</span></td>
|
|
26287
|
+
<td data-label="${t(locale, "table.duration")}" class="small ${task.status === "running" ? "" : "muted"}">${formatDuration(task.startedAt, task.finishedAt)}</td>
|
|
26288
|
+
<td data-label="${t(locale, "table.retries")}" class="muted small">${(task.retryCount ?? 0) > 0 ? task.retryCount : "\u2014"}</td>
|
|
26289
|
+
<td data-label="${t(locale, "table.actions")}"><div class="actions">
|
|
26290
|
+
<button type="button" class="btn" onclick="showDetail(${task.id})">${t(locale, "action.details")}</button>
|
|
26291
|
+
${task.status === "failed" || task.status === "dead_letter" ? `<button type="button" class="btn btn-warning" onclick="retryTask(${task.id})">${t(locale, "action.retry")}</button>` : ""}
|
|
26292
|
+
${["pending", "running", "failed"].includes(task.status ?? "") ? `<button type="button" class="btn btn-warning" onclick="cancelTask(${task.id})">${t(locale, "action.cancel")}</button>` : ""}
|
|
26293
|
+
${task.status === "running" || executionActive ? "" : `<button type="button" class="btn btn-danger" onclick="deleteTask(${task.id})">${t(locale, "action.delete")}</button>`}
|
|
26294
|
+
</div></td>
|
|
26295
|
+
</tr>`;
|
|
26296
|
+
}).join("");
|
|
26297
|
+
const table = tasks4.length === 0 ? emptyState(t(locale, "empty.tasks"), t(locale, "empty.tasksHint")) : `<div class="table-wrap"><table class="responsive-table">
|
|
26298
|
+
<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>
|
|
26299
|
+
<tbody>${rows}</tbody>
|
|
26300
|
+
</table></div>
|
|
26301
|
+
<div id="search-empty" hidden>${emptyState(t(locale, "filter.noResults"), "")}</div>`;
|
|
26302
|
+
const suffix = statusFilter ? `&status=${statusFilter}` : "";
|
|
25727
26303
|
const body = `
|
|
25728
|
-
<div class="
|
|
25729
|
-
|
|
25730
|
-
|
|
25731
|
-
|
|
25732
|
-
|
|
26304
|
+
<div class="stats-grid">
|
|
26305
|
+
${statCard(counts.pending, t(locale, "stats.pending"), "tone-neutral", icon("clock"))}
|
|
26306
|
+
${statCard(counts.running, t(locale, "stats.running"), "tone-blue", icon("activity"), "reveal-delay-1")}
|
|
26307
|
+
${statCard(counts.done, t(locale, "stats.done"), "tone-green", icon("check"), "reveal-delay-1")}
|
|
26308
|
+
${statCard(counts.failed, t(locale, "stats.failedDead"), "tone-red", icon("alert"), "reveal-delay-2")}
|
|
25733
26309
|
</div>
|
|
25734
|
-
|
|
25735
|
-
|
|
25736
|
-
<
|
|
25737
|
-
|
|
25738
|
-
</
|
|
25739
|
-
${
|
|
25740
|
-
return c.html(renderLayout(
|
|
26310
|
+
<div class="toolbar reveal reveal-delay-1">
|
|
26311
|
+
<div class="filters">${filters}</div>
|
|
26312
|
+
<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>
|
|
26313
|
+
</div>
|
|
26314
|
+
<section class="panel reveal reveal-delay-2">${table}</section>
|
|
26315
|
+
${pagination(locale, "/", page, totalPages, filteredTotal, suffix)}`;
|
|
26316
|
+
return c.html(renderLayout({ locale, activeTab: "tasks", body }));
|
|
25741
26317
|
});
|
|
25742
26318
|
app.get("/templates", async (c) => {
|
|
26319
|
+
const locale = resolveLocale(c);
|
|
25743
26320
|
const templates = await TaskTemplateService.list(100);
|
|
25744
|
-
const enabled = templates.filter((
|
|
26321
|
+
const enabled = templates.filter((template) => template.enabled).length;
|
|
25745
26322
|
const disabled = templates.length - enabled;
|
|
25746
|
-
|
|
25747
|
-
|
|
25748
|
-
const
|
|
25749
|
-
|
|
25750
|
-
|
|
25751
|
-
|
|
25752
|
-
if (
|
|
25753
|
-
|
|
25754
|
-
|
|
25755
|
-
|
|
25756
|
-
|
|
25757
|
-
|
|
25758
|
-
<td
|
|
25759
|
-
<td
|
|
25760
|
-
|
|
25761
|
-
<td
|
|
25762
|
-
<td
|
|
25763
|
-
<td>${
|
|
25764
|
-
|
|
25765
|
-
|
|
25766
|
-
|
|
25767
|
-
|
|
25768
|
-
<button class="btn btn-sm btn-primary" onclick="triggerTmpl(${t.id})">\u89E6\u53D1</button>
|
|
25769
|
-
${toggleBtn}
|
|
25770
|
-
<button class="btn btn-sm btn-danger" onclick="deleteTmpl(${t.id})">\u5220\u9664</button>
|
|
25771
|
-
</td></tr>`;
|
|
25772
|
-
}
|
|
25773
|
-
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>` : "";
|
|
26323
|
+
const rows = templates.map((template) => {
|
|
26324
|
+
const scheduleType = ["cron", "recurring", "delayed"].includes(template.scheduleType) ? template.scheduleType : "unknown";
|
|
26325
|
+
const typeLabel = scheduleType === "cron" ? t(locale, "schedule.cron") : scheduleType === "recurring" ? t(locale, "schedule.recurring") : scheduleType === "delayed" ? t(locale, "schedule.delayed") : t(locale, "schedule.unknown");
|
|
26326
|
+
let rule = "\u2014";
|
|
26327
|
+
if (template.scheduleType === "cron") rule = template.cronExpr || "\u2014";
|
|
26328
|
+
if (template.scheduleType === "recurring" && template.intervalMs) rule = formatInterval(template.intervalMs, locale);
|
|
26329
|
+
if (template.scheduleType === "delayed") rule = formatDateTime(template.runAt, locale);
|
|
26330
|
+
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>`;
|
|
26331
|
+
return `<tr>
|
|
26332
|
+
<td class="faint" data-label="${t(locale, "table.id")}">#${template.id}</td>
|
|
26333
|
+
<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>
|
|
26334
|
+
<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>
|
|
26335
|
+
<td data-label="${t(locale, "table.type")}"><span class="tag t-${scheduleType}">${typeLabel}</span></td>
|
|
26336
|
+
<td data-label="${t(locale, "table.rule")}" class="m small">${esc(rule)}</td>
|
|
26337
|
+
<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>
|
|
26338
|
+
<td data-label="${t(locale, "table.lastRun")}" class="small muted">${formatRelative(template.lastRunAt, locale)}</td>
|
|
26339
|
+
<td data-label="${t(locale, "table.nextRun")}" class="small">${formatFuture(template.nextRunAt, locale)}</td>
|
|
26340
|
+
<td data-label="${t(locale, "table.actions")}"><div class="actions"><button type="button" class="btn" onclick="showTemplateDetail(${template.id})">${t(locale, "action.details")}</button>
|
|
26341
|
+
<button type="button" class="btn btn-primary" onclick="triggerTmpl(${template.id})">${t(locale, "action.trigger")}</button>${toggle}
|
|
26342
|
+
<button type="button" class="btn btn-danger" onclick="deleteTmpl(${template.id})">${t(locale, "action.delete")}</button></div></td>
|
|
26343
|
+
</tr>`;
|
|
26344
|
+
}).join("");
|
|
25774
26345
|
const body = `
|
|
25775
|
-
<div class="
|
|
25776
|
-
|
|
25777
|
-
|
|
25778
|
-
|
|
26346
|
+
<div class="stats-grid three">
|
|
26347
|
+
${statCard(templates.length, t(locale, "stats.templates"), "tone-purple", icon("templates"))}
|
|
26348
|
+
${statCard(enabled, t(locale, "stats.enabled"), "tone-green", icon("check"), "reveal-delay-1")}
|
|
26349
|
+
${statCard(disabled, t(locale, "stats.disabled"), "tone-neutral", icon("clock"), "reveal-delay-2")}
|
|
25779
26350
|
</div>
|
|
25780
|
-
<
|
|
25781
|
-
<div class="
|
|
25782
|
-
|
|
25783
|
-
|
|
25784
|
-
|
|
25785
|
-
</table>
|
|
25786
|
-
</div>`;
|
|
25787
|
-
return c.html(renderLayout("\u5B9A\u65F6\u4EFB\u52A1", "templates", body));
|
|
26351
|
+
<section class="panel reveal reveal-delay-2">
|
|
26352
|
+
<div class="panel-head"><h2>${t(locale, "page.templates.title")}</h2></div>
|
|
26353
|
+
${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>`}
|
|
26354
|
+
</section>`;
|
|
26355
|
+
return c.html(renderLayout({ locale, activeTab: "templates", body }));
|
|
25788
26356
|
});
|
|
25789
26357
|
app.get("/runs", async (c) => {
|
|
26358
|
+
const locale = resolveLocale(c);
|
|
25790
26359
|
const page = parsePositiveInteger2(c.req.query("page") || "1");
|
|
25791
26360
|
if (page === null) return c.text("invalid page", 400);
|
|
25792
26361
|
const limit = 50;
|
|
25793
26362
|
const offset = (page - 1) * limit;
|
|
25794
|
-
const { taskRuns:
|
|
26363
|
+
const { taskRuns: taskRuns4, tasks: tasks4 } = schema_exports;
|
|
25795
26364
|
const runs = await db.select({
|
|
25796
|
-
id:
|
|
25797
|
-
taskId:
|
|
25798
|
-
sessionId:
|
|
25799
|
-
model:
|
|
25800
|
-
status:
|
|
25801
|
-
startedAt:
|
|
25802
|
-
finishedAt:
|
|
25803
|
-
log:
|
|
25804
|
-
heartbeatAt:
|
|
25805
|
-
workerPid:
|
|
25806
|
-
childPid:
|
|
25807
|
-
taskName:
|
|
25808
|
-
taskAgent:
|
|
25809
|
-
}).from(
|
|
25810
|
-
const totalResult = await db.select({ count: sql`count(*)` }).from(
|
|
26365
|
+
id: taskRuns4.id,
|
|
26366
|
+
taskId: taskRuns4.taskId,
|
|
26367
|
+
sessionId: taskRuns4.sessionId,
|
|
26368
|
+
model: taskRuns4.model,
|
|
26369
|
+
status: taskRuns4.status,
|
|
26370
|
+
startedAt: taskRuns4.startedAt,
|
|
26371
|
+
finishedAt: taskRuns4.finishedAt,
|
|
26372
|
+
log: taskRuns4.log,
|
|
26373
|
+
heartbeatAt: taskRuns4.heartbeatAt,
|
|
26374
|
+
workerPid: taskRuns4.workerPid,
|
|
26375
|
+
childPid: taskRuns4.childPid,
|
|
26376
|
+
taskName: tasks4.name,
|
|
26377
|
+
taskAgent: tasks4.agent
|
|
26378
|
+
}).from(taskRuns4).innerJoin(tasks4, eq(taskRuns4.taskId, tasks4.id)).orderBy(desc(taskRuns4.startedAt), desc(taskRuns4.id)).limit(limit).offset(offset);
|
|
26379
|
+
const totalResult = await db.select({ count: sql`count(*)` }).from(taskRuns4);
|
|
25811
26380
|
const total = Number(totalResult[0]?.count ?? 0);
|
|
25812
|
-
const totalPages = Math.ceil(total / limit);
|
|
25813
|
-
|
|
25814
|
-
const
|
|
25815
|
-
for (const run of runs) {
|
|
26381
|
+
const totalPages = Math.max(1, Math.ceil(total / limit));
|
|
26382
|
+
const logs = [];
|
|
26383
|
+
const rows = runs.map((run) => {
|
|
25816
26384
|
const status = safeStatus(run.status);
|
|
25817
|
-
const shortSession = run.sessionId ? run.sessionId.slice(4, 7) + "***" + run.sessionId.slice(-3) : "-";
|
|
25818
|
-
const logBtn = run.log ? `<button class="btn btn-sm" onclick="toggleLog(${run.id})">\u65E5\u5FD7</button>` : "";
|
|
25819
|
-
rows += `<tr>
|
|
25820
|
-
<td class="mu">#${run.id}</td>
|
|
25821
|
-
<td><div style="font-weight:500">${esc(run.taskName)} <span class="mu">(#${run.taskId})</span></div>
|
|
25822
|
-
${run.model ? `<div class="sm"><span class="tag">${esc(run.model)}</span></div>` : ""}</td>
|
|
25823
|
-
<td><span class="tag">${esc(run.taskAgent)}</span></td>
|
|
25824
|
-
<td><span class="badge b-${status}">${status.toUpperCase()}</span></td>
|
|
25825
|
-
<td class="sm">${formatDuration(run.startedAt, run.finishedAt)}</td>
|
|
25826
|
-
<td class="sm mu">${run.heartbeatAt ? timeAgo(run.heartbeatAt) : "-"}</td>
|
|
25827
|
-
<td><button class="btn btn-sm" onclick="showRunDetail(${run.id})">\u8BE6\u60C5</button>${logBtn}</td>
|
|
25828
|
-
</tr>`;
|
|
25829
26385
|
if (run.log) {
|
|
25830
|
-
|
|
25831
|
-
|
|
25832
|
-
|
|
25833
|
-
|
|
25834
|
-
|
|
25835
|
-
|
|
25836
|
-
|
|
25837
|
-
|
|
25838
|
-
|
|
25839
|
-
|
|
25840
|
-
|
|
26386
|
+
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>`);
|
|
26387
|
+
}
|
|
26388
|
+
return `<tr>
|
|
26389
|
+
<td class="faint" data-label="${t(locale, "table.run")}">#${run.id}</td>
|
|
26390
|
+
<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>
|
|
26391
|
+
<td data-label="${t(locale, "table.agent")}"><span class="tag">${esc(run.taskAgent)}</span></td>
|
|
26392
|
+
<td data-label="${t(locale, "table.status")}"><span class="badge b-${status}">${statusText(locale, status)}</span></td>
|
|
26393
|
+
<td data-label="${t(locale, "table.duration")}" class="small">${formatDuration(run.startedAt, run.finishedAt)}</td>
|
|
26394
|
+
<td data-label="${t(locale, "table.heartbeat")}" class="small muted">${formatRelative(run.heartbeatAt, locale)}</td>
|
|
26395
|
+
<td data-label="${t(locale, "table.actions")}"><div class="actions"><button type="button" class="btn" onclick="showRunDetail(${run.id})">${t(locale, "action.details")}</button>
|
|
26396
|
+
${run.log ? `<button type="button" class="btn" aria-expanded="false" onclick="toggleLog(${run.id},this)">${t(locale, "action.logs")}</button>` : ""}</div></td>
|
|
26397
|
+
</tr>`;
|
|
26398
|
+
}).join("");
|
|
25841
26399
|
const body = `
|
|
25842
|
-
<div class="
|
|
25843
|
-
|
|
25844
|
-
|
|
25845
|
-
|
|
25846
|
-
|
|
26400
|
+
<div class="stats-grid">
|
|
26401
|
+
${statCard(total, t(locale, "stats.records"), "tone-purple", icon("runs"))}
|
|
26402
|
+
${statCard(runs.filter((run) => run.status === "done").length, t(locale, "stats.pageDone"), "tone-green", icon("check"), "reveal-delay-1")}
|
|
26403
|
+
${statCard(runs.filter((run) => run.status === "failed").length, t(locale, "stats.pageFailed"), "tone-red", icon("alert"), "reveal-delay-1")}
|
|
26404
|
+
${statCard(runs.filter((run) => run.status === "running").length, t(locale, "stats.pageRunning"), "tone-blue", icon("activity"), "reveal-delay-2")}
|
|
25847
26405
|
</div>
|
|
25848
|
-
<
|
|
25849
|
-
|
|
25850
|
-
|
|
25851
|
-
</table></div>
|
|
25852
|
-
${logsHtml.join("")}
|
|
25853
|
-
${paging}`;
|
|
25854
|
-
return c.html(renderLayout("\u6267\u884C\u65E5\u5FD7", "runs", body));
|
|
26406
|
+
<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>
|
|
26407
|
+
${logs.join("")}${pagination(locale, "/runs", page, totalPages, total)}`;
|
|
26408
|
+
return c.html(renderLayout({ locale, activeTab: "runs", body }));
|
|
25855
26409
|
});
|
|
25856
26410
|
app.get("/system", async (c) => {
|
|
26411
|
+
const locale = resolveLocale(c);
|
|
25857
26412
|
const config = loadConfig();
|
|
25858
26413
|
const configPath = getConfigPath();
|
|
25859
|
-
const stats = await
|
|
25860
|
-
|
|
25861
|
-
|
|
26414
|
+
const [stats, runningRuns, templates] = await Promise.all([
|
|
26415
|
+
TaskService.stats({}),
|
|
26416
|
+
TaskRunService.getAllRunningRuns(),
|
|
26417
|
+
TaskTemplateService.list(100)
|
|
26418
|
+
]);
|
|
25862
26419
|
const configExists = existsSync7(configPath);
|
|
25863
|
-
|
|
25864
|
-
|
|
25865
|
-
|
|
25866
|
-
|
|
25867
|
-
|
|
25868
|
-
|
|
25869
|
-
|
|
25870
|
-
|
|
25871
|
-
<td class="sm">${formatDate(run.startedAt)}</td>
|
|
25872
|
-
<td class="sm">${run.heartbeatAt ? timeAgo(run.heartbeatAt) : "-"}</td>
|
|
25873
|
-
<td class="m sm">W:${run.workerPid ?? "-"} C:${run.childPid ?? "-"}</td>
|
|
25874
|
-
<td class="sm">${formatDuration(run.startedAt, null)}</td>
|
|
25875
|
-
</tr>`;
|
|
25876
|
-
}
|
|
25877
|
-
}
|
|
25878
|
-
const schedulerStatus = config.scheduler.enabled ? '<span class="badge b-done">\u5DF2\u542F\u7528</span>' : '<span class="badge b-cancelled">\u5DF2\u7981\u7528</span>';
|
|
25879
|
-
const configFileStatus = configExists ? '<span class="badge b-done">\u662F</span>' : '<span class="badge b-cancelled">\u5426 (\u4F7F\u7528\u9ED8\u8BA4\u503C)</span>';
|
|
26420
|
+
const runRows = runningRuns.map((run) => {
|
|
26421
|
+
const session = run.sessionId ? `${run.sessionId.slice(4, 7)}***${run.sessionId.slice(-3)}` : "\u2014";
|
|
26422
|
+
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>
|
|
26423
|
+
<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>
|
|
26424
|
+
<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>
|
|
26425
|
+
<td data-label="${t(locale, "table.duration")}" class="small">${formatDuration(run.startedAt, null)}</td></tr>`;
|
|
26426
|
+
}).join("");
|
|
26427
|
+
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>`;
|
|
25880
26428
|
const body = `
|
|
25881
|
-
<form id="config-form" onsubmit="event.preventDefault();saveConfig()
|
|
25882
|
-
|
|
25883
|
-
|
|
25884
|
-
|
|
25885
|
-
|
|
25886
|
-
|
|
25887
|
-
|
|
25888
|
-
|
|
26429
|
+
<form id="config-form" onsubmit="event.preventDefault();saveConfig()">
|
|
26430
|
+
<div class="settings-grid reveal">
|
|
26431
|
+
<section class="card settings-card"><h2 class="settings-title"><span>${icon("activity")}${t(locale, "system.worker")}</span></h2>
|
|
26432
|
+
<div class="field"><label for="mc">${t(locale, "system.maxConcurrency")}</label>${unitInput("mc", config.worker.maxConcurrency, 1, "\xD7", 20)}</div>
|
|
26433
|
+
<div class="field"><label for="pi">${t(locale, "system.pollInterval")}</label>${unitInput("pi", config.worker.pollIntervalMs, 100, t(locale, "system.milliseconds"))}</div>
|
|
26434
|
+
<div class="field"><label for="hi">${t(locale, "system.heartbeatInterval")}</label>${unitInput("hi", config.worker.heartbeatIntervalMs / 1e3, 5, t(locale, "system.seconds"))}</div>
|
|
26435
|
+
<div class="field"><label for="to">${t(locale, "system.taskTimeout")}</label>${unitInput("to", config.worker.taskTimeoutMs / 6e4, 1, t(locale, "system.minutes"))}</div>
|
|
26436
|
+
</section>
|
|
26437
|
+
<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>
|
|
26438
|
+
<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>
|
|
26439
|
+
<div class="field"><label for="si">${t(locale, "system.checkInterval")}</label>${unitInput("si", config.scheduler.checkIntervalMs, 100, t(locale, "system.milliseconds"))}</div>
|
|
26440
|
+
<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>
|
|
26441
|
+
</section>
|
|
26442
|
+
<section class="card settings-card reveal-delay-2"><h2 class="settings-title"><span>${icon("system")}${t(locale, "system.watchdog")}</span></h2>
|
|
26443
|
+
<div class="field"><label for="wt">${t(locale, "system.heartbeatTimeout")}</label>${unitInput("wt", config.watchdog.heartbeatTimeoutMs / 1e3, 10, t(locale, "system.seconds"))}</div>
|
|
26444
|
+
<div class="field"><label for="wci">${t(locale, "system.checkInterval")}</label>${unitInput("wci", config.watchdog.checkIntervalMs / 1e3, 1, t(locale, "system.seconds"))}</div>
|
|
26445
|
+
<div class="field"><label for="wcl">${t(locale, "system.cleanupInterval")}</label>${unitInput("wcl", config.watchdog.cleanupIntervalMs / 36e5, 1, t(locale, "system.hours"))}</div>
|
|
26446
|
+
<div class="field"><label for="rd">${t(locale, "system.retentionDays")}</label>${unitInput("rd", config.watchdog.retentionDays, 1, t(locale, "system.days"))}</div>
|
|
26447
|
+
</section>
|
|
25889
26448
|
</div>
|
|
25890
|
-
<div class="
|
|
25891
|
-
<h3 style="margin:0 0 12px;font-size:14px">Scheduler \u914D\u7F6E</h3>
|
|
25892
|
-
<div class="form-row"><label>\u542F\u7528\u8C03\u5EA6</label><input type="checkbox" name="se" ${config.scheduler.enabled ? "checked" : ""}></div>
|
|
25893
|
-
<div class="form-row"><label>\u68C0\u67E5\u95F4\u9694(ms)</label><input type="number" name="si" value="${config.scheduler.checkIntervalMs}" min="100" style="width:100px"></div>
|
|
25894
|
-
<div class="ir"><span class="ik">\u6D3B\u8DC3\u6A21\u677F</span><span class="iv">${templates.filter((t) => t.enabled).length} / ${templates.length}</span></div>
|
|
25895
|
-
</div>
|
|
25896
|
-
<div class="card">
|
|
25897
|
-
<h3 style="margin:0 0 12px;font-size:14px">Watchdog \u914D\u7F6E</h3>
|
|
25898
|
-
<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>
|
|
25899
|
-
<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>
|
|
25900
|
-
<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>
|
|
25901
|
-
<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>
|
|
25902
|
-
</div>
|
|
25903
|
-
</div>
|
|
25904
|
-
<div style="text-align:center;margin-bottom:24px">
|
|
25905
|
-
<button type="submit" class="rf" style="font-size:14px;padding:10px 30px">\u4FDD\u5B58\u914D\u7F6E</button>
|
|
25906
|
-
<span class="mu sm" style="margin-left:12px">\u4FDD\u5B58\u540E\u9700\u91CD\u542F Gateway \u751F\u6548</span>
|
|
25907
|
-
</div>
|
|
26449
|
+
<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>
|
|
25908
26450
|
</form>
|
|
25909
|
-
|
|
25910
|
-
|
|
25911
|
-
|
|
25912
|
-
|
|
25913
|
-
|
|
25914
|
-
|
|
25915
|
-
|
|
25916
|
-
|
|
25917
|
-
|
|
25918
|
-
|
|
25919
|
-
|
|
25920
|
-
<div class="
|
|
25921
|
-
|
|
25922
|
-
|
|
25923
|
-
|
|
25924
|
-
|
|
25925
|
-
|
|
25926
|
-
</div>
|
|
25927
|
-
|
|
25928
|
-
<div class="card mt16">
|
|
25929
|
-
<h3 style="margin:0 0 12px;font-size:14px">\u914D\u7F6E\u6587\u4EF6</h3>
|
|
25930
|
-
<div class="ir"><span class="ik">\u8DEF\u5F84</span><span class="iv m sm">${esc(configPath)}</span></div>
|
|
25931
|
-
<div class="ir"><span class="ik">\u6587\u4EF6\u5B58\u5728</span><span class="iv">${configFileStatus}</span></div>
|
|
25932
|
-
</div>
|
|
25933
|
-
|
|
25934
|
-
<div class="card mt16" style="border-color:var(--red)">
|
|
25935
|
-
<h3 style="margin:0 0 12px;font-size:14px;color:var(--red)">\u5371\u9669\u64CD\u4F5C</h3>
|
|
25936
|
-
<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>
|
|
25937
|
-
<button class="btn btn-danger" style="border-color:var(--red);color:var(--red);padding:6px 16px" onclick="clearDatabase()">\u6E05\u7A7A\u6570\u636E\u5E93</button>
|
|
25938
|
-
</div>`;
|
|
25939
|
-
return c.html(renderLayout("\u7CFB\u7EDF\u72B6\u6001", "system", body));
|
|
26451
|
+
<section class="panel reveal">
|
|
26452
|
+
<div class="panel-head"><h2>${t(locale, "system.runningTasks", { running: runningRuns.length, limit: config.worker.maxConcurrency })}</h2></div>
|
|
26453
|
+
${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>`}
|
|
26454
|
+
</section>
|
|
26455
|
+
<section class="panel reveal reveal-delay-1"><div class="panel-head"><h2>${t(locale, "system.taskStats")}</h2></div><div class="overview-grid">
|
|
26456
|
+
<div class="overview-item"><span>${statusText(locale, "pending")}</span><strong>${stats.pending || 0}</strong></div>
|
|
26457
|
+
<div class="overview-item"><span>${statusText(locale, "running")}</span><strong style="color:var(--blue)">${stats.running || 0}</strong></div>
|
|
26458
|
+
<div class="overview-item"><span>${statusText(locale, "done")}</span><strong style="color:var(--green)">${stats.done || 0}</strong></div>
|
|
26459
|
+
<div class="overview-item"><span>${t(locale, "stats.failedDead")}</span><strong style="color:var(--red)">${(stats.failed || 0) + (stats.dead_letter || 0)}</strong></div>
|
|
26460
|
+
</div></section>
|
|
26461
|
+
<section class="panel reveal reveal-delay-1"><div class="panel-head"><h2>${t(locale, "system.configFile")}</h2></div><div class="info-list">
|
|
26462
|
+
<div class="info-row"><span class="info-key">${t(locale, "system.path")}</span><span class="info-value m small">${esc(configPath)}</span></div>
|
|
26463
|
+
<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>
|
|
26464
|
+
</div></section>
|
|
26465
|
+
<section class="card danger-card reveal reveal-delay-2"><h2>${icon("alert")}${t(locale, "system.danger")}</h2><p>${t(locale, "system.dangerDescription")}</p>
|
|
26466
|
+
<button type="button" class="btn btn-danger" onclick="clearDatabase()">${icon("database")}${t(locale, "action.clearDatabase")}</button></section>`;
|
|
26467
|
+
return c.html(renderLayout({ locale, activeTab: "system", body }));
|
|
25940
26468
|
});
|
|
25941
26469
|
app.get("/api/tasks/:id", async (c) => {
|
|
25942
26470
|
const id = parsePositiveInteger2(c.req.param("id"));
|
|
@@ -25956,9 +26484,9 @@ var init_web = __esm({
|
|
|
25956
26484
|
app.get("/api/templates/:id", async (c) => {
|
|
25957
26485
|
const id = parsePositiveInteger2(c.req.param("id"));
|
|
25958
26486
|
if (id === null) return c.json({ error: "invalid id" }, 400);
|
|
25959
|
-
const
|
|
25960
|
-
if (!
|
|
25961
|
-
return c.json(
|
|
26487
|
+
const template = await TaskTemplateService.getById(id);
|
|
26488
|
+
if (!template) return c.json({ error: "not found" }, 404);
|
|
26489
|
+
return c.json(template);
|
|
25962
26490
|
});
|
|
25963
26491
|
app.post("/api/tasks/:id/retry", async (c) => {
|
|
25964
26492
|
const id = parsePositiveInteger2(c.req.param("id"));
|
|
@@ -26002,14 +26530,14 @@ var init_web = __esm({
|
|
|
26002
26530
|
app.delete("/api/templates/:id", async (c) => {
|
|
26003
26531
|
const id = parsePositiveInteger2(c.req.param("id"));
|
|
26004
26532
|
if (id === null) return c.json({ error: "invalid id" }, 400);
|
|
26005
|
-
const
|
|
26006
|
-
return
|
|
26533
|
+
const deleted = await TaskTemplateService.delete(id);
|
|
26534
|
+
return deleted ? c.json({ success: true }) : c.json({ error: "not found" }, 404);
|
|
26007
26535
|
});
|
|
26008
26536
|
app.post("/api/templates/:id/trigger", async (c) => {
|
|
26009
26537
|
const id = parsePositiveInteger2(c.req.param("id"));
|
|
26010
26538
|
if (id === null) return c.json({ error: "invalid id" }, 400);
|
|
26011
|
-
const
|
|
26012
|
-
if (!
|
|
26539
|
+
const template = await TaskTemplateService.getById(id);
|
|
26540
|
+
if (!template) return c.json({ error: "not found" }, 404);
|
|
26013
26541
|
const task = await triggerTaskFromTemplate(id);
|
|
26014
26542
|
if (!task) return c.json({ error: "maxInstances reached" }, 409);
|
|
26015
26543
|
return c.json({ success: true, taskId: task.id });
|
|
@@ -26018,24 +26546,27 @@ var init_web = __esm({
|
|
|
26018
26546
|
try {
|
|
26019
26547
|
const body = await c.req.json();
|
|
26020
26548
|
const current = readCurrentConfig();
|
|
26021
|
-
const
|
|
26022
|
-
const
|
|
26023
|
-
const
|
|
26024
|
-
const
|
|
26025
|
-
const
|
|
26026
|
-
const
|
|
26549
|
+
const currentWorker = current.worker ?? {};
|
|
26550
|
+
const currentScheduler = current.scheduler ?? {};
|
|
26551
|
+
const currentWatchdog = current.watchdog ?? {};
|
|
26552
|
+
const bodyWorker = body.worker ?? {};
|
|
26553
|
+
const bodyScheduler = body.scheduler ?? {};
|
|
26554
|
+
const bodyWatchdog = body.watchdog ?? {};
|
|
26027
26555
|
const merged = {
|
|
26028
26556
|
...current,
|
|
26029
26557
|
...body,
|
|
26030
26558
|
configVersion: 2,
|
|
26031
|
-
worker: { ...
|
|
26032
|
-
scheduler: { ...
|
|
26033
|
-
watchdog: { ...
|
|
26559
|
+
worker: { ...currentWorker, ...bodyWorker },
|
|
26560
|
+
scheduler: { ...currentScheduler, ...bodyScheduler },
|
|
26561
|
+
watchdog: { ...currentWatchdog, ...bodyWatchdog }
|
|
26034
26562
|
};
|
|
26035
26563
|
writeConfig(validateConfig(merged));
|
|
26036
26564
|
return c.json({ success: true });
|
|
26037
|
-
} catch (
|
|
26038
|
-
return c.json({
|
|
26565
|
+
} catch (error) {
|
|
26566
|
+
return c.json({
|
|
26567
|
+
success: false,
|
|
26568
|
+
error: error instanceof Error ? error.message : String(error)
|
|
26569
|
+
}, 400);
|
|
26039
26570
|
}
|
|
26040
26571
|
});
|
|
26041
26572
|
app.post("/api/database/clear", async (c) => {
|
|
@@ -26046,9 +26577,12 @@ var init_web = __esm({
|
|
|
26046
26577
|
try {
|
|
26047
26578
|
const result = DatabaseMaintenanceService.clear({ allowCurrentGateway: true });
|
|
26048
26579
|
return c.json({ success: true, ...result });
|
|
26049
|
-
} catch (
|
|
26050
|
-
const status =
|
|
26051
|
-
return c.json({
|
|
26580
|
+
} catch (error) {
|
|
26581
|
+
const status = error instanceof DatabaseMaintenanceConflictError ? 409 : 500;
|
|
26582
|
+
return c.json({
|
|
26583
|
+
success: false,
|
|
26584
|
+
error: error instanceof Error ? error.message : String(error)
|
|
26585
|
+
}, status);
|
|
26052
26586
|
}
|
|
26053
26587
|
});
|
|
26054
26588
|
dashboardApp = app;
|