opencode-supertask 0.1.31 → 0.1.32-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -31
- package/dist/cli/index.js +1560 -538
- package/dist/cli/index.js.map +1 -1
- package/dist/config-CCUuD6Az.d.ts +26 -0
- package/dist/gateway/index.js +1533 -221
- package/dist/gateway/index.js.map +1 -1
- package/dist/plugin/supertask.js +237 -48
- package/dist/plugin/supertask.js.map +1 -1
- package/dist/web/index.d.ts +30 -1
- package/dist/web/index.js +1426 -181
- package/dist/web/index.js.map +1 -1
- package/dist/worker/index.d.ts +1 -24
- package/dist/worker/index.js +173 -23
- package/dist/worker/index.js.map +1 -1
- package/dist/worker/launcher.js +39 -7
- package/dist/worker/launcher.js.map +1 -1
- package/package.json +2 -2
package/dist/gateway/index.js
CHANGED
|
@@ -1204,7 +1204,7 @@ var init_sql = __esm({
|
|
|
1204
1204
|
return new SQL([new StringChunk(str)]);
|
|
1205
1205
|
}
|
|
1206
1206
|
sql2.raw = raw2;
|
|
1207
|
-
function
|
|
1207
|
+
function join6(chunks, separator) {
|
|
1208
1208
|
const result = [];
|
|
1209
1209
|
for (const [i, chunk] of chunks.entries()) {
|
|
1210
1210
|
if (i > 0 && separator !== void 0) {
|
|
@@ -1214,7 +1214,7 @@ var init_sql = __esm({
|
|
|
1214
1214
|
}
|
|
1215
1215
|
return new SQL(result);
|
|
1216
1216
|
}
|
|
1217
|
-
sql2.join =
|
|
1217
|
+
sql2.join = join6;
|
|
1218
1218
|
function identifier(value) {
|
|
1219
1219
|
return new Name(value);
|
|
1220
1220
|
}
|
|
@@ -3830,7 +3830,7 @@ var init_select2 = __esm({
|
|
|
3830
3830
|
return (table, on) => {
|
|
3831
3831
|
const baseTableName = this.tableName;
|
|
3832
3832
|
const tableName = getTableLikeName(table);
|
|
3833
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
3833
|
+
if (typeof tableName === "string" && this.config.joins?.some((join6) => join6.alias === tableName)) {
|
|
3834
3834
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
3835
3835
|
}
|
|
3836
3836
|
if (!this.isPartialSelect) {
|
|
@@ -4672,7 +4672,7 @@ var init_update = __esm({
|
|
|
4672
4672
|
createJoin(joinType) {
|
|
4673
4673
|
return (table, on) => {
|
|
4674
4674
|
const tableName = getTableLikeName(table);
|
|
4675
|
-
if (typeof tableName === "string" && this.config.joins.some((
|
|
4675
|
+
if (typeof tableName === "string" && this.config.joins.some((join6) => join6.alias === tableName)) {
|
|
4676
4676
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
4677
4677
|
}
|
|
4678
4678
|
if (typeof on === "function") {
|
|
@@ -6107,8 +6107,8 @@ import { homedir } from "os";
|
|
|
6107
6107
|
import { join, dirname } from "path";
|
|
6108
6108
|
import { fileURLToPath } from "url";
|
|
6109
6109
|
function getMigrationsFolder() {
|
|
6110
|
-
const
|
|
6111
|
-
let dir =
|
|
6110
|
+
const __dirname2 = dirname(fileURLToPath(import.meta.url));
|
|
6111
|
+
let dir = __dirname2;
|
|
6112
6112
|
for (let i = 0; i < 5; i++) {
|
|
6113
6113
|
const candidate = join(dir, "drizzle", "meta", "_journal.json");
|
|
6114
6114
|
if (existsSync(candidate)) {
|
|
@@ -6116,7 +6116,7 @@ function getMigrationsFolder() {
|
|
|
6116
6116
|
}
|
|
6117
6117
|
dir = dirname(dir);
|
|
6118
6118
|
}
|
|
6119
|
-
return join(
|
|
6119
|
+
return join(__dirname2, "../../drizzle");
|
|
6120
6120
|
}
|
|
6121
6121
|
function ensureGatewayLock(sqliteDb) {
|
|
6122
6122
|
sqliteDb.exec(`
|
|
@@ -6391,6 +6391,54 @@ var init_backoff = __esm({
|
|
|
6391
6391
|
}
|
|
6392
6392
|
});
|
|
6393
6393
|
|
|
6394
|
+
// src/core/task-working-directory.ts
|
|
6395
|
+
import { statSync } from "fs";
|
|
6396
|
+
import { isAbsolute } from "path";
|
|
6397
|
+
function validateTaskWorkingDirectory(cwd) {
|
|
6398
|
+
if (cwd == null) return;
|
|
6399
|
+
if (!cwd.trim()) {
|
|
6400
|
+
throw new InvalidTaskWorkingDirectoryError("\u4EFB\u52A1\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u80FD\u4E3A\u7A7A");
|
|
6401
|
+
}
|
|
6402
|
+
if (!isAbsolute(cwd)) {
|
|
6403
|
+
throw new InvalidTaskWorkingDirectoryError(`\u4EFB\u52A1\u5DE5\u4F5C\u76EE\u5F55\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84\uFF1A${cwd}`);
|
|
6404
|
+
}
|
|
6405
|
+
let stat;
|
|
6406
|
+
try {
|
|
6407
|
+
stat = statSync(cwd);
|
|
6408
|
+
} catch (error) {
|
|
6409
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
6410
|
+
throw new InvalidTaskWorkingDirectoryError(`\u4EFB\u52A1\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u65E0\u6CD5\u8BBF\u95EE\uFF1A${cwd}\uFF08${detail}\uFF09`);
|
|
6411
|
+
}
|
|
6412
|
+
if (!stat.isDirectory()) {
|
|
6413
|
+
throw new InvalidTaskWorkingDirectoryError(`\u4EFB\u52A1\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u662F\u76EE\u5F55\uFF1A${cwd}`);
|
|
6414
|
+
}
|
|
6415
|
+
}
|
|
6416
|
+
var InvalidTaskWorkingDirectoryError;
|
|
6417
|
+
var init_task_working_directory = __esm({
|
|
6418
|
+
"src/core/task-working-directory.ts"() {
|
|
6419
|
+
"use strict";
|
|
6420
|
+
InvalidTaskWorkingDirectoryError = class extends Error {
|
|
6421
|
+
constructor(message) {
|
|
6422
|
+
super(message);
|
|
6423
|
+
this.name = "InvalidTaskWorkingDirectoryError";
|
|
6424
|
+
}
|
|
6425
|
+
};
|
|
6426
|
+
}
|
|
6427
|
+
});
|
|
6428
|
+
|
|
6429
|
+
// src/core/task-batch.ts
|
|
6430
|
+
function normalizeTaskBatchId(batchId) {
|
|
6431
|
+
if (batchId == null) return batchId;
|
|
6432
|
+
return batchId.trim() || null;
|
|
6433
|
+
}
|
|
6434
|
+
var TASK_BATCH_TRIM_CHARACTERS;
|
|
6435
|
+
var init_task_batch = __esm({
|
|
6436
|
+
"src/core/task-batch.ts"() {
|
|
6437
|
+
"use strict";
|
|
6438
|
+
TASK_BATCH_TRIM_CHARACTERS = " \n\v\f\r\xA0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF";
|
|
6439
|
+
}
|
|
6440
|
+
});
|
|
6441
|
+
|
|
6394
6442
|
// src/core/services/task.service.ts
|
|
6395
6443
|
function hasNoExecutableDependents() {
|
|
6396
6444
|
return sql`NOT EXISTS (
|
|
@@ -6444,6 +6492,8 @@ var init_task_service = __esm({
|
|
|
6444
6492
|
init_db2();
|
|
6445
6493
|
init_drizzle_orm();
|
|
6446
6494
|
init_backoff();
|
|
6495
|
+
init_task_working_directory();
|
|
6496
|
+
init_task_batch();
|
|
6447
6497
|
({ tasks: tasks2, taskRuns: taskRuns2 } = schema_exports);
|
|
6448
6498
|
cleanupInvocation = 0;
|
|
6449
6499
|
TaskDeletionConflictError = class extends Error {
|
|
@@ -6461,34 +6511,75 @@ var init_task_service = __esm({
|
|
|
6461
6511
|
return conditions;
|
|
6462
6512
|
}
|
|
6463
6513
|
static async add(data) {
|
|
6464
|
-
|
|
6514
|
+
const normalizedData = {
|
|
6515
|
+
...data,
|
|
6516
|
+
batchId: normalizeTaskBatchId(data.batchId)
|
|
6517
|
+
};
|
|
6518
|
+
this.validateNewTask(normalizedData);
|
|
6465
6519
|
return db.transaction((tx) => {
|
|
6466
|
-
if (
|
|
6520
|
+
if (normalizedData.dependsOn != null) {
|
|
6467
6521
|
const dependency = tx.select({
|
|
6468
6522
|
id: tasks2.id,
|
|
6469
6523
|
cwd: tasks2.cwd,
|
|
6470
6524
|
status: tasks2.status,
|
|
6471
6525
|
retryCount: tasks2.retryCount,
|
|
6472
6526
|
maxRetries: tasks2.maxRetries
|
|
6473
|
-
}).from(tasks2).where(eq(tasks2.id,
|
|
6527
|
+
}).from(tasks2).where(eq(tasks2.id, normalizedData.dependsOn)).get();
|
|
6474
6528
|
if (!dependency) {
|
|
6475
|
-
throw new Error(`dependsOn \u6307\u5411\u7684\u4EFB\u52A1 #${
|
|
6529
|
+
throw new Error(`dependsOn \u6307\u5411\u7684\u4EFB\u52A1 #${normalizedData.dependsOn} \u4E0D\u5B58\u5728`);
|
|
6476
6530
|
}
|
|
6477
|
-
if ((dependency.cwd ?? null) !== (
|
|
6531
|
+
if ((dependency.cwd ?? null) !== (normalizedData.cwd ?? null)) {
|
|
6478
6532
|
throw new Error("dependsOn \u5FC5\u987B\u6307\u5411\u540C\u4E00 cwd \u7684\u4EFB\u52A1");
|
|
6479
6533
|
}
|
|
6480
6534
|
const dependencyIsRecoverable = dependency.status === "pending" || dependency.status === "running" || dependency.status === "done" || dependency.status === "failed" && (dependency.retryCount ?? 0) <= (dependency.maxRetries ?? 3);
|
|
6481
6535
|
if (!dependencyIsRecoverable) {
|
|
6482
|
-
throw new Error(`dependsOn \u6307\u5411\u7684\u4EFB\u52A1 #${
|
|
6536
|
+
throw new Error(`dependsOn \u6307\u5411\u7684\u4EFB\u52A1 #${normalizedData.dependsOn} \u5DF2\u8FDB\u5165\u4E0D\u53EF\u6062\u590D\u7EC8\u6001`);
|
|
6483
6537
|
}
|
|
6484
6538
|
}
|
|
6485
|
-
return tx.insert(tasks2).values(
|
|
6539
|
+
return tx.insert(tasks2).values(normalizedData).returning().get();
|
|
6540
|
+
}, { behavior: "immediate" });
|
|
6541
|
+
}
|
|
6542
|
+
static async update(id, data, scope = {}) {
|
|
6543
|
+
if (Object.keys(data).length === 0) throw new Error("\u81F3\u5C11\u63D0\u4F9B\u4E00\u4E2A\u8981\u4FEE\u6539\u7684\u5B57\u6BB5");
|
|
6544
|
+
const normalizedData = data.batchId === void 0 ? data : { ...data, batchId: normalizeTaskBatchId(data.batchId) ?? null };
|
|
6545
|
+
return db.transaction((tx) => {
|
|
6546
|
+
const task = tx.select().from(tasks2).where(and(
|
|
6547
|
+
eq(tasks2.id, id),
|
|
6548
|
+
sql`${tasks2.status} IN ('pending', 'failed', 'dead_letter')`,
|
|
6549
|
+
...this.buildScopeWhere(scope)
|
|
6550
|
+
)).get();
|
|
6551
|
+
if (!task) return null;
|
|
6552
|
+
this.validateNewTask({
|
|
6553
|
+
name: normalizedData.name ?? task.name,
|
|
6554
|
+
agent: normalizedData.agent ?? task.agent,
|
|
6555
|
+
model: normalizedData.model ?? task.model,
|
|
6556
|
+
prompt: normalizedData.prompt ?? task.prompt,
|
|
6557
|
+
cwd: task.cwd,
|
|
6558
|
+
category: normalizedData.category ?? task.category,
|
|
6559
|
+
importance: normalizedData.importance ?? task.importance,
|
|
6560
|
+
urgency: normalizedData.urgency ?? task.urgency,
|
|
6561
|
+
batchId: normalizedData.batchId === void 0 ? task.batchId : normalizedData.batchId,
|
|
6562
|
+
maxRetries: normalizedData.maxRetries ?? task.maxRetries,
|
|
6563
|
+
retryBackoffMs: normalizedData.retryBackoffMs ?? task.retryBackoffMs,
|
|
6564
|
+
timeoutMs: normalizedData.timeoutMs === void 0 ? task.timeoutMs : normalizedData.timeoutMs,
|
|
6565
|
+
dependsOn: task.dependsOn
|
|
6566
|
+
});
|
|
6567
|
+
const maxRetries = normalizedData.maxRetries ?? task.maxRetries ?? 3;
|
|
6568
|
+
const exhausted = task.status === "failed" && (task.retryCount ?? 0) > maxRetries;
|
|
6569
|
+
return tx.update(tasks2).set({
|
|
6570
|
+
...normalizedData,
|
|
6571
|
+
...exhausted ? {
|
|
6572
|
+
status: "dead_letter",
|
|
6573
|
+
retryAfter: null
|
|
6574
|
+
} : {}
|
|
6575
|
+
}).where(eq(tasks2.id, id)).returning().get() ?? null;
|
|
6486
6576
|
}, { behavior: "immediate" });
|
|
6487
6577
|
}
|
|
6488
6578
|
static validateNewTask(data) {
|
|
6489
6579
|
if (!data.name.trim()) throw new Error("name \u4E0D\u80FD\u4E3A\u7A7A");
|
|
6490
6580
|
if (!data.agent.trim()) throw new Error("agent \u4E0D\u80FD\u4E3A\u7A7A");
|
|
6491
6581
|
if (!data.prompt.trim()) throw new Error("prompt \u4E0D\u80FD\u4E3A\u7A7A");
|
|
6582
|
+
validateTaskWorkingDirectory(data.cwd);
|
|
6492
6583
|
this.validateInteger("importance", data.importance, 1, 5);
|
|
6493
6584
|
this.validateInteger("urgency", data.urgency, 1, 5);
|
|
6494
6585
|
this.validateInteger("maxRetries", data.maxRetries, 0, 1e3);
|
|
@@ -6509,12 +6600,14 @@ var init_task_service = __esm({
|
|
|
6509
6600
|
isNull(tasks2.retryAfter),
|
|
6510
6601
|
sql`${tasks2.retryAfter} <= ${nowMs}`
|
|
6511
6602
|
);
|
|
6512
|
-
const
|
|
6603
|
+
const excludedBatchIds = [...new Set((scope.excludedBatchIds ?? []).map((batchId) => normalizeTaskBatchId(batchId)).filter((batchId) => Boolean(batchId)))];
|
|
6604
|
+
const hasExcludedBatches = excludedBatchIds.length > 0;
|
|
6513
6605
|
let batchFilter;
|
|
6514
6606
|
if (hasExcludedBatches) {
|
|
6515
6607
|
batchFilter = or(
|
|
6516
6608
|
isNull(tasks2.batchId),
|
|
6517
|
-
sql
|
|
6609
|
+
sql`trim(${tasks2.batchId}, ${TASK_BATCH_TRIM_CHARACTERS}) = ''`,
|
|
6610
|
+
sql`trim(${tasks2.batchId}, ${TASK_BATCH_TRIM_CHARACTERS}) NOT IN ${excludedBatchIds}`
|
|
6518
6611
|
);
|
|
6519
6612
|
}
|
|
6520
6613
|
const statusConditions = or(
|
|
@@ -6548,9 +6641,11 @@ var init_task_service = __esm({
|
|
|
6548
6641
|
),
|
|
6549
6642
|
or(
|
|
6550
6643
|
isNull(tasks2.batchId),
|
|
6644
|
+
sql`trim(${tasks2.batchId}, ${TASK_BATCH_TRIM_CHARACTERS}) = ''`,
|
|
6551
6645
|
sql`NOT EXISTS (
|
|
6552
6646
|
SELECT 1 FROM tasks AS running_batch_task
|
|
6553
|
-
WHERE running_batch_task.batch_id
|
|
6647
|
+
WHERE trim(running_batch_task.batch_id, ${TASK_BATCH_TRIM_CHARACTERS})
|
|
6648
|
+
= trim(${tasks2.batchId}, ${TASK_BATCH_TRIM_CHARACTERS})
|
|
6554
6649
|
AND (
|
|
6555
6650
|
running_batch_task.status = 'running'
|
|
6556
6651
|
OR EXISTS (
|
|
@@ -6574,12 +6669,18 @@ var init_task_service = __esm({
|
|
|
6574
6669
|
).limit(1);
|
|
6575
6670
|
return result[0] ?? null;
|
|
6576
6671
|
}
|
|
6577
|
-
static async countRunning() {
|
|
6672
|
+
static async countRunning(scope = {}) {
|
|
6673
|
+
const scopeConditions = scope.legacyCwd ? [sql`${tasks2.cwd} IS NULL OR trim(${tasks2.cwd}) = ''`] : this.buildScopeWhere(scope);
|
|
6674
|
+
if (scope.batchId !== void 0) {
|
|
6675
|
+
const batchId = normalizeTaskBatchId(scope.batchId);
|
|
6676
|
+
scopeConditions.push(batchId ? sql`trim(${tasks2.batchId}, ${TASK_BATCH_TRIM_CHARACTERS}) = ${batchId}` : sql`0`);
|
|
6677
|
+
}
|
|
6578
6678
|
return db.transaction((tx) => {
|
|
6579
|
-
const runningTasks = tx.select({ count: sql`count(*)` }).from(tasks2).where(eq(tasks2.status, "running")).get();
|
|
6679
|
+
const runningTasks = tx.select({ count: sql`count(*)` }).from(tasks2).where(and(eq(tasks2.status, "running"), ...scopeConditions)).get();
|
|
6580
6680
|
const runsWithoutRunningTask = tx.select({ count: sql`count(DISTINCT ${taskRuns2.taskId})` }).from(taskRuns2).innerJoin(tasks2, eq(tasks2.id, taskRuns2.taskId)).where(and(
|
|
6581
6681
|
eq(taskRuns2.status, "running"),
|
|
6582
|
-
sql`${tasks2.status} <> 'running'
|
|
6682
|
+
sql`${tasks2.status} <> 'running'`,
|
|
6683
|
+
...scopeConditions
|
|
6583
6684
|
)).get();
|
|
6584
6685
|
return Number(runningTasks?.count ?? 0) + Number(runsWithoutRunningTask?.count ?? 0);
|
|
6585
6686
|
}, { behavior: "deferred" });
|
|
@@ -6912,15 +7013,17 @@ var init_task_service = __esm({
|
|
|
6912
7013
|
}).where(and(...conditions, hasViableDependency())).returning().get() ?? null, { behavior: "immediate" });
|
|
6913
7014
|
}
|
|
6914
7015
|
static async retryBatch(batchId, scope = {}) {
|
|
7016
|
+
const normalizedBatchId = normalizeTaskBatchId(batchId);
|
|
7017
|
+
if (!normalizedBatchId) return 0;
|
|
6915
7018
|
const sqlite2 = getSqlite();
|
|
6916
7019
|
const scopeFilter = scope.cwd === void 0 ? "" : "AND candidate.cwd = ?";
|
|
6917
|
-
const parameters = scope.cwd === void 0 ? [
|
|
7020
|
+
const parameters = scope.cwd === void 0 ? [TASK_BATCH_TRIM_CHARACTERS, normalizedBatchId] : [TASK_BATCH_TRIM_CHARACTERS, normalizedBatchId, scope.cwd];
|
|
6918
7021
|
return db.transaction(() => sqlite2.query(`
|
|
6919
7022
|
WITH RECURSIVE
|
|
6920
7023
|
candidate(id, cwd, depends_on) AS MATERIALIZED (
|
|
6921
7024
|
SELECT candidate.id, candidate.cwd, candidate.depends_on
|
|
6922
7025
|
FROM tasks AS candidate
|
|
6923
|
-
WHERE candidate.batch_id = ?
|
|
7026
|
+
WHERE trim(candidate.batch_id, ?) = ?
|
|
6924
7027
|
AND candidate.status IN ('failed', 'dead_letter')
|
|
6925
7028
|
${scopeFilter}
|
|
6926
7029
|
),
|
|
@@ -6974,16 +7077,28 @@ var init_task_service = __esm({
|
|
|
6974
7077
|
static async list(options = {}) {
|
|
6975
7078
|
let query = db.select().from(tasks2).$dynamic();
|
|
6976
7079
|
const conditions = [];
|
|
6977
|
-
if (options.
|
|
7080
|
+
if (options.activeExecution) {
|
|
7081
|
+
conditions.push(or(
|
|
7082
|
+
eq(tasks2.status, "running"),
|
|
7083
|
+
sql`EXISTS (
|
|
7084
|
+
SELECT 1 FROM task_runs AS active_list_run
|
|
7085
|
+
WHERE active_list_run.task_id = ${tasks2.id}
|
|
7086
|
+
AND active_list_run.status = 'running'
|
|
7087
|
+
)`
|
|
7088
|
+
));
|
|
7089
|
+
} else if (options.status) {
|
|
6978
7090
|
conditions.push(eq(tasks2.status, options.status));
|
|
6979
7091
|
}
|
|
6980
|
-
if (options.batchId) {
|
|
6981
|
-
|
|
7092
|
+
if (options.batchId !== void 0) {
|
|
7093
|
+
const batchId = normalizeTaskBatchId(options.batchId);
|
|
7094
|
+
conditions.push(batchId ? sql`trim(${tasks2.batchId}, ${TASK_BATCH_TRIM_CHARACTERS}) = ${batchId}` : sql`0`);
|
|
6982
7095
|
}
|
|
6983
7096
|
if (options.category) {
|
|
6984
7097
|
conditions.push(eq(tasks2.category, options.category));
|
|
6985
7098
|
}
|
|
6986
|
-
if (options.
|
|
7099
|
+
if (options.legacyCwd) {
|
|
7100
|
+
conditions.push(sql`${tasks2.cwd} IS NULL OR trim(${tasks2.cwd}) = ''`);
|
|
7101
|
+
} else if (options.cwd !== void 0) {
|
|
6987
7102
|
conditions.push(eq(tasks2.cwd, options.cwd));
|
|
6988
7103
|
}
|
|
6989
7104
|
if (conditions.length > 0) {
|
|
@@ -7001,9 +7116,12 @@ var init_task_service = __esm({
|
|
|
7001
7116
|
static async stats(options = {}) {
|
|
7002
7117
|
const conditions = [];
|
|
7003
7118
|
if (options.batchId !== void 0) {
|
|
7004
|
-
|
|
7119
|
+
const batchId = normalizeTaskBatchId(options.batchId);
|
|
7120
|
+
conditions.push(batchId ? sql`trim(${tasks2.batchId}, ${TASK_BATCH_TRIM_CHARACTERS}) = ${batchId}` : sql`0`);
|
|
7005
7121
|
}
|
|
7006
|
-
if (options.
|
|
7122
|
+
if (options.legacyCwd) {
|
|
7123
|
+
conditions.push(sql`${tasks2.cwd} IS NULL OR trim(${tasks2.cwd}) = ''`);
|
|
7124
|
+
} else if (options.cwd !== void 0) {
|
|
7007
7125
|
conditions.push(eq(tasks2.cwd, options.cwd));
|
|
7008
7126
|
}
|
|
7009
7127
|
const whereCondition = conditions.length > 0 ? and(...conditions) : void 0;
|
|
@@ -7028,6 +7146,33 @@ var init_task_service = __esm({
|
|
|
7028
7146
|
}
|
|
7029
7147
|
return stats;
|
|
7030
7148
|
}
|
|
7149
|
+
static async projectSummaries(limit = 100) {
|
|
7150
|
+
this.validateInteger("limit", limit, 1, 1e3);
|
|
7151
|
+
const lastCreatedAt = sql`max(${tasks2.createdAt})`;
|
|
7152
|
+
const lastTaskId = sql`max(${tasks2.id})`;
|
|
7153
|
+
const rows = await db.select({
|
|
7154
|
+
cwd: tasks2.cwd,
|
|
7155
|
+
total: sql`count(*)`,
|
|
7156
|
+
pending: sql`sum(CASE WHEN ${tasks2.status} = 'pending' THEN 1 ELSE 0 END)`,
|
|
7157
|
+
running: sql`sum(CASE WHEN ${tasks2.status} = 'running' OR EXISTS (
|
|
7158
|
+
SELECT 1 FROM task_runs AS active_project_run
|
|
7159
|
+
WHERE active_project_run.task_id = ${tasks2.id}
|
|
7160
|
+
AND active_project_run.status = 'running'
|
|
7161
|
+
) THEN 1 ELSE 0 END)`,
|
|
7162
|
+
failed: sql`sum(CASE WHEN ${tasks2.status} IN ('failed', 'dead_letter') THEN 1 ELSE 0 END)`,
|
|
7163
|
+
done: sql`sum(CASE WHEN ${tasks2.status} = 'done' THEN 1 ELSE 0 END)`,
|
|
7164
|
+
lastCreatedAt
|
|
7165
|
+
}).from(tasks2).where(sql`${tasks2.cwd} IS NOT NULL AND trim(${tasks2.cwd}) <> ''`).groupBy(tasks2.cwd).orderBy(desc(lastCreatedAt), desc(lastTaskId)).limit(limit);
|
|
7166
|
+
return rows.flatMap((row) => row.cwd === null ? [] : [{
|
|
7167
|
+
cwd: row.cwd,
|
|
7168
|
+
total: Number(row.total),
|
|
7169
|
+
pending: Number(row.pending),
|
|
7170
|
+
running: Number(row.running),
|
|
7171
|
+
failed: Number(row.failed),
|
|
7172
|
+
done: Number(row.done),
|
|
7173
|
+
lastCreatedAt: row.lastCreatedAt === null ? null : Number(row.lastCreatedAt) * 1e3
|
|
7174
|
+
}]);
|
|
7175
|
+
}
|
|
7031
7176
|
static async delete(id, scope = {}) {
|
|
7032
7177
|
const conditions = [
|
|
7033
7178
|
eq(tasks2.id, id),
|
|
@@ -7161,12 +7306,15 @@ var init_task_service = __esm({
|
|
|
7161
7306
|
function isLaunchIdentity(value) {
|
|
7162
7307
|
return value != null && /^gateway-[1-9]\d*:launch:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
|
|
7163
7308
|
}
|
|
7309
|
+
function drainProofAckForIdentity(launchIdentity) {
|
|
7310
|
+
return { type: DRAIN_PROOF_ACK_MESSAGE_TYPE, identity: launchIdentity };
|
|
7311
|
+
}
|
|
7164
7312
|
function isMatchingDrainProof(message, launchIdentity) {
|
|
7165
7313
|
if (typeof message !== "object" || message == null) return false;
|
|
7166
7314
|
const candidate = message;
|
|
7167
7315
|
return candidate.type === DRAIN_PROOF_MESSAGE_TYPE && candidate.identity === launchIdentity;
|
|
7168
7316
|
}
|
|
7169
|
-
var LEGACY_GUARDIAN_LAUNCH_PROTOCOL, TOKEN_GUARDIAN_LAUNCH_PROTOCOL, LAUNCH_IDENTITY_ARGUMENT, DRAIN_PROOF_MESSAGE_TYPE, MANAGED_RUN_ENV, MANAGED_RUN_ENV_VALUE;
|
|
7317
|
+
var LEGACY_GUARDIAN_LAUNCH_PROTOCOL, TOKEN_GUARDIAN_LAUNCH_PROTOCOL, LAUNCH_IDENTITY_ARGUMENT, DRAIN_PROOF_MESSAGE_TYPE, DRAIN_PROOF_ACK_MESSAGE_TYPE, MANAGED_RUN_ENV, MANAGED_RUN_ENV_VALUE;
|
|
7170
7318
|
var init_launch_protocol = __esm({
|
|
7171
7319
|
"src/core/launch-protocol.ts"() {
|
|
7172
7320
|
"use strict";
|
|
@@ -7174,6 +7322,7 @@ var init_launch_protocol = __esm({
|
|
|
7174
7322
|
TOKEN_GUARDIAN_LAUNCH_PROTOCOL = "gated-v3-token-guardian";
|
|
7175
7323
|
LAUNCH_IDENTITY_ARGUMENT = "--supertask-launch-identity";
|
|
7176
7324
|
DRAIN_PROOF_MESSAGE_TYPE = "supertask-drained";
|
|
7325
|
+
DRAIN_PROOF_ACK_MESSAGE_TYPE = "supertask-drained-ack";
|
|
7177
7326
|
MANAGED_RUN_ENV = "SUPERTASK_MANAGED_RUN";
|
|
7178
7327
|
MANAGED_RUN_ENV_VALUE = "1";
|
|
7179
7328
|
}
|
|
@@ -16760,8 +16909,8 @@ var require_CronFileParser = __commonJS({
|
|
|
16760
16909
|
* @throws If file cannot be read
|
|
16761
16910
|
*/
|
|
16762
16911
|
static parseFileSync(filePath) {
|
|
16763
|
-
const { readFileSync:
|
|
16764
|
-
const data =
|
|
16912
|
+
const { readFileSync: readFileSync5 } = __require("fs");
|
|
16913
|
+
const data = readFileSync5(filePath, "utf8");
|
|
16765
16914
|
return _CronFileParser.#parseContent(data);
|
|
16766
16915
|
}
|
|
16767
16916
|
/**
|
|
@@ -16894,27 +17043,34 @@ var init_task_template_service = __esm({
|
|
|
16894
17043
|
init_db2();
|
|
16895
17044
|
init_drizzle_orm();
|
|
16896
17045
|
init_cron_parser();
|
|
17046
|
+
init_task_working_directory();
|
|
17047
|
+
init_task_batch();
|
|
16897
17048
|
({ taskTemplates: taskTemplates2 } = schema_exports);
|
|
16898
17049
|
TaskTemplateService = class {
|
|
16899
17050
|
static async create(data) {
|
|
16900
|
-
|
|
17051
|
+
const normalizedData = {
|
|
17052
|
+
...data,
|
|
17053
|
+
batchId: normalizeTaskBatchId(data.batchId)
|
|
17054
|
+
};
|
|
17055
|
+
this.validate(normalizedData);
|
|
16901
17056
|
const now = Date.now();
|
|
16902
|
-
const nextRunAt =
|
|
16903
|
-
|
|
17057
|
+
const nextRunAt = normalizedData.nextRunAt ?? this.calculateNextRunAt(
|
|
17058
|
+
normalizedData.scheduleType,
|
|
16904
17059
|
{
|
|
16905
|
-
cronExpr:
|
|
16906
|
-
intervalMs:
|
|
16907
|
-
runAt:
|
|
17060
|
+
cronExpr: normalizedData.cronExpr ?? null,
|
|
17061
|
+
intervalMs: normalizedData.intervalMs ?? null,
|
|
17062
|
+
runAt: normalizedData.runAt ?? null
|
|
16908
17063
|
},
|
|
16909
17064
|
now
|
|
16910
17065
|
);
|
|
16911
|
-
const result = await db.insert(taskTemplates2).values({ ...
|
|
17066
|
+
const result = await db.insert(taskTemplates2).values({ ...normalizedData, nextRunAt, createdAt: now, updatedAt: now }).returning();
|
|
16912
17067
|
return result[0];
|
|
16913
17068
|
}
|
|
16914
17069
|
static validate(data) {
|
|
16915
17070
|
if (!data.name.trim()) throw new Error("name \u4E0D\u80FD\u4E3A\u7A7A");
|
|
16916
17071
|
if (!data.agent.trim()) throw new Error("agent \u4E0D\u80FD\u4E3A\u7A7A");
|
|
16917
17072
|
if (!data.prompt.trim()) throw new Error("prompt \u4E0D\u80FD\u4E3A\u7A7A");
|
|
17073
|
+
validateTaskWorkingDirectory(data.cwd);
|
|
16918
17074
|
const scheduleType = data.scheduleType;
|
|
16919
17075
|
if (!["cron", "delayed", "recurring"].includes(scheduleType)) {
|
|
16920
17076
|
throw new Error("scheduleType \u5FC5\u987B\u662F cron\u3001delayed \u6216 recurring");
|
|
@@ -16941,13 +17097,40 @@ var init_task_template_service = __esm({
|
|
|
16941
17097
|
throw new Error(`${name} \u5FC5\u987B\u662F ${min} \u5230 ${max} \u4E4B\u95F4\u7684\u6574\u6570`);
|
|
16942
17098
|
}
|
|
16943
17099
|
}
|
|
16944
|
-
static async list(limit = 50) {
|
|
16945
|
-
return await db.select().from(taskTemplates2).orderBy(desc(taskTemplates2.createdAt), desc(taskTemplates2.id)).limit(limit);
|
|
17100
|
+
static async list(limit = 50, offset = 0) {
|
|
17101
|
+
return await db.select().from(taskTemplates2).orderBy(desc(taskTemplates2.createdAt), desc(taskTemplates2.id)).limit(limit).offset(offset);
|
|
17102
|
+
}
|
|
17103
|
+
static async stats() {
|
|
17104
|
+
const result = await db.select({
|
|
17105
|
+
total: sql`count(*)`,
|
|
17106
|
+
enabled: sql`sum(case when ${taskTemplates2.enabled} = 1 then 1 else 0 end)`
|
|
17107
|
+
}).from(taskTemplates2);
|
|
17108
|
+
const total = Number(result[0]?.total ?? 0);
|
|
17109
|
+
const enabled = Number(result[0]?.enabled ?? 0);
|
|
17110
|
+
return { total, enabled, disabled: total - enabled };
|
|
16946
17111
|
}
|
|
16947
17112
|
static async getById(id) {
|
|
16948
17113
|
const result = await db.select().from(taskTemplates2).where(eq(taskTemplates2.id, id));
|
|
16949
17114
|
return result[0] || null;
|
|
16950
17115
|
}
|
|
17116
|
+
static async update(id, data) {
|
|
17117
|
+
const normalizedData = {
|
|
17118
|
+
...data,
|
|
17119
|
+
batchId: normalizeTaskBatchId(data.batchId) ?? null
|
|
17120
|
+
};
|
|
17121
|
+
this.validate(normalizedData);
|
|
17122
|
+
const now = Date.now();
|
|
17123
|
+
const nextRunAt = this.calculateNextRunAt(
|
|
17124
|
+
normalizedData.scheduleType,
|
|
17125
|
+
normalizedData,
|
|
17126
|
+
now
|
|
17127
|
+
);
|
|
17128
|
+
return db.transaction((tx) => {
|
|
17129
|
+
const existing = tx.select({ id: taskTemplates2.id }).from(taskTemplates2).where(eq(taskTemplates2.id, id)).limit(1).get();
|
|
17130
|
+
if (!existing) return null;
|
|
17131
|
+
return tx.update(taskTemplates2).set({ ...normalizedData, nextRunAt, updatedAt: now }).where(eq(taskTemplates2.id, id)).returning().get() ?? null;
|
|
17132
|
+
}, { behavior: "immediate" });
|
|
17133
|
+
}
|
|
16951
17134
|
static async enable(id) {
|
|
16952
17135
|
return db.transaction((tx) => {
|
|
16953
17136
|
const template = tx.select().from(taskTemplates2).where(eq(taskTemplates2.id, id)).limit(1).get();
|
|
@@ -17034,8 +17217,11 @@ var init_task_template_service = __esm({
|
|
|
17034
17217
|
});
|
|
17035
17218
|
|
|
17036
17219
|
// src/gateway/scheduler/job-templates.ts
|
|
17037
|
-
async function cloneTaskFromTemplate(templateId) {
|
|
17038
|
-
return createTaskFromTemplate(templateId, {
|
|
17220
|
+
async function cloneTaskFromTemplate(templateId, expectedNextRunAt) {
|
|
17221
|
+
return createTaskFromTemplate(templateId, {
|
|
17222
|
+
advanceSchedule: true,
|
|
17223
|
+
expectedNextRunAt
|
|
17224
|
+
});
|
|
17039
17225
|
}
|
|
17040
17226
|
async function triggerTaskFromTemplate(templateId) {
|
|
17041
17227
|
return createTaskFromTemplate(templateId, {
|
|
@@ -17048,37 +17234,40 @@ function createTaskFromTemplate(templateId, options) {
|
|
|
17048
17234
|
return db.transaction((tx) => {
|
|
17049
17235
|
const tmpl = tx.select().from(taskTemplates3).where(eq(taskTemplates3.id, templateId)).limit(1).get();
|
|
17050
17236
|
if (!tmpl || options.advanceSchedule && !tmpl.enabled) return null;
|
|
17051
|
-
|
|
17052
|
-
|
|
17053
|
-
sql
|
|
17054
|
-
|
|
17055
|
-
|
|
17056
|
-
|
|
17057
|
-
|
|
17058
|
-
|
|
17059
|
-
|
|
17060
|
-
|
|
17061
|
-
|
|
17062
|
-
eq(schema_exports.tasks.
|
|
17063
|
-
|
|
17064
|
-
|
|
17065
|
-
|
|
17066
|
-
${schema_exports.tasks.status}
|
|
17067
|
-
|
|
17068
|
-
|
|
17069
|
-
|
|
17070
|
-
|
|
17071
|
-
|
|
17072
|
-
|
|
17073
|
-
|
|
17074
|
-
|
|
17075
|
-
|
|
17076
|
-
|
|
17077
|
-
|
|
17078
|
-
|
|
17079
|
-
|
|
17237
|
+
if (options.advanceSchedule && options.expectedNextRunAt !== void 0 && tmpl.nextRunAt !== options.expectedNextRunAt) return null;
|
|
17238
|
+
if (options.advanceSchedule) {
|
|
17239
|
+
const activeTasks = tx.select({ count: sql`count(*)` }).from(schema_exports.tasks).where(and(
|
|
17240
|
+
eq(schema_exports.tasks.templateId, templateId),
|
|
17241
|
+
sql`${schema_exports.tasks.status} IN ('pending', 'running')`
|
|
17242
|
+
)).get();
|
|
17243
|
+
const retryableTasks = tx.select({ count: sql`count(*)` }).from(schema_exports.tasks).where(and(
|
|
17244
|
+
eq(schema_exports.tasks.templateId, templateId),
|
|
17245
|
+
eq(schema_exports.tasks.status, "failed"),
|
|
17246
|
+
sql`${schema_exports.tasks.retryCount} <= ${schema_exports.tasks.maxRetries}`
|
|
17247
|
+
)).get();
|
|
17248
|
+
const detachedRuns = tx.select({ count: sql`count(DISTINCT ${schema_exports.taskRuns.taskId})` }).from(schema_exports.taskRuns).innerJoin(schema_exports.tasks, eq(schema_exports.tasks.id, schema_exports.taskRuns.taskId)).where(and(
|
|
17249
|
+
eq(schema_exports.taskRuns.status, "running"),
|
|
17250
|
+
eq(schema_exports.tasks.templateId, templateId),
|
|
17251
|
+
sql`NOT (
|
|
17252
|
+
${schema_exports.tasks.status} IN ('pending', 'running')
|
|
17253
|
+
OR (
|
|
17254
|
+
${schema_exports.tasks.status} = 'failed'
|
|
17255
|
+
AND ${schema_exports.tasks.retryCount} <= ${schema_exports.tasks.maxRetries}
|
|
17256
|
+
)
|
|
17257
|
+
)`
|
|
17258
|
+
)).get();
|
|
17259
|
+
const activeCount = Number(activeTasks?.count ?? 0) + Number(retryableTasks?.count ?? 0) + Number(detachedRuns?.count ?? 0);
|
|
17260
|
+
if (activeCount >= (tmpl.maxInstances ?? 1)) {
|
|
17261
|
+
if (tmpl.scheduleType !== "delayed") {
|
|
17262
|
+
const nextRunAt2 = TaskTemplateService.calculateNextRunAt(
|
|
17263
|
+
tmpl.scheduleType,
|
|
17264
|
+
tmpl,
|
|
17265
|
+
nowMs
|
|
17266
|
+
);
|
|
17267
|
+
tx.update(taskTemplates3).set({ nextRunAt: nextRunAt2, updatedAt: nowMs }).where(and(eq(taskTemplates3.id, templateId), eq(taskTemplates3.enabled, true))).run();
|
|
17268
|
+
}
|
|
17269
|
+
return null;
|
|
17080
17270
|
}
|
|
17081
|
-
return null;
|
|
17082
17271
|
}
|
|
17083
17272
|
const isDelayed = tmpl.scheduleType === "delayed";
|
|
17084
17273
|
const nextRunAt = isDelayed ? null : TaskTemplateService.calculateNextRunAt(
|
|
@@ -17095,7 +17284,7 @@ function createTaskFromTemplate(templateId, options) {
|
|
|
17095
17284
|
category: tmpl.category ?? "general",
|
|
17096
17285
|
importance: tmpl.importance ?? 3,
|
|
17097
17286
|
urgency: tmpl.urgency ?? 3,
|
|
17098
|
-
batchId: tmpl.batchId,
|
|
17287
|
+
batchId: normalizeTaskBatchId(tmpl.batchId),
|
|
17099
17288
|
maxRetries: tmpl.maxRetries ?? 3,
|
|
17100
17289
|
retryBackoffMs: tmpl.retryBackoffMs ?? 3e4,
|
|
17101
17290
|
timeoutMs: tmpl.timeoutMs,
|
|
@@ -17156,11 +17345,38 @@ var init_job_templates = __esm({
|
|
|
17156
17345
|
init_db2();
|
|
17157
17346
|
init_drizzle_orm();
|
|
17158
17347
|
init_task_template_service();
|
|
17348
|
+
init_task_batch();
|
|
17159
17349
|
({ taskTemplates: taskTemplates3 } = schema_exports);
|
|
17160
17350
|
DUE_TEMPLATE_BATCH_SIZE = 100;
|
|
17161
17351
|
}
|
|
17162
17352
|
});
|
|
17163
17353
|
|
|
17354
|
+
// src/core/package-version.ts
|
|
17355
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
17356
|
+
import { dirname as dirname4, join as join4 } from "path";
|
|
17357
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
17358
|
+
function getPackageVersion() {
|
|
17359
|
+
let directory = dirname4(fileURLToPath4(import.meta.url));
|
|
17360
|
+
for (let depth = 0; depth < 5; depth += 1) {
|
|
17361
|
+
const packagePath = join4(directory, "package.json");
|
|
17362
|
+
if (existsSync4(packagePath)) {
|
|
17363
|
+
try {
|
|
17364
|
+
const pkg = JSON.parse(readFileSync2(packagePath, "utf8"));
|
|
17365
|
+
if (typeof pkg.version === "string") return pkg.version;
|
|
17366
|
+
} catch {
|
|
17367
|
+
return "0.0.0";
|
|
17368
|
+
}
|
|
17369
|
+
}
|
|
17370
|
+
directory = dirname4(directory);
|
|
17371
|
+
}
|
|
17372
|
+
return "0.0.0";
|
|
17373
|
+
}
|
|
17374
|
+
var init_package_version = __esm({
|
|
17375
|
+
"src/core/package-version.ts"() {
|
|
17376
|
+
"use strict";
|
|
17377
|
+
}
|
|
17378
|
+
});
|
|
17379
|
+
|
|
17164
17380
|
// node_modules/hono/dist/compose.js
|
|
17165
17381
|
var compose;
|
|
17166
17382
|
var init_compose = __esm({
|
|
@@ -19377,6 +19593,41 @@ var init_dist = __esm({
|
|
|
19377
19593
|
}
|
|
19378
19594
|
});
|
|
19379
19595
|
|
|
19596
|
+
// src/core/duration.ts
|
|
19597
|
+
function parseDuration(input) {
|
|
19598
|
+
const trimmed = input.trim();
|
|
19599
|
+
const simple = DURATION_REGEX.exec(trimmed);
|
|
19600
|
+
if (simple) {
|
|
19601
|
+
const value = parseFloat(simple[1]);
|
|
19602
|
+
const unit = simple[2].toLowerCase();
|
|
19603
|
+
if (unit === "ms") return value;
|
|
19604
|
+
if (unit === "s" || unit === "sec" || unit === "second" || unit === "seconds") return value * 1e3;
|
|
19605
|
+
if (unit === "min" || unit === "minute" || unit === "minutes" || unit === "m") return value * 6e4;
|
|
19606
|
+
if (unit === "h" || unit === "hour" || unit === "hours") return value * 36e5;
|
|
19607
|
+
if (unit === "d" || unit === "day" || unit === "days") return value * 864e5;
|
|
19608
|
+
if (unit === "w" || unit === "week" || unit === "weeks") return value * 6048e5;
|
|
19609
|
+
}
|
|
19610
|
+
const iso = ISO8601_REGEX.exec(trimmed);
|
|
19611
|
+
if (iso) {
|
|
19612
|
+
const days = parseFloat(iso[1] ?? "0");
|
|
19613
|
+
const hours = parseFloat(iso[2] ?? "0");
|
|
19614
|
+
const minutes = parseFloat(iso[3] ?? "0");
|
|
19615
|
+
const seconds = parseFloat(iso[4] ?? "0");
|
|
19616
|
+
return (days * 86400 + hours * 3600 + minutes * 60 + seconds) * 1e3;
|
|
19617
|
+
}
|
|
19618
|
+
const asNumber = Number(trimmed);
|
|
19619
|
+
if (!isNaN(asNumber) && asNumber > 0) return asNumber;
|
|
19620
|
+
return null;
|
|
19621
|
+
}
|
|
19622
|
+
var DURATION_REGEX, ISO8601_REGEX;
|
|
19623
|
+
var init_duration = __esm({
|
|
19624
|
+
"src/core/duration.ts"() {
|
|
19625
|
+
"use strict";
|
|
19626
|
+
DURATION_REGEX = /^(\d+(?:\.\d+)?)\s*(ms|s|sec|seconds?|min|minutes?|m|h|hours?|d|days?|w|weeks?)$/i;
|
|
19627
|
+
ISO8601_REGEX = /^P(?:([.\d]+)D)?(?:T(?:([.\d]+)H)?(?:([.\d]+)M)?(?:([.\d]+)S)?)?$/i;
|
|
19628
|
+
}
|
|
19629
|
+
});
|
|
19630
|
+
|
|
19380
19631
|
// src/core/services/database-maintenance.service.ts
|
|
19381
19632
|
import { Database as Database3 } from "bun:sqlite";
|
|
19382
19633
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -19385,7 +19636,7 @@ import {
|
|
|
19385
19636
|
existsSync as existsSync5,
|
|
19386
19637
|
mkdirSync as mkdirSync2,
|
|
19387
19638
|
renameSync,
|
|
19388
|
-
statSync,
|
|
19639
|
+
statSync as statSync2,
|
|
19389
19640
|
unlinkSync,
|
|
19390
19641
|
writeFileSync
|
|
19391
19642
|
} from "fs";
|
|
@@ -19487,9 +19738,9 @@ var init_database_maintenance_service = __esm({
|
|
|
19487
19738
|
if (!existsSync5(source)) {
|
|
19488
19739
|
throw new Error(`\u5907\u4EFD\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${source}`);
|
|
19489
19740
|
}
|
|
19490
|
-
const sourceStat =
|
|
19741
|
+
const sourceStat = statSync2(source);
|
|
19491
19742
|
if (!sourceStat.isFile()) throw new Error(`\u5907\u4EFD\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6\uFF1A${source}`);
|
|
19492
|
-
const liveStat =
|
|
19743
|
+
const liveStat = statSync2(livePath);
|
|
19493
19744
|
if (sourceStat.dev === liveStat.dev && sourceStat.ino === liveStat.ino) {
|
|
19494
19745
|
throw new Error("\u6062\u590D\u6765\u6E90\u4E0D\u80FD\u662F\u5F53\u524D\u6570\u636E\u5E93\u7684\u7B26\u53F7\u94FE\u63A5\u6216\u786C\u94FE\u63A5\u522B\u540D");
|
|
19495
19746
|
}
|
|
@@ -19730,7 +19981,7 @@ var init_database_maintenance_service = __esm({
|
|
|
19730
19981
|
const counts = missingTables.length === 0 ? this.readCounts(sqlite2) : { tasks: 0, taskRuns: 0, taskTemplates: 0 };
|
|
19731
19982
|
const runningTasks = missingTables.includes("tasks") ? 0 : this.scalar(sqlite2, "SELECT COUNT(*) AS count FROM tasks WHERE status = 'running'");
|
|
19732
19983
|
const runningRuns = missingTables.includes("task_runs") ? 0 : this.scalar(sqlite2, "SELECT COUNT(*) AS count FROM task_runs WHERE status = 'running'");
|
|
19733
|
-
const sizeBytes = path === ":memory:" || !existsSync5(path) ? sqlite2.serialize().byteLength :
|
|
19984
|
+
const sizeBytes = path === ":memory:" || !existsSync5(path) ? sqlite2.serialize().byteLength : statSync2(path).size;
|
|
19734
19985
|
return {
|
|
19735
19986
|
ok: integrityMessages.length === 1 && integrityMessages[0] === "ok" && foreignKeyViolations === 0 && missingTables.length === 0,
|
|
19736
19987
|
path: normalizedPath(path),
|
|
@@ -19767,7 +20018,7 @@ var init_database_maintenance_service = __esm({
|
|
|
19767
20018
|
const check = this.inspectFile(temporary);
|
|
19768
20019
|
if (!check.ok) throw new Error("\u65B0\u5907\u4EFD\u672A\u901A\u8FC7\u5B8C\u6574\u6027\u6821\u9A8C");
|
|
19769
20020
|
renameSync(temporary, output);
|
|
19770
|
-
const finalCheck = { ...check, path: output, sizeBytes:
|
|
20021
|
+
const finalCheck = { ...check, path: output, sizeBytes: statSync2(output).size };
|
|
19771
20022
|
return { path: output, sizeBytes: finalCheck.sizeBytes, check: finalCheck };
|
|
19772
20023
|
} catch (error) {
|
|
19773
20024
|
safeUnlink(temporary);
|
|
@@ -19813,6 +20064,445 @@ var init_database_maintenance_service = __esm({
|
|
|
19813
20064
|
}
|
|
19814
20065
|
});
|
|
19815
20066
|
|
|
20067
|
+
// src/daemon/management-lock.ts
|
|
20068
|
+
import { Database as Database4 } from "bun:sqlite";
|
|
20069
|
+
var init_management_lock = __esm({
|
|
20070
|
+
"src/daemon/management-lock.ts"() {
|
|
20071
|
+
"use strict";
|
|
20072
|
+
}
|
|
20073
|
+
});
|
|
20074
|
+
|
|
20075
|
+
// src/daemon/pm2.ts
|
|
20076
|
+
import { execSync, spawnSync as spawnSync2 } from "child_process";
|
|
20077
|
+
import {
|
|
20078
|
+
accessSync,
|
|
20079
|
+
chmodSync as chmodSync2,
|
|
20080
|
+
constants,
|
|
20081
|
+
existsSync as existsSync6,
|
|
20082
|
+
mkdirSync as mkdirSync3,
|
|
20083
|
+
readFileSync as readFileSync3,
|
|
20084
|
+
rmSync,
|
|
20085
|
+
statSync as statSync3,
|
|
20086
|
+
writeFileSync as writeFileSync2
|
|
20087
|
+
} from "fs";
|
|
20088
|
+
import { homedir as homedir3, userInfo } from "os";
|
|
20089
|
+
import { delimiter, dirname as dirname6, isAbsolute as isAbsolute2, join as join5, resolve as resolve3 } from "path";
|
|
20090
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
20091
|
+
import { Database as Database5 } from "bun:sqlite";
|
|
20092
|
+
function runtimeHome(env) {
|
|
20093
|
+
return resolve3(env.HOME || homedir3());
|
|
20094
|
+
}
|
|
20095
|
+
function runtimePath(value, cwd) {
|
|
20096
|
+
return resolve3(cwd, value);
|
|
20097
|
+
}
|
|
20098
|
+
function versionFile(env = process.env, cwd = process.cwd()) {
|
|
20099
|
+
return env.SUPERTASK_VERSION_FILE ? runtimePath(env.SUPERTASK_VERSION_FILE, cwd) : join5(runtimeHome(env), ".local/share/opencode/supertask-gateway-version");
|
|
20100
|
+
}
|
|
20101
|
+
function getRunningVersion(env = process.env, cwd = process.cwd()) {
|
|
20102
|
+
try {
|
|
20103
|
+
const path = versionFile(env, cwd);
|
|
20104
|
+
if (!existsSync6(path)) return null;
|
|
20105
|
+
return readFileSync3(path, "utf-8").trim() || null;
|
|
20106
|
+
} catch {
|
|
20107
|
+
return null;
|
|
20108
|
+
}
|
|
20109
|
+
}
|
|
20110
|
+
function packageVersionFromGatewayEntry(gatewayEntry) {
|
|
20111
|
+
if (gatewayEntry === null) return null;
|
|
20112
|
+
let directory = dirname6(gatewayEntry);
|
|
20113
|
+
for (let depth = 0; depth < 6; depth += 1) {
|
|
20114
|
+
const packagePath = join5(directory, "package.json");
|
|
20115
|
+
if (existsSync6(packagePath)) {
|
|
20116
|
+
try {
|
|
20117
|
+
const pkg = JSON.parse(readFileSync3(packagePath, "utf8"));
|
|
20118
|
+
if (pkg.name === "opencode-supertask" && typeof pkg.version === "string") {
|
|
20119
|
+
return pkg.version;
|
|
20120
|
+
}
|
|
20121
|
+
} catch {
|
|
20122
|
+
}
|
|
20123
|
+
}
|
|
20124
|
+
const parent = dirname6(directory);
|
|
20125
|
+
if (parent === directory) break;
|
|
20126
|
+
directory = parent;
|
|
20127
|
+
}
|
|
20128
|
+
return null;
|
|
20129
|
+
}
|
|
20130
|
+
function resolveRuntimeExecutable(command, env, cwd) {
|
|
20131
|
+
if (isAbsolute2(command) || command.includes("/")) return runtimePath(command, cwd);
|
|
20132
|
+
for (const entry of (env.PATH ?? "").split(delimiter)) {
|
|
20133
|
+
if (!entry) continue;
|
|
20134
|
+
const candidate = resolve3(cwd, entry, command);
|
|
20135
|
+
try {
|
|
20136
|
+
accessSync(candidate, constants.X_OK);
|
|
20137
|
+
return candidate;
|
|
20138
|
+
} catch {
|
|
20139
|
+
}
|
|
20140
|
+
}
|
|
20141
|
+
return command;
|
|
20142
|
+
}
|
|
20143
|
+
function runtimeScope(runtime) {
|
|
20144
|
+
const { cwd, env } = runtime;
|
|
20145
|
+
const home = runtimeHome(env);
|
|
20146
|
+
return {
|
|
20147
|
+
cwd: resolve3(cwd),
|
|
20148
|
+
databasePath: env.SUPERTASK_DB_PATH ? runtimePath(env.SUPERTASK_DB_PATH, cwd) : join5(home, ".local/share/opencode/tasks.db"),
|
|
20149
|
+
configPath: env.SUPERTASK_CONFIG_PATH ? runtimePath(env.SUPERTASK_CONFIG_PATH, cwd) : join5(home, ".config/opencode/supertask.json"),
|
|
20150
|
+
opencodePath: resolveRuntimeExecutable(env.SUPERTASK_OPENCODE_BIN ?? "opencode", env, cwd),
|
|
20151
|
+
home,
|
|
20152
|
+
pm2Home: env.PM2_HOME ? runtimePath(env.PM2_HOME, cwd) : join5(home, ".pm2"),
|
|
20153
|
+
managementLockPath: canonicalManagementLockPath(env, cwd)
|
|
20154
|
+
};
|
|
20155
|
+
}
|
|
20156
|
+
function scopesMatch(left, right) {
|
|
20157
|
+
return left.databasePath === right.databasePath && left.configPath === right.configPath && left.opencodePath === right.opencodePath && left.home === right.home && left.pm2Home === right.pm2Home && left.managementLockPath === right.managementLockPath;
|
|
20158
|
+
}
|
|
20159
|
+
function currentScope() {
|
|
20160
|
+
return runtimeScope({ cwd: process.cwd(), env: process.env });
|
|
20161
|
+
}
|
|
20162
|
+
function getGatewayDiagnostic() {
|
|
20163
|
+
const producerScope = currentScope();
|
|
20164
|
+
if (!isPm2Installed()) {
|
|
20165
|
+
return {
|
|
20166
|
+
pm2Installed: false,
|
|
20167
|
+
processFound: false,
|
|
20168
|
+
status: null,
|
|
20169
|
+
pid: null,
|
|
20170
|
+
ready: false,
|
|
20171
|
+
runningVersion: getRunningVersion(),
|
|
20172
|
+
gatewayEntry: null,
|
|
20173
|
+
gatewayPackageVersion: null,
|
|
20174
|
+
logRotationInstalled: false,
|
|
20175
|
+
startupConfigured: process.platform === "darwin" || process.platform === "linux" ? false : null,
|
|
20176
|
+
currentScope: producerScope,
|
|
20177
|
+
gatewayScope: null,
|
|
20178
|
+
scopeMatches: false
|
|
20179
|
+
};
|
|
20180
|
+
}
|
|
20181
|
+
const processes = pm2JsonList();
|
|
20182
|
+
const gateway = processes.find((item) => item.name === PROCESS_NAME);
|
|
20183
|
+
const runtime = gatewayRuntimeFromProcess(gateway);
|
|
20184
|
+
const gatewayEnv = runtime?.env ?? process.env;
|
|
20185
|
+
const managedScope = runtime ? runtimeScope(runtime) : null;
|
|
20186
|
+
const pid = typeof gateway?.pid === "number" ? gateway.pid : null;
|
|
20187
|
+
const readyPath = managedScope?.databasePath ?? databasePath();
|
|
20188
|
+
const lockedVersion = pid == null ? void 0 : gatewayVersionFromLock(pid, readyPath);
|
|
20189
|
+
const gatewayEntry = runtime?.gatewayEntry ?? null;
|
|
20190
|
+
return {
|
|
20191
|
+
pm2Installed: true,
|
|
20192
|
+
processFound: gateway != null,
|
|
20193
|
+
status: gateway?.pm2_env?.status ?? null,
|
|
20194
|
+
pid,
|
|
20195
|
+
ready: pid != null && isGatewayReady(pid, readyPath),
|
|
20196
|
+
runningVersion: lockedVersion === void 0 ? getRunningVersion(gatewayEnv, runtime?.cwd) : lockedVersion,
|
|
20197
|
+
gatewayEntry,
|
|
20198
|
+
gatewayPackageVersion: packageVersionFromGatewayEntry(gatewayEntry),
|
|
20199
|
+
logRotationInstalled: processes.some((item) => item.name === "pm2-logrotate" && item.pm2_env?.status === "online"),
|
|
20200
|
+
startupConfigured: process.platform === "darwin" ? isMacLaunchAgentConfigured(
|
|
20201
|
+
gatewayEnv.PM2_HOME ?? join5(runtimeHome(gatewayEnv), ".pm2"),
|
|
20202
|
+
runtime ?? void 0
|
|
20203
|
+
) : process.platform === "linux" ? isLinuxStartupConfigured(gatewayEnv, runtime?.cwd, runtime ?? void 0) : null,
|
|
20204
|
+
currentScope: producerScope,
|
|
20205
|
+
gatewayScope: managedScope,
|
|
20206
|
+
scopeMatches: managedScope != null && scopesMatch(producerScope, managedScope)
|
|
20207
|
+
};
|
|
20208
|
+
}
|
|
20209
|
+
function pm2Bin(env = process.env) {
|
|
20210
|
+
return env.SUPERTASK_PM2_BIN ?? (process.platform === "win32" ? "pm2.cmd" : "pm2");
|
|
20211
|
+
}
|
|
20212
|
+
function pm2CommandTimeoutMs(env = process.env) {
|
|
20213
|
+
const configured = Number(env.SUPERTASK_PM2_COMMAND_TIMEOUT_MS ?? 15e3);
|
|
20214
|
+
return Number.isFinite(configured) && configured > 0 ? configured : 15e3;
|
|
20215
|
+
}
|
|
20216
|
+
function resolvePm2Bin() {
|
|
20217
|
+
const configured = pm2Bin();
|
|
20218
|
+
if (isAbsolute2(configured)) return configured;
|
|
20219
|
+
const result = spawnSync2("which", [configured], { encoding: "utf8" });
|
|
20220
|
+
const resolved = result.status === 0 ? result.stdout.trim().split("\n")[0] : "";
|
|
20221
|
+
if (!resolved) throw new Error(`[supertask] \u65E0\u6CD5\u89E3\u6790 pm2 \u53EF\u6267\u884C\u6587\u4EF6: ${configured}`);
|
|
20222
|
+
return resolved;
|
|
20223
|
+
}
|
|
20224
|
+
function launchAgentPath() {
|
|
20225
|
+
return process.env.SUPERTASK_LAUNCH_AGENT_PATH ?? join5(runtimeHome(process.env), "Library/LaunchAgents", `${MAC_LAUNCH_AGENT_LABEL}.plist`);
|
|
20226
|
+
}
|
|
20227
|
+
function launchctlBin() {
|
|
20228
|
+
return process.env.SUPERTASK_LAUNCHCTL_BIN ?? "launchctl";
|
|
20229
|
+
}
|
|
20230
|
+
function xmlUnescape(value) {
|
|
20231
|
+
return value.replaceAll("'", "'").replaceAll(""", '"').replaceAll(">", ">").replaceAll("<", "<").replaceAll("&", "&");
|
|
20232
|
+
}
|
|
20233
|
+
function plistValue(contents, key) {
|
|
20234
|
+
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
20235
|
+
const match2 = contents.match(new RegExp(`<key>\\s*${escapedKey}\\s*</key>\\s*<string>([^<]*)</string>`));
|
|
20236
|
+
return match2?.[1] ? xmlUnescape(match2[1]) : null;
|
|
20237
|
+
}
|
|
20238
|
+
function isMacLaunchAgentConfigured(expectedPm2Home, expectedRuntime) {
|
|
20239
|
+
if (typeof process.getuid !== "function") return false;
|
|
20240
|
+
const plistPath = launchAgentPath();
|
|
20241
|
+
if (!existsSync6(plistPath)) return false;
|
|
20242
|
+
try {
|
|
20243
|
+
const contents = readFileSync3(plistPath, "utf8");
|
|
20244
|
+
const argumentsBlock = contents.match(
|
|
20245
|
+
/<key>\s*ProgramArguments\s*<\/key>\s*<array>([\s\S]*?)<\/array>/
|
|
20246
|
+
)?.[1];
|
|
20247
|
+
const programArguments = argumentsBlock ? [...argumentsBlock.matchAll(/<string>([^<]*)<\/string>/g)].map((match2) => xmlUnescape(match2[1])) : [];
|
|
20248
|
+
const bunPath = programArguments[0];
|
|
20249
|
+
const supervisorEntry = programArguments[1];
|
|
20250
|
+
const pm2Path = programArguments[2];
|
|
20251
|
+
const pm2Home = plistValue(contents, "PM2_HOME");
|
|
20252
|
+
const managementLock = plistValue(contents, "SUPERTASK_PM2_MANAGEMENT_LOCK");
|
|
20253
|
+
if (!bunPath || !supervisorEntry || !pm2Path || !pm2Home || !managementLock) return false;
|
|
20254
|
+
if (expectedPm2Home && pm2Home !== expectedPm2Home) return false;
|
|
20255
|
+
const expectedManagementLock = expectedRuntime ? managementLockPath(expectedRuntime.env, expectedRuntime.cwd) : process.env.SUPERTASK_PM2_MANAGEMENT_LOCK ? managementLockPath() : join5(expectedPm2Home ?? currentScope().pm2Home, "supertask-gateway.manage.sqlite");
|
|
20256
|
+
if (managementLock !== expectedManagementLock) return false;
|
|
20257
|
+
accessSync(bunPath, constants.X_OK);
|
|
20258
|
+
accessSync(supervisorEntry, constants.R_OK);
|
|
20259
|
+
accessSync(pm2Path, constants.X_OK);
|
|
20260
|
+
if (spawnSync2(bunPath, ["--version"], {
|
|
20261
|
+
stdio: "ignore",
|
|
20262
|
+
timeout: pm2CommandTimeoutMs(),
|
|
20263
|
+
killSignal: "SIGKILL"
|
|
20264
|
+
}).status !== 0) return false;
|
|
20265
|
+
if (spawnSync2(pm2Path, ["--version"], {
|
|
20266
|
+
stdio: "ignore",
|
|
20267
|
+
env: { ...process.env, PM2_HOME: pm2Home },
|
|
20268
|
+
timeout: pm2CommandTimeoutMs(),
|
|
20269
|
+
killSignal: "SIGKILL"
|
|
20270
|
+
}).status !== 0) return false;
|
|
20271
|
+
const loaded = spawnSync2(
|
|
20272
|
+
launchctlBin(),
|
|
20273
|
+
["print", `gui/${process.getuid()}/${MAC_LAUNCH_AGENT_LABEL}`],
|
|
20274
|
+
{
|
|
20275
|
+
encoding: "utf8",
|
|
20276
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
20277
|
+
timeout: pm2CommandTimeoutMs(),
|
|
20278
|
+
killSignal: "SIGKILL"
|
|
20279
|
+
}
|
|
20280
|
+
);
|
|
20281
|
+
if (loaded.status !== 0) return false;
|
|
20282
|
+
if (!loaded.stdout.includes("state = running")) return false;
|
|
20283
|
+
if (!loaded.stdout.includes(`path = ${plistPath}`)) return false;
|
|
20284
|
+
if (!loaded.stdout.includes(`program = ${bunPath}`)) return false;
|
|
20285
|
+
const dumpPath = join5(pm2Home, "dump.pm2");
|
|
20286
|
+
const dump = JSON.parse(readFileSync3(dumpPath, "utf8"));
|
|
20287
|
+
if (!Array.isArray(dump)) return false;
|
|
20288
|
+
const gateway = dump.find((item) => item.name === PROCESS_NAME);
|
|
20289
|
+
const dumpRuntime = gatewayRuntimeFromProcess(gateway);
|
|
20290
|
+
if (!dumpRuntime || !hasRestorableSavedGatewayRuntime(gateway)) return false;
|
|
20291
|
+
return expectedRuntime === void 0 || dumpRuntime.gatewayEntry === expectedRuntime.gatewayEntry && dumpRuntime.bunPath === expectedRuntime.bunPath && dumpRuntime.cwd === expectedRuntime.cwd && scopesMatch(runtimeScope(dumpRuntime), runtimeScope(expectedRuntime));
|
|
20292
|
+
} catch {
|
|
20293
|
+
return false;
|
|
20294
|
+
}
|
|
20295
|
+
}
|
|
20296
|
+
function systemctlBin(env = process.env) {
|
|
20297
|
+
return env.SUPERTASK_SYSTEMCTL_BIN ?? "systemctl";
|
|
20298
|
+
}
|
|
20299
|
+
function isLinuxStartupConfigured(env = process.env, cwd = process.cwd(), expectedRuntime) {
|
|
20300
|
+
const unit = env.SUPERTASK_PM2_SYSTEMD_UNIT ?? `pm2-${userInfo().username}.service`;
|
|
20301
|
+
const enabled = spawnSync2(systemctlBin(env), ["is-enabled", unit], {
|
|
20302
|
+
encoding: "utf8",
|
|
20303
|
+
env,
|
|
20304
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
20305
|
+
timeout: pm2CommandTimeoutMs(env),
|
|
20306
|
+
killSignal: "SIGKILL"
|
|
20307
|
+
});
|
|
20308
|
+
if (enabled.status !== 0 || enabled.stdout.trim() !== "enabled") return false;
|
|
20309
|
+
const contents = spawnSync2(systemctlBin(env), ["cat", unit], {
|
|
20310
|
+
encoding: "utf8",
|
|
20311
|
+
env,
|
|
20312
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
20313
|
+
timeout: pm2CommandTimeoutMs(env),
|
|
20314
|
+
killSignal: "SIGKILL"
|
|
20315
|
+
});
|
|
20316
|
+
if (contents.status !== 0) return false;
|
|
20317
|
+
const expectedPm2Home = env.PM2_HOME ?? join5(runtimeHome(env), ".pm2");
|
|
20318
|
+
if (!contents.stdout.includes("resurrect") || !contents.stdout.includes(expectedPm2Home)) {
|
|
20319
|
+
return false;
|
|
20320
|
+
}
|
|
20321
|
+
try {
|
|
20322
|
+
const dump = JSON.parse(readFileSync3(join5(expectedPm2Home, "dump.pm2"), "utf8"));
|
|
20323
|
+
if (!Array.isArray(dump)) return false;
|
|
20324
|
+
const gateway = dump.find((item) => item.name === PROCESS_NAME);
|
|
20325
|
+
const runtime = gatewayRuntimeFromProcess(gateway);
|
|
20326
|
+
if (!runtime || !hasRestorableSavedGatewayRuntime(gateway)) return false;
|
|
20327
|
+
return runtime.cwd === resolve3(cwd) && scopesMatch(runtimeScope(runtime), runtimeScope({ cwd, env })) && (expectedRuntime === void 0 || runtime.gatewayEntry === expectedRuntime.gatewayEntry && runtime.bunPath === expectedRuntime.bunPath);
|
|
20328
|
+
} catch {
|
|
20329
|
+
return false;
|
|
20330
|
+
}
|
|
20331
|
+
}
|
|
20332
|
+
function isPm2Installed(env = process.env) {
|
|
20333
|
+
const result = spawnSync2(pm2Bin(env), ["--version"], {
|
|
20334
|
+
stdio: "ignore",
|
|
20335
|
+
env,
|
|
20336
|
+
shell: process.platform === "win32",
|
|
20337
|
+
timeout: pm2CommandTimeoutMs(env),
|
|
20338
|
+
killSignal: "SIGKILL"
|
|
20339
|
+
});
|
|
20340
|
+
return result.status === 0;
|
|
20341
|
+
}
|
|
20342
|
+
function resolveCommand(command) {
|
|
20343
|
+
const lookup = process.platform === "win32" ? "where" : "which";
|
|
20344
|
+
const result = spawnSync2(lookup, [command], { encoding: "utf8" });
|
|
20345
|
+
return result.status === 0 ? result.stdout.trim().split("\n")[0] || null : null;
|
|
20346
|
+
}
|
|
20347
|
+
function npmPreferredEnvironment() {
|
|
20348
|
+
if (process.platform === "win32") return null;
|
|
20349
|
+
const npm = resolveCommand("npm");
|
|
20350
|
+
const node = resolveCommand("node");
|
|
20351
|
+
if (!npm || !node) return null;
|
|
20352
|
+
const command = resolvePm2Bin();
|
|
20353
|
+
const path = [.../* @__PURE__ */ new Set([
|
|
20354
|
+
dirname6(command),
|
|
20355
|
+
dirname6(npm),
|
|
20356
|
+
dirname6(node),
|
|
20357
|
+
"/usr/local/bin",
|
|
20358
|
+
"/usr/bin",
|
|
20359
|
+
"/bin"
|
|
20360
|
+
])].join(delimiter);
|
|
20361
|
+
return { command, env: { ...process.env, PATH: path } };
|
|
20362
|
+
}
|
|
20363
|
+
function pm2Exec(args, options = {}) {
|
|
20364
|
+
const npmEnvironment = options.preferNpm ? npmPreferredEnvironment() : null;
|
|
20365
|
+
const effectiveEnv = options.env ?? npmEnvironment?.env ?? process.env;
|
|
20366
|
+
const result = spawnSync2(npmEnvironment?.command ?? pm2Bin(effectiveEnv), args, {
|
|
20367
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
20368
|
+
encoding: "utf-8",
|
|
20369
|
+
env: effectiveEnv,
|
|
20370
|
+
shell: process.platform === "win32",
|
|
20371
|
+
timeout: options.timeoutMs ?? pm2CommandTimeoutMs(effectiveEnv),
|
|
20372
|
+
killSignal: "SIGKILL"
|
|
20373
|
+
});
|
|
20374
|
+
const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
|
|
20375
|
+
if (result.error) return { ok: false, output: result.error.message };
|
|
20376
|
+
return { ok: result.status === 0, output };
|
|
20377
|
+
}
|
|
20378
|
+
function requirePm2(args, action, options = {}) {
|
|
20379
|
+
const result = pm2Exec(args, options);
|
|
20380
|
+
if (!result.ok) throw new Error(`[supertask] ${action} failed: ${result.output || "unknown pm2 error"}`);
|
|
20381
|
+
return result.output;
|
|
20382
|
+
}
|
|
20383
|
+
function pm2JsonList(env = process.env) {
|
|
20384
|
+
const output = requirePm2(["jlist"], "pm2 jlist", { env });
|
|
20385
|
+
try {
|
|
20386
|
+
const parsed = JSON.parse(output);
|
|
20387
|
+
if (!Array.isArray(parsed)) throw new Error("result is not an array");
|
|
20388
|
+
return parsed;
|
|
20389
|
+
} catch (error) {
|
|
20390
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
20391
|
+
throw new Error(`[supertask] Invalid pm2 jlist output: ${message}`);
|
|
20392
|
+
}
|
|
20393
|
+
}
|
|
20394
|
+
function gatewayEntryFromProcess(processInfo) {
|
|
20395
|
+
const args = processInfo?.pm2_env?.args ?? processInfo?.args;
|
|
20396
|
+
const candidates = Array.isArray(args) ? [...args].reverse() : typeof args === "string" ? [args] : [];
|
|
20397
|
+
const savedCwd = processInfo?.pm2_env?.pm_cwd ?? processInfo?.pm_cwd;
|
|
20398
|
+
for (const candidate of candidates) {
|
|
20399
|
+
const path = typeof savedCwd === "string" ? runtimePath(candidate, savedCwd) : candidate;
|
|
20400
|
+
if (existsSync6(path)) return resolve3(path);
|
|
20401
|
+
}
|
|
20402
|
+
return null;
|
|
20403
|
+
}
|
|
20404
|
+
function gatewayEnvironmentFromProcess(processInfo) {
|
|
20405
|
+
const saved = processInfo?.pm2_env?.env ?? processInfo?.env;
|
|
20406
|
+
if (!saved) return { ...process.env };
|
|
20407
|
+
const env = {};
|
|
20408
|
+
for (const [key, value] of Object.entries(saved)) {
|
|
20409
|
+
if (typeof value === "string") env[key] = value;
|
|
20410
|
+
}
|
|
20411
|
+
return env;
|
|
20412
|
+
}
|
|
20413
|
+
function gatewayRuntimeFromProcess(processInfo) {
|
|
20414
|
+
const gatewayEntry = gatewayEntryFromProcess(processInfo);
|
|
20415
|
+
if (!gatewayEntry) return null;
|
|
20416
|
+
const savedBunPath = processInfo?.pm2_env?.pm_exec_path ?? processInfo?.pm_exec_path;
|
|
20417
|
+
const savedCwd = processInfo?.pm2_env?.pm_cwd ?? processInfo?.pm_cwd;
|
|
20418
|
+
const savedEnv = gatewayEnvironmentFromProcess(processInfo);
|
|
20419
|
+
if (typeof savedBunPath !== "string" || typeof savedCwd !== "string") return null;
|
|
20420
|
+
try {
|
|
20421
|
+
accessSync(savedBunPath, constants.X_OK);
|
|
20422
|
+
if (!statSync3(savedCwd).isDirectory()) return null;
|
|
20423
|
+
if (spawnSync2(savedBunPath, ["--version"], {
|
|
20424
|
+
stdio: "ignore",
|
|
20425
|
+
env: savedEnv,
|
|
20426
|
+
timeout: pm2CommandTimeoutMs(savedEnv),
|
|
20427
|
+
killSignal: "SIGKILL"
|
|
20428
|
+
}).status !== 0) return null;
|
|
20429
|
+
} catch {
|
|
20430
|
+
return null;
|
|
20431
|
+
}
|
|
20432
|
+
const killTimeout = processInfo?.pm2_env?.kill_timeout ?? processInfo?.kill_timeout;
|
|
20433
|
+
return {
|
|
20434
|
+
gatewayEntry,
|
|
20435
|
+
bunPath: savedBunPath,
|
|
20436
|
+
cwd: resolve3(savedCwd),
|
|
20437
|
+
env: savedEnv,
|
|
20438
|
+
killTimeoutMs: Number.isInteger(killTimeout) && killTimeout >= 5e3 ? killTimeout : void 0
|
|
20439
|
+
};
|
|
20440
|
+
}
|
|
20441
|
+
function hasRestorableSavedGatewayRuntime(processInfo) {
|
|
20442
|
+
return gatewayRuntimeFromProcess(processInfo) !== null;
|
|
20443
|
+
}
|
|
20444
|
+
function databasePath(env = process.env, cwd = process.cwd()) {
|
|
20445
|
+
return env.SUPERTASK_DB_PATH ? runtimePath(env.SUPERTASK_DB_PATH, cwd) : join5(runtimeHome(env), ".local/share/opencode/tasks.db");
|
|
20446
|
+
}
|
|
20447
|
+
function isGatewayReady(expectedPid, path = databasePath()) {
|
|
20448
|
+
if (!existsSync6(path)) return false;
|
|
20449
|
+
let database = null;
|
|
20450
|
+
try {
|
|
20451
|
+
database = new Database5(path, { readonly: true });
|
|
20452
|
+
const row = database.query(
|
|
20453
|
+
"SELECT pid, heartbeat_at, ready_at FROM gateway_lock WHERE id = 1"
|
|
20454
|
+
).get();
|
|
20455
|
+
if (!row || row.ready_at == null) return false;
|
|
20456
|
+
if (expectedPid !== void 0 && row.pid !== expectedPid) return false;
|
|
20457
|
+
const ageMs = Date.now() - row.heartbeat_at;
|
|
20458
|
+
return ageMs >= -5e3 && ageMs < GATEWAY_LOCK_STALE_MS;
|
|
20459
|
+
} catch {
|
|
20460
|
+
return false;
|
|
20461
|
+
} finally {
|
|
20462
|
+
database?.close();
|
|
20463
|
+
}
|
|
20464
|
+
}
|
|
20465
|
+
function gatewayVersionFromLock(expectedPid, path) {
|
|
20466
|
+
if (!existsSync6(path)) return void 0;
|
|
20467
|
+
let database = null;
|
|
20468
|
+
try {
|
|
20469
|
+
database = new Database5(path, { readonly: true });
|
|
20470
|
+
const row = database.query(
|
|
20471
|
+
"SELECT pid, version FROM gateway_lock WHERE id = 1"
|
|
20472
|
+
).get();
|
|
20473
|
+
if (!row || row.pid !== expectedPid) return void 0;
|
|
20474
|
+
return row.version;
|
|
20475
|
+
} catch {
|
|
20476
|
+
return void 0;
|
|
20477
|
+
} finally {
|
|
20478
|
+
database?.close();
|
|
20479
|
+
}
|
|
20480
|
+
}
|
|
20481
|
+
function managementLockPath(env = process.env, cwd = process.cwd()) {
|
|
20482
|
+
const override = env.SUPERTASK_PM2_MANAGEMENT_LOCK;
|
|
20483
|
+
if (override) return runtimePath(override, cwd);
|
|
20484
|
+
return join5(runtimeScope({ env, cwd }).pm2Home, "supertask-gateway.manage.sqlite");
|
|
20485
|
+
}
|
|
20486
|
+
function canonicalManagementLockPath(env = process.env, cwd = process.cwd()) {
|
|
20487
|
+
const home = runtimeHome(env);
|
|
20488
|
+
const pm2Home = env.PM2_HOME ? runtimePath(env.PM2_HOME, cwd) : join5(home, ".pm2");
|
|
20489
|
+
return join5(pm2Home, "supertask-gateway.manage.sqlite");
|
|
20490
|
+
}
|
|
20491
|
+
var __dirname, PROCESS_NAME, MAC_LAUNCH_AGENT_LABEL, GATEWAY_LOCK_STALE_MS;
|
|
20492
|
+
var init_pm2 = __esm({
|
|
20493
|
+
"src/daemon/pm2.ts"() {
|
|
20494
|
+
"use strict";
|
|
20495
|
+
init_config();
|
|
20496
|
+
init_package_version();
|
|
20497
|
+
init_management_lock();
|
|
20498
|
+
init_package_version();
|
|
20499
|
+
__dirname = dirname6(fileURLToPath5(import.meta.url));
|
|
20500
|
+
PROCESS_NAME = "supertask-gateway";
|
|
20501
|
+
MAC_LAUNCH_AGENT_LABEL = "com.supertask.pm2-resurrect";
|
|
20502
|
+
GATEWAY_LOCK_STALE_MS = 3e4;
|
|
20503
|
+
}
|
|
20504
|
+
});
|
|
20505
|
+
|
|
19816
20506
|
// src/web/ui.ts
|
|
19817
20507
|
function t(locale, key, values = {}) {
|
|
19818
20508
|
const template = (locale === "en" ? EN : ZH)[key];
|
|
@@ -19822,6 +20512,14 @@ function statusText(locale, status) {
|
|
|
19822
20512
|
const key = `status.${status}`;
|
|
19823
20513
|
return key in ZH ? t(locale, key) : t(locale, "status.unknown");
|
|
19824
20514
|
}
|
|
20515
|
+
function runStatusText(locale, status) {
|
|
20516
|
+
const key = `runStatus.${status}`;
|
|
20517
|
+
return key in ZH ? t(locale, key) : t(locale, "status.unknown");
|
|
20518
|
+
}
|
|
20519
|
+
function resolveEditedRunAt(originalEpoch, originalLocal, currentLocal) {
|
|
20520
|
+
if (originalEpoch !== null && originalLocal === currentLocal) return originalEpoch;
|
|
20521
|
+
return new Date(currentLocal).getTime();
|
|
20522
|
+
}
|
|
19825
20523
|
function formatRelative(timestamp2, locale) {
|
|
19826
20524
|
if (!timestamp2) return "\u2014";
|
|
19827
20525
|
const deltaMs = timestamp2 - Date.now();
|
|
@@ -19898,13 +20596,31 @@ function clientMessages(locale) {
|
|
|
19898
20596
|
"dialog.clearTitle",
|
|
19899
20597
|
"dialog.clearBody",
|
|
19900
20598
|
"dialog.clearInstruction",
|
|
20599
|
+
"dialog.restartGateway",
|
|
20600
|
+
"dialog.restartGatewayBody",
|
|
20601
|
+
"dialog.restartGatewayRunningBody",
|
|
19901
20602
|
"feedback.retryFailed",
|
|
19902
20603
|
"feedback.cancelFailed",
|
|
19903
20604
|
"feedback.deleteFailed",
|
|
19904
20605
|
"feedback.requestFailed",
|
|
19905
20606
|
"feedback.triggered",
|
|
20607
|
+
"feedback.taskCreated",
|
|
20608
|
+
"feedback.taskUpdated",
|
|
20609
|
+
"task.projectExisting",
|
|
20610
|
+
"task.projectNew",
|
|
20611
|
+
"task.createTitle",
|
|
20612
|
+
"task.editTitle",
|
|
20613
|
+
"action.saveTask",
|
|
20614
|
+
"action.updateTask",
|
|
19906
20615
|
"feedback.configSaved",
|
|
19907
20616
|
"feedback.databaseCleared",
|
|
20617
|
+
"feedback.templateCreated",
|
|
20618
|
+
"feedback.templateUpdated",
|
|
20619
|
+
"feedback.sessionCommandCopied",
|
|
20620
|
+
"feedback.restarting",
|
|
20621
|
+
"feedback.restartTimeout",
|
|
20622
|
+
"template.createTitle",
|
|
20623
|
+
"template.editTitle",
|
|
19908
20624
|
"filter.noResults"
|
|
19909
20625
|
];
|
|
19910
20626
|
return Object.fromEntries(keys.map((key) => [key, t(locale, key)]));
|
|
@@ -19998,6 +20714,7 @@ function renderLayout(options) {
|
|
|
19998
20714
|
themeMedia.addEventListener?.('change',()=>{if((localStorage.getItem('supertask-theme')||'system')==='system')applyTheme('system');});
|
|
19999
20715
|
applyTheme(localStorage.getItem('supertask-theme')||'system');
|
|
20000
20716
|
requestAnimationFrame(()=>document.documentElement.classList.add('ui-ready'));
|
|
20717
|
+
${resolveEditedRunAt.toString()}
|
|
20001
20718
|
function refreshPage(button){button.classList.add('refreshing');button.setAttribute('aria-label','${t(locale, "a11y.refreshing")}');location.reload();}
|
|
20002
20719
|
function showToast(message,type='ok'){const region=document.getElementById('toast-region');const node=document.createElement('div');node.className='toast '+type;node.innerHTML=(type==='error'?'${icon("alert")}':'${icon("check")}')+'<span></span>';node.querySelector('span').textContent=message;region.appendChild(node);setTimeout(()=>{node.classList.add('leaving');setTimeout(()=>node.remove(),220)},3600);}
|
|
20003
20720
|
async function readJson(response){const data=await response.json().catch(()=>({}));if(!response.ok)throw new Error(data.error||text('feedback.requestFailed'));return data;}
|
|
@@ -20009,6 +20726,22 @@ function renderLayout(options) {
|
|
|
20009
20726
|
async function showRecord(url){try{const data=await readJson(await fetch(url));document.getElementById('detail-content').textContent=JSON.stringify(data,null,2);document.getElementById('detail-dialog').showModal()}catch(error){showToast(error.message,'error')}}
|
|
20010
20727
|
const showDetail=id=>showRecord('/api/tasks/'+id);const showRunDetail=id=>showRecord('/api/runs/'+id);const showTemplateDetail=id=>showRecord('/api/templates/'+id);
|
|
20011
20728
|
async function copyDetails(){try{await navigator.clipboard.writeText(document.getElementById('detail-content').textContent);showToast(text('details.copySuccess'))}catch{showToast(text('feedback.copyFailed'),'error')}}
|
|
20729
|
+
async function copySessionCommand(id){try{const data=await readJson(await fetch('/api/runs/'+id+'/session-command'));await navigator.clipboard.writeText(data.command);showToast(text('feedback.sessionCommandCopied'))}catch(error){showToast(error.message||text('feedback.copyFailed'),'error')}}
|
|
20730
|
+
function taskField(name){return document.getElementById('task-'+name)}
|
|
20731
|
+
function taskProjects(){const node=document.getElementById('task-project-data');if(!node)return {};try{return JSON.parse(node.textContent||'{}')}catch{return {}}}
|
|
20732
|
+
function updateTaskProjectStatus(){const node=taskField('project-status');if(!node)return;const cwd=taskField('cwd').value.trim();if(!cwd){node.textContent='';return}const project=taskProjects()[cwd];node.textContent=project?text('task.projectExisting',project):text('task.projectNew')}
|
|
20733
|
+
function openTaskCreator(){const form=document.getElementById('task-form');form.reset();taskField('id').value='';taskField('cwd').readOnly=false;taskField('cwd').value=form.dataset.defaultCwd||'';taskField('dialog-title').textContent=text('task.createTitle');taskField('save').textContent=text('action.saveTask');updateTaskProjectStatus();document.getElementById('task-dialog').showModal();setTimeout(()=>taskField('name').focus(),50)}
|
|
20734
|
+
async function openTaskEditor(id){try{const data=await readJson(await fetch('/api/tasks/'+id));taskField('id').value=String(id);taskField('name').value=data.name||'';taskField('cwd').value=data.cwd||'';taskField('cwd').readOnly=true;taskField('agent').value=data.agent||'';taskField('model').value=data.model||'default';taskField('prompt').value=data.prompt||'';taskField('category').value=data.category||'general';taskField('batch').value=data.batchId||'';taskField('importance').value=String(data.importance??3);taskField('urgency').value=String(data.urgency??3);taskField('max-retries').value=String(data.maxRetries??3);taskField('retry-backoff').value=durationInput(data.retryBackoffMs??30000);taskField('timeout').value=durationInput(data.timeoutMs);taskField('dialog-title').textContent=text('task.editTitle');taskField('save').textContent=text('action.updateTask');updateTaskProjectStatus();document.getElementById('task-dialog').showModal();setTimeout(()=>taskField('name').focus(),50)}catch(error){showToast(error.message,'error')}}
|
|
20735
|
+
async function saveTask(event){event.preventDefault();const form=document.getElementById('task-form');if(!form.reportValidity())return;const id=taskField('id').value;const body={name:taskField('name').value,cwd:taskField('cwd').value,agent:taskField('agent').value,model:taskField('model').value,prompt:taskField('prompt').value,category:taskField('category').value,batchId:taskField('batch').value,importance:Number(taskField('importance').value),urgency:Number(taskField('urgency').value),maxRetries:Number(taskField('max-retries').value),retryBackoff:taskField('retry-backoff').value,timeout:taskField('timeout').value};const button=taskField('save');button.disabled=true;try{const data=await readJson(await fetch(id?'/api/tasks/'+id:'/api/tasks',{method:id?'PUT':'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}));showToast(text(id?'feedback.taskUpdated':'feedback.taskCreated',{id:data.task.id}));document.getElementById('task-dialog').close();setTimeout(()=>location.assign(id?location.href:'/?cwd='+encodeURIComponent(data.task.cwd||'')),450)}catch(error){showToast(error.message,'error')}finally{button.disabled=false}}
|
|
20736
|
+
function templateField(name){return document.getElementById('template-'+name)}
|
|
20737
|
+
function durationInput(milliseconds){if(milliseconds==null)return '';if(milliseconds===0)return '0';const units=[['d',86400000],['h',3600000],['min',60000],['s',1000],['ms',1]];for(const [unit,factor] of units){if(milliseconds%factor===0)return String(milliseconds/factor)+unit}return String(milliseconds)+'ms'}
|
|
20738
|
+
function localDateTime(milliseconds){const date=new Date(milliseconds);const local=new Date(milliseconds-date.getTimezoneOffset()*60000);return local.toISOString().slice(0,23)}
|
|
20739
|
+
function updateTemplateScheduleFields(){const type=templateField('schedule-type').value;const fields={cron:templateField('cron-field'),recurring:templateField('interval-field'),delayed:templateField('run-at-field')};for(const [name,node] of Object.entries(fields)){node.hidden=name!==type;node.querySelector('input').required=name===type}}
|
|
20740
|
+
function setOriginalRunAt(epoch){const input=templateField('run-at');const local=epoch?localDateTime(epoch):'';input.value=local;input.dataset.originalEpoch=epoch?String(epoch):'';input.dataset.originalLocal=local}
|
|
20741
|
+
function selectedRunAt(){const input=templateField('run-at');return resolveEditedRunAt(input.dataset.originalEpoch?Number(input.dataset.originalEpoch):null,input.dataset.originalLocal||'',input.value)}
|
|
20742
|
+
function openTemplateCreator(){const form=document.getElementById('template-form');form.reset();templateField('id').value='';templateField('dialog-title').textContent=text('template.createTitle');setOriginalRunAt(null);templateField('run-at').value=localDateTime(Date.now()+3600000);updateTemplateScheduleFields();document.getElementById('template-dialog').showModal();setTimeout(()=>templateField('name').focus(),50)}
|
|
20743
|
+
async function openTemplateEditor(id){try{const data=await readJson(await fetch('/api/templates/'+id));templateField('id').value=String(id);templateField('dialog-title').textContent=text('template.editTitle');templateField('name').value=data.name||'';templateField('cwd').value=data.cwd||'';templateField('agent').value=data.agent||'';templateField('model').value=data.model||'default';templateField('prompt').value=data.prompt||'';templateField('schedule-type').value=data.scheduleType;templateField('cron').value=data.cronExpr||'';templateField('interval').value=durationInput(data.intervalMs);setOriginalRunAt(data.runAt||null);if(!data.runAt)templateField('run-at').value=localDateTime(Date.now()+3600000);templateField('category').value=data.category||'general';templateField('batch').value=data.batchId||'';templateField('importance').value=String(data.importance??3);templateField('urgency').value=String(data.urgency??3);templateField('max-instances').value=String(data.maxInstances??1);templateField('max-retries').value=String(data.maxRetries??3);templateField('retry-backoff').value=durationInput(data.retryBackoffMs??30000);templateField('timeout').value=durationInput(data.timeoutMs);updateTemplateScheduleFields();document.getElementById('template-dialog').showModal();setTimeout(()=>templateField('name').focus(),50)}catch(error){showToast(error.message,'error')}}
|
|
20744
|
+
async function saveTemplate(event){event.preventDefault();const form=document.getElementById('template-form');if(!form.reportValidity())return;const id=templateField('id').value;const type=templateField('schedule-type').value;const body={name:templateField('name').value,cwd:templateField('cwd').value,agent:templateField('agent').value,model:templateField('model').value,prompt:templateField('prompt').value,scheduleType:type,cronExpr:templateField('cron').value,interval:templateField('interval').value,runAt:type==='delayed'?selectedRunAt():null,category:templateField('category').value,batchId:templateField('batch').value,importance:Number(templateField('importance').value),urgency:Number(templateField('urgency').value),maxInstances:Number(templateField('max-instances').value),maxRetries:Number(templateField('max-retries').value),retryBackoff:templateField('retry-backoff').value,timeout:templateField('timeout').value};const button=templateField('save');button.disabled=true;try{await readJson(await fetch(id?'/api/templates/'+id:'/api/templates',{method:id?'PUT':'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}));showToast(text(id?'feedback.templateUpdated':'feedback.templateCreated'));document.getElementById('template-dialog').close();setTimeout(()=>location.assign(id?location.href:'/templates'),450)}catch(error){showToast(error.message,'error')}finally{button.disabled=false}}
|
|
20012
20745
|
async function enableTmpl(id){try{await readJson(await fetch('/api/templates/'+id+'/enable',{method:'POST'}));location.reload()}catch(error){showToast(error.message,'error')}}
|
|
20013
20746
|
async function disableTmpl(id){if(!await ask(text('dialog.disableTemplate'),text('dialog.disableTemplateBody')))return;try{await readJson(await fetch('/api/templates/'+id+'/disable',{method:'POST'}));location.reload()}catch(error){showToast(error.message,'error')}}
|
|
20014
20747
|
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')}}
|
|
@@ -20016,7 +20749,9 @@ function renderLayout(options) {
|
|
|
20016
20749
|
function toggleLog(id,button){const panel=document.getElementById('log-'+id);const hidden=!panel.hidden;panel.hidden=hidden;button.setAttribute('aria-expanded',String(!hidden));button.textContent=text(hidden?'action.logs':'action.hideLogs');}
|
|
20017
20750
|
function filterTasks(value){const query=value.trim().toLocaleLowerCase();let visible=0;document.querySelectorAll('[data-task-row]').forEach(row=>{const match=!query||row.dataset.search.toLocaleLowerCase().includes(query);row.hidden=!match;if(match)visible++});const empty=document.getElementById('search-empty');if(empty)empty.hidden=visible!==0;}
|
|
20018
20751
|
async function clearDatabase(){if(!await askDanger())return;try{const data=await readJson(await fetch('/api/database/clear',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({confirmation:'CLEAR'})}));showToast(text('feedback.databaseCleared',{path:data.backupPath}));setTimeout(()=>location.reload(),1000)}catch(error){showToast(error.message,'error')}}
|
|
20019
|
-
async function
|
|
20752
|
+
async function confirmGatewayRestart(runningCount=0){const body=runningCount>0?text('dialog.restartGatewayRunningBody',{count:runningCount}):text('dialog.restartGatewayBody');return await ask(text('dialog.restartGateway'),body)}
|
|
20753
|
+
async function restartGateway(){try{const data=await readJson(await fetch('/api/gateway/restart',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({confirmation:'RESTART'})}));showToast(text('feedback.restarting'));for(let attempt=0;attempt<120;attempt++){await new Promise(resolve=>setTimeout(resolve,500));try{const status=await readJson(await fetch('/api/gateway/status',{cache:'no-store'}));if(status.pid!==data.previousPid&&status.managed&&status.ready&&!status.restartRequired){location.reload();return true}}catch{}}showToast(text('feedback.restartTimeout'),'error');return false}catch(error){showToast(error.message,'error');return false}}
|
|
20754
|
+
async function saveConfig(restartAfterSave=false,runningCount=0){if(restartAfterSave&&!await confirmGatewayRestart(runningCount))return;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'));if(restartAfterSave){const restarted=await restartGateway();if(!restarted)setTimeout(()=>location.reload(),500)}else{setTimeout(()=>location.reload(),500)}}catch(error){showToast(error.message,'error')}}
|
|
20020
20755
|
</script>
|
|
20021
20756
|
</body>
|
|
20022
20757
|
</html>`;
|
|
@@ -20028,17 +20763,17 @@ var init_ui = __esm({
|
|
|
20028
20763
|
ZH = {
|
|
20029
20764
|
"app.name": "SuperTask",
|
|
20030
20765
|
"app.dashboard": "\u63A7\u5236\u53F0",
|
|
20031
|
-
"app.tagline": "\u53EF\u9760\u7684\u672C\u5730 Agent \
|
|
20766
|
+
"app.tagline": "\u53EF\u9760\u7684\u672C\u5730 Agent \u4EFB\u52A1\u4E2D\u5FC3",
|
|
20032
20767
|
"app.local": "\u672C\u5730\u8FD0\u884C",
|
|
20033
20768
|
"app.footer": "Local-first \xB7 \u6570\u636E\u7559\u5728\u4F60\u7684\u8BBE\u5907\u4E0A",
|
|
20034
20769
|
"nav.tasks": "\u4EFB\u52A1",
|
|
20035
|
-
"nav.templates": "\
|
|
20770
|
+
"nav.templates": "\u5B9A\u65F6\u4EFB\u52A1",
|
|
20036
20771
|
"nav.runs": "\u6267\u884C\u8BB0\u5F55",
|
|
20037
20772
|
"nav.system": "\u7CFB\u7EDF",
|
|
20038
20773
|
"page.tasks.title": "\u4EFB\u52A1\u961F\u5217",
|
|
20039
20774
|
"page.tasks.description": "\u67E5\u770B\u4F18\u5148\u7EA7\u3001\u6267\u884C\u72B6\u6001\u4E0E\u91CD\u8BD5\u60C5\u51B5\uFF0C\u5FEB\u901F\u5904\u7406\u9700\u8981\u5173\u6CE8\u7684\u4EFB\u52A1\u3002",
|
|
20040
|
-
"page.templates.title": "\
|
|
20041
|
-
"page.templates.description": "\
|
|
20775
|
+
"page.templates.title": "\u5B9A\u65F6\u4EFB\u52A1",
|
|
20776
|
+
"page.templates.description": "\u5728\u7F51\u9875\u521B\u5EFA\u548C\u7F16\u8F91 Cron\u3001\u56FA\u5B9A\u95F4\u9694\u53CA\u4E00\u6B21\u6027\u4EFB\u52A1\uFF0C\u5305\u62EC\u6A21\u578B\u3001Agent \u548C\u63D0\u793A\u8BCD\u3002",
|
|
20042
20777
|
"page.runs.title": "\u6267\u884C\u8BB0\u5F55",
|
|
20043
20778
|
"page.runs.description": "\u8FFD\u8E2A\u6BCF\u6B21 Agent \u6267\u884C\u7684\u72B6\u6001\u3001\u8017\u65F6\u3001\u5FC3\u8DF3\u4E0E\u8F93\u51FA\u3002",
|
|
20044
20779
|
"page.system.title": "\u7CFB\u7EDF\u8BBE\u7F6E",
|
|
@@ -20048,29 +20783,43 @@ var init_ui = __esm({
|
|
|
20048
20783
|
"action.retry": "\u91CD\u8BD5",
|
|
20049
20784
|
"action.cancel": "\u53D6\u6D88",
|
|
20050
20785
|
"action.delete": "\u5220\u9664",
|
|
20786
|
+
"action.edit": "\u7F16\u8F91",
|
|
20051
20787
|
"action.enable": "\u542F\u7528",
|
|
20052
20788
|
"action.disable": "\u7981\u7528",
|
|
20053
|
-
"action.trigger": "\u7ACB\u5373\
|
|
20789
|
+
"action.trigger": "\u7ACB\u5373\u8FD0\u884C\u4E00\u6B21",
|
|
20790
|
+
"action.continueSession": "\u7EE7\u7EED\u4F1A\u8BDD",
|
|
20054
20791
|
"action.logs": "\u67E5\u770B\u65E5\u5FD7",
|
|
20055
20792
|
"action.hideLogs": "\u6536\u8D77\u65E5\u5FD7",
|
|
20056
20793
|
"action.save": "\u4FDD\u5B58\u8BBE\u7F6E",
|
|
20794
|
+
"action.saveAndRestart": "\u4FDD\u5B58\u5E76\u91CD\u542F",
|
|
20057
20795
|
"action.copy": "\u590D\u5236 JSON",
|
|
20058
20796
|
"action.close": "\u5173\u95ED",
|
|
20059
20797
|
"action.confirm": "\u786E\u8BA4",
|
|
20060
20798
|
"action.clearDatabase": "\u6E05\u7A7A\u6570\u636E\u5E93",
|
|
20799
|
+
"action.createTask": "\u65B0\u5EFA\u4EFB\u52A1",
|
|
20800
|
+
"action.saveTask": "\u52A0\u5165\u961F\u5217",
|
|
20801
|
+
"action.updateTask": "\u4FDD\u5B58\u4FEE\u6539",
|
|
20802
|
+
"action.createTemplate": "\u65B0\u5EFA\u5B9A\u65F6\u4EFB\u52A1",
|
|
20803
|
+
"action.saveTemplate": "\u4FDD\u5B58\u5B9A\u65F6\u4EFB\u52A1",
|
|
20061
20804
|
"status.pending": "\u5F85\u6267\u884C",
|
|
20062
20805
|
"status.running": "\u8FD0\u884C\u4E2D",
|
|
20063
20806
|
"status.done": "\u5DF2\u5B8C\u6210",
|
|
20064
|
-
"status.failed": "\
|
|
20065
|
-
"status.dead_letter": "\
|
|
20807
|
+
"status.failed": "\u7B49\u5F85\u91CD\u8BD5",
|
|
20808
|
+
"status.dead_letter": "\u5DF2\u505C\u6B62",
|
|
20809
|
+
"status.deadLetterHint": "\u7CFB\u7EDF\u4E0D\u4F1A\u518D\u81EA\u52A8\u8FD0\u884C\u3002\u53EF\u80FD\u662F\u91CD\u8BD5\u6B21\u6570\u5DF2\u7528\u5B8C\uFF0C\u6216\u4F9D\u8D56\u4EFB\u52A1\u65E0\u6CD5\u7EE7\u7EED\uFF1B\u8BF7\u67E5\u770B\u5931\u8D25\u539F\u56E0\uFF0C\u786E\u8BA4\u540E\u624B\u52A8\u91CD\u8BD5\u3002",
|
|
20810
|
+
"status.deadLetterAction": "\u9700\u68C0\u67E5\u539F\u56E0\u540E\u624B\u52A8\u91CD\u8BD5",
|
|
20066
20811
|
"status.cancelled": "\u5DF2\u53D6\u6D88",
|
|
20812
|
+
"status.executionStillActive": "\u6267\u884C\u8FDB\u7A0B\u4ECD\u5728\u9000\u51FA\uFF0C\u6682\u65F6\u5360\u7528\u5E76\u53D1",
|
|
20067
20813
|
"status.unknown": "\u672A\u77E5",
|
|
20814
|
+
"runStatus.running": "\u8FD0\u884C\u4E2D",
|
|
20815
|
+
"runStatus.done": "\u6210\u529F",
|
|
20816
|
+
"runStatus.failed": "\u5931\u8D25",
|
|
20068
20817
|
"stats.total": "\u603B\u4EFB\u52A1",
|
|
20069
20818
|
"stats.pending": "\u5F85\u6267\u884C",
|
|
20070
20819
|
"stats.running": "\u8FD0\u884C\u4E2D",
|
|
20071
20820
|
"stats.done": "\u5DF2\u5B8C\u6210",
|
|
20072
|
-
"stats.failedDead": "\
|
|
20073
|
-
"stats.templates": "\
|
|
20821
|
+
"stats.failedDead": "\u7B49\u5F85\u91CD\u8BD5\u4E0E\u5DF2\u505C\u6B62",
|
|
20822
|
+
"stats.templates": "\u5B9A\u65F6\u4EFB\u52A1\u603B\u6570",
|
|
20074
20823
|
"stats.enabled": "\u5DF2\u542F\u7528",
|
|
20075
20824
|
"stats.disabled": "\u5DF2\u7981\u7528",
|
|
20076
20825
|
"stats.records": "\u6267\u884C\u603B\u6570",
|
|
@@ -20078,7 +20827,7 @@ var init_ui = __esm({
|
|
|
20078
20827
|
"stats.pageFailed": "\u672C\u9875\u5931\u8D25",
|
|
20079
20828
|
"stats.pageRunning": "\u672C\u9875\u8FD0\u884C\u4E2D",
|
|
20080
20829
|
"filter.all": "\u5168\u90E8",
|
|
20081
|
-
"filter.searchTasks": "\u641C\u7D22\u5F53\u524D\u9875\u7684\u4EFB\u52A1\u3001Agent \u6216\u63D0\u793A\u8BCD",
|
|
20830
|
+
"filter.searchTasks": "\u641C\u7D22\u5F53\u524D\u9875\u7684\u4EFB\u52A1\u3001\u9879\u76EE\u3001\u6279\u6B21\u3001Agent \u6216\u63D0\u793A\u8BCD",
|
|
20082
20831
|
"filter.noResults": "\u6CA1\u6709\u7B26\u5408\u5F53\u524D\u641C\u7D22\u6761\u4EF6\u7684\u4EFB\u52A1",
|
|
20083
20832
|
"table.id": "ID",
|
|
20084
20833
|
"table.task": "\u4EFB\u52A1",
|
|
@@ -20094,7 +20843,7 @@ var init_ui = __esm({
|
|
|
20094
20843
|
"table.nextRun": "\u4E0B\u6B21\u6267\u884C",
|
|
20095
20844
|
"table.run": "Run",
|
|
20096
20845
|
"table.heartbeat": "\u5FC3\u8DF3",
|
|
20097
|
-
"table.session": "
|
|
20846
|
+
"table.session": "\u4F1A\u8BDD",
|
|
20098
20847
|
"table.model": "\u6A21\u578B",
|
|
20099
20848
|
"table.startedAt": "\u542F\u52A8\u65F6\u95F4",
|
|
20100
20849
|
"table.pid": "PID",
|
|
@@ -20102,14 +20851,14 @@ var init_ui = __esm({
|
|
|
20102
20851
|
"pagination.next": "\u4E0B\u4E00\u9875",
|
|
20103
20852
|
"pagination.summary": "\u7B2C {page} \u9875\uFF0C\u5171 {pages} \u9875 \xB7 {total} \u6761",
|
|
20104
20853
|
"empty.tasks": "\u961F\u5217\u91CC\u8FD8\u6CA1\u6709\u4EFB\u52A1",
|
|
20105
|
-
"empty.tasksHint": "\u901A\u8FC7 OpenCode \u63D2\u4EF6\
|
|
20106
|
-
"empty.templates": "\u8FD8\u6CA1\u6709\
|
|
20107
|
-
"empty.templatesHint": "\
|
|
20854
|
+
"empty.tasksHint": "\u70B9\u51FB\u201C\u65B0\u5EFA\u4EFB\u52A1\u201D\uFF0C\u6216\u901A\u8FC7 OpenCode \u63D2\u4EF6\u548C supertask add \u521B\u5EFA\u7B2C\u4E00\u4E2A\u4EFB\u52A1\u3002",
|
|
20855
|
+
"empty.templates": "\u8FD8\u6CA1\u6709\u5B9A\u65F6\u4EFB\u52A1",
|
|
20856
|
+
"empty.templatesHint": "\u70B9\u51FB\u53F3\u4E0A\u89D2\u201C\u65B0\u5EFA\u5B9A\u65F6\u4EFB\u52A1\u201D\u5F00\u59CB\u521B\u5EFA\u3002",
|
|
20108
20857
|
"empty.runs": "\u8FD8\u6CA1\u6709\u6267\u884C\u8BB0\u5F55",
|
|
20109
20858
|
"empty.running": "\u5F53\u524D\u6CA1\u6709\u8FD0\u884C\u4E2D\u7684\u4EFB\u52A1",
|
|
20110
20859
|
"schedule.cron": "Cron",
|
|
20111
|
-
"schedule.recurring": "\
|
|
20112
|
-
"schedule.delayed": "\
|
|
20860
|
+
"schedule.recurring": "\u56FA\u5B9A\u95F4\u9694",
|
|
20861
|
+
"schedule.delayed": "\u4E00\u6B21\u6027",
|
|
20113
20862
|
"schedule.unknown": "\u672A\u77E5",
|
|
20114
20863
|
"schedule.enabled": "\u5DF2\u542F\u7528",
|
|
20115
20864
|
"schedule.disabled": "\u5DF2\u7981\u7528",
|
|
@@ -20118,14 +20867,14 @@ var init_ui = __esm({
|
|
|
20118
20867
|
"schedule.hours": "{count} \u5C0F\u65F6",
|
|
20119
20868
|
"schedule.days": "{count} \u5929",
|
|
20120
20869
|
"schedule.overdue": "\u5DF2\u5230\u671F",
|
|
20121
|
-
"system.worker": "
|
|
20122
|
-
"system.scheduler": "
|
|
20123
|
-
"system.watchdog": "
|
|
20870
|
+
"system.worker": "\u4EFB\u52A1\u6267\u884C",
|
|
20871
|
+
"system.scheduler": "\u5B9A\u65F6\u4EFB\u52A1\u670D\u52A1",
|
|
20872
|
+
"system.watchdog": "\u8FD0\u884C\u76D1\u63A7",
|
|
20124
20873
|
"system.maxConcurrency": "\u6700\u5927\u5E76\u53D1",
|
|
20125
20874
|
"system.pollInterval": "\u8F6E\u8BE2\u95F4\u9694",
|
|
20126
20875
|
"system.heartbeatInterval": "\u5FC3\u8DF3\u95F4\u9694",
|
|
20127
20876
|
"system.taskTimeout": "\u4EFB\u52A1\u8D85\u65F6",
|
|
20128
|
-
"system.schedulerEnabled": "\u542F\u7528\
|
|
20877
|
+
"system.schedulerEnabled": "\u542F\u7528\u5B9A\u65F6\u4EFB\u52A1",
|
|
20129
20878
|
"system.checkInterval": "\u68C0\u67E5\u95F4\u9694",
|
|
20130
20879
|
"system.heartbeatTimeout": "\u5FC3\u8DF3\u8D85\u65F6",
|
|
20131
20880
|
"system.cleanupInterval": "\u6E05\u7406\u95F4\u9694",
|
|
@@ -20135,8 +20884,12 @@ var init_ui = __esm({
|
|
|
20135
20884
|
"system.minutes": "\u5206\u949F",
|
|
20136
20885
|
"system.hours": "\u5C0F\u65F6",
|
|
20137
20886
|
"system.days": "\u5929",
|
|
20138
|
-
"system.activeTemplates": "\
|
|
20139
|
-
"system.saveHint": "\u4FDD\u5B58\u540E\u9700\u91CD\u542F Gateway \u751F\u6548",
|
|
20887
|
+
"system.activeTemplates": "\u5DF2\u542F\u7528\u5B9A\u65F6\u4EFB\u52A1",
|
|
20888
|
+
"system.saveHint": "\u8BBE\u7F6E\u4FDD\u5B58\u540E\u9700\u8981\u91CD\u542F Gateway \u624D\u80FD\u751F\u6548\u3002",
|
|
20889
|
+
"system.configApplied": "\u9875\u9762\u4E2D\u7684\u8BBE\u7F6E\u6B63\u5728\u751F\u6548\u3002",
|
|
20890
|
+
"system.configPending": "\u8BBE\u7F6E\u5DF2\u4FDD\u5B58\uFF0C\u4F46\u5F53\u524D Gateway \u4ECD\u5728\u4F7F\u7528\u4E0A\u6B21\u542F\u52A8\u65F6\u7684\u8BBE\u7F6E\u3002",
|
|
20891
|
+
"system.configForeground": "\u6B64\u9875\u9762\u672A\u8FDE\u63A5\u5230 Gateway \u7684\u8FD0\u884C\u914D\u7F6E\uFF1B\u4FDD\u5B58\u540E\u8BF7\u91CD\u542F Gateway\u3002",
|
|
20892
|
+
"system.configRestartManually": "\u8BBE\u7F6E\u5DF2\u4FDD\u5B58\u4F46\u5C1A\u672A\u751F\u6548\u3002\u8BF7\u56DE\u5230\u542F\u52A8 Gateway \u7684\u7EC8\u7AEF\uFF0C\u6309 Ctrl-C \u540E\u91CD\u65B0\u8FD0\u884C supertask gateway\u3002",
|
|
20140
20893
|
"system.runningTasks": "\u5F53\u524D\u8FD0\u884C\u4EFB\u52A1\uFF08{running} / {limit} \u5E76\u53D1\uFF09",
|
|
20141
20894
|
"system.taskStats": "\u4EFB\u52A1\u6982\u89C8",
|
|
20142
20895
|
"system.configFile": "\u914D\u7F6E\u6587\u4EF6",
|
|
@@ -20145,7 +20898,46 @@ var init_ui = __esm({
|
|
|
20145
20898
|
"system.yes": "\u662F",
|
|
20146
20899
|
"system.noDefault": "\u5426\uFF0C\u5F53\u524D\u4F7F\u7528\u9ED8\u8BA4\u503C",
|
|
20147
20900
|
"system.danger": "\u5371\u9669\u64CD\u4F5C",
|
|
20148
|
-
"system.dangerDescription": "\u7CFB\u7EDF\u4F1A\u5148\u521B\u5EFA\u53EF\u6821\u9A8C\u5907\u4EFD\uFF0C\u518D\u4E8B\u52A1\u6027\u6E05\u7A7A\u4EFB\u52A1\u3001\u6267\u884C\u8BB0\u5F55\u548C\
|
|
20901
|
+
"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\u5B9A\u65F6\u4EFB\u52A1\uFF1B\u5B58\u5728\u8FD0\u884C\u4EFB\u52A1\u65F6\u4F1A\u62D2\u7EDD\u64CD\u4F5C\u3002",
|
|
20902
|
+
"template.createTitle": "\u65B0\u5EFA\u5B9A\u65F6\u4EFB\u52A1",
|
|
20903
|
+
"template.editTitle": "\u7F16\u8F91\u5B9A\u65F6\u4EFB\u52A1",
|
|
20904
|
+
"template.formSubtitle": "\u8BBE\u7F6E\u4F55\u65F6\u8FD0\u884C\uFF0C\u4EE5\u53CA OpenCode \u8981\u4F7F\u7528\u7684\u9879\u76EE\u3001\u6A21\u578B\u548C\u63D0\u793A\u8BCD\u3002",
|
|
20905
|
+
"template.name": "\u540D\u79F0",
|
|
20906
|
+
"template.cwd": "\u9879\u76EE\u76EE\u5F55",
|
|
20907
|
+
"template.cwdHint": "OpenCode \u4F1A\u5728\u8FD9\u4E2A\u76EE\u5F55\u4E2D\u6267\u884C\u4EFB\u52A1\u3002",
|
|
20908
|
+
"template.agent": "Agent",
|
|
20909
|
+
"template.model": "\u6A21\u578B",
|
|
20910
|
+
"template.prompt": "\u63D0\u793A\u8BCD",
|
|
20911
|
+
"template.scheduleType": "\u6267\u884C\u65B9\u5F0F",
|
|
20912
|
+
"template.cronExpr": "Cron \u8868\u8FBE\u5F0F",
|
|
20913
|
+
"template.cronHint": "\u4F8B\u5982\uFF1A0 9 * * *\uFF08\u6BCF\u5929 09:00\uFF09",
|
|
20914
|
+
"template.interval": "\u6267\u884C\u95F4\u9694",
|
|
20915
|
+
"template.runAt": "\u6267\u884C\u65F6\u95F4",
|
|
20916
|
+
"template.durationHint": "\u652F\u6301 30s\u30015min\u30011h\u30012d",
|
|
20917
|
+
"template.advanced": "\u66F4\u591A\u6267\u884C\u8BBE\u7F6E",
|
|
20918
|
+
"template.category": "\u5206\u7C7B",
|
|
20919
|
+
"template.batchId": "\u6279\u6B21 ID",
|
|
20920
|
+
"template.importance": "\u91CD\u8981\u7A0B\u5EA6\uFF081-5\uFF09",
|
|
20921
|
+
"template.urgency": "\u7D27\u6025\u7A0B\u5EA6\uFF081-5\uFF09",
|
|
20922
|
+
"template.maxInstances": "\u81EA\u52A8\u8C03\u5EA6\u4E0A\u9650",
|
|
20923
|
+
"template.maxInstancesHint": "\u4EC5\u9650\u5236\u81EA\u52A8\u8C03\u5EA6\uFF1B\u624B\u52A8\u201C\u7ACB\u5373\u8FD0\u884C\u4E00\u6B21\u201D\u59CB\u7EC8\u52A0\u5165\u961F\u5217\u3002\u6D3B\u8DC3\u4EFB\u52A1\u5305\u542B\u5F85\u6267\u884C\u3001\u8FD0\u884C\u4E2D\u548C\u7B49\u5F85\u91CD\u8BD5\u3002",
|
|
20924
|
+
"template.maxRetries": "\u5931\u8D25\u91CD\u8BD5\u6B21\u6570",
|
|
20925
|
+
"template.retryBackoff": "\u91CD\u8BD5\u7B49\u5F85",
|
|
20926
|
+
"template.timeout": "\u5355\u6B21\u8D85\u65F6",
|
|
20927
|
+
"template.optional": "\u53EF\u9009",
|
|
20928
|
+
"template.futureOnly": "\u4FDD\u5B58\u540E\u7684\u8BBE\u7F6E\u7528\u4E8E\u4EE5\u540E\u751F\u6210\u7684\u4EFB\u52A1\uFF1B\u7F16\u8F91\u4E0D\u4F1A\u6539\u53D8\u5DF2\u7ECF\u8FDB\u5165\u961F\u5217\u6216\u6B63\u5728\u6267\u884C\u7684\u4EFB\u52A1\u3002",
|
|
20929
|
+
"projects.title": "\u9879\u76EE\u5206\u7EC4",
|
|
20930
|
+
"projects.description": "\u9879\u76EE\u76EE\u5F55\u662F\u9694\u79BB\u952E\uFF1B\u5148\u770B\u6BCF\u4E2A\u9879\u76EE\u6709\u6CA1\u6709\u8FD0\u884C\u4E2D\u6216\u6392\u961F\u4EFB\u52A1\uFF0C\u518D\u51B3\u5B9A\u662F\u5426\u7EE7\u7EED\u6DFB\u52A0\u3002",
|
|
20931
|
+
"projects.all": "\u5168\u90E8\u9879\u76EE",
|
|
20932
|
+
"projects.legacy": "\u672A\u5206\u7EC4",
|
|
20933
|
+
"projects.legacyHint": "\u65E7\u7248\u672C\u4E2D\u6CA1\u6709\u9879\u76EE\u76EE\u5F55\u7684\u4EFB\u52A1\uFF1B\u53EF\u67E5\u770B\u3001\u53D6\u6D88\u6216\u5220\u9664\uFF0C\u9700\u8981\u4FEE\u6539\u65F6\u8BF7\u5728\u6B63\u786E\u9879\u76EE\u76EE\u5F55\u4E0B\u91CD\u65B0\u521B\u5EFA\u3002",
|
|
20934
|
+
"projects.counts": "\u8FD0\u884C {running} \xB7 \u6392\u961F {pending} \xB7 \u5F02\u5E38 {failed}",
|
|
20935
|
+
"task.createTitle": "\u65B0\u5EFA\u4EFB\u52A1",
|
|
20936
|
+
"task.editTitle": "\u7F16\u8F91\u4EFB\u52A1",
|
|
20937
|
+
"task.formSubtitle": "\u4EFB\u52A1\u4F1A\u7ACB\u5373\u52A0\u5165\u6301\u4E45\u961F\u5217\uFF1B\u5E76\u53D1\u5DF2\u6EE1\u65F6\u4F1A\u81EA\u52A8\u7B49\u5F85\u3002",
|
|
20938
|
+
"task.batchHint": "\u76F8\u540C\u975E\u7A7A\u6279\u6B21 ID \u7684\u4EFB\u52A1\u4E25\u683C\u4E32\u884C\uFF1B\u7559\u7A7A\u5219\u4E0D\u53D7\u6279\u6B21\u4E32\u884C\u9650\u5236\uFF0C\u4F46\u4ECD\u53D7\u5168\u5C40\u5E76\u53D1\u548C\u4F9D\u8D56\u7EA6\u675F\u3002",
|
|
20939
|
+
"task.projectExisting": "\u6B64\u9879\u76EE\u73B0\u6709 {total} \u4E2A\u4EFB\u52A1\uFF1A\u8FD0\u884C {running}\uFF0C\u6392\u961F {pending}\uFF0C\u5F02\u5E38 {failed}\u3002",
|
|
20940
|
+
"task.projectNew": "\u8FD9\u662F\u4E00\u4E2A\u65B0\u9879\u76EE\u5206\u7EC4\uFF1B\u521B\u5EFA\u540E\u4F1A\u51FA\u73B0\u5728\u9879\u76EE\u5217\u8868\u4E2D\u3002",
|
|
20149
20941
|
"theme.label": "\u4E3B\u9898",
|
|
20150
20942
|
"theme.system": "\u8DDF\u968F\u7CFB\u7EDF",
|
|
20151
20943
|
"theme.light": "\u6D45\u8272",
|
|
@@ -20160,21 +20952,31 @@ var init_ui = __esm({
|
|
|
20160
20952
|
"dialog.retryTaskBody": "\u4EFB\u52A1\u5C06\u56DE\u5230\u5F85\u6267\u884C\u72B6\u6001\uFF0C\u5E76\u91CD\u7F6E\u81EA\u52A8\u91CD\u8BD5\u9884\u7B97\u3002",
|
|
20161
20953
|
"dialog.deleteTask": "\u5220\u9664\u4EFB\u52A1 #{id}\uFF1F",
|
|
20162
20954
|
"dialog.deleteTaskBody": "\u4EFB\u52A1\u53CA\u5173\u8054\u6267\u884C\u8BB0\u5F55\u5C06\u6C38\u4E45\u5220\u9664\uFF0C\u6B64\u64CD\u4F5C\u65E0\u6CD5\u64A4\u9500\u3002",
|
|
20163
|
-
"dialog.disableTemplate": "\u7981\u7528\u8FD9\u4E2A\
|
|
20164
|
-
"dialog.disableTemplateBody": "\
|
|
20165
|
-
"dialog.deleteTemplate": "\u5220\u9664\u8FD9\u4E2A\
|
|
20166
|
-
"dialog.deleteTemplateBody": "\
|
|
20167
|
-
"dialog.triggerTemplate": "\u7ACB\u5373\
|
|
20168
|
-
"dialog.triggerTemplateBody": "\u7CFB\u7EDF\u4F1A\u6309\u5F53\u524D\
|
|
20955
|
+
"dialog.disableTemplate": "\u7981\u7528\u8FD9\u4E2A\u5B9A\u65F6\u4EFB\u52A1\uFF1F",
|
|
20956
|
+
"dialog.disableTemplateBody": "\u5B83\u5C06\u505C\u6B62\u81EA\u52A8\u521B\u5EFA\u65B0\u4EFB\u52A1\uFF0C\u5DF2\u6709\u4EFB\u52A1\u4E0D\u53D7\u5F71\u54CD\u3002",
|
|
20957
|
+
"dialog.deleteTemplate": "\u5220\u9664\u8FD9\u4E2A\u5B9A\u65F6\u4EFB\u52A1\uFF1F",
|
|
20958
|
+
"dialog.deleteTemplateBody": "\u5B9A\u65F6\u4EFB\u52A1\u914D\u7F6E\u5C06\u6C38\u4E45\u5220\u9664\uFF0C\u6B64\u64CD\u4F5C\u65E0\u6CD5\u64A4\u9500\u3002",
|
|
20959
|
+
"dialog.triggerTemplate": "\u7ACB\u5373\u8FD0\u884C\u4E00\u6B21\uFF1F",
|
|
20960
|
+
"dialog.triggerTemplateBody": "\u7CFB\u7EDF\u4F1A\u6309\u5F53\u524D\u8BBE\u7F6E\u7ACB\u5373\u521B\u5EFA\u4E00\u4E2A\u4EFB\u52A1\u5E76\u52A0\u5165\u961F\u5217\uFF1B\u82E5\u5168\u5C40\u5E76\u53D1\u5DF2\u6EE1\uFF0C\u4EFB\u52A1\u4F1A\u7B49\u5F85\u6267\u884C\u3002",
|
|
20169
20961
|
"dialog.clearTitle": "\u786E\u8BA4\u6E05\u7A7A\u6570\u636E\u5E93",
|
|
20170
|
-
"dialog.clearBody": "\u8FD9\u4F1A\u5220\u9664\u5168\u90E8\u4EFB\u52A1\u3001\u6267\u884C\u8BB0\u5F55\u548C\
|
|
20962
|
+
"dialog.clearBody": "\u8FD9\u4F1A\u5220\u9664\u5168\u90E8\u4EFB\u52A1\u3001\u6267\u884C\u8BB0\u5F55\u548C\u5B9A\u65F6\u4EFB\u52A1\u3002\u7CFB\u7EDF\u4F1A\u5148\u81EA\u52A8\u5907\u4EFD\u3002",
|
|
20171
20963
|
"dialog.clearInstruction": "\u8F93\u5165 CLEAR \u4EE5\u786E\u8BA4",
|
|
20964
|
+
"dialog.restartGateway": "\u91CD\u542F\u5E76\u5E94\u7528\u8BBE\u7F6E\uFF1F",
|
|
20965
|
+
"dialog.restartGatewayBody": "Gateway \u4F1A\u77ED\u6682\u79BB\u7EBF\uFF0C\u901A\u5E38\u6570\u79D2\u540E\u7531 PM2 \u81EA\u52A8\u6062\u590D\u3002",
|
|
20966
|
+
"dialog.restartGatewayRunningBody": "\u5F53\u524D\u6709 {count} \u4E2A\u4EFB\u52A1\u5728\u8FD0\u884C\u3002\u7CFB\u7EDF\u4F1A\u5148\u7B49\u5F85\u5B83\u4EEC\u5B8C\u6210\uFF1B\u8D85\u65F6\u540E\uFF0C\u672A\u5B8C\u6210\u4EFB\u52A1\u4F1A\u88AB\u5B89\u5168\u505C\u6B62\u5E76\u653E\u56DE\u961F\u5217\uFF0C\u7136\u540E Gateway \u7531 PM2 \u81EA\u52A8\u6062\u590D\u3002",
|
|
20172
20967
|
"feedback.retryFailed": "\u91CD\u8BD5\u5931\u8D25",
|
|
20173
20968
|
"feedback.cancelFailed": "\u53D6\u6D88\u5931\u8D25",
|
|
20174
20969
|
"feedback.deleteFailed": "\u5220\u9664\u5931\u8D25",
|
|
20175
20970
|
"feedback.requestFailed": "\u8BF7\u6C42\u5931\u8D25",
|
|
20176
20971
|
"feedback.triggered": "\u5DF2\u521B\u5EFA\u4EFB\u52A1 #{id}",
|
|
20177
|
-
"feedback.
|
|
20972
|
+
"feedback.taskCreated": "\u4EFB\u52A1 #{id} \u5DF2\u52A0\u5165\u961F\u5217",
|
|
20973
|
+
"feedback.taskUpdated": "\u4EFB\u52A1 #{id} \u5DF2\u66F4\u65B0",
|
|
20974
|
+
"feedback.templateCreated": "\u5B9A\u65F6\u4EFB\u52A1\u5DF2\u521B\u5EFA",
|
|
20975
|
+
"feedback.templateUpdated": "\u5B9A\u65F6\u4EFB\u52A1\u5DF2\u66F4\u65B0",
|
|
20976
|
+
"feedback.configSaved": "\u8BBE\u7F6E\u5DF2\u4FDD\u5B58",
|
|
20977
|
+
"feedback.sessionCommandCopied": "\u4F1A\u8BDD\u547D\u4EE4\u5DF2\u590D\u5236\uFF0C\u53EF\u76F4\u63A5\u7C98\u8D34\u5230\u7EC8\u7AEF\u8FD0\u884C",
|
|
20978
|
+
"feedback.restarting": "Gateway \u6B63\u5728\u91CD\u542F\uFF0C\u8BF7\u7A0D\u5019\u2026",
|
|
20979
|
+
"feedback.restartTimeout": "Gateway \u5C1A\u672A\u6062\u590D\uFF0C\u8BF7\u7A0D\u540E\u5237\u65B0\u6216\u68C0\u67E5 PM2 \u72B6\u6001",
|
|
20178
20980
|
"feedback.databaseCleared": "\u6570\u636E\u5E93\u5DF2\u6E05\u7A7A\uFF0C\u5907\u4EFD\u4F4D\u4E8E\uFF1A{path}",
|
|
20179
20981
|
"feedback.copyFailed": "\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u9009\u62E9\u5185\u5BB9",
|
|
20180
20982
|
"a11y.skip": "\u8DF3\u5230\u4E3B\u8981\u5185\u5BB9",
|
|
@@ -20187,13 +20989,13 @@ var init_ui = __esm({
|
|
|
20187
20989
|
"app.local": "Running locally",
|
|
20188
20990
|
"app.footer": "Local-first \xB7 Your data stays on this device",
|
|
20189
20991
|
"nav.tasks": "Tasks",
|
|
20190
|
-
"nav.templates": "
|
|
20992
|
+
"nav.templates": "Scheduled tasks",
|
|
20191
20993
|
"nav.runs": "Runs",
|
|
20192
20994
|
"nav.system": "System",
|
|
20193
20995
|
"page.tasks.title": "Task queue",
|
|
20194
20996
|
"page.tasks.description": "Track priority, execution state, and retries, then act on tasks that need attention.",
|
|
20195
|
-
"page.templates.title": "
|
|
20196
|
-
"page.templates.description": "
|
|
20997
|
+
"page.templates.title": "Scheduled tasks",
|
|
20998
|
+
"page.templates.description": "Create and edit cron, fixed-interval, and one-time tasks, including their model, agent, and prompt.",
|
|
20197
20999
|
"page.runs.title": "Execution history",
|
|
20198
21000
|
"page.runs.description": "Inspect the status, duration, heartbeat, and output of every agent run.",
|
|
20199
21001
|
"page.system.title": "System settings",
|
|
@@ -20203,29 +21005,43 @@ var init_ui = __esm({
|
|
|
20203
21005
|
"action.retry": "Retry",
|
|
20204
21006
|
"action.cancel": "Cancel",
|
|
20205
21007
|
"action.delete": "Delete",
|
|
21008
|
+
"action.edit": "Edit",
|
|
20206
21009
|
"action.enable": "Enable",
|
|
20207
21010
|
"action.disable": "Disable",
|
|
20208
21011
|
"action.trigger": "Run now",
|
|
21012
|
+
"action.continueSession": "Continue session",
|
|
20209
21013
|
"action.logs": "View log",
|
|
20210
21014
|
"action.hideLogs": "Hide log",
|
|
20211
21015
|
"action.save": "Save settings",
|
|
21016
|
+
"action.saveAndRestart": "Save and restart",
|
|
20212
21017
|
"action.copy": "Copy JSON",
|
|
20213
21018
|
"action.close": "Close",
|
|
20214
21019
|
"action.confirm": "Confirm",
|
|
20215
21020
|
"action.clearDatabase": "Clear database",
|
|
21021
|
+
"action.createTask": "New task",
|
|
21022
|
+
"action.saveTask": "Add to queue",
|
|
21023
|
+
"action.updateTask": "Save changes",
|
|
21024
|
+
"action.createTemplate": "New scheduled task",
|
|
21025
|
+
"action.saveTemplate": "Save scheduled task",
|
|
20216
21026
|
"status.pending": "Pending",
|
|
20217
21027
|
"status.running": "Running",
|
|
20218
21028
|
"status.done": "Done",
|
|
20219
|
-
"status.failed": "
|
|
20220
|
-
"status.dead_letter": "
|
|
21029
|
+
"status.failed": "Waiting to retry",
|
|
21030
|
+
"status.dead_letter": "Stopped",
|
|
21031
|
+
"status.deadLetterHint": "The system will not run this task automatically again. Its retries may be exhausted or a dependency cannot continue; inspect the error, then retry manually if appropriate.",
|
|
21032
|
+
"status.deadLetterAction": "Inspect the error, then retry manually",
|
|
20221
21033
|
"status.cancelled": "Cancelled",
|
|
21034
|
+
"status.executionStillActive": "The execution process is still stopping and temporarily occupies a slot",
|
|
20222
21035
|
"status.unknown": "Unknown",
|
|
21036
|
+
"runStatus.running": "Running",
|
|
21037
|
+
"runStatus.done": "Succeeded",
|
|
21038
|
+
"runStatus.failed": "Failed",
|
|
20223
21039
|
"stats.total": "Total tasks",
|
|
20224
21040
|
"stats.pending": "Pending",
|
|
20225
21041
|
"stats.running": "Running",
|
|
20226
21042
|
"stats.done": "Completed",
|
|
20227
|
-
"stats.failedDead": "
|
|
20228
|
-
"stats.templates": "
|
|
21043
|
+
"stats.failedDead": "Waiting to retry & stopped",
|
|
21044
|
+
"stats.templates": "Scheduled tasks",
|
|
20229
21045
|
"stats.enabled": "Enabled",
|
|
20230
21046
|
"stats.disabled": "Disabled",
|
|
20231
21047
|
"stats.records": "Total runs",
|
|
@@ -20233,7 +21049,7 @@ var init_ui = __esm({
|
|
|
20233
21049
|
"stats.pageFailed": "Failed here",
|
|
20234
21050
|
"stats.pageRunning": "Running here",
|
|
20235
21051
|
"filter.all": "All",
|
|
20236
|
-
"filter.searchTasks": "Search tasks, agents, or prompts on this page",
|
|
21052
|
+
"filter.searchTasks": "Search tasks, projects, batches, agents, or prompts on this page",
|
|
20237
21053
|
"filter.noResults": "No tasks match this search",
|
|
20238
21054
|
"table.id": "ID",
|
|
20239
21055
|
"table.task": "Task",
|
|
@@ -20257,14 +21073,14 @@ var init_ui = __esm({
|
|
|
20257
21073
|
"pagination.next": "Next",
|
|
20258
21074
|
"pagination.summary": "Page {page} of {pages} \xB7 {total} items",
|
|
20259
21075
|
"empty.tasks": "Your queue is empty",
|
|
20260
|
-
"empty.tasksHint": "
|
|
20261
|
-
"empty.templates": "No
|
|
20262
|
-
"empty.templatesHint": "
|
|
21076
|
+
"empty.tasksHint": "Select \u201CNew task\u201D, or use the OpenCode plugin or supertask add.",
|
|
21077
|
+
"empty.templates": "No scheduled tasks yet",
|
|
21078
|
+
"empty.templatesHint": "Select \u201CNew scheduled task\u201D to create one.",
|
|
20263
21079
|
"empty.runs": "No execution history yet",
|
|
20264
21080
|
"empty.running": "No tasks are running right now",
|
|
20265
21081
|
"schedule.cron": "Cron",
|
|
20266
|
-
"schedule.recurring": "
|
|
20267
|
-
"schedule.delayed": "
|
|
21082
|
+
"schedule.recurring": "Fixed interval",
|
|
21083
|
+
"schedule.delayed": "One-time",
|
|
20268
21084
|
"schedule.unknown": "Unknown",
|
|
20269
21085
|
"schedule.enabled": "Enabled",
|
|
20270
21086
|
"schedule.disabled": "Disabled",
|
|
@@ -20273,14 +21089,14 @@ var init_ui = __esm({
|
|
|
20273
21089
|
"schedule.hours": "{count} hr",
|
|
20274
21090
|
"schedule.days": "{count} days",
|
|
20275
21091
|
"schedule.overdue": "Overdue",
|
|
20276
|
-
"system.worker": "
|
|
20277
|
-
"system.scheduler": "
|
|
20278
|
-
"system.watchdog": "
|
|
21092
|
+
"system.worker": "Task execution",
|
|
21093
|
+
"system.scheduler": "Scheduled task service",
|
|
21094
|
+
"system.watchdog": "Runtime monitor",
|
|
20279
21095
|
"system.maxConcurrency": "Max concurrency",
|
|
20280
21096
|
"system.pollInterval": "Poll interval",
|
|
20281
21097
|
"system.heartbeatInterval": "Heartbeat interval",
|
|
20282
21098
|
"system.taskTimeout": "Task timeout",
|
|
20283
|
-
"system.schedulerEnabled": "Enable
|
|
21099
|
+
"system.schedulerEnabled": "Enable scheduled tasks",
|
|
20284
21100
|
"system.checkInterval": "Check interval",
|
|
20285
21101
|
"system.heartbeatTimeout": "Heartbeat timeout",
|
|
20286
21102
|
"system.cleanupInterval": "Cleanup interval",
|
|
@@ -20290,8 +21106,12 @@ var init_ui = __esm({
|
|
|
20290
21106
|
"system.minutes": "minutes",
|
|
20291
21107
|
"system.hours": "hours",
|
|
20292
21108
|
"system.days": "days",
|
|
20293
|
-
"system.activeTemplates": "
|
|
20294
|
-
"system.saveHint": "Restart Gateway to apply saved settings",
|
|
21109
|
+
"system.activeTemplates": "Enabled scheduled tasks",
|
|
21110
|
+
"system.saveHint": "Restart Gateway to apply saved settings.",
|
|
21111
|
+
"system.configApplied": "The settings shown here are active.",
|
|
21112
|
+
"system.configPending": "Settings are saved, but this Gateway is still using the values from its last start.",
|
|
21113
|
+
"system.configForeground": "This page is not connected to Gateway runtime settings. Restart Gateway after saving.",
|
|
21114
|
+
"system.configRestartManually": "Settings are saved but not active. Return to the terminal that launched Gateway, press Ctrl-C, then run supertask gateway again.",
|
|
20295
21115
|
"system.runningTasks": "Active tasks ({running} / {limit} concurrent)",
|
|
20296
21116
|
"system.taskStats": "Task overview",
|
|
20297
21117
|
"system.configFile": "Configuration file",
|
|
@@ -20301,6 +21121,45 @@ var init_ui = __esm({
|
|
|
20301
21121
|
"system.noDefault": "No, using defaults",
|
|
20302
21122
|
"system.danger": "Danger zone",
|
|
20303
21123
|
"system.dangerDescription": "A verified backup is created first, then tasks, runs, and templates are cleared transactionally. Active work blocks this operation.",
|
|
21124
|
+
"template.createTitle": "New scheduled task",
|
|
21125
|
+
"template.editTitle": "Edit scheduled task",
|
|
21126
|
+
"template.formSubtitle": "Choose when to run and which project, model, and prompt OpenCode should use.",
|
|
21127
|
+
"template.name": "Name",
|
|
21128
|
+
"template.cwd": "Project directory",
|
|
21129
|
+
"template.cwdHint": "OpenCode runs the task in this directory.",
|
|
21130
|
+
"template.agent": "Agent",
|
|
21131
|
+
"template.model": "Model",
|
|
21132
|
+
"template.prompt": "Prompt",
|
|
21133
|
+
"template.scheduleType": "Schedule",
|
|
21134
|
+
"template.cronExpr": "Cron expression",
|
|
21135
|
+
"template.cronHint": "Example: 0 9 * * * (daily at 09:00)",
|
|
21136
|
+
"template.interval": "Interval",
|
|
21137
|
+
"template.runAt": "Run at",
|
|
21138
|
+
"template.durationHint": "Supports 30s, 5min, 1h, 2d",
|
|
21139
|
+
"template.advanced": "More execution settings",
|
|
21140
|
+
"template.category": "Category",
|
|
21141
|
+
"template.batchId": "Batch ID",
|
|
21142
|
+
"template.importance": "Importance (1-5)",
|
|
21143
|
+
"template.urgency": "Urgency (1-5)",
|
|
21144
|
+
"template.maxInstances": "Automatic scheduling limit",
|
|
21145
|
+
"template.maxInstancesHint": "Limits automatic scheduling only. \u201CRun now\u201D always queues a task. Active instances include pending, running, and retry-waiting tasks.",
|
|
21146
|
+
"template.maxRetries": "Failure retries",
|
|
21147
|
+
"template.retryBackoff": "Retry delay",
|
|
21148
|
+
"template.timeout": "Run timeout",
|
|
21149
|
+
"template.optional": "Optional",
|
|
21150
|
+
"template.futureOnly": "Saved settings apply to tasks created in the future. Editing does not change queued or running tasks.",
|
|
21151
|
+
"projects.title": "Projects",
|
|
21152
|
+
"projects.description": "The project directory is the isolation key. Check active and queued work before adding more.",
|
|
21153
|
+
"projects.all": "All projects",
|
|
21154
|
+
"projects.legacy": "Ungrouped",
|
|
21155
|
+
"projects.legacyHint": "Legacy tasks without a project directory. View, cancel, or delete them; recreate under the correct project to make changes.",
|
|
21156
|
+
"projects.counts": "Running {running} \xB7 queued {pending} \xB7 issues {failed}",
|
|
21157
|
+
"task.createTitle": "New task",
|
|
21158
|
+
"task.editTitle": "Edit task",
|
|
21159
|
+
"task.formSubtitle": "The task enters the durable queue immediately and waits automatically when concurrency is full.",
|
|
21160
|
+
"task.batchHint": "Tasks with the same non-empty batch ID run serially. Blank removes the batch constraint; global concurrency and dependencies still apply.",
|
|
21161
|
+
"task.projectExisting": "This project has {total} tasks: {running} running, {pending} queued, and {failed} with issues.",
|
|
21162
|
+
"task.projectNew": "This is a new project group. It appears in the project list after creation.",
|
|
20304
21163
|
"theme.label": "Theme",
|
|
20305
21164
|
"theme.system": "System",
|
|
20306
21165
|
"theme.light": "Light",
|
|
@@ -20318,18 +21177,28 @@ var init_ui = __esm({
|
|
|
20318
21177
|
"dialog.disableTemplate": "Disable this schedule?",
|
|
20319
21178
|
"dialog.disableTemplateBody": "It will stop creating new tasks automatically. Existing tasks are unchanged.",
|
|
20320
21179
|
"dialog.deleteTemplate": "Delete this schedule?",
|
|
20321
|
-
"dialog.deleteTemplateBody": "This
|
|
21180
|
+
"dialog.deleteTemplateBody": "This scheduled task configuration will be permanently deleted.",
|
|
20322
21181
|
"dialog.triggerTemplate": "Run this schedule now?",
|
|
20323
|
-
"dialog.triggerTemplateBody": "A
|
|
21182
|
+
"dialog.triggerTemplateBody": "A task is queued immediately using the current settings. If global concurrency is full, it waits to run.",
|
|
20324
21183
|
"dialog.clearTitle": "Confirm database clear",
|
|
20325
|
-
"dialog.clearBody": "This deletes every task, run, and
|
|
21184
|
+
"dialog.clearBody": "This deletes every task, run, and scheduled task after creating a backup.",
|
|
20326
21185
|
"dialog.clearInstruction": "Type CLEAR to confirm",
|
|
21186
|
+
"dialog.restartGateway": "Restart and apply settings?",
|
|
21187
|
+
"dialog.restartGatewayBody": "Gateway will be briefly unavailable and should recover through PM2 within a few seconds.",
|
|
21188
|
+
"dialog.restartGatewayRunningBody": "{count} tasks are running. The system waits for them first; after the grace period, unfinished tasks are stopped safely and returned to the queue before PM2 restores Gateway.",
|
|
20327
21189
|
"feedback.retryFailed": "Retry failed",
|
|
20328
21190
|
"feedback.cancelFailed": "Cancellation failed",
|
|
20329
21191
|
"feedback.deleteFailed": "Delete failed",
|
|
20330
21192
|
"feedback.requestFailed": "Request failed",
|
|
20331
21193
|
"feedback.triggered": "Task #{id} created",
|
|
20332
|
-
"feedback.
|
|
21194
|
+
"feedback.taskCreated": "Task #{id} added to the queue",
|
|
21195
|
+
"feedback.taskUpdated": "Task #{id} updated",
|
|
21196
|
+
"feedback.templateCreated": "Scheduled task created",
|
|
21197
|
+
"feedback.templateUpdated": "Scheduled task updated",
|
|
21198
|
+
"feedback.configSaved": "Settings saved",
|
|
21199
|
+
"feedback.sessionCommandCopied": "Session command copied. Paste it into a terminal to continue.",
|
|
21200
|
+
"feedback.restarting": "Gateway is restarting\u2026",
|
|
21201
|
+
"feedback.restartTimeout": "Gateway has not recovered yet. Refresh later or check PM2 status.",
|
|
20333
21202
|
"feedback.databaseCleared": "Database cleared. Backup: {path}",
|
|
20334
21203
|
"feedback.copyFailed": "Copy failed. Select the content manually.",
|
|
20335
21204
|
"a11y.skip": "Skip to main content",
|
|
@@ -20364,8 +21233,8 @@ var init_ui = __esm({
|
|
|
20364
21233
|
radial-gradient(circle at 10% -10%,var(--bg-glow),transparent 34rem),var(--bg);
|
|
20365
21234
|
font-family:Inter,ui-sans-serif,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
|
20366
21235
|
font-size:14px; line-height:1.5; -webkit-font-smoothing:antialiased; }
|
|
20367
|
-
button,input,select { font:inherit; }
|
|
20368
|
-
button,a,select,input { -webkit-tap-highlight-color:transparent; }
|
|
21236
|
+
button,input,select,textarea { font:inherit; }
|
|
21237
|
+
button,a,select,input,textarea { -webkit-tap-highlight-color:transparent; }
|
|
20369
21238
|
a { color:inherit; }
|
|
20370
21239
|
code,.mono,.m { font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace; }
|
|
20371
21240
|
.skip-link { position:fixed; left:16px; top:-60px; z-index:200; padding:10px 14px; border-radius:8px;
|
|
@@ -20446,6 +21315,16 @@ var init_ui = __esm({
|
|
|
20446
21315
|
.panel-head { min-height:52px; display:flex; align-items:center; justify-content:space-between; gap:12px; padding:0 18px;
|
|
20447
21316
|
border-bottom:1px solid var(--border); }
|
|
20448
21317
|
.panel-head h2,.panel-head h3 { margin:0; font-size:14px; letter-spacing:-.01em; }
|
|
21318
|
+
.panel-head p { margin:3px 0 0; color:var(--text-2); font-size:11px; }
|
|
21319
|
+
.project-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(230px,1fr)); gap:10px; padding:14px; }
|
|
21320
|
+
.project-card { min-width:0; padding:12px; border:1px solid var(--border); border-radius:10px; color:var(--text); background:var(--surface-2); text-decoration:none; }
|
|
21321
|
+
.project-card:hover { border-color:var(--border-strong); background:var(--surface-3); }
|
|
21322
|
+
.project-card.active { border-color:color-mix(in srgb,var(--primary) 45%,var(--border)); background:var(--primary-soft); }
|
|
21323
|
+
.project-card-head { display:flex; align-items:center; justify-content:space-between; gap:10px; }
|
|
21324
|
+
.project-card-head strong { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
21325
|
+
.project-card-head span { color:var(--text-3); font-size:11px; }
|
|
21326
|
+
.project-path { margin-top:4px; overflow:hidden; color:var(--text-3); font-family:"SFMono-Regular",Consolas,monospace; font-size:10px; text-overflow:ellipsis; white-space:nowrap; }
|
|
21327
|
+
.project-counts { margin-top:8px; color:var(--text-2); font-size:11px; }
|
|
20449
21328
|
.table-wrap { width:100%; overflow-x:auto; }
|
|
20450
21329
|
table { width:100%; border-collapse:separate; border-spacing:0; font-size:13px; }
|
|
20451
21330
|
th { height:42px; padding:0 13px; color:var(--text-3); background:var(--surface-2); border-bottom:1px solid var(--border);
|
|
@@ -20456,6 +21335,7 @@ var init_ui = __esm({
|
|
|
20456
21335
|
tbody tr:hover { background:color-mix(in srgb,var(--primary-soft) 30%,transparent); }
|
|
20457
21336
|
.task-name { font-weight:680; color:var(--text); }
|
|
20458
21337
|
.task-prompt { max-width:520px; margin-top:3px; color:var(--text-2); font-size:12px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
|
21338
|
+
.task-context { margin-top:6px; }
|
|
20459
21339
|
.muted,.mu { color:var(--text-2); }
|
|
20460
21340
|
.faint { color:var(--text-3); }
|
|
20461
21341
|
.small,.sm { font-size:12px; }
|
|
@@ -20539,6 +21419,18 @@ var init_ui = __esm({
|
|
|
20539
21419
|
.dialog-head p { margin:3px 0 0; color:var(--text-2); font-size:11px; }
|
|
20540
21420
|
.dialog-body { max-height:70vh; overflow:auto; padding:18px; }
|
|
20541
21421
|
.dialog-actions { display:flex; align-items:center; justify-content:flex-end; gap:8px; padding:14px 18px; border-top:1px solid var(--border); }
|
|
21422
|
+
.template-dialog { width:min(880px,calc(100% - 32px)); }
|
|
21423
|
+
.template-form-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:15px; }
|
|
21424
|
+
.form-field { display:flex; min-width:0; flex-direction:column; gap:6px; color:var(--text-2); font-size:12px; font-weight:650; }
|
|
21425
|
+
.form-field-wide { grid-column:1 / -1; }
|
|
21426
|
+
.form-field input,.form-field select,.form-field textarea { width:100%; min-height:39px; padding:8px 10px; border:1px solid var(--border); border-radius:9px;
|
|
21427
|
+
outline:none; color:var(--text); background:var(--surface-2); font-weight:450; }
|
|
21428
|
+
.form-field textarea { resize:vertical; line-height:1.5; }
|
|
21429
|
+
.form-field input:focus,.form-field select:focus,.form-field textarea:focus { border-color:var(--primary); box-shadow:var(--focus); background:var(--surface); }
|
|
21430
|
+
.form-field small { color:var(--text-3); font-size:10px; font-weight:450; }
|
|
21431
|
+
.advanced-fields { margin-top:18px; padding-top:14px; border-top:1px solid var(--border); }
|
|
21432
|
+
.advanced-fields summary { margin-bottom:14px; color:var(--text-2); cursor:pointer; font-size:12px; font-weight:700; }
|
|
21433
|
+
.form-note { margin:16px 0 0; color:var(--text-3); font-size:11px; }
|
|
20542
21434
|
.json-view { min-height:160px; margin:0; padding:15px; overflow:auto; border:1px solid var(--border); border-radius:10px; color:var(--text-2);
|
|
20543
21435
|
background:var(--surface-2); font-size:12px; white-space:pre-wrap; overflow-wrap:anywhere; }
|
|
20544
21436
|
.confirm-copy { color:var(--text-2); margin:0; }
|
|
@@ -20604,6 +21496,9 @@ var init_ui = __esm({
|
|
|
20604
21496
|
.responsive-table td[data-primary]::before { display:none; }
|
|
20605
21497
|
.responsive-table .task-prompt { max-width:100%; }
|
|
20606
21498
|
.responsive-table .actions { justify-content:flex-start; }
|
|
21499
|
+
.project-grid { grid-template-columns:1fr; }
|
|
21500
|
+
.template-form-grid { grid-template-columns:1fr; }
|
|
21501
|
+
.form-field-wide { grid-column:auto; }
|
|
20607
21502
|
}
|
|
20608
21503
|
@media (max-width:520px) {
|
|
20609
21504
|
.stats-grid,.stats-grid.three { grid-template-columns:1fr 1fr; }
|
|
@@ -20631,10 +21526,45 @@ var init_ui = __esm({
|
|
|
20631
21526
|
var web_exports = {};
|
|
20632
21527
|
__export(web_exports, {
|
|
20633
21528
|
dashboardApp: () => dashboardApp,
|
|
20634
|
-
default: () => web_default
|
|
21529
|
+
default: () => web_default,
|
|
21530
|
+
isSafeDashboardRestartTarget: () => isSafeDashboardRestartTarget,
|
|
21531
|
+
resolveDashboardConfigState: () => resolveDashboardConfigState,
|
|
21532
|
+
setDashboardRuntimeConfig: () => setDashboardRuntimeConfig
|
|
20635
21533
|
});
|
|
20636
|
-
import { existsSync as
|
|
20637
|
-
import { dirname as
|
|
21534
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync4, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
21535
|
+
import { basename as basename3, dirname as dirname7 } from "path";
|
|
21536
|
+
function setDashboardRuntimeConfig(config) {
|
|
21537
|
+
runtimeConfig = config === null ? null : structuredClone(config);
|
|
21538
|
+
}
|
|
21539
|
+
function isValidSessionId(value) {
|
|
21540
|
+
return typeof value === "string" && SESSION_ID_PATTERN.test(value);
|
|
21541
|
+
}
|
|
21542
|
+
function maskSessionId(value) {
|
|
21543
|
+
return isValidSessionId(value) ? `${value.slice(4, 7)}***${value.slice(-3)}` : "\u2014";
|
|
21544
|
+
}
|
|
21545
|
+
function sessionCommand(value) {
|
|
21546
|
+
if (!isValidSessionId(value)) throw new Error("session unavailable");
|
|
21547
|
+
return `opencode --session ${value}`;
|
|
21548
|
+
}
|
|
21549
|
+
function configsEqual(left, right) {
|
|
21550
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
21551
|
+
}
|
|
21552
|
+
function resolveDashboardConfigState(runtimeAvailable, restartRequired, managedRestart) {
|
|
21553
|
+
if (!runtimeAvailable) return "foreground";
|
|
21554
|
+
if (!restartRequired) return "applied";
|
|
21555
|
+
return managedRestart ? "pending" : "manual";
|
|
21556
|
+
}
|
|
21557
|
+
function isSafeDashboardRestartTarget(diagnostic, currentPid) {
|
|
21558
|
+
return diagnostic.pm2Installed && diagnostic.processFound && diagnostic.status === "online" && diagnostic.pid === currentPid && diagnostic.ready && diagnostic.scopeMatches;
|
|
21559
|
+
}
|
|
21560
|
+
function canRestartCurrentGateway() {
|
|
21561
|
+
if (runtimeConfig === null) return false;
|
|
21562
|
+
try {
|
|
21563
|
+
return isSafeDashboardRestartTarget(getGatewayDiagnostic(), process.pid);
|
|
21564
|
+
} catch {
|
|
21565
|
+
return false;
|
|
21566
|
+
}
|
|
21567
|
+
}
|
|
20638
21568
|
function parsePositiveInteger(value) {
|
|
20639
21569
|
if (!/^\d+$/.test(value)) return null;
|
|
20640
21570
|
const parsed = Number(value);
|
|
@@ -20643,6 +21573,134 @@ function parsePositiveInteger(value) {
|
|
|
20643
21573
|
function parseTaskStatus(value) {
|
|
20644
21574
|
return TASK_STATUSES.has(value) ? value : null;
|
|
20645
21575
|
}
|
|
21576
|
+
function parseTaskPayload(value) {
|
|
21577
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
21578
|
+
throw new Error("\u8BF7\u6C42\u5185\u5BB9\u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
21579
|
+
}
|
|
21580
|
+
const input = value;
|
|
21581
|
+
const requiredString = (name) => {
|
|
21582
|
+
const field = input[name];
|
|
21583
|
+
if (typeof field !== "string" || !field.trim()) throw new Error(`${name} \u4E0D\u80FD\u4E3A\u7A7A`);
|
|
21584
|
+
return field.trim();
|
|
21585
|
+
};
|
|
21586
|
+
const optionalString = (name, fallback = null) => {
|
|
21587
|
+
const field = input[name];
|
|
21588
|
+
if (field === void 0 || field === null || field === "") return fallback;
|
|
21589
|
+
if (typeof field !== "string") throw new Error(`${name} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
|
|
21590
|
+
return field.trim() || fallback;
|
|
21591
|
+
};
|
|
21592
|
+
const integer2 = (name, fallback) => {
|
|
21593
|
+
const field = input[name];
|
|
21594
|
+
if (field === void 0 || field === null || field === "") return fallback;
|
|
21595
|
+
if (typeof field !== "number" || !Number.isSafeInteger(field)) {
|
|
21596
|
+
throw new Error(`${name} \u5FC5\u987B\u662F\u6574\u6570`);
|
|
21597
|
+
}
|
|
21598
|
+
return field;
|
|
21599
|
+
};
|
|
21600
|
+
const duration = (name, fallback, allowZero = false) => {
|
|
21601
|
+
const raw2 = optionalString(name, fallback);
|
|
21602
|
+
if (raw2 === null) return null;
|
|
21603
|
+
if (allowZero && raw2 === "0") return 0;
|
|
21604
|
+
if (Number.isFinite(Number(raw2))) {
|
|
21605
|
+
throw new Error(`${name} \u5FC5\u987B\u5E26\u65F6\u95F4\u5355\u4F4D\uFF0C\u8BF7\u4F7F\u7528 30s\u30015min\u30011h \u6216 2d`);
|
|
21606
|
+
}
|
|
21607
|
+
const milliseconds = parseDuration(raw2);
|
|
21608
|
+
if (milliseconds === null || !Number.isSafeInteger(milliseconds)) {
|
|
21609
|
+
throw new Error(`${name} \u683C\u5F0F\u65E0\u6548\uFF0C\u8BF7\u4F7F\u7528 30s\u30015min\u30011h \u6216 2d`);
|
|
21610
|
+
}
|
|
21611
|
+
return milliseconds;
|
|
21612
|
+
};
|
|
21613
|
+
return {
|
|
21614
|
+
name: requiredString("name"),
|
|
21615
|
+
cwd: requiredString("cwd"),
|
|
21616
|
+
agent: requiredString("agent"),
|
|
21617
|
+
model: optionalString("model", "default"),
|
|
21618
|
+
prompt: requiredString("prompt"),
|
|
21619
|
+
category: optionalString("category", "general"),
|
|
21620
|
+
batchId: optionalString("batchId"),
|
|
21621
|
+
importance: integer2("importance", 3),
|
|
21622
|
+
urgency: integer2("urgency", 3),
|
|
21623
|
+
maxRetries: integer2("maxRetries", 3),
|
|
21624
|
+
retryBackoffMs: duration("retryBackoff", "30s", true),
|
|
21625
|
+
timeoutMs: duration("timeout", null)
|
|
21626
|
+
};
|
|
21627
|
+
}
|
|
21628
|
+
function editableTaskPayload(input) {
|
|
21629
|
+
return {
|
|
21630
|
+
name: input.name,
|
|
21631
|
+
agent: input.agent,
|
|
21632
|
+
model: input.model ?? "default",
|
|
21633
|
+
prompt: input.prompt,
|
|
21634
|
+
category: input.category ?? "general",
|
|
21635
|
+
batchId: input.batchId ?? null,
|
|
21636
|
+
importance: input.importance ?? 3,
|
|
21637
|
+
urgency: input.urgency ?? 3,
|
|
21638
|
+
maxRetries: input.maxRetries ?? 3,
|
|
21639
|
+
retryBackoffMs: input.retryBackoffMs ?? 3e4,
|
|
21640
|
+
timeoutMs: input.timeoutMs ?? null
|
|
21641
|
+
};
|
|
21642
|
+
}
|
|
21643
|
+
function parseTemplatePayload(value) {
|
|
21644
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
21645
|
+
throw new Error("\u8BF7\u6C42\u5185\u5BB9\u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
21646
|
+
}
|
|
21647
|
+
const input = value;
|
|
21648
|
+
const requiredString = (name) => {
|
|
21649
|
+
const field = input[name];
|
|
21650
|
+
if (typeof field !== "string" || !field.trim()) throw new Error(`${name} \u4E0D\u80FD\u4E3A\u7A7A`);
|
|
21651
|
+
return field.trim();
|
|
21652
|
+
};
|
|
21653
|
+
const optionalString = (name, fallback = null) => {
|
|
21654
|
+
const field = input[name];
|
|
21655
|
+
if (field === void 0 || field === null || field === "") return fallback;
|
|
21656
|
+
if (typeof field !== "string") throw new Error(`${name} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
|
|
21657
|
+
return field.trim() || fallback;
|
|
21658
|
+
};
|
|
21659
|
+
const integer2 = (name, fallback) => {
|
|
21660
|
+
const field = input[name];
|
|
21661
|
+
if (field === void 0 || field === null || field === "") return fallback;
|
|
21662
|
+
if (typeof field !== "number" || !Number.isSafeInteger(field)) {
|
|
21663
|
+
throw new Error(`${name} \u5FC5\u987B\u662F\u6574\u6570`);
|
|
21664
|
+
}
|
|
21665
|
+
return field;
|
|
21666
|
+
};
|
|
21667
|
+
const duration = (name, fallback, allowZero = false) => {
|
|
21668
|
+
const raw2 = optionalString(name, fallback);
|
|
21669
|
+
if (raw2 === null) return null;
|
|
21670
|
+
if (allowZero && raw2 === "0") return 0;
|
|
21671
|
+
if (Number.isFinite(Number(raw2))) {
|
|
21672
|
+
throw new Error(`${name} \u5FC5\u987B\u5E26\u65F6\u95F4\u5355\u4F4D\uFF0C\u8BF7\u4F7F\u7528 30s\u30015min\u30011h \u6216 2d`);
|
|
21673
|
+
}
|
|
21674
|
+
const milliseconds = parseDuration(raw2);
|
|
21675
|
+
if (milliseconds === null || !Number.isSafeInteger(milliseconds)) {
|
|
21676
|
+
throw new Error(`${name} \u683C\u5F0F\u65E0\u6548\uFF0C\u8BF7\u4F7F\u7528 30s\u30015min\u30011h \u6216 2d`);
|
|
21677
|
+
}
|
|
21678
|
+
return milliseconds;
|
|
21679
|
+
};
|
|
21680
|
+
const scheduleType = requiredString("scheduleType");
|
|
21681
|
+
if (!["cron", "delayed", "recurring"].includes(scheduleType)) {
|
|
21682
|
+
throw new Error("scheduleType \u5FC5\u987B\u662F cron\u3001delayed \u6216 recurring");
|
|
21683
|
+
}
|
|
21684
|
+
return {
|
|
21685
|
+
name: requiredString("name"),
|
|
21686
|
+
agent: requiredString("agent"),
|
|
21687
|
+
model: optionalString("model", "default"),
|
|
21688
|
+
prompt: requiredString("prompt"),
|
|
21689
|
+
cwd: requiredString("cwd"),
|
|
21690
|
+
category: optionalString("category", "general"),
|
|
21691
|
+
importance: integer2("importance", 3),
|
|
21692
|
+
urgency: integer2("urgency", 3),
|
|
21693
|
+
batchId: optionalString("batchId"),
|
|
21694
|
+
scheduleType,
|
|
21695
|
+
cronExpr: scheduleType === "cron" ? requiredString("cronExpr") : null,
|
|
21696
|
+
intervalMs: scheduleType === "recurring" ? duration("interval", null) : null,
|
|
21697
|
+
runAt: scheduleType === "delayed" ? integer2("runAt", null) : null,
|
|
21698
|
+
maxInstances: integer2("maxInstances", 1),
|
|
21699
|
+
maxRetries: integer2("maxRetries", 3),
|
|
21700
|
+
retryBackoffMs: duration("retryBackoff", "30s", true),
|
|
21701
|
+
timeoutMs: duration("timeout", null)
|
|
21702
|
+
};
|
|
21703
|
+
}
|
|
20646
21704
|
function safeStatus(value) {
|
|
20647
21705
|
return value && TASK_STATUSES.has(value) ? value : "unknown";
|
|
20648
21706
|
}
|
|
@@ -20677,19 +21735,19 @@ function esc(value) {
|
|
|
20677
21735
|
}
|
|
20678
21736
|
function readCurrentConfig() {
|
|
20679
21737
|
const configPath = getConfigPath();
|
|
20680
|
-
if (!
|
|
21738
|
+
if (!existsSync7(configPath)) return {};
|
|
20681
21739
|
try {
|
|
20682
|
-
return JSON.parse(
|
|
21740
|
+
return JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
20683
21741
|
} catch {
|
|
20684
21742
|
return {};
|
|
20685
21743
|
}
|
|
20686
21744
|
}
|
|
20687
21745
|
function writeConfig(cfg) {
|
|
20688
21746
|
const configPath = getConfigPath();
|
|
20689
|
-
const dir =
|
|
20690
|
-
if (!
|
|
21747
|
+
const dir = dirname7(configPath);
|
|
21748
|
+
if (!existsSync7(dir)) mkdirSync4(dir, { recursive: true });
|
|
20691
21749
|
const tempPath = `${configPath}.${process.pid}.tmp`;
|
|
20692
|
-
|
|
21750
|
+
writeFileSync3(tempPath, JSON.stringify(cfg, null, 2) + "\n", { mode: 384 });
|
|
20693
21751
|
renameSync2(tempPath, configPath);
|
|
20694
21752
|
}
|
|
20695
21753
|
function statCard(value, label, tone, cardIcon, delay = "") {
|
|
@@ -20722,13 +21780,14 @@ function pagination(locale, basePath, page, pages, total, suffix = "") {
|
|
|
20722
21780
|
const next = page < pages ? `<a class="btn" href="${basePath}?page=${page + 1}${suffix}">${t(locale, "pagination.next")}${icon("chevronRight")}</a>` : "";
|
|
20723
21781
|
return `<div class="pagination">${previous}<span class="summary">${t(locale, "pagination.summary", { page, pages, total })}</span>${next}</div>`;
|
|
20724
21782
|
}
|
|
20725
|
-
var app, TASK_STATUSES, dashboardApp, web_default;
|
|
21783
|
+
var app, LEGACY_PROJECT_FILTER, TASK_STATUSES, SESSION_ID_PATTERN, runtimeConfig, restartScheduled, dashboardApp, web_default;
|
|
20726
21784
|
var init_web = __esm({
|
|
20727
21785
|
"src/web/index.tsx"() {
|
|
20728
21786
|
"use strict";
|
|
20729
21787
|
init_dist();
|
|
20730
21788
|
init_drizzle_orm();
|
|
20731
21789
|
init_db2();
|
|
21790
|
+
init_duration();
|
|
20732
21791
|
init_database_maintenance_service();
|
|
20733
21792
|
init_task_run_service();
|
|
20734
21793
|
init_task_service();
|
|
@@ -20736,8 +21795,10 @@ var init_web = __esm({
|
|
|
20736
21795
|
init_config();
|
|
20737
21796
|
init_health();
|
|
20738
21797
|
init_job_templates();
|
|
21798
|
+
init_pm2();
|
|
20739
21799
|
init_ui();
|
|
20740
21800
|
app = new Hono2();
|
|
21801
|
+
LEGACY_PROJECT_FILTER = "__supertask_legacy__";
|
|
20741
21802
|
TASK_STATUSES = /* @__PURE__ */ new Set([
|
|
20742
21803
|
"pending",
|
|
20743
21804
|
"running",
|
|
@@ -20746,6 +21807,9 @@ var init_web = __esm({
|
|
|
20746
21807
|
"dead_letter",
|
|
20747
21808
|
"cancelled"
|
|
20748
21809
|
]);
|
|
21810
|
+
SESSION_ID_PATTERN = /^ses_[A-Za-z0-9_]+$/;
|
|
21811
|
+
runtimeConfig = null;
|
|
21812
|
+
restartScheduled = false;
|
|
20749
21813
|
app.use("*", async (c, next) => {
|
|
20750
21814
|
await next();
|
|
20751
21815
|
c.header("X-Content-Type-Options", "nosniff");
|
|
@@ -20782,24 +21846,52 @@ var init_web = __esm({
|
|
|
20782
21846
|
const page = parsePositiveInteger(c.req.query("page") || "1");
|
|
20783
21847
|
if (page === null) return c.text("invalid page", 400);
|
|
20784
21848
|
const statusFilter = c.req.query("status") || "";
|
|
21849
|
+
const requestedCwd = c.req.query("cwd") || "";
|
|
21850
|
+
const legacyProjectFilter = requestedCwd === LEGACY_PROJECT_FILTER;
|
|
21851
|
+
const cwdFilter = legacyProjectFilter ? "" : requestedCwd;
|
|
21852
|
+
const projectFilter = legacyProjectFilter ? LEGACY_PROJECT_FILTER : cwdFilter;
|
|
20785
21853
|
const parsedStatus = statusFilter ? parseTaskStatus(statusFilter) : null;
|
|
20786
21854
|
if (statusFilter && !parsedStatus) return c.text("invalid status", 400);
|
|
20787
21855
|
const limit = 50;
|
|
20788
21856
|
const offset = (page - 1) * limit;
|
|
20789
|
-
const [tasks4, statsData] = await Promise.all([
|
|
20790
|
-
TaskService.list({
|
|
20791
|
-
|
|
21857
|
+
const [tasks4, statsData, globalStats, projects, globalRunning, legacyStats, legacyRunning] = await Promise.all([
|
|
21858
|
+
TaskService.list({
|
|
21859
|
+
limit,
|
|
21860
|
+
offset,
|
|
21861
|
+
...parsedStatus === "running" ? { activeExecution: true } : parsedStatus ? { status: parsedStatus } : {},
|
|
21862
|
+
...legacyProjectFilter ? { legacyCwd: true } : cwdFilter ? { cwd: cwdFilter } : {}
|
|
21863
|
+
}),
|
|
21864
|
+
TaskService.stats(legacyProjectFilter ? { legacyCwd: true } : cwdFilter ? { cwd: cwdFilter } : {}),
|
|
21865
|
+
TaskService.stats(),
|
|
21866
|
+
TaskService.projectSummaries(1e3),
|
|
21867
|
+
TaskService.countRunning(),
|
|
21868
|
+
TaskService.stats({ legacyCwd: true }),
|
|
21869
|
+
TaskService.countRunning({ legacyCwd: true })
|
|
20792
21870
|
]);
|
|
20793
21871
|
const latestRuns = await TaskRunService.getLatestByTaskIds(tasks4.map((task) => task.id));
|
|
21872
|
+
const selectedRunning = legacyProjectFilter ? legacyRunning : cwdFilter ? projects.find((project) => project.cwd === cwdFilter)?.running ?? 0 : globalRunning;
|
|
20794
21873
|
const counts = {
|
|
20795
21874
|
pending: statsData.pending || 0,
|
|
20796
|
-
running:
|
|
21875
|
+
running: selectedRunning,
|
|
20797
21876
|
done: statsData.done || 0,
|
|
20798
21877
|
failed: (statsData.failed || 0) + (statsData.dead_letter || 0),
|
|
20799
21878
|
total: statsData.total || 0
|
|
20800
21879
|
};
|
|
20801
|
-
const filteredTotal = parsedStatus ? Number(statsData[parsedStatus] ?? 0) : counts.total;
|
|
21880
|
+
const filteredTotal = parsedStatus === "running" ? selectedRunning : parsedStatus ? Number(statsData[parsedStatus] ?? 0) : counts.total;
|
|
20802
21881
|
const totalPages = Math.max(1, Math.ceil(filteredTotal / limit));
|
|
21882
|
+
if (page > totalPages) {
|
|
21883
|
+
const params = new URLSearchParams({ page: String(totalPages) });
|
|
21884
|
+
if (statusFilter) params.set("status", statusFilter);
|
|
21885
|
+
if (projectFilter) params.set("cwd", projectFilter);
|
|
21886
|
+
return c.redirect(`/?${params.toString()}`);
|
|
21887
|
+
}
|
|
21888
|
+
const taskListUrl = (status, cwd) => {
|
|
21889
|
+
const params = new URLSearchParams();
|
|
21890
|
+
if (status) params.set("status", status);
|
|
21891
|
+
if (cwd) params.set("cwd", cwd);
|
|
21892
|
+
const query = params.toString();
|
|
21893
|
+
return query ? `/?${query}` : "/";
|
|
21894
|
+
};
|
|
20803
21895
|
const filterItems = [
|
|
20804
21896
|
{ status: "", label: t(locale, "filter.all") },
|
|
20805
21897
|
{ status: "pending", label: statusText(locale, "pending") },
|
|
@@ -20810,22 +21902,27 @@ var init_web = __esm({
|
|
|
20810
21902
|
{ status: "cancelled", label: statusText(locale, "cancelled") }
|
|
20811
21903
|
];
|
|
20812
21904
|
const filters = filterItems.map(({ status, label }) => {
|
|
20813
|
-
const href = status
|
|
20814
|
-
return `<a href="${href}" class="filter-chip ${statusFilter === status ? "active" : ""}">${label}</a>`;
|
|
21905
|
+
const href = taskListUrl(status, projectFilter);
|
|
21906
|
+
return `<a href="${esc(href)}" class="filter-chip ${statusFilter === status ? "active" : ""}">${label}</a>`;
|
|
20815
21907
|
}).join("");
|
|
20816
21908
|
const rows = tasks4.map((task) => {
|
|
20817
21909
|
const status = safeStatus(task.status);
|
|
20818
|
-
const
|
|
20819
|
-
const
|
|
21910
|
+
const latestRun = latestRuns.get(task.id);
|
|
21911
|
+
const executionActive = latestRun?.status === "running";
|
|
21912
|
+
const batchId = task.batchId?.trim() || null;
|
|
21913
|
+
const searchable = esc(`${task.name} ${task.agent} ${task.prompt} ${task.cwd ?? ""} ${task.batchId ?? ""} ${task.category ?? ""}`);
|
|
20820
21914
|
return `<tr data-task-row data-search="${searchable}">
|
|
20821
21915
|
<td class="faint" data-label="${t(locale, "table.id")}">#${task.id}</td>
|
|
20822
|
-
<td data-primary data-label="${t(locale, "table.task")}"><div class="task-name">${esc(task.name)}</div><div class="task-prompt" title="${esc(task.prompt)}">${esc(task.prompt.substring(0, 160))}</div
|
|
21916
|
+
<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>
|
|
21917
|
+
<div class="actions task-context"><span class="tag" title="${esc(task.cwd ?? "")}">${esc(task.cwd ? basename3(task.cwd) || task.cwd : t(locale, "projects.legacy"))}</span>${batchId ? `<span class="tag" title="${esc(t(locale, "template.batchId"))}">${esc(batchId)}</span>` : ""}</div></td>
|
|
20823
21918
|
<td data-label="${t(locale, "table.agent")}"><span class="tag">${esc(task.agent)}</span></td>
|
|
20824
|
-
<td data-label="${t(locale, "table.status")}"><span class="badge b-${status}">${statusText(locale, status)}</span
|
|
20825
|
-
<td data-label="${t(locale, "table.duration")}" class="small ${task.status === "running" ? "" : "muted"}">${formatDuration(task.startedAt, task.finishedAt)}</td>
|
|
21919
|
+
<td data-label="${t(locale, "table.status")}"><span class="badge b-${status}" ${status === "dead_letter" ? `title="${esc(t(locale, "status.deadLetterHint"))}"` : ""}>${statusText(locale, status)}</span>${status === "dead_letter" ? `<div class="muted small" style="margin-top:5px">${t(locale, "status.deadLetterAction")}</div>` : ""}${executionActive && status !== "running" ? `<div class="muted small" style="margin-top:5px">${t(locale, "status.executionStillActive")}</div>` : ""}</td>
|
|
21920
|
+
<td data-label="${t(locale, "table.duration")}" class="small ${executionActive || task.status === "running" ? "" : "muted"}">${formatDuration(task.startedAt, task.finishedAt)}</td>
|
|
20826
21921
|
<td data-label="${t(locale, "table.retries")}" class="muted small">${(task.retryCount ?? 0) > 0 ? task.retryCount : "\u2014"}</td>
|
|
20827
21922
|
<td data-label="${t(locale, "table.actions")}"><div class="actions">
|
|
21923
|
+
${task.cwd?.trim() && ["pending", "failed", "dead_letter"].includes(task.status ?? "") ? `<button type="button" class="btn" onclick="openTaskEditor(${task.id})">${t(locale, "action.edit")}</button>` : ""}
|
|
20828
21924
|
<button type="button" class="btn" onclick="showDetail(${task.id})">${t(locale, "action.details")}</button>
|
|
21925
|
+
${isValidSessionId(latestRun?.sessionId) ? `<button type="button" class="btn" onclick="copySessionCommand(${latestRun.id})">${icon("copy")}${t(locale, "action.continueSession")}</button>` : ""}
|
|
20829
21926
|
${task.status === "failed" || task.status === "dead_letter" ? `<button type="button" class="btn btn-warning" onclick="retryTask(${task.id})">${t(locale, "action.retry")}</button>` : ""}
|
|
20830
21927
|
${["pending", "running", "failed"].includes(task.status ?? "") ? `<button type="button" class="btn btn-warning" onclick="cancelTask(${task.id})">${t(locale, "action.cancel")}</button>` : ""}
|
|
20831
21928
|
${task.status === "running" || executionActive ? "" : `<button type="button" class="btn btn-danger" onclick="deleteTask(${task.id})">${t(locale, "action.delete")}</button>`}
|
|
@@ -20837,7 +21934,32 @@ var init_web = __esm({
|
|
|
20837
21934
|
<tbody>${rows}</tbody>
|
|
20838
21935
|
</table></div>
|
|
20839
21936
|
<div id="search-empty" hidden>${emptyState(t(locale, "filter.noResults"), "")}</div>`;
|
|
20840
|
-
const
|
|
21937
|
+
const paginationParts = [];
|
|
21938
|
+
if (statusFilter) paginationParts.push(`status=${encodeURIComponent(statusFilter)}`);
|
|
21939
|
+
if (projectFilter) paginationParts.push(`cwd=${encodeURIComponent(projectFilter)}`);
|
|
21940
|
+
const suffix = paginationParts.length > 0 ? `&${paginationParts.join("&")}` : "";
|
|
21941
|
+
const projectCards = [
|
|
21942
|
+
`<a class="project-card ${projectFilter ? "" : "active"}" href="${esc(taskListUrl(statusFilter, ""))}">
|
|
21943
|
+
<div class="project-card-head"><strong>${t(locale, "projects.all")}</strong><span>${globalStats.total || 0}</span></div>
|
|
21944
|
+
<div class="project-counts">${t(locale, "projects.counts", { running: globalRunning, pending: globalStats.pending || 0, failed: (globalStats.failed || 0) + (globalStats.dead_letter || 0) })}</div>
|
|
21945
|
+
</a>`,
|
|
21946
|
+
...legacyStats.total > 0 ? [`<a class="project-card ${legacyProjectFilter ? "active" : ""}" href="${esc(taskListUrl(statusFilter, LEGACY_PROJECT_FILTER))}">
|
|
21947
|
+
<div class="project-card-head"><strong>${t(locale, "projects.legacy")}</strong><span>${legacyStats.total}</span></div>
|
|
21948
|
+
<div class="project-path">${t(locale, "projects.legacyHint")}</div>
|
|
21949
|
+
<div class="project-counts">${t(locale, "projects.counts", { running: legacyRunning, pending: legacyStats.pending || 0, failed: (legacyStats.failed || 0) + (legacyStats.dead_letter || 0) })}</div>
|
|
21950
|
+
</a>`] : [],
|
|
21951
|
+
...projects.map((project) => `<a class="project-card ${cwdFilter === project.cwd ? "active" : ""}" href="${esc(taskListUrl(statusFilter, project.cwd))}" title="${esc(project.cwd)}">
|
|
21952
|
+
<div class="project-card-head"><strong>${esc(basename3(project.cwd) || project.cwd)}</strong><span>${project.total}</span></div>
|
|
21953
|
+
<div class="project-path">${esc(project.cwd)}</div>
|
|
21954
|
+
<div class="project-counts">${t(locale, "projects.counts", { running: project.running, pending: project.pending, failed: project.failed })}</div>
|
|
21955
|
+
</a>`)
|
|
21956
|
+
].join("");
|
|
21957
|
+
const projectData = JSON.stringify(Object.fromEntries(projects.map((project) => [project.cwd, {
|
|
21958
|
+
total: project.total,
|
|
21959
|
+
pending: project.pending,
|
|
21960
|
+
running: project.running,
|
|
21961
|
+
failed: project.failed
|
|
21962
|
+
}]))).replace(/</g, "\\u003c");
|
|
20841
21963
|
const body = `
|
|
20842
21964
|
<div class="stats-grid">
|
|
20843
21965
|
${statCard(counts.pending, t(locale, "stats.pending"), "tone-neutral", icon("clock"))}
|
|
@@ -20845,19 +21967,61 @@ var init_web = __esm({
|
|
|
20845
21967
|
${statCard(counts.done, t(locale, "stats.done"), "tone-green", icon("check"), "reveal-delay-1")}
|
|
20846
21968
|
${statCard(counts.failed, t(locale, "stats.failedDead"), "tone-red", icon("alert"), "reveal-delay-2")}
|
|
20847
21969
|
</div>
|
|
21970
|
+
<section class="panel project-panel reveal reveal-delay-1">
|
|
21971
|
+
<div class="panel-head"><div><h2>${t(locale, "projects.title")}</h2><p>${t(locale, "projects.description")}</p></div><button type="button" class="btn btn-primary" onclick="openTaskCreator()">${t(locale, "action.createTask")}</button></div>
|
|
21972
|
+
<div class="project-grid">${projectCards}</div>
|
|
21973
|
+
</section>
|
|
20848
21974
|
<div class="toolbar reveal reveal-delay-1">
|
|
20849
21975
|
<div class="filters">${filters}</div>
|
|
20850
21976
|
<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>
|
|
20851
21977
|
</div>
|
|
20852
21978
|
<section class="panel reveal reveal-delay-2">${table}</section>
|
|
20853
|
-
${pagination(locale, "/", page, totalPages, filteredTotal, suffix)}
|
|
21979
|
+
${pagination(locale, "/", page, totalPages, filteredTotal, suffix)}
|
|
21980
|
+
<script type="application/json" id="task-project-data">${projectData}</script>
|
|
21981
|
+
<dialog id="task-dialog" class="template-dialog">
|
|
21982
|
+
<form id="task-form" data-default-cwd="${esc(cwdFilter)}" onsubmit="saveTask(event)">
|
|
21983
|
+
<input id="task-id" type="hidden">
|
|
21984
|
+
<div class="dialog-head"><div><h2 id="task-dialog-title">${t(locale, "task.createTitle")}</h2><p>${t(locale, "task.formSubtitle")}</p></div><button type="button" class="icon-button" onclick="document.getElementById('task-dialog').close()" aria-label="${t(locale, "action.close")}">${icon("close")}</button></div>
|
|
21985
|
+
<div class="dialog-body">
|
|
21986
|
+
<div class="template-form-grid">
|
|
21987
|
+
<label class="form-field"><span>${t(locale, "template.name")}</span><input id="task-name" required maxlength="200" autocomplete="off"></label>
|
|
21988
|
+
<label class="form-field"><span>${t(locale, "template.cwd")}</span><input id="task-cwd" required autocomplete="off" list="task-cwd-options" placeholder="/path/to/project" oninput="updateTaskProjectStatus()"><small>${t(locale, "template.cwdHint")}</small></label>
|
|
21989
|
+
<datalist id="task-cwd-options">${projects.map((project) => `<option value="${esc(project.cwd)}"></option>`).join("")}</datalist>
|
|
21990
|
+
<label class="form-field"><span>${t(locale, "template.agent")}</span><input id="task-agent" required autocomplete="off" value="build" placeholder="build"></label>
|
|
21991
|
+
<label class="form-field"><span>${t(locale, "template.model")}</span><input id="task-model" required autocomplete="off" value="default" placeholder="default"></label>
|
|
21992
|
+
<label class="form-field form-field-wide"><span>${t(locale, "template.prompt")}</span><textarea id="task-prompt" rows="6" required></textarea></label>
|
|
21993
|
+
</div>
|
|
21994
|
+
<p id="task-project-status" class="form-note"></p>
|
|
21995
|
+
<details class="advanced-fields">
|
|
21996
|
+
<summary>${t(locale, "template.advanced")}</summary>
|
|
21997
|
+
<div class="template-form-grid">
|
|
21998
|
+
<label class="form-field"><span>${t(locale, "template.category")}</span><input id="task-category" autocomplete="off" value="general"></label>
|
|
21999
|
+
<label class="form-field"><span>${t(locale, "template.batchId")}</span><input id="task-batch" autocomplete="off"><small>${t(locale, "task.batchHint")}</small></label>
|
|
22000
|
+
<label class="form-field"><span>${t(locale, "template.importance")}</span><input id="task-importance" type="number" min="1" max="5" step="1" value="3" required></label>
|
|
22001
|
+
<label class="form-field"><span>${t(locale, "template.urgency")}</span><input id="task-urgency" type="number" min="1" max="5" step="1" value="3" required></label>
|
|
22002
|
+
<label class="form-field"><span>${t(locale, "template.maxRetries")}</span><input id="task-max-retries" type="number" min="0" max="1000" step="1" value="3" required></label>
|
|
22003
|
+
<label class="form-field"><span>${t(locale, "template.retryBackoff")}</span><input id="task-retry-backoff" autocomplete="off" value="30s"><small>${t(locale, "template.durationHint")}</small></label>
|
|
22004
|
+
<label class="form-field"><span>${t(locale, "template.timeout")}</span><input id="task-timeout" autocomplete="off" placeholder="30min"><small>${t(locale, "template.optional")}</small></label>
|
|
22005
|
+
</div>
|
|
22006
|
+
</details>
|
|
22007
|
+
</div>
|
|
22008
|
+
<div class="dialog-actions"><button type="button" class="btn" onclick="document.getElementById('task-dialog').close()">${t(locale, "action.cancel")}</button><button id="task-save" type="submit" class="btn btn-primary">${t(locale, "action.saveTask")}</button></div>
|
|
22009
|
+
</form>
|
|
22010
|
+
</dialog>`;
|
|
20854
22011
|
return c.html(renderLayout({ locale, activeTab: "tasks", body }));
|
|
20855
22012
|
});
|
|
20856
22013
|
app.get("/templates", async (c) => {
|
|
20857
22014
|
const locale = resolveLocale(c);
|
|
20858
|
-
const
|
|
20859
|
-
|
|
20860
|
-
const
|
|
22015
|
+
const page = parsePositiveInteger(c.req.query("page") || "1");
|
|
22016
|
+
if (page === null) return c.text("invalid page", 400);
|
|
22017
|
+
const limit = 50;
|
|
22018
|
+
const offset = (page - 1) * limit;
|
|
22019
|
+
const [templates, templateStats] = await Promise.all([
|
|
22020
|
+
TaskTemplateService.list(limit, offset),
|
|
22021
|
+
TaskTemplateService.stats()
|
|
22022
|
+
]);
|
|
22023
|
+
const totalPages = Math.max(1, Math.ceil(templateStats.total / limit));
|
|
22024
|
+
if (page > totalPages) return c.redirect(`/templates?page=${totalPages}`);
|
|
20861
22025
|
const rows = templates.map((template) => {
|
|
20862
22026
|
const scheduleType = ["cron", "recurring", "delayed"].includes(template.scheduleType) ? template.scheduleType : "unknown";
|
|
20863
22027
|
const typeLabel = scheduleType === "cron" ? t(locale, "schedule.cron") : scheduleType === "recurring" ? t(locale, "schedule.recurring") : scheduleType === "delayed" ? t(locale, "schedule.delayed") : t(locale, "schedule.unknown");
|
|
@@ -20875,21 +22039,56 @@ var init_web = __esm({
|
|
|
20875
22039
|
<td data-label="${t(locale, "table.status")}"><span class="badge ${template.enabled ? "b-done" : "b-cancelled"}">${t(locale, template.enabled ? "schedule.enabled" : "schedule.disabled")}</span></td>
|
|
20876
22040
|
<td data-label="${t(locale, "table.lastRun")}" class="small muted">${formatRelative(template.lastRunAt, locale)}</td>
|
|
20877
22041
|
<td data-label="${t(locale, "table.nextRun")}" class="small">${formatFuture(template.nextRunAt, locale)}</td>
|
|
20878
|
-
<td data-label="${t(locale, "table.actions")}"><div class="actions"><button type="button" class="btn" onclick="showTemplateDetail(${template.id})">${t(locale, "action.details")}</button>
|
|
22042
|
+
<td data-label="${t(locale, "table.actions")}"><div class="actions"><button type="button" class="btn" onclick="openTemplateEditor(${template.id})">${t(locale, "action.edit")}</button><button type="button" class="btn" onclick="showTemplateDetail(${template.id})">${t(locale, "action.details")}</button>
|
|
20879
22043
|
<button type="button" class="btn btn-primary" onclick="triggerTmpl(${template.id})">${t(locale, "action.trigger")}</button>${toggle}
|
|
20880
22044
|
<button type="button" class="btn btn-danger" onclick="deleteTmpl(${template.id})">${t(locale, "action.delete")}</button></div></td>
|
|
20881
22045
|
</tr>`;
|
|
20882
22046
|
}).join("");
|
|
20883
22047
|
const body = `
|
|
20884
22048
|
<div class="stats-grid three">
|
|
20885
|
-
${statCard(
|
|
20886
|
-
${statCard(enabled, t(locale, "stats.enabled"), "tone-green", icon("check"), "reveal-delay-1")}
|
|
20887
|
-
${statCard(disabled, t(locale, "stats.disabled"), "tone-neutral", icon("clock"), "reveal-delay-2")}
|
|
22049
|
+
${statCard(templateStats.total, t(locale, "stats.templates"), "tone-purple", icon("templates"))}
|
|
22050
|
+
${statCard(templateStats.enabled, t(locale, "stats.enabled"), "tone-green", icon("check"), "reveal-delay-1")}
|
|
22051
|
+
${statCard(templateStats.disabled, t(locale, "stats.disabled"), "tone-neutral", icon("clock"), "reveal-delay-2")}
|
|
20888
22052
|
</div>
|
|
20889
22053
|
<section class="panel reveal reveal-delay-2">
|
|
20890
|
-
<div class="panel-head"><h2>${t(locale, "page.templates.title")}</h2></div>
|
|
20891
|
-
${templates.length === 0 ? emptyState(t(locale, "empty.templates"), t(locale, "empty.templatesHint")
|
|
20892
|
-
</section
|
|
22054
|
+
<div class="panel-head"><h2>${t(locale, "page.templates.title")}</h2><button type="button" class="btn btn-primary" onclick="openTemplateCreator()">${t(locale, "action.createTemplate")}</button></div>
|
|
22055
|
+
${templates.length === 0 ? emptyState(t(locale, "empty.templates"), t(locale, "empty.templatesHint")) : `<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>`}
|
|
22056
|
+
</section>
|
|
22057
|
+
<dialog id="template-dialog" class="template-dialog">
|
|
22058
|
+
<form id="template-form" onsubmit="saveTemplate(event)">
|
|
22059
|
+
<input id="template-id" type="hidden">
|
|
22060
|
+
<div class="dialog-head"><div><h2 id="template-dialog-title">${t(locale, "template.createTitle")}</h2><p>${t(locale, "template.formSubtitle")}</p></div><button type="button" class="icon-button" onclick="document.getElementById('template-dialog').close()" aria-label="${t(locale, "action.close")}">${icon("close")}</button></div>
|
|
22061
|
+
<div class="dialog-body">
|
|
22062
|
+
<div class="template-form-grid">
|
|
22063
|
+
<label class="form-field"><span>${t(locale, "template.name")}</span><input id="template-name" required maxlength="200" autocomplete="off"></label>
|
|
22064
|
+
<label class="form-field"><span>${t(locale, "template.cwd")}</span><input id="template-cwd" required autocomplete="off" placeholder="/path/to/project"><small>${t(locale, "template.cwdHint")}</small></label>
|
|
22065
|
+
<label class="form-field"><span>${t(locale, "template.agent")}</span><input id="template-agent" required autocomplete="off" placeholder="build"></label>
|
|
22066
|
+
<label class="form-field"><span>${t(locale, "template.model")}</span><input id="template-model" required autocomplete="off" value="default" placeholder="default"></label>
|
|
22067
|
+
<label class="form-field form-field-wide"><span>${t(locale, "template.prompt")}</span><textarea id="template-prompt" rows="6" required></textarea></label>
|
|
22068
|
+
<label class="form-field"><span>${t(locale, "template.scheduleType")}</span><select id="template-schedule-type" onchange="updateTemplateScheduleFields()"><option value="recurring">${t(locale, "schedule.recurring")}</option><option value="delayed">${t(locale, "schedule.delayed")}</option><option value="cron">${t(locale, "schedule.cron")}</option></select></label>
|
|
22069
|
+
<label id="template-cron-field" class="form-field" hidden><span>${t(locale, "template.cronExpr")}</span><input id="template-cron" autocomplete="off" placeholder="0 9 * * *"><small>${t(locale, "template.cronHint")}</small></label>
|
|
22070
|
+
<label id="template-interval-field" class="form-field"><span>${t(locale, "template.interval")}</span><input id="template-interval" autocomplete="off" value="1h" placeholder="30s / 5min / 1h"><small>${t(locale, "template.durationHint")}</small></label>
|
|
22071
|
+
<label id="template-run-at-field" class="form-field" hidden><span>${t(locale, "template.runAt")}</span><input id="template-run-at" type="datetime-local" step="0.001"></label>
|
|
22072
|
+
</div>
|
|
22073
|
+
<details class="advanced-fields">
|
|
22074
|
+
<summary>${t(locale, "template.advanced")}</summary>
|
|
22075
|
+
<div class="template-form-grid">
|
|
22076
|
+
<label class="form-field"><span>${t(locale, "template.category")}</span><input id="template-category" autocomplete="off" value="general"></label>
|
|
22077
|
+
<label class="form-field"><span>${t(locale, "template.batchId")}</span><input id="template-batch" autocomplete="off"><small>${t(locale, "template.optional")}</small></label>
|
|
22078
|
+
<label class="form-field"><span>${t(locale, "template.importance")}</span><input id="template-importance" type="number" min="1" max="5" step="1" value="3" required></label>
|
|
22079
|
+
<label class="form-field"><span>${t(locale, "template.urgency")}</span><input id="template-urgency" type="number" min="1" max="5" step="1" value="3" required></label>
|
|
22080
|
+
<label class="form-field"><span>${t(locale, "template.maxInstances")}</span><input id="template-max-instances" type="number" min="1" max="1000" step="1" value="1" required><small>${t(locale, "template.maxInstancesHint")}</small></label>
|
|
22081
|
+
<label class="form-field"><span>${t(locale, "template.maxRetries")}</span><input id="template-max-retries" type="number" min="0" max="1000" step="1" value="3" required></label>
|
|
22082
|
+
<label class="form-field"><span>${t(locale, "template.retryBackoff")}</span><input id="template-retry-backoff" autocomplete="off" value="30s"><small>${t(locale, "template.durationHint")}</small></label>
|
|
22083
|
+
<label class="form-field"><span>${t(locale, "template.timeout")}</span><input id="template-timeout" autocomplete="off" placeholder="30min"><small>${t(locale, "template.optional")}</small></label>
|
|
22084
|
+
</div>
|
|
22085
|
+
</details>
|
|
22086
|
+
<p class="form-note">${t(locale, "template.futureOnly")}</p>
|
|
22087
|
+
</div>
|
|
22088
|
+
<div class="dialog-actions"><button type="button" class="btn" onclick="document.getElementById('template-dialog').close()">${t(locale, "action.cancel")}</button><button id="template-save" type="submit" class="btn btn-primary">${t(locale, "action.saveTemplate")}</button></div>
|
|
22089
|
+
</form>
|
|
22090
|
+
</dialog>
|
|
22091
|
+
${pagination(locale, "/templates", page, totalPages, templateStats.total)}`;
|
|
20893
22092
|
return c.html(renderLayout({ locale, activeTab: "templates", body }));
|
|
20894
22093
|
});
|
|
20895
22094
|
app.get("/runs", async (c) => {
|
|
@@ -20917,9 +22116,11 @@ var init_web = __esm({
|
|
|
20917
22116
|
const totalResult = await db.select({ count: sql`count(*)` }).from(taskRuns4);
|
|
20918
22117
|
const total = Number(totalResult[0]?.count ?? 0);
|
|
20919
22118
|
const totalPages = Math.max(1, Math.ceil(total / limit));
|
|
22119
|
+
if (page > totalPages) return c.redirect(`/runs?page=${totalPages}`);
|
|
20920
22120
|
const logs = [];
|
|
20921
22121
|
const rows = runs.map((run) => {
|
|
20922
22122
|
const status = safeStatus(run.status);
|
|
22123
|
+
const resumable = isValidSessionId(run.sessionId);
|
|
20923
22124
|
if (run.log) {
|
|
20924
22125
|
logs.push(`<section id="log-${run.id}" class="panel log-panel" hidden><div class="panel-head"><h3>Run #${run.id} \xB7 ${esc(run.taskName)}</h3></div><div class="log-box">${esc(run.log)}</div></section>`);
|
|
20925
22126
|
}
|
|
@@ -20927,10 +22128,12 @@ var init_web = __esm({
|
|
|
20927
22128
|
<td class="faint" data-label="${t(locale, "table.run")}">#${run.id}</td>
|
|
20928
22129
|
<td data-primary data-label="${t(locale, "table.task")}"><div class="task-name">${esc(run.taskName)} <span class="faint">#${run.taskId}</span></div>${run.model ? `<div style="margin-top:4px"><span class="tag">${esc(run.model)}</span></div>` : ""}</td>
|
|
20929
22130
|
<td data-label="${t(locale, "table.agent")}"><span class="tag">${esc(run.taskAgent)}</span></td>
|
|
20930
|
-
<td data-label="${t(locale, "table.
|
|
22131
|
+
<td data-label="${t(locale, "table.session")}" class="m small">${esc(maskSessionId(run.sessionId))}</td>
|
|
22132
|
+
<td data-label="${t(locale, "table.status")}"><span class="badge b-${status}">${runStatusText(locale, run.status ?? "unknown")}</span></td>
|
|
20931
22133
|
<td data-label="${t(locale, "table.duration")}" class="small">${formatDuration(run.startedAt, run.finishedAt)}</td>
|
|
20932
22134
|
<td data-label="${t(locale, "table.heartbeat")}" class="small muted">${formatRelative(run.heartbeatAt, locale)}</td>
|
|
20933
22135
|
<td data-label="${t(locale, "table.actions")}"><div class="actions"><button type="button" class="btn" onclick="showRunDetail(${run.id})">${t(locale, "action.details")}</button>
|
|
22136
|
+
${resumable ? `<button type="button" class="btn" onclick="copySessionCommand(${run.id})">${icon("copy")}${t(locale, "action.continueSession")}</button>` : ""}
|
|
20934
22137
|
${run.log ? `<button type="button" class="btn" aria-expanded="false" onclick="toggleLog(${run.id},this)">${t(locale, "action.logs")}</button>` : ""}</div></td>
|
|
20935
22138
|
</tr>`;
|
|
20936
22139
|
}).join("");
|
|
@@ -20941,22 +22144,37 @@ var init_web = __esm({
|
|
|
20941
22144
|
${statCard(runs.filter((run) => run.status === "failed").length, t(locale, "stats.pageFailed"), "tone-red", icon("alert"), "reveal-delay-1")}
|
|
20942
22145
|
${statCard(runs.filter((run) => run.status === "running").length, t(locale, "stats.pageRunning"), "tone-blue", icon("activity"), "reveal-delay-2")}
|
|
20943
22146
|
</div>
|
|
20944
|
-
<section class="panel reveal reveal-delay-2">${runs.length === 0 ? emptyState(t(locale, "empty.runs"), "") : `<div class="table-wrap"><table class="responsive-table"><thead><tr><th>${t(locale, "table.run")}</th><th>${t(locale, "table.task")}</th><th>${t(locale, "table.agent")}</th><th>${t(locale, "table.status")}</th><th>${t(locale, "table.duration")}</th><th>${t(locale, "table.heartbeat")}</th><th>${t(locale, "table.actions")}</th></tr></thead><tbody>${rows}</tbody></table></div>`}</section>
|
|
22147
|
+
<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.session")}</th><th>${t(locale, "table.status")}</th><th>${t(locale, "table.duration")}</th><th>${t(locale, "table.heartbeat")}</th><th>${t(locale, "table.actions")}</th></tr></thead><tbody>${rows}</tbody></table></div>`}</section>
|
|
20945
22148
|
${logs.join("")}${pagination(locale, "/runs", page, totalPages, total)}`;
|
|
20946
22149
|
return c.html(renderLayout({ locale, activeTab: "runs", body }));
|
|
20947
22150
|
});
|
|
20948
22151
|
app.get("/system", async (c) => {
|
|
20949
22152
|
const locale = resolveLocale(c);
|
|
20950
22153
|
const config = loadConfig();
|
|
22154
|
+
const activeConfig = runtimeConfig ?? config;
|
|
22155
|
+
const restartRequired = runtimeConfig !== null && !configsEqual(config, runtimeConfig);
|
|
22156
|
+
const managedRestart = canRestartCurrentGateway();
|
|
22157
|
+
const configState = resolveDashboardConfigState(
|
|
22158
|
+
runtimeConfig !== null,
|
|
22159
|
+
restartRequired,
|
|
22160
|
+
managedRestart
|
|
22161
|
+
);
|
|
22162
|
+
const configStateKey = {
|
|
22163
|
+
foreground: "system.configForeground",
|
|
22164
|
+
applied: "system.configApplied",
|
|
22165
|
+
pending: "system.configPending",
|
|
22166
|
+
manual: "system.configRestartManually"
|
|
22167
|
+
}[configState];
|
|
22168
|
+
const configStateText = t(locale, configStateKey);
|
|
20951
22169
|
const configPath = getConfigPath();
|
|
20952
|
-
const [stats, runningRuns,
|
|
22170
|
+
const [stats, runningRuns, templateStats] = await Promise.all([
|
|
20953
22171
|
TaskService.stats({}),
|
|
20954
22172
|
TaskRunService.getAllRunningRuns(),
|
|
20955
|
-
TaskTemplateService.
|
|
22173
|
+
TaskTemplateService.stats()
|
|
20956
22174
|
]);
|
|
20957
|
-
const configExists =
|
|
22175
|
+
const configExists = existsSync7(configPath);
|
|
20958
22176
|
const runRows = runningRuns.map((run) => {
|
|
20959
|
-
const session =
|
|
22177
|
+
const session = maskSessionId(run.sessionId);
|
|
20960
22178
|
return `<tr><td class="faint" data-label="${t(locale, "table.run")}">#${run.id}</td><td data-primary data-label="${t(locale, "table.task")}">#${run.taskId}</td><td data-label="${t(locale, "table.session")}" class="m small">${esc(session)}</td>
|
|
20961
22179
|
<td data-label="${t(locale, "table.model")}" class="small">${esc(run.model) || "\u2014"}</td><td data-label="${t(locale, "table.startedAt")}" class="small">${formatDateTime(run.startedAt, locale)}</td>
|
|
20962
22180
|
<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>
|
|
@@ -20972,10 +22190,10 @@ var init_web = __esm({
|
|
|
20972
22190
|
<div class="field"><label for="hi">${t(locale, "system.heartbeatInterval")}</label>${unitInput("hi", config.worker.heartbeatIntervalMs / 1e3, 5, t(locale, "system.seconds"))}</div>
|
|
20973
22191
|
<div class="field"><label for="to">${t(locale, "system.taskTimeout")}</label>${unitInput("to", config.worker.taskTimeoutMs / 6e4, 1, t(locale, "system.minutes"))}</div>
|
|
20974
22192
|
</section>
|
|
20975
|
-
<section class="card settings-card reveal-delay-1"><h2 class="settings-title"><span>${icon("templates")}${t(locale, "system.scheduler")}</span><span class="badge ${
|
|
22193
|
+
<section class="card settings-card reveal-delay-1"><h2 class="settings-title"><span>${icon("templates")}${t(locale, "system.scheduler")}</span><span class="badge ${activeConfig.scheduler.enabled ? "b-done" : "b-cancelled"}">${t(locale, activeConfig.scheduler.enabled ? "schedule.enabled" : "schedule.disabled")}</span></h2>
|
|
20976
22194
|
<div class="switch-field"><label for="se">${t(locale, "system.schedulerEnabled")}</label><label class="switch"><input id="se" type="checkbox" name="se" ${config.scheduler.enabled ? "checked" : ""}><span></span></label></div>
|
|
20977
22195
|
<div class="field"><label for="si">${t(locale, "system.checkInterval")}</label>${unitInput("si", config.scheduler.checkIntervalMs, 100, t(locale, "system.milliseconds"))}</div>
|
|
20978
|
-
<div class="info-row"><span class="info-key">${t(locale, "system.activeTemplates")}</span><span class="info-value">${
|
|
22196
|
+
<div class="info-row"><span class="info-key">${t(locale, "system.activeTemplates")}</span><span class="info-value">${templateStats.enabled} / ${templateStats.total}</span></div>
|
|
20979
22197
|
</section>
|
|
20980
22198
|
<section class="card settings-card reveal-delay-2"><h2 class="settings-title"><span>${icon("system")}${t(locale, "system.watchdog")}</span></h2>
|
|
20981
22199
|
<div class="field"><label for="wt">${t(locale, "system.heartbeatTimeout")}</label>${unitInput("wt", config.watchdog.heartbeatTimeoutMs / 1e3, 10, t(locale, "system.seconds"))}</div>
|
|
@@ -20984,10 +22202,10 @@ var init_web = __esm({
|
|
|
20984
22202
|
<div class="field"><label for="rd">${t(locale, "system.retentionDays")}</label>${unitInput("rd", config.watchdog.retentionDays, 1, t(locale, "system.days"))}</div>
|
|
20985
22203
|
</section>
|
|
20986
22204
|
</div>
|
|
20987
|
-
<div class="save-row"><span class="muted small">${t(locale, "
|
|
22205
|
+
<div class="save-row"><span class="muted small">${configStateText}</span><div class="actions"><button type="submit" class="btn">${t(locale, "action.save")}</button>${managedRestart ? `<button type="button" class="btn btn-primary" onclick="saveConfig(true,${runningRuns.length})">${t(locale, "action.saveAndRestart")}</button>` : ""}</div></div>
|
|
20988
22206
|
</form>
|
|
20989
22207
|
<section class="panel reveal">
|
|
20990
|
-
<div class="panel-head"><h2>${t(locale, "system.runningTasks", { running: runningRuns.length, limit:
|
|
22208
|
+
<div class="panel-head"><h2>${t(locale, "system.runningTasks", { running: runningRuns.length, limit: activeConfig.worker.maxConcurrency })}</h2></div>
|
|
20991
22209
|
${runningRuns.length === 0 ? emptyState(t(locale, "empty.running"), "") : `<div class="table-wrap"><table class="responsive-table"><thead><tr><th>${t(locale, "table.run")}</th><th>${t(locale, "table.task")}</th><th>${t(locale, "table.session")}</th><th>${t(locale, "table.model")}</th><th>${t(locale, "table.startedAt")}</th><th>${t(locale, "table.heartbeat")}</th><th>${t(locale, "table.pid")}</th><th>${t(locale, "table.duration")}</th></tr></thead><tbody>${runRows}</tbody></table></div>`}
|
|
20992
22210
|
</section>
|
|
20993
22211
|
<section class="panel reveal reveal-delay-1"><div class="panel-head"><h2>${t(locale, "system.taskStats")}</h2></div><div class="overview-grid">
|
|
@@ -21004,6 +22222,30 @@ var init_web = __esm({
|
|
|
21004
22222
|
<button type="button" class="btn btn-danger" onclick="clearDatabase()">${icon("database")}${t(locale, "action.clearDatabase")}</button></section>`;
|
|
21005
22223
|
return c.html(renderLayout({ locale, activeTab: "system", body }));
|
|
21006
22224
|
});
|
|
22225
|
+
app.post("/api/tasks", async (c) => {
|
|
22226
|
+
try {
|
|
22227
|
+
const task = await TaskService.add(parseTaskPayload(await c.req.json()));
|
|
22228
|
+
return c.json({ success: true, task }, 201);
|
|
22229
|
+
} catch (error) {
|
|
22230
|
+
return c.json({
|
|
22231
|
+
error: error instanceof Error ? error.message : String(error)
|
|
22232
|
+
}, 400);
|
|
22233
|
+
}
|
|
22234
|
+
});
|
|
22235
|
+
app.put("/api/tasks/:id", async (c) => {
|
|
22236
|
+
const id = parsePositiveInteger(c.req.param("id"));
|
|
22237
|
+
if (id === null) return c.json({ error: "invalid id" }, 400);
|
|
22238
|
+
try {
|
|
22239
|
+
const input = parseTaskPayload(await c.req.json());
|
|
22240
|
+
const task = await TaskService.update(id, editableTaskPayload(input));
|
|
22241
|
+
if (task) return c.json({ success: true, task });
|
|
22242
|
+
return await TaskService.getById(id) ? c.json({ error: "task status does not allow editing" }, 409) : c.json({ error: "not found" }, 404);
|
|
22243
|
+
} catch (error) {
|
|
22244
|
+
return c.json({
|
|
22245
|
+
error: error instanceof Error ? error.message : String(error)
|
|
22246
|
+
}, 400);
|
|
22247
|
+
}
|
|
22248
|
+
});
|
|
21007
22249
|
app.get("/api/tasks/:id", async (c) => {
|
|
21008
22250
|
const id = parsePositiveInteger(c.req.param("id"));
|
|
21009
22251
|
if (id === null) return c.json({ error: "invalid id" }, 400);
|
|
@@ -21019,6 +22261,24 @@ var init_web = __esm({
|
|
|
21019
22261
|
if (!run) return c.json({ error: "not found" }, 404);
|
|
21020
22262
|
return c.json(run);
|
|
21021
22263
|
});
|
|
22264
|
+
app.get("/api/runs/:id/session-command", async (c) => {
|
|
22265
|
+
const id = parsePositiveInteger(c.req.param("id"));
|
|
22266
|
+
if (id === null) return c.json({ error: "invalid id" }, 400);
|
|
22267
|
+
const run = await TaskRunService.getById(id);
|
|
22268
|
+
if (!run) return c.json({ error: "not found" }, 404);
|
|
22269
|
+
if (!isValidSessionId(run.sessionId)) return c.json({ error: "session unavailable" }, 409);
|
|
22270
|
+
return c.json({ command: sessionCommand(run.sessionId) });
|
|
22271
|
+
});
|
|
22272
|
+
app.get("/api/gateway/status", (c) => {
|
|
22273
|
+
const savedConfig = loadConfig();
|
|
22274
|
+
const managed = canRestartCurrentGateway();
|
|
22275
|
+
return c.json({
|
|
22276
|
+
pid: process.pid,
|
|
22277
|
+
managed,
|
|
22278
|
+
ready: managed && getGatewayHealth().status === "ok",
|
|
22279
|
+
restartRequired: runtimeConfig === null || !configsEqual(savedConfig, runtimeConfig)
|
|
22280
|
+
});
|
|
22281
|
+
});
|
|
21022
22282
|
app.get("/api/templates/:id", async (c) => {
|
|
21023
22283
|
const id = parsePositiveInteger(c.req.param("id"));
|
|
21024
22284
|
if (id === null) return c.json({ error: "invalid id" }, 400);
|
|
@@ -21026,6 +22286,30 @@ var init_web = __esm({
|
|
|
21026
22286
|
if (!template) return c.json({ error: "not found" }, 404);
|
|
21027
22287
|
return c.json(template);
|
|
21028
22288
|
});
|
|
22289
|
+
app.post("/api/templates", async (c) => {
|
|
22290
|
+
try {
|
|
22291
|
+
const input = parseTemplatePayload(await c.req.json());
|
|
22292
|
+
const template = await TaskTemplateService.create(input);
|
|
22293
|
+
return c.json({ success: true, template }, 201);
|
|
22294
|
+
} catch (error) {
|
|
22295
|
+
return c.json({
|
|
22296
|
+
error: error instanceof Error ? error.message : String(error)
|
|
22297
|
+
}, 400);
|
|
22298
|
+
}
|
|
22299
|
+
});
|
|
22300
|
+
app.put("/api/templates/:id", async (c) => {
|
|
22301
|
+
const id = parsePositiveInteger(c.req.param("id"));
|
|
22302
|
+
if (id === null) return c.json({ error: "invalid id" }, 400);
|
|
22303
|
+
try {
|
|
22304
|
+
const input = parseTemplatePayload(await c.req.json());
|
|
22305
|
+
const template = await TaskTemplateService.update(id, input);
|
|
22306
|
+
return template ? c.json({ success: true, template }) : c.json({ error: "not found" }, 404);
|
|
22307
|
+
} catch (error) {
|
|
22308
|
+
return c.json({
|
|
22309
|
+
error: error instanceof Error ? error.message : String(error)
|
|
22310
|
+
}, 400);
|
|
22311
|
+
}
|
|
22312
|
+
});
|
|
21029
22313
|
app.post("/api/tasks/:id/retry", async (c) => {
|
|
21030
22314
|
const id = parsePositiveInteger(c.req.param("id"));
|
|
21031
22315
|
if (id === null) return c.json({ error: "invalid id" }, 400);
|
|
@@ -21074,22 +22358,31 @@ var init_web = __esm({
|
|
|
21074
22358
|
app.post("/api/templates/:id/trigger", async (c) => {
|
|
21075
22359
|
const id = parsePositiveInteger(c.req.param("id"));
|
|
21076
22360
|
if (id === null) return c.json({ error: "invalid id" }, 400);
|
|
21077
|
-
const template = await TaskTemplateService.getById(id);
|
|
21078
|
-
if (!template) return c.json({ error: "not found" }, 404);
|
|
21079
22361
|
const task = await triggerTaskFromTemplate(id);
|
|
21080
|
-
|
|
21081
|
-
return c.json({ success: true, taskId: task.id });
|
|
22362
|
+
return task ? c.json({ success: true, taskId: task.id }) : c.json({ error: "not found" }, 404);
|
|
21082
22363
|
});
|
|
21083
22364
|
app.put("/api/config", async (c) => {
|
|
21084
22365
|
try {
|
|
21085
|
-
const
|
|
22366
|
+
const rawBody = await c.req.json();
|
|
22367
|
+
if (!rawBody || typeof rawBody !== "object" || Array.isArray(rawBody)) {
|
|
22368
|
+
throw new Error("\u8BF7\u6C42\u5185\u5BB9\u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
22369
|
+
}
|
|
22370
|
+
const body = rawBody;
|
|
22371
|
+
const section = (name) => {
|
|
22372
|
+
const value = body[name];
|
|
22373
|
+
if (value === void 0) return {};
|
|
22374
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
22375
|
+
throw new Error(`${name} \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
22376
|
+
}
|
|
22377
|
+
return value;
|
|
22378
|
+
};
|
|
21086
22379
|
const current = readCurrentConfig();
|
|
21087
22380
|
const currentWorker = current.worker ?? {};
|
|
21088
22381
|
const currentScheduler = current.scheduler ?? {};
|
|
21089
22382
|
const currentWatchdog = current.watchdog ?? {};
|
|
21090
|
-
const bodyWorker =
|
|
21091
|
-
const bodyScheduler =
|
|
21092
|
-
const bodyWatchdog =
|
|
22383
|
+
const bodyWorker = section("worker");
|
|
22384
|
+
const bodyScheduler = section("scheduler");
|
|
22385
|
+
const bodyWatchdog = section("watchdog");
|
|
21093
22386
|
const merged = {
|
|
21094
22387
|
...current,
|
|
21095
22388
|
...body,
|
|
@@ -21098,8 +22391,13 @@ var init_web = __esm({
|
|
|
21098
22391
|
scheduler: { ...currentScheduler, ...bodyScheduler },
|
|
21099
22392
|
watchdog: { ...currentWatchdog, ...bodyWatchdog }
|
|
21100
22393
|
};
|
|
21101
|
-
|
|
21102
|
-
|
|
22394
|
+
const savedConfig = validateConfig(merged);
|
|
22395
|
+
writeConfig(savedConfig);
|
|
22396
|
+
return c.json({
|
|
22397
|
+
success: true,
|
|
22398
|
+
restartRequired: runtimeConfig === null || !configsEqual(savedConfig, runtimeConfig),
|
|
22399
|
+
managed: canRestartCurrentGateway()
|
|
22400
|
+
});
|
|
21103
22401
|
} catch (error) {
|
|
21104
22402
|
return c.json({
|
|
21105
22403
|
success: false,
|
|
@@ -21107,9 +22405,27 @@ var init_web = __esm({
|
|
|
21107
22405
|
}, 400);
|
|
21108
22406
|
}
|
|
21109
22407
|
});
|
|
22408
|
+
app.post("/api/gateway/restart", async (c) => {
|
|
22409
|
+
const rawBody = await c.req.json().catch(() => null);
|
|
22410
|
+
const confirmation = rawBody && typeof rawBody === "object" && !Array.isArray(rawBody) ? rawBody.confirmation : void 0;
|
|
22411
|
+
if (confirmation !== "RESTART") {
|
|
22412
|
+
return c.json({ error: "confirmation must be RESTART" }, 400);
|
|
22413
|
+
}
|
|
22414
|
+
if (restartScheduled) return c.json({ error: "restart already scheduled" }, 409);
|
|
22415
|
+
if (!canRestartCurrentGateway()) {
|
|
22416
|
+
return c.json({ error: "\u5F53\u524D Gateway \u4E0D\u662F\u7531\u5339\u914D\u8FD0\u884C\u4F5C\u7528\u57DF\u7684 PM2 \u8FDB\u7A0B\u6258\u7BA1\uFF0C\u65E0\u6CD5\u4ECE\u7F51\u9875\u5B89\u5168\u91CD\u542F" }, 409);
|
|
22417
|
+
}
|
|
22418
|
+
restartScheduled = true;
|
|
22419
|
+
const previousPid = process.pid;
|
|
22420
|
+
setTimeout(() => {
|
|
22421
|
+
process.kill(previousPid, "SIGTERM");
|
|
22422
|
+
}, 500);
|
|
22423
|
+
return c.json({ success: true, previousPid }, 202);
|
|
22424
|
+
});
|
|
21110
22425
|
app.post("/api/database/clear", async (c) => {
|
|
21111
|
-
const
|
|
21112
|
-
|
|
22426
|
+
const rawBody = await c.req.json().catch(() => null);
|
|
22427
|
+
const confirmation = rawBody && typeof rawBody === "object" && !Array.isArray(rawBody) ? rawBody.confirmation : void 0;
|
|
22428
|
+
if (confirmation !== "CLEAR") {
|
|
21113
22429
|
return c.json({ success: false, error: "confirmation must be CLEAR" }, 400);
|
|
21114
22430
|
}
|
|
21115
22431
|
try {
|
|
@@ -21142,6 +22458,8 @@ init_task_run_service();
|
|
|
21142
22458
|
init_health();
|
|
21143
22459
|
init_process_control();
|
|
21144
22460
|
init_launch_protocol();
|
|
22461
|
+
init_task_working_directory();
|
|
22462
|
+
init_task_batch();
|
|
21145
22463
|
import { spawn } from "child_process";
|
|
21146
22464
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
21147
22465
|
import { existsSync as existsSync3 } from "fs";
|
|
@@ -21259,7 +22577,8 @@ var WorkerEngine = class {
|
|
|
21259
22577
|
if (!task) break;
|
|
21260
22578
|
if (this.stopped) break;
|
|
21261
22579
|
if (!await TaskService.start(task.id)) continue;
|
|
21262
|
-
|
|
22580
|
+
const batchId = normalizeTaskBatchId(task.batchId);
|
|
22581
|
+
if (batchId) this.activeBatchIds.add(batchId);
|
|
21263
22582
|
if (this.stopped) {
|
|
21264
22583
|
await TaskService.resetRunningToPending([task.id]);
|
|
21265
22584
|
this.releaseBatch(task);
|
|
@@ -21290,6 +22609,14 @@ var WorkerEngine = class {
|
|
|
21290
22609
|
this.releaseBatch(task);
|
|
21291
22610
|
continue;
|
|
21292
22611
|
}
|
|
22612
|
+
try {
|
|
22613
|
+
validateTaskWorkingDirectory(task.cwd);
|
|
22614
|
+
} catch (error) {
|
|
22615
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
22616
|
+
await TaskService.failRun(task.id, run.id, message, { setDeadLetter: true });
|
|
22617
|
+
this.releaseBatch(task);
|
|
22618
|
+
continue;
|
|
22619
|
+
}
|
|
21293
22620
|
await this.spawnTask(task, run.id, launchIdentity);
|
|
21294
22621
|
} catch (err) {
|
|
21295
22622
|
this.releaseBatch(task);
|
|
@@ -21375,11 +22702,16 @@ var WorkerEngine = class {
|
|
|
21375
22702
|
child.on("message", (message) => {
|
|
21376
22703
|
if (isMatchingDrainProof(message, entry.launchIdentity)) {
|
|
21377
22704
|
entry.guardianDrained = true;
|
|
22705
|
+
try {
|
|
22706
|
+
child.send(drainProofAckForIdentity(entry.launchIdentity));
|
|
22707
|
+
} catch (error) {
|
|
22708
|
+
this.logError("drain proof acknowledgment failed", error, task.id);
|
|
22709
|
+
}
|
|
21378
22710
|
}
|
|
21379
22711
|
});
|
|
21380
22712
|
let spawnError = null;
|
|
21381
|
-
const spawned = new Promise((
|
|
21382
|
-
child.once("spawn",
|
|
22713
|
+
const spawned = new Promise((resolve4, reject) => {
|
|
22714
|
+
child.once("spawn", resolve4);
|
|
21383
22715
|
child.once("error", (error) => {
|
|
21384
22716
|
spawnError = error;
|
|
21385
22717
|
reject(error);
|
|
@@ -21427,10 +22759,10 @@ var WorkerEngine = class {
|
|
|
21427
22759
|
return;
|
|
21428
22760
|
}
|
|
21429
22761
|
try {
|
|
21430
|
-
await new Promise((
|
|
22762
|
+
await new Promise((resolve4, reject) => {
|
|
21431
22763
|
child.stdin.end("START\n", (error) => {
|
|
21432
22764
|
if (error) reject(error);
|
|
21433
|
-
else
|
|
22765
|
+
else resolve4();
|
|
21434
22766
|
});
|
|
21435
22767
|
});
|
|
21436
22768
|
} catch (error) {
|
|
@@ -21654,7 +22986,8 @@ ${output}` : ""}` : output;
|
|
|
21654
22986
|
);
|
|
21655
22987
|
}
|
|
21656
22988
|
releaseBatch(task) {
|
|
21657
|
-
|
|
22989
|
+
const batchId = normalizeTaskBatchId(task.batchId);
|
|
22990
|
+
if (batchId) this.activeBatchIds.delete(batchId);
|
|
21658
22991
|
}
|
|
21659
22992
|
resolveModel(taskModel) {
|
|
21660
22993
|
if (!taskModel || taskModel === "default") return null;
|
|
@@ -21994,7 +23327,7 @@ var Scheduler = class {
|
|
|
21994
23327
|
const lastTemplate = dueTemplates.at(-1);
|
|
21995
23328
|
for (const tmpl of dueTemplates) {
|
|
21996
23329
|
try {
|
|
21997
|
-
const task = await cloneTaskFromTemplate(tmpl.id);
|
|
23330
|
+
const task = await cloneTaskFromTemplate(tmpl.id, tmpl.nextRunAt);
|
|
21998
23331
|
if (task) {
|
|
21999
23332
|
console.log(JSON.stringify({
|
|
22000
23333
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -22060,29 +23393,7 @@ init_db2();
|
|
|
22060
23393
|
init_task_service();
|
|
22061
23394
|
init_health();
|
|
22062
23395
|
init_process_control();
|
|
22063
|
-
|
|
22064
|
-
// src/core/package-version.ts
|
|
22065
|
-
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
22066
|
-
import { dirname as dirname4, join as join4 } from "path";
|
|
22067
|
-
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
22068
|
-
function getPackageVersion() {
|
|
22069
|
-
let directory = dirname4(fileURLToPath4(import.meta.url));
|
|
22070
|
-
for (let depth = 0; depth < 5; depth += 1) {
|
|
22071
|
-
const packagePath = join4(directory, "package.json");
|
|
22072
|
-
if (existsSync4(packagePath)) {
|
|
22073
|
-
try {
|
|
22074
|
-
const pkg = JSON.parse(readFileSync2(packagePath, "utf8"));
|
|
22075
|
-
if (typeof pkg.version === "string") return pkg.version;
|
|
22076
|
-
} catch {
|
|
22077
|
-
return "0.0.0";
|
|
22078
|
-
}
|
|
22079
|
-
}
|
|
22080
|
-
directory = dirname4(directory);
|
|
22081
|
-
}
|
|
22082
|
-
return "0.0.0";
|
|
22083
|
-
}
|
|
22084
|
-
|
|
22085
|
-
// src/gateway/index.ts
|
|
23396
|
+
init_package_version();
|
|
22086
23397
|
var STALE_THRESHOLD_MS = 3e4;
|
|
22087
23398
|
async function runGatewayShutdownStep(failures, step, operation) {
|
|
22088
23399
|
try {
|
|
@@ -22301,8 +23612,9 @@ async function main() {
|
|
|
22301
23612
|
await scheduler.start();
|
|
22302
23613
|
if (shuttingDown) return;
|
|
22303
23614
|
if (cfg.dashboard.enabled) {
|
|
22304
|
-
const { dashboardApp: dashboardApp2 } = await Promise.resolve().then(() => (init_web(), web_exports));
|
|
23615
|
+
const { dashboardApp: dashboardApp2, setDashboardRuntimeConfig: setDashboardRuntimeConfig2 } = await Promise.resolve().then(() => (init_web(), web_exports));
|
|
22305
23616
|
if (shuttingDown) return;
|
|
23617
|
+
setDashboardRuntimeConfig2(cfg);
|
|
22306
23618
|
dashboardServer = Bun.serve({
|
|
22307
23619
|
hostname: "127.0.0.1",
|
|
22308
23620
|
port: cfg.dashboard.port,
|