opencode-supertask 0.1.39 → 0.1.41
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/CHANGELOG.md +27 -1
- package/LICENSE +21 -0
- package/README.md +159 -352
- package/README.zh-CN.md +250 -0
- package/dist/cli/index.js +580 -171
- package/dist/cli/index.js.map +1 -1
- package/dist/daemon/gateway-diagnostic-runner.d.ts +2 -0
- package/dist/daemon/gateway-diagnostic-runner.js +461 -0
- package/dist/daemon/gateway-diagnostic-runner.js.map +1 -0
- package/dist/gateway/index.js +543 -615
- package/dist/gateway/index.js.map +1 -1
- package/dist/plugin/supertask.js +90 -28
- package/dist/plugin/supertask.js.map +1 -1
- package/dist/web/index.js +410 -528
- package/dist/web/index.js.map +1 -1
- package/dist/worker/index.d.ts +9 -0
- package/dist/worker/index.js +225 -69
- package/dist/worker/index.js.map +1 -1
- package/dist/worker/launcher.js.map +1 -1
- package/drizzle/0008_good_smasher.sql +3 -0
- package/drizzle/meta/0008_snapshot.json +606 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +12 -3
package/dist/gateway/index.js
CHANGED
|
@@ -6005,6 +6005,7 @@ var init_schema = __esm({
|
|
|
6005
6005
|
name: text("name").notNull(),
|
|
6006
6006
|
agent: text("agent").notNull(),
|
|
6007
6007
|
model: text("model").default("default"),
|
|
6008
|
+
variant: text("variant"),
|
|
6008
6009
|
prompt: text("prompt").notNull(),
|
|
6009
6010
|
cwd: text("cwd"),
|
|
6010
6011
|
// 分类与优先级
|
|
@@ -6049,6 +6050,7 @@ var init_schema = __esm({
|
|
|
6049
6050
|
taskId: integer("task_id").notNull().references(() => tasks.id, { onDelete: "cascade" }),
|
|
6050
6051
|
sessionId: text("session_id"),
|
|
6051
6052
|
model: text("model"),
|
|
6053
|
+
variant: text("variant"),
|
|
6052
6054
|
status: text("status").default("running"),
|
|
6053
6055
|
startedAt: integer("started_at", { mode: "timestamp" }).$defaultFn(() => /* @__PURE__ */ new Date()),
|
|
6054
6056
|
finishedAt: integer("finished_at", { mode: "timestamp" }),
|
|
@@ -6069,6 +6071,7 @@ var init_schema = __esm({
|
|
|
6069
6071
|
name: text("name").notNull(),
|
|
6070
6072
|
agent: text("agent").notNull(),
|
|
6071
6073
|
model: text("model").default("default"),
|
|
6074
|
+
variant: text("variant"),
|
|
6072
6075
|
prompt: text("prompt").notNull(),
|
|
6073
6076
|
cwd: text("cwd"),
|
|
6074
6077
|
category: text("category").default("general"),
|
|
@@ -6107,8 +6110,8 @@ import { homedir } from "os";
|
|
|
6107
6110
|
import { join, dirname } from "path";
|
|
6108
6111
|
import { fileURLToPath } from "url";
|
|
6109
6112
|
function getMigrationsFolder() {
|
|
6110
|
-
const
|
|
6111
|
-
let dir =
|
|
6113
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6114
|
+
let dir = __dirname;
|
|
6112
6115
|
for (let i = 0; i < 5; i++) {
|
|
6113
6116
|
const candidate = join(dir, "drizzle", "meta", "_journal.json");
|
|
6114
6117
|
if (existsSync(candidate)) {
|
|
@@ -6116,7 +6119,7 @@ function getMigrationsFolder() {
|
|
|
6116
6119
|
}
|
|
6117
6120
|
dir = dirname(dir);
|
|
6118
6121
|
}
|
|
6119
|
-
return join(
|
|
6122
|
+
return join(__dirname, "../../drizzle");
|
|
6120
6123
|
}
|
|
6121
6124
|
function ensureGatewayLock(sqliteDb) {
|
|
6122
6125
|
sqliteDb.exec(`
|
|
@@ -6439,6 +6442,28 @@ var init_task_batch = __esm({
|
|
|
6439
6442
|
}
|
|
6440
6443
|
});
|
|
6441
6444
|
|
|
6445
|
+
// src/core/model-variant.ts
|
|
6446
|
+
function normalizeModelVariant(value) {
|
|
6447
|
+
if (value === void 0 || value === null) return value;
|
|
6448
|
+
const normalized = value.trim();
|
|
6449
|
+
if (!normalized) return null;
|
|
6450
|
+
if (normalized.length > MAX_MODEL_VARIANT_LENGTH) {
|
|
6451
|
+
throw new Error(`variant \u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC7 ${MAX_MODEL_VARIANT_LENGTH} \u4E2A\u5B57\u7B26`);
|
|
6452
|
+
}
|
|
6453
|
+
if (CONTROL_CHARACTER_PATTERN.test(normalized)) {
|
|
6454
|
+
throw new Error("variant \u4E0D\u80FD\u5305\u542B\u63A7\u5236\u5B57\u7B26");
|
|
6455
|
+
}
|
|
6456
|
+
return normalized;
|
|
6457
|
+
}
|
|
6458
|
+
var MAX_MODEL_VARIANT_LENGTH, CONTROL_CHARACTER_PATTERN;
|
|
6459
|
+
var init_model_variant = __esm({
|
|
6460
|
+
"src/core/model-variant.ts"() {
|
|
6461
|
+
"use strict";
|
|
6462
|
+
MAX_MODEL_VARIANT_LENGTH = 128;
|
|
6463
|
+
CONTROL_CHARACTER_PATTERN = /[\u0000-\u001F\u007F]/;
|
|
6464
|
+
}
|
|
6465
|
+
});
|
|
6466
|
+
|
|
6442
6467
|
// src/core/services/task.service.ts
|
|
6443
6468
|
function hasNoExecutableDependents() {
|
|
6444
6469
|
return sql`NOT EXISTS (
|
|
@@ -6494,6 +6519,7 @@ var init_task_service = __esm({
|
|
|
6494
6519
|
init_backoff();
|
|
6495
6520
|
init_task_working_directory();
|
|
6496
6521
|
init_task_batch();
|
|
6522
|
+
init_model_variant();
|
|
6497
6523
|
({ tasks: tasks2, taskRuns: taskRuns2 } = schema_exports);
|
|
6498
6524
|
cleanupInvocation = 0;
|
|
6499
6525
|
TaskDeletionConflictError = class extends Error {
|
|
@@ -6513,7 +6539,8 @@ var init_task_service = __esm({
|
|
|
6513
6539
|
static async add(data) {
|
|
6514
6540
|
const normalizedData = {
|
|
6515
6541
|
...data,
|
|
6516
|
-
batchId: normalizeTaskBatchId(data.batchId)
|
|
6542
|
+
batchId: normalizeTaskBatchId(data.batchId),
|
|
6543
|
+
variant: normalizeModelVariant(data.variant)
|
|
6517
6544
|
};
|
|
6518
6545
|
this.validateNewTask(normalizedData);
|
|
6519
6546
|
return db.transaction((tx) => {
|
|
@@ -6541,7 +6568,11 @@ var init_task_service = __esm({
|
|
|
6541
6568
|
}
|
|
6542
6569
|
static async update(id, data, scope = {}) {
|
|
6543
6570
|
if (Object.keys(data).length === 0) throw new Error("\u81F3\u5C11\u63D0\u4F9B\u4E00\u4E2A\u8981\u4FEE\u6539\u7684\u5B57\u6BB5");
|
|
6544
|
-
const normalizedData =
|
|
6571
|
+
const normalizedData = {
|
|
6572
|
+
...data,
|
|
6573
|
+
...data.batchId === void 0 ? {} : { batchId: normalizeTaskBatchId(data.batchId) ?? null },
|
|
6574
|
+
...data.variant === void 0 ? {} : { variant: normalizeModelVariant(data.variant) ?? null }
|
|
6575
|
+
};
|
|
6545
6576
|
return db.transaction((tx) => {
|
|
6546
6577
|
const task = tx.select().from(tasks2).where(and(
|
|
6547
6578
|
eq(tasks2.id, id),
|
|
@@ -6553,6 +6584,7 @@ var init_task_service = __esm({
|
|
|
6553
6584
|
name: normalizedData.name ?? task.name,
|
|
6554
6585
|
agent: normalizedData.agent ?? task.agent,
|
|
6555
6586
|
model: normalizedData.model ?? task.model,
|
|
6587
|
+
variant: normalizedData.variant === void 0 ? task.variant : normalizedData.variant,
|
|
6556
6588
|
prompt: normalizedData.prompt ?? task.prompt,
|
|
6557
6589
|
cwd: task.cwd,
|
|
6558
6590
|
category: normalizedData.category ?? task.category,
|
|
@@ -6566,13 +6598,23 @@ var init_task_service = __esm({
|
|
|
6566
6598
|
});
|
|
6567
6599
|
const maxRetries = normalizedData.maxRetries ?? task.maxRetries ?? 3;
|
|
6568
6600
|
const exhausted = task.status === "failed" && (task.retryCount ?? 0) > maxRetries;
|
|
6569
|
-
|
|
6601
|
+
const updated = tx.update(tasks2).set({
|
|
6570
6602
|
...normalizedData,
|
|
6571
6603
|
...exhausted ? {
|
|
6572
6604
|
status: "dead_letter",
|
|
6573
6605
|
retryAfter: null
|
|
6574
6606
|
} : {}
|
|
6575
6607
|
}).where(eq(tasks2.id, id)).returning().get() ?? null;
|
|
6608
|
+
if (exhausted && updated) {
|
|
6609
|
+
const finishedAt = /* @__PURE__ */ new Date();
|
|
6610
|
+
tx.update(tasks2).set({
|
|
6611
|
+
status: "dead_letter",
|
|
6612
|
+
finishedAt,
|
|
6613
|
+
retryAfter: null,
|
|
6614
|
+
resultLog: `\u4F9D\u8D56\u4EFB\u52A1 #${id} \u5DF2\u8FDB\u5165\u4E0D\u53EF\u6062\u590D\u7EC8\u6001`
|
|
6615
|
+
}).where(blockedDependentsOf(id)).run();
|
|
6616
|
+
}
|
|
6617
|
+
return updated;
|
|
6576
6618
|
}, { behavior: "immediate" });
|
|
6577
6619
|
}
|
|
6578
6620
|
static validateNewTask(data) {
|
|
@@ -6593,7 +6635,7 @@ var init_task_service = __esm({
|
|
|
6593
6635
|
throw new Error(`${name} \u5FC5\u987B\u662F ${min} \u5230 ${max} \u4E4B\u95F4\u7684\u6574\u6570`);
|
|
6594
6636
|
}
|
|
6595
6637
|
}
|
|
6596
|
-
static
|
|
6638
|
+
static buildRunnableTaskWhere(scope) {
|
|
6597
6639
|
const baseConditions = [...this.buildScopeWhere(scope)];
|
|
6598
6640
|
const nowMs = Date.now();
|
|
6599
6641
|
const retryAfterFilter = or(
|
|
@@ -6628,40 +6670,43 @@ var init_task_service = __esm({
|
|
|
6628
6670
|
if (batchFilter) {
|
|
6629
6671
|
conditions.push(batchFilter);
|
|
6630
6672
|
}
|
|
6631
|
-
|
|
6673
|
+
return and(
|
|
6632
6674
|
...conditions,
|
|
6633
6675
|
or(
|
|
6634
6676
|
isNull(tasks2.dependsOn),
|
|
6635
6677
|
sql`EXISTS (
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6678
|
+
SELECT 1 FROM tasks AS dependency_task
|
|
6679
|
+
WHERE dependency_task.id = ${tasks2.dependsOn}
|
|
6680
|
+
AND dependency_task.status = 'done'
|
|
6681
|
+
AND dependency_task.cwd IS ${tasks2.cwd}
|
|
6682
|
+
)`
|
|
6641
6683
|
),
|
|
6642
6684
|
or(
|
|
6643
6685
|
isNull(tasks2.batchId),
|
|
6644
6686
|
sql`trim(${tasks2.batchId}, ${TASK_BATCH_TRIM_CHARACTERS}) = ''`,
|
|
6645
6687
|
sql`NOT EXISTS (
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
)
|
|
6688
|
+
SELECT 1 FROM tasks AS running_batch_task
|
|
6689
|
+
WHERE trim(running_batch_task.batch_id, ${TASK_BATCH_TRIM_CHARACTERS})
|
|
6690
|
+
= trim(${tasks2.batchId}, ${TASK_BATCH_TRIM_CHARACTERS})
|
|
6691
|
+
AND (
|
|
6692
|
+
running_batch_task.status = 'running'
|
|
6693
|
+
OR EXISTS (
|
|
6694
|
+
SELECT 1 FROM task_runs AS running_batch_run
|
|
6695
|
+
WHERE running_batch_run.task_id = running_batch_task.id
|
|
6696
|
+
AND running_batch_run.status = 'running'
|
|
6656
6697
|
)
|
|
6657
|
-
|
|
6698
|
+
)
|
|
6699
|
+
)`
|
|
6658
6700
|
),
|
|
6659
6701
|
sql`NOT EXISTS (
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
)
|
|
6702
|
+
SELECT 1 FROM task_runs AS candidate_active_run
|
|
6703
|
+
WHERE candidate_active_run.task_id = ${tasks2.id}
|
|
6704
|
+
AND candidate_active_run.status = 'running'
|
|
6705
|
+
)`
|
|
6706
|
+
);
|
|
6707
|
+
}
|
|
6708
|
+
static async next(scope = {}) {
|
|
6709
|
+
const result = await db.select().from(tasks2).where(this.buildRunnableTaskWhere(scope)).orderBy(
|
|
6665
6710
|
desc(tasks2.urgency),
|
|
6666
6711
|
desc(tasks2.importance),
|
|
6667
6712
|
asc(tasks2.createdAt),
|
|
@@ -6669,6 +6714,22 @@ var init_task_service = __esm({
|
|
|
6669
6714
|
).limit(1);
|
|
6670
6715
|
return result[0] ?? null;
|
|
6671
6716
|
}
|
|
6717
|
+
static async claimNext(scope = {}) {
|
|
6718
|
+
return db.transaction((tx) => {
|
|
6719
|
+
const candidate = tx.select().from(tasks2).where(this.buildRunnableTaskWhere(scope)).orderBy(
|
|
6720
|
+
desc(tasks2.urgency),
|
|
6721
|
+
desc(tasks2.importance),
|
|
6722
|
+
asc(tasks2.createdAt),
|
|
6723
|
+
asc(tasks2.id)
|
|
6724
|
+
).limit(1).get();
|
|
6725
|
+
if (!candidate) return null;
|
|
6726
|
+
return tx.update(tasks2).set({
|
|
6727
|
+
status: "running",
|
|
6728
|
+
startedAt: /* @__PURE__ */ new Date(),
|
|
6729
|
+
finishedAt: null
|
|
6730
|
+
}).where(eq(tasks2.id, candidate.id)).returning().get() ?? null;
|
|
6731
|
+
}, { behavior: "immediate" });
|
|
6732
|
+
}
|
|
6672
6733
|
static async countRunning(scope = {}) {
|
|
6673
6734
|
const scopeConditions = scope.legacyCwd ? [sql`${tasks2.cwd} IS NULL OR trim(${tasks2.cwd}) = ''`] : this.buildScopeWhere(scope);
|
|
6674
6735
|
if (scope.batchId !== void 0) {
|
|
@@ -7329,9 +7390,34 @@ var init_launch_protocol = __esm({
|
|
|
7329
7390
|
});
|
|
7330
7391
|
|
|
7331
7392
|
// src/core/process-control.ts
|
|
7332
|
-
import { spawnSync } from "child_process";
|
|
7333
7393
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7334
7394
|
import { basename, dirname as dirname2, resolve } from "path";
|
|
7395
|
+
function runBoundedOsCommand(command, args, captureOutput = true) {
|
|
7396
|
+
const result = Bun.spawnSync({
|
|
7397
|
+
cmd: [
|
|
7398
|
+
process.execPath,
|
|
7399
|
+
"-e",
|
|
7400
|
+
BOUNDED_OS_COMMAND_RUNNER,
|
|
7401
|
+
"supertask-os-command",
|
|
7402
|
+
command,
|
|
7403
|
+
...args
|
|
7404
|
+
],
|
|
7405
|
+
detached: process.platform !== "win32",
|
|
7406
|
+
maxBuffer: OS_COMMAND_MAX_OUTPUT_BYTES + 1024,
|
|
7407
|
+
stdin: "ignore",
|
|
7408
|
+
stdout: "pipe",
|
|
7409
|
+
stderr: "ignore"
|
|
7410
|
+
});
|
|
7411
|
+
const output = new TextDecoder().decode(result.stdout);
|
|
7412
|
+
const markerIndex = output.lastIndexOf(OS_COMMAND_RESULT_MARKER);
|
|
7413
|
+
if (markerIndex < 0) return { status: null, stdout: "" };
|
|
7414
|
+
const statusText2 = output.slice(markerIndex + OS_COMMAND_RESULT_MARKER.length);
|
|
7415
|
+
if (!/^\d+$/.test(statusText2)) return { status: null, stdout: "" };
|
|
7416
|
+
return {
|
|
7417
|
+
status: Number(statusText2),
|
|
7418
|
+
stdout: captureOutput ? output.slice(0, markerIndex) : ""
|
|
7419
|
+
};
|
|
7420
|
+
}
|
|
7335
7421
|
function isSafePid(pid) {
|
|
7336
7422
|
return Number.isInteger(pid) && pid > 1 && pid !== process.pid;
|
|
7337
7423
|
}
|
|
@@ -7368,12 +7454,10 @@ function inspectSpawnedProcessTreePresence(pid) {
|
|
|
7368
7454
|
}
|
|
7369
7455
|
}
|
|
7370
7456
|
function inspectUnixProcess(pid) {
|
|
7371
|
-
const result =
|
|
7372
|
-
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
killSignal: "SIGKILL"
|
|
7376
|
-
});
|
|
7457
|
+
const result = runBoundedOsCommand(
|
|
7458
|
+
"ps",
|
|
7459
|
+
["-o", "pgid=", "-o", "command=", "-p", String(pid)]
|
|
7460
|
+
);
|
|
7377
7461
|
if (result.status !== 0) return null;
|
|
7378
7462
|
const match2 = result.stdout.trim().match(/^(\d+)\s+(.+)$/s);
|
|
7379
7463
|
if (!match2) return null;
|
|
@@ -7381,30 +7465,18 @@ function inspectUnixProcess(pid) {
|
|
|
7381
7465
|
}
|
|
7382
7466
|
function inspectWindowsCommand(pid) {
|
|
7383
7467
|
const script = `(Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}").CommandLine`;
|
|
7384
|
-
const result =
|
|
7468
|
+
const result = runBoundedOsCommand(
|
|
7385
7469
|
"powershell.exe",
|
|
7386
|
-
["-NoProfile", "-NonInteractive", "-Command", script]
|
|
7387
|
-
{
|
|
7388
|
-
encoding: "utf8",
|
|
7389
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
7390
|
-
timeout: OS_COMMAND_TIMEOUT_MS,
|
|
7391
|
-
killSignal: "SIGKILL"
|
|
7392
|
-
}
|
|
7470
|
+
["-NoProfile", "-NonInteractive", "-Command", script]
|
|
7393
7471
|
);
|
|
7394
7472
|
if (result.status !== 0) return null;
|
|
7395
7473
|
return result.stdout.trim() || null;
|
|
7396
7474
|
}
|
|
7397
7475
|
function inspectWindowsProcessTree(rootPid) {
|
|
7398
7476
|
const script = `$root=${rootPid}; $all=@(Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId); $ids=New-Object 'System.Collections.Generic.HashSet[int]'; [void]$ids.Add($root); do { $added=$false; foreach($p in $all) { if($ids.Contains([int]$p.ParentProcessId) -and $ids.Add([int]$p.ProcessId)) { $added=$true } } } while($added); $all | Where-Object { $ids.Contains([int]$_.ProcessId) } | Select-Object -ExpandProperty ProcessId`;
|
|
7399
|
-
const result =
|
|
7477
|
+
const result = runBoundedOsCommand(
|
|
7400
7478
|
"powershell.exe",
|
|
7401
|
-
["-NoProfile", "-NonInteractive", "-Command", script]
|
|
7402
|
-
{
|
|
7403
|
-
encoding: "utf8",
|
|
7404
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
7405
|
-
timeout: OS_COMMAND_TIMEOUT_MS,
|
|
7406
|
-
killSignal: "SIGKILL"
|
|
7407
|
-
}
|
|
7479
|
+
["-NoProfile", "-NonInteractive", "-Command", script]
|
|
7408
7480
|
);
|
|
7409
7481
|
if (result.status !== 0) return null;
|
|
7410
7482
|
return result.stdout.split(/\s+/).filter(Boolean).map(Number).filter((pid) => Number.isInteger(pid) && pid > 0);
|
|
@@ -7475,11 +7547,7 @@ function signalRecordedProcessTreeWithResult(pid, signal, expectedExecutable, la
|
|
|
7475
7547
|
}
|
|
7476
7548
|
const args = ["/PID", String(pid), "/T"];
|
|
7477
7549
|
if (signal === "SIGKILL") args.push("/F");
|
|
7478
|
-
const status =
|
|
7479
|
-
stdio: "ignore",
|
|
7480
|
-
timeout: OS_COMMAND_TIMEOUT_MS,
|
|
7481
|
-
killSignal: "SIGKILL"
|
|
7482
|
-
}).status;
|
|
7550
|
+
const status = runBoundedOsCommand("taskkill", args, false).status;
|
|
7483
7551
|
if (status === 0) return "signalled";
|
|
7484
7552
|
return isProcessAlive(pid) ? "signal-failed" : absentLeaderResult(pid);
|
|
7485
7553
|
}
|
|
@@ -7508,12 +7576,64 @@ async function waitForSpawnedProcessTreeExit(pid, timeoutMs = 5e3) {
|
|
|
7508
7576
|
}
|
|
7509
7577
|
return inspectSpawnedProcessTreePresence(pid) === "not-running";
|
|
7510
7578
|
}
|
|
7511
|
-
var OS_COMMAND_TIMEOUT_MS;
|
|
7579
|
+
var OS_COMMAND_TIMEOUT_MS, OS_COMMAND_MAX_OUTPUT_BYTES, OS_COMMAND_RESULT_MARKER, BOUNDED_OS_COMMAND_RUNNER;
|
|
7512
7580
|
var init_process_control = __esm({
|
|
7513
7581
|
"src/core/process-control.ts"() {
|
|
7514
7582
|
"use strict";
|
|
7515
7583
|
init_launch_protocol();
|
|
7516
7584
|
OS_COMMAND_TIMEOUT_MS = 2e3;
|
|
7585
|
+
OS_COMMAND_MAX_OUTPUT_BYTES = 4 * 1024 * 1024;
|
|
7586
|
+
OS_COMMAND_RESULT_MARKER = "\n\0supertask-os-command-result:";
|
|
7587
|
+
BOUNDED_OS_COMMAND_RUNNER = `
|
|
7588
|
+
const { writeSync } = await import('fs');
|
|
7589
|
+
let child;
|
|
7590
|
+
try {
|
|
7591
|
+
child = Bun.spawn(process.argv.slice(2), {
|
|
7592
|
+
stdin: 'ignore',
|
|
7593
|
+
stdout: 'pipe',
|
|
7594
|
+
stderr: 'ignore',
|
|
7595
|
+
});
|
|
7596
|
+
} catch {
|
|
7597
|
+
process.exit(127);
|
|
7598
|
+
}
|
|
7599
|
+
const terminateGroup = () => {
|
|
7600
|
+
if (process.platform !== 'win32') {
|
|
7601
|
+
try {
|
|
7602
|
+
process.kill(-process.pid, 'SIGKILL');
|
|
7603
|
+
} catch {
|
|
7604
|
+
}
|
|
7605
|
+
}
|
|
7606
|
+
try {
|
|
7607
|
+
child.kill('SIGKILL');
|
|
7608
|
+
} catch {
|
|
7609
|
+
}
|
|
7610
|
+
process.exit(1);
|
|
7611
|
+
};
|
|
7612
|
+
const timer = setTimeout(terminateGroup, ${OS_COMMAND_TIMEOUT_MS});
|
|
7613
|
+
try {
|
|
7614
|
+
const chunks = [];
|
|
7615
|
+
let outputBytes = 0;
|
|
7616
|
+
const readOutput = async () => {
|
|
7617
|
+
const reader = child.stdout.getReader();
|
|
7618
|
+
while (true) {
|
|
7619
|
+
const { done, value } = await reader.read();
|
|
7620
|
+
if (done) return;
|
|
7621
|
+
outputBytes += value.byteLength;
|
|
7622
|
+
if (outputBytes > ${OS_COMMAND_MAX_OUTPUT_BYTES}) terminateGroup();
|
|
7623
|
+
chunks.push(value);
|
|
7624
|
+
}
|
|
7625
|
+
};
|
|
7626
|
+
const [exitCode] = await Promise.all([child.exited, readOutput()]);
|
|
7627
|
+
clearTimeout(timer);
|
|
7628
|
+
for (const chunk of chunks) writeSync(1, chunk);
|
|
7629
|
+
writeSync(1, ${JSON.stringify(OS_COMMAND_RESULT_MARKER)} + String(exitCode));
|
|
7630
|
+
if (process.platform !== 'win32') terminateGroup();
|
|
7631
|
+
process.exit(0);
|
|
7632
|
+
} catch {
|
|
7633
|
+
clearTimeout(timer);
|
|
7634
|
+
terminateGroup();
|
|
7635
|
+
}
|
|
7636
|
+
`;
|
|
7517
7637
|
}
|
|
7518
7638
|
});
|
|
7519
7639
|
|
|
@@ -7665,7 +7785,7 @@ var init_task_run_service = __esm({
|
|
|
7665
7785
|
}
|
|
7666
7786
|
if (current.childPid != null) {
|
|
7667
7787
|
throw new LegacyRunAbandonConflictError(
|
|
7668
|
-
`run #${runId} \u5DF2\u8BB0\u5F55 child PID ${current.childPid}\uFF0C\u5FC5\u987B\u7531 Worker/Watchdog \u786E\u8BA4\u8FDB\u7A0B\
|
|
7788
|
+
`run #${runId} \u5DF2\u8BB0\u5F55 child PID ${current.childPid}\uFF0C\u5FC5\u987B\u7531 Worker/Watchdog \u786E\u8BA4\u53D7\u7BA1\u8FDB\u7A0B\u7EC4\u6392\u7A7A`
|
|
7669
7789
|
);
|
|
7670
7790
|
}
|
|
7671
7791
|
if (current.workerPid != null && isProcessAlive(current.workerPid)) {
|
|
@@ -16909,8 +17029,8 @@ var require_CronFileParser = __commonJS({
|
|
|
16909
17029
|
* @throws If file cannot be read
|
|
16910
17030
|
*/
|
|
16911
17031
|
static parseFileSync(filePath) {
|
|
16912
|
-
const { readFileSync:
|
|
16913
|
-
const data =
|
|
17032
|
+
const { readFileSync: readFileSync4 } = __require("fs");
|
|
17033
|
+
const data = readFileSync4(filePath, "utf8");
|
|
16914
17034
|
return _CronFileParser.#parseContent(data);
|
|
16915
17035
|
}
|
|
16916
17036
|
/**
|
|
@@ -17045,12 +17165,14 @@ var init_task_template_service = __esm({
|
|
|
17045
17165
|
init_cron_parser();
|
|
17046
17166
|
init_task_working_directory();
|
|
17047
17167
|
init_task_batch();
|
|
17168
|
+
init_model_variant();
|
|
17048
17169
|
({ taskTemplates: taskTemplates2 } = schema_exports);
|
|
17049
17170
|
TaskTemplateService = class {
|
|
17050
17171
|
static async create(data) {
|
|
17051
17172
|
const normalizedData = {
|
|
17052
17173
|
...data,
|
|
17053
|
-
batchId: normalizeTaskBatchId(data.batchId)
|
|
17174
|
+
batchId: normalizeTaskBatchId(data.batchId),
|
|
17175
|
+
variant: normalizeModelVariant(data.variant)
|
|
17054
17176
|
};
|
|
17055
17177
|
this.validate(normalizedData);
|
|
17056
17178
|
const now = Date.now();
|
|
@@ -17116,7 +17238,8 @@ var init_task_template_service = __esm({
|
|
|
17116
17238
|
static async update(id, data) {
|
|
17117
17239
|
const normalizedData = {
|
|
17118
17240
|
...data,
|
|
17119
|
-
batchId: normalizeTaskBatchId(data.batchId) ?? null
|
|
17241
|
+
batchId: normalizeTaskBatchId(data.batchId) ?? null,
|
|
17242
|
+
...data.variant === void 0 ? {} : { variant: normalizeModelVariant(data.variant) ?? null }
|
|
17120
17243
|
};
|
|
17121
17244
|
this.validate(normalizedData);
|
|
17122
17245
|
const now = Date.now();
|
|
@@ -17279,6 +17402,7 @@ function createTaskFromTemplate(templateId, options) {
|
|
|
17279
17402
|
name: `${options.namePrefix ?? ""}${tmpl.name}`,
|
|
17280
17403
|
agent: tmpl.agent,
|
|
17281
17404
|
model: tmpl.model ?? "default",
|
|
17405
|
+
variant: tmpl.variant,
|
|
17282
17406
|
prompt: tmpl.prompt,
|
|
17283
17407
|
cwd: tmpl.cwd ?? null,
|
|
17284
17408
|
category: tmpl.category ?? "general",
|
|
@@ -17351,32 +17475,6 @@ var init_job_templates = __esm({
|
|
|
17351
17475
|
}
|
|
17352
17476
|
});
|
|
17353
17477
|
|
|
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
|
-
|
|
17380
17478
|
// node_modules/hono/dist/compose.js
|
|
17381
17479
|
var compose;
|
|
17382
17480
|
var init_compose = __esm({
|
|
@@ -19634,21 +19732,53 @@ function cleanOutput(value) {
|
|
|
19634
19732
|
return value.replace(ANSI_PATTERN, "").replace(/\r/g, "");
|
|
19635
19733
|
}
|
|
19636
19734
|
function runOpenCode(executable, args, cwd, timeoutMs) {
|
|
19637
|
-
return new Promise((
|
|
19735
|
+
return new Promise((resolve3, reject) => {
|
|
19638
19736
|
const child = spawn2(executable, args, {
|
|
19639
19737
|
cwd,
|
|
19640
19738
|
env: process.env,
|
|
19641
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
19739
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
19740
|
+
detached: process.platform !== "win32"
|
|
19642
19741
|
});
|
|
19643
19742
|
let stdout = "";
|
|
19644
19743
|
let stderr = "";
|
|
19645
19744
|
let failure = null;
|
|
19646
|
-
let
|
|
19745
|
+
let settled = false;
|
|
19746
|
+
let forceKillTimer = null;
|
|
19747
|
+
let finalRejectTimer = null;
|
|
19748
|
+
let timer;
|
|
19749
|
+
const signalProcessTree = (signal) => {
|
|
19750
|
+
if (process.platform !== "win32" && child.pid) {
|
|
19751
|
+
try {
|
|
19752
|
+
process.kill(-child.pid, signal);
|
|
19753
|
+
return;
|
|
19754
|
+
} catch {
|
|
19755
|
+
}
|
|
19756
|
+
}
|
|
19757
|
+
child.kill(signal);
|
|
19758
|
+
};
|
|
19759
|
+
const rejectOnce = (error) => {
|
|
19760
|
+
failure ??= error;
|
|
19761
|
+
if (settled) return;
|
|
19762
|
+
settled = true;
|
|
19763
|
+
clearTimeout(timer);
|
|
19764
|
+
if (forceKillTimer) clearTimeout(forceKillTimer);
|
|
19765
|
+
if (finalRejectTimer) clearTimeout(finalRejectTimer);
|
|
19766
|
+
reject(failure);
|
|
19767
|
+
};
|
|
19768
|
+
const terminate = (error) => {
|
|
19769
|
+
if (failure) return;
|
|
19770
|
+
failure = error;
|
|
19771
|
+
signalProcessTree("SIGTERM");
|
|
19772
|
+
forceKillTimer = setTimeout(() => {
|
|
19773
|
+
signalProcessTree("SIGKILL");
|
|
19774
|
+
rejectOnce(error);
|
|
19775
|
+
}, 1e3);
|
|
19776
|
+
finalRejectTimer = setTimeout(() => rejectOnce(error), 2e3);
|
|
19777
|
+
};
|
|
19647
19778
|
const append = (current, chunk) => {
|
|
19648
19779
|
const next = current + chunk.toString();
|
|
19649
19780
|
if (Buffer.byteLength(next) > MAX_OUTPUT_BYTES && failure === null) {
|
|
19650
|
-
|
|
19651
|
-
child.kill("SIGTERM");
|
|
19781
|
+
terminate(new Error(`OpenCode \u8F93\u51FA\u8D85\u8FC7 ${MAX_OUTPUT_BYTES} bytes`));
|
|
19652
19782
|
}
|
|
19653
19783
|
return next.slice(-MAX_OUTPUT_BYTES);
|
|
19654
19784
|
};
|
|
@@ -19659,32 +19789,80 @@ function runOpenCode(executable, args, cwd, timeoutMs) {
|
|
|
19659
19789
|
stderr = append(stderr, chunk);
|
|
19660
19790
|
});
|
|
19661
19791
|
child.once("error", (error) => {
|
|
19662
|
-
|
|
19792
|
+
if (forceKillTimer) return;
|
|
19793
|
+
rejectOnce(error);
|
|
19663
19794
|
});
|
|
19664
|
-
|
|
19665
|
-
|
|
19666
|
-
child.kill("SIGTERM");
|
|
19795
|
+
timer = setTimeout(() => {
|
|
19796
|
+
terminate(new Error(`OpenCode \u547D\u4EE4\u8D85\u8FC7 ${timeoutMs}ms \u672A\u5B8C\u6210`));
|
|
19667
19797
|
}, timeoutMs);
|
|
19668
19798
|
child.once("close", (code) => {
|
|
19669
|
-
if (finished) return;
|
|
19670
|
-
finished = true;
|
|
19671
19799
|
clearTimeout(timer);
|
|
19800
|
+
if (forceKillTimer) return;
|
|
19801
|
+
if (finalRejectTimer) clearTimeout(finalRejectTimer);
|
|
19802
|
+
if (settled) return;
|
|
19803
|
+
settled = true;
|
|
19672
19804
|
if (failure) {
|
|
19673
19805
|
reject(failure);
|
|
19674
19806
|
return;
|
|
19675
19807
|
}
|
|
19676
19808
|
if (code !== 0) {
|
|
19677
19809
|
const detail = cleanOutput(stderr).trim() || `\u9000\u51FA\u7801 ${code ?? "null"}`;
|
|
19678
|
-
reject(new
|
|
19810
|
+
reject(new OpenCodeCommandExitError(`OpenCode ${args.join(" ")} \u5931\u8D25\uFF1A${detail}`));
|
|
19679
19811
|
return;
|
|
19680
19812
|
}
|
|
19681
|
-
|
|
19813
|
+
resolve3(cleanOutput(stdout));
|
|
19682
19814
|
});
|
|
19683
19815
|
});
|
|
19684
19816
|
}
|
|
19685
19817
|
function parseOpenCodeModels(output) {
|
|
19686
19818
|
return [...new Set(cleanOutput(output).split("\n").map((line) => line.trim()).filter((line) => /^[^\s/]+\/.+/.test(line)))].sort((left, right) => left.localeCompare(right));
|
|
19687
19819
|
}
|
|
19820
|
+
function isRecord(value) {
|
|
19821
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19822
|
+
}
|
|
19823
|
+
function findJsonObjectEnd(value, start) {
|
|
19824
|
+
let depth = 0;
|
|
19825
|
+
let inString = false;
|
|
19826
|
+
let escaped = false;
|
|
19827
|
+
for (let index2 = start; index2 < value.length; index2 += 1) {
|
|
19828
|
+
const character = value[index2];
|
|
19829
|
+
if (inString) {
|
|
19830
|
+
if (escaped) escaped = false;
|
|
19831
|
+
else if (character === "\\") escaped = true;
|
|
19832
|
+
else if (character === '"') inString = false;
|
|
19833
|
+
continue;
|
|
19834
|
+
}
|
|
19835
|
+
if (character === '"') inString = true;
|
|
19836
|
+
else if (character === "{") depth += 1;
|
|
19837
|
+
else if (character === "}" && --depth === 0) return index2 + 1;
|
|
19838
|
+
}
|
|
19839
|
+
return null;
|
|
19840
|
+
}
|
|
19841
|
+
function parseOpenCodeModelMetadata(output) {
|
|
19842
|
+
const cleaned = cleanOutput(output);
|
|
19843
|
+
const models = /* @__PURE__ */ new Set();
|
|
19844
|
+
const variantsByModel = {};
|
|
19845
|
+
const headingPattern = /^([^\s/]+\/[^\r\n]+)\r?\n\s*(?=\{)/gm;
|
|
19846
|
+
for (const match2 of cleaned.matchAll(headingPattern)) {
|
|
19847
|
+
const model = match2[1].trim();
|
|
19848
|
+
models.add(model);
|
|
19849
|
+
let objectStart = (match2.index ?? 0) + match2[0].length;
|
|
19850
|
+
while (/\s/.test(cleaned[objectStart] ?? "")) objectStart += 1;
|
|
19851
|
+
if (cleaned[objectStart] !== "{") continue;
|
|
19852
|
+
const objectEnd = findJsonObjectEnd(cleaned, objectStart);
|
|
19853
|
+
if (objectEnd === null) continue;
|
|
19854
|
+
try {
|
|
19855
|
+
const metadata = JSON.parse(cleaned.slice(objectStart, objectEnd));
|
|
19856
|
+
if (!isRecord(metadata) || !isRecord(metadata.variants)) continue;
|
|
19857
|
+
variantsByModel[model] = Object.keys(metadata.variants).filter((variant) => variant.trim().length > 0).sort((left, right) => left.localeCompare(right));
|
|
19858
|
+
} catch {
|
|
19859
|
+
}
|
|
19860
|
+
}
|
|
19861
|
+
return {
|
|
19862
|
+
models: [...models].sort((left, right) => left.localeCompare(right)),
|
|
19863
|
+
variantsByModel
|
|
19864
|
+
};
|
|
19865
|
+
}
|
|
19688
19866
|
function parseOpenCodeAgents(output) {
|
|
19689
19867
|
const agents = /* @__PURE__ */ new Map();
|
|
19690
19868
|
for (const line of cleanOutput(output).split("\n")) {
|
|
@@ -19703,19 +19881,23 @@ async function loadOpenCodeCatalog(cwd, options = {}) {
|
|
|
19703
19881
|
const executable = options.executable ?? process.env.SUPERTASK_OPENCODE_BIN ?? "opencode";
|
|
19704
19882
|
const timeoutMs = options.timeoutMs ?? COMMAND_TIMEOUT_MS;
|
|
19705
19883
|
const cacheKey = `${executable}\0${cwd}`;
|
|
19706
|
-
const
|
|
19707
|
-
if (options.useCache !== false &&
|
|
19708
|
-
return
|
|
19884
|
+
const cached2 = catalogCache.get(cacheKey);
|
|
19885
|
+
if (options.useCache !== false && cached2 && cached2.expiresAt > Date.now()) {
|
|
19886
|
+
return cached2.result;
|
|
19709
19887
|
}
|
|
19710
19888
|
const result = Promise.all([
|
|
19711
|
-
runOpenCode(executable, ["models"], cwd, timeoutMs)
|
|
19889
|
+
runOpenCode(executable, ["models", "--verbose"], cwd, timeoutMs).catch((error) => {
|
|
19890
|
+
if (!(error instanceof OpenCodeCommandExitError)) throw error;
|
|
19891
|
+
return runOpenCode(executable, ["models"], cwd, timeoutMs);
|
|
19892
|
+
}),
|
|
19712
19893
|
runOpenCode(executable, ["agent", "list"], cwd, timeoutMs)
|
|
19713
19894
|
]).then(([modelsOutput, agentsOutput]) => {
|
|
19714
|
-
const
|
|
19895
|
+
const metadata = parseOpenCodeModelMetadata(modelsOutput);
|
|
19896
|
+
const models = metadata.models.length > 0 ? metadata.models : parseOpenCodeModels(modelsOutput);
|
|
19715
19897
|
const agents = parseOpenCodeAgents(agentsOutput).filter((agent) => agent.mode !== "subagent");
|
|
19716
19898
|
if (models.length === 0) throw new Error("OpenCode \u6CA1\u6709\u8FD4\u56DE\u53EF\u7528\u6A21\u578B");
|
|
19717
19899
|
if (agents.length === 0) throw new Error("OpenCode \u6CA1\u6709\u8FD4\u56DE\u53EF\u76F4\u63A5\u8FD0\u884C\u7684\u4E3B Agent");
|
|
19718
|
-
return { cwd, models, agents };
|
|
19900
|
+
return { cwd, models, variantsByModel: metadata.variantsByModel, agents };
|
|
19719
19901
|
});
|
|
19720
19902
|
if (options.useCache !== false) {
|
|
19721
19903
|
catalogCache.set(cacheKey, { expiresAt: Date.now() + CATALOG_CACHE_MS, result });
|
|
@@ -19725,7 +19907,7 @@ async function loadOpenCodeCatalog(cwd, options = {}) {
|
|
|
19725
19907
|
}
|
|
19726
19908
|
return result;
|
|
19727
19909
|
}
|
|
19728
|
-
var CATALOG_CACHE_MS, COMMAND_TIMEOUT_MS, MAX_OUTPUT_BYTES, ANSI_PATTERN, catalogCache;
|
|
19910
|
+
var CATALOG_CACHE_MS, COMMAND_TIMEOUT_MS, MAX_OUTPUT_BYTES, ANSI_PATTERN, OpenCodeCommandExitError, catalogCache;
|
|
19729
19911
|
var init_opencode_catalog = __esm({
|
|
19730
19912
|
"src/core/opencode-catalog.ts"() {
|
|
19731
19913
|
"use strict";
|
|
@@ -19734,6 +19916,8 @@ var init_opencode_catalog = __esm({
|
|
|
19734
19916
|
COMMAND_TIMEOUT_MS = 2e4;
|
|
19735
19917
|
MAX_OUTPUT_BYTES = 4 * 1024 * 1024;
|
|
19736
19918
|
ANSI_PATTERN = /\x1B(?:[@-_][0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\))/g;
|
|
19919
|
+
OpenCodeCommandExitError = class extends Error {
|
|
19920
|
+
};
|
|
19737
19921
|
catalogCache = /* @__PURE__ */ new Map();
|
|
19738
19922
|
}
|
|
19739
19923
|
});
|
|
@@ -20174,464 +20358,106 @@ var init_database_maintenance_service = __esm({
|
|
|
20174
20358
|
}
|
|
20175
20359
|
});
|
|
20176
20360
|
|
|
20177
|
-
// src/
|
|
20178
|
-
import {
|
|
20179
|
-
|
|
20180
|
-
|
|
20181
|
-
"use strict";
|
|
20182
|
-
}
|
|
20183
|
-
});
|
|
20184
|
-
|
|
20185
|
-
// src/daemon/pm2.ts
|
|
20186
|
-
import { execSync, spawnSync as spawnSync2 } from "child_process";
|
|
20187
|
-
import {
|
|
20188
|
-
accessSync,
|
|
20189
|
-
chmodSync as chmodSync2,
|
|
20190
|
-
constants,
|
|
20191
|
-
existsSync as existsSync6,
|
|
20192
|
-
mkdirSync as mkdirSync3,
|
|
20193
|
-
readFileSync as readFileSync3,
|
|
20194
|
-
rmSync,
|
|
20195
|
-
statSync as statSync3,
|
|
20196
|
-
writeFileSync as writeFileSync2
|
|
20197
|
-
} from "fs";
|
|
20198
|
-
import { homedir as homedir3, userInfo } from "os";
|
|
20199
|
-
import { delimiter, dirname as dirname6, isAbsolute as isAbsolute2, join as join5, resolve as resolve3 } from "path";
|
|
20361
|
+
// src/web/gateway-diagnostic.ts
|
|
20362
|
+
import { spawn as spawn3 } from "child_process";
|
|
20363
|
+
import { existsSync as existsSync6 } from "fs";
|
|
20364
|
+
import { dirname as dirname6, join as join5 } from "path";
|
|
20200
20365
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
20201
|
-
|
|
20202
|
-
|
|
20203
|
-
return resolve3(env.HOME || homedir3());
|
|
20204
|
-
}
|
|
20205
|
-
function runtimePath(value, cwd) {
|
|
20206
|
-
return resolve3(cwd, value);
|
|
20207
|
-
}
|
|
20208
|
-
function versionFile(env = process.env, cwd = process.cwd()) {
|
|
20209
|
-
return env.SUPERTASK_VERSION_FILE ? runtimePath(env.SUPERTASK_VERSION_FILE, cwd) : join5(runtimeHome(env), ".local/share/opencode/supertask-gateway-version");
|
|
20210
|
-
}
|
|
20211
|
-
function getRunningVersion(env = process.env, cwd = process.cwd()) {
|
|
20212
|
-
try {
|
|
20213
|
-
const path = versionFile(env, cwd);
|
|
20214
|
-
if (!existsSync6(path)) return null;
|
|
20215
|
-
return readFileSync3(path, "utf-8").trim() || null;
|
|
20216
|
-
} catch {
|
|
20217
|
-
return null;
|
|
20218
|
-
}
|
|
20219
|
-
}
|
|
20220
|
-
function packageVersionFromGatewayEntry(gatewayEntry) {
|
|
20221
|
-
if (gatewayEntry === null) return null;
|
|
20222
|
-
let directory = dirname6(gatewayEntry);
|
|
20223
|
-
for (let depth = 0; depth < 6; depth += 1) {
|
|
20224
|
-
const packagePath = join5(directory, "package.json");
|
|
20225
|
-
if (existsSync6(packagePath)) {
|
|
20226
|
-
try {
|
|
20227
|
-
const pkg = JSON.parse(readFileSync3(packagePath, "utf8"));
|
|
20228
|
-
if (pkg.name === "opencode-supertask" && typeof pkg.version === "string") {
|
|
20229
|
-
return pkg.version;
|
|
20230
|
-
}
|
|
20231
|
-
} catch {
|
|
20232
|
-
}
|
|
20233
|
-
}
|
|
20234
|
-
const parent = dirname6(directory);
|
|
20235
|
-
if (parent === directory) break;
|
|
20236
|
-
directory = parent;
|
|
20237
|
-
}
|
|
20238
|
-
return null;
|
|
20239
|
-
}
|
|
20240
|
-
function resolveRuntimeExecutable(command, env, cwd) {
|
|
20241
|
-
if (isAbsolute2(command) || command.includes("/")) return runtimePath(command, cwd);
|
|
20242
|
-
for (const entry of (env.PATH ?? "").split(delimiter)) {
|
|
20243
|
-
if (!entry) continue;
|
|
20244
|
-
const candidate = resolve3(cwd, entry, command);
|
|
20366
|
+
function killDiagnosticGroup(pid) {
|
|
20367
|
+
if (process.platform !== "win32") {
|
|
20245
20368
|
try {
|
|
20246
|
-
|
|
20247
|
-
return
|
|
20369
|
+
process.kill(-pid, "SIGKILL");
|
|
20370
|
+
return;
|
|
20248
20371
|
} catch {
|
|
20249
20372
|
}
|
|
20250
20373
|
}
|
|
20251
|
-
return command;
|
|
20252
|
-
}
|
|
20253
|
-
function runtimeScope(runtime) {
|
|
20254
|
-
const { cwd, env } = runtime;
|
|
20255
|
-
const home = runtimeHome(env);
|
|
20256
|
-
return {
|
|
20257
|
-
cwd: resolve3(cwd),
|
|
20258
|
-
databasePath: env.SUPERTASK_DB_PATH ? runtimePath(env.SUPERTASK_DB_PATH, cwd) : join5(home, ".local/share/opencode/tasks.db"),
|
|
20259
|
-
configPath: env.SUPERTASK_CONFIG_PATH ? runtimePath(env.SUPERTASK_CONFIG_PATH, cwd) : join5(home, ".config/opencode/supertask.json"),
|
|
20260
|
-
opencodePath: resolveRuntimeExecutable(env.SUPERTASK_OPENCODE_BIN ?? "opencode", env, cwd),
|
|
20261
|
-
home,
|
|
20262
|
-
pm2Home: env.PM2_HOME ? runtimePath(env.PM2_HOME, cwd) : join5(home, ".pm2"),
|
|
20263
|
-
managementLockPath: canonicalManagementLockPath(env, cwd)
|
|
20264
|
-
};
|
|
20265
|
-
}
|
|
20266
|
-
function scopesMatch(left, right) {
|
|
20267
|
-
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;
|
|
20268
|
-
}
|
|
20269
|
-
function currentScope() {
|
|
20270
|
-
return runtimeScope({ cwd: process.cwd(), env: process.env });
|
|
20271
|
-
}
|
|
20272
|
-
function diagnoseOpenCodeRuntime(runtime = {
|
|
20273
|
-
cwd: process.cwd(),
|
|
20274
|
-
env: process.env
|
|
20275
|
-
}) {
|
|
20276
|
-
const executable = runtimeScope(runtime).opencodePath;
|
|
20277
|
-
const result = spawnSync2(executable, ["--version"], {
|
|
20278
|
-
cwd: runtime.cwd,
|
|
20279
|
-
env: runtime.env,
|
|
20280
|
-
encoding: "utf8",
|
|
20281
|
-
timeout: 1e4,
|
|
20282
|
-
killSignal: "SIGKILL"
|
|
20283
|
-
});
|
|
20284
|
-
const ok = result.status === 0 && result.error === void 0;
|
|
20285
|
-
return {
|
|
20286
|
-
ok,
|
|
20287
|
-
executable,
|
|
20288
|
-
version: ok ? result.stdout.trim() || result.stderr.trim() || null : null,
|
|
20289
|
-
error: ok ? null : result.error?.message || result.stderr.trim() || result.stdout.trim() || `exit code ${result.status}`
|
|
20290
|
-
};
|
|
20291
|
-
}
|
|
20292
|
-
function getGatewayDiagnostic(options = {}) {
|
|
20293
|
-
const producerScope = currentScope();
|
|
20294
|
-
if (!isPm2Installed()) {
|
|
20295
|
-
return {
|
|
20296
|
-
pm2Installed: false,
|
|
20297
|
-
processFound: false,
|
|
20298
|
-
status: null,
|
|
20299
|
-
pid: null,
|
|
20300
|
-
ready: false,
|
|
20301
|
-
runningVersion: getRunningVersion(),
|
|
20302
|
-
gatewayEntry: null,
|
|
20303
|
-
gatewayPackageVersion: null,
|
|
20304
|
-
logRotationInstalled: false,
|
|
20305
|
-
startupConfigured: process.platform === "darwin" || process.platform === "linux" ? false : null,
|
|
20306
|
-
currentScope: producerScope,
|
|
20307
|
-
gatewayScope: null,
|
|
20308
|
-
scopeMatches: false,
|
|
20309
|
-
gatewayOpenCode: null
|
|
20310
|
-
};
|
|
20311
|
-
}
|
|
20312
|
-
const processes = pm2JsonList();
|
|
20313
|
-
const gateway = processes.find((item) => item.name === PROCESS_NAME);
|
|
20314
|
-
const runtime = gatewayRuntimeFromProcess(gateway);
|
|
20315
|
-
const gatewayEnv = runtime?.env ?? process.env;
|
|
20316
|
-
const managedScope = runtime ? runtimeScope(runtime) : null;
|
|
20317
|
-
const pid = typeof gateway?.pid === "number" ? gateway.pid : null;
|
|
20318
|
-
const readyPath = managedScope?.databasePath ?? databasePath();
|
|
20319
|
-
const lockedVersion = pid == null ? void 0 : gatewayVersionFromLock(pid, readyPath);
|
|
20320
|
-
const gatewayEntry = runtime?.gatewayEntry ?? null;
|
|
20321
|
-
return {
|
|
20322
|
-
pm2Installed: true,
|
|
20323
|
-
processFound: gateway != null,
|
|
20324
|
-
status: gateway?.pm2_env?.status ?? null,
|
|
20325
|
-
pid,
|
|
20326
|
-
ready: pid != null && isGatewayReady(pid, readyPath),
|
|
20327
|
-
runningVersion: lockedVersion === void 0 ? getRunningVersion(gatewayEnv, runtime?.cwd) : lockedVersion,
|
|
20328
|
-
gatewayEntry,
|
|
20329
|
-
gatewayPackageVersion: packageVersionFromGatewayEntry(gatewayEntry),
|
|
20330
|
-
logRotationInstalled: processes.some((item) => item.name === "pm2-logrotate" && item.pm2_env?.status === "online"),
|
|
20331
|
-
startupConfigured: process.platform === "darwin" ? isMacLaunchAgentConfigured(
|
|
20332
|
-
gatewayEnv.PM2_HOME ?? join5(runtimeHome(gatewayEnv), ".pm2"),
|
|
20333
|
-
runtime ?? void 0
|
|
20334
|
-
) : process.platform === "linux" ? isLinuxStartupConfigured(gatewayEnv, runtime?.cwd, runtime ?? void 0) : null,
|
|
20335
|
-
currentScope: producerScope,
|
|
20336
|
-
gatewayScope: managedScope,
|
|
20337
|
-
scopeMatches: managedScope != null && scopesMatch(producerScope, managedScope),
|
|
20338
|
-
gatewayOpenCode: runtime === null || !options.probeOpenCode ? null : diagnoseOpenCodeRuntime(runtime)
|
|
20339
|
-
};
|
|
20340
|
-
}
|
|
20341
|
-
function pm2Bin(env = process.env) {
|
|
20342
|
-
return env.SUPERTASK_PM2_BIN ?? (process.platform === "win32" ? "pm2.cmd" : "pm2");
|
|
20343
|
-
}
|
|
20344
|
-
function pm2CommandTimeoutMs(env = process.env) {
|
|
20345
|
-
const configured = Number(env.SUPERTASK_PM2_COMMAND_TIMEOUT_MS ?? 15e3);
|
|
20346
|
-
return Number.isFinite(configured) && configured > 0 ? configured : 15e3;
|
|
20347
|
-
}
|
|
20348
|
-
function resolvePm2Bin() {
|
|
20349
|
-
const configured = pm2Bin();
|
|
20350
|
-
if (isAbsolute2(configured)) return configured;
|
|
20351
|
-
const result = spawnSync2("which", [configured], { encoding: "utf8" });
|
|
20352
|
-
const resolved = result.status === 0 ? result.stdout.trim().split("\n")[0] : "";
|
|
20353
|
-
if (!resolved) throw new Error(`[supertask] \u65E0\u6CD5\u89E3\u6790 pm2 \u53EF\u6267\u884C\u6587\u4EF6: ${configured}`);
|
|
20354
|
-
return resolved;
|
|
20355
|
-
}
|
|
20356
|
-
function launchAgentPath() {
|
|
20357
|
-
return process.env.SUPERTASK_LAUNCH_AGENT_PATH ?? join5(runtimeHome(process.env), "Library/LaunchAgents", `${MAC_LAUNCH_AGENT_LABEL}.plist`);
|
|
20358
|
-
}
|
|
20359
|
-
function launchctlBin() {
|
|
20360
|
-
return process.env.SUPERTASK_LAUNCHCTL_BIN ?? "launchctl";
|
|
20361
|
-
}
|
|
20362
|
-
function xmlUnescape(value) {
|
|
20363
|
-
return value.replaceAll("'", "'").replaceAll(""", '"').replaceAll(">", ">").replaceAll("<", "<").replaceAll("&", "&");
|
|
20364
|
-
}
|
|
20365
|
-
function plistValue(contents, key) {
|
|
20366
|
-
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
20367
|
-
const match2 = contents.match(new RegExp(`<key>\\s*${escapedKey}\\s*</key>\\s*<string>([^<]*)</string>`));
|
|
20368
|
-
return match2?.[1] ? xmlUnescape(match2[1]) : null;
|
|
20369
|
-
}
|
|
20370
|
-
function isMacLaunchAgentConfigured(expectedPm2Home, expectedRuntime) {
|
|
20371
|
-
if (typeof process.getuid !== "function") return false;
|
|
20372
|
-
const plistPath = launchAgentPath();
|
|
20373
|
-
if (!existsSync6(plistPath)) return false;
|
|
20374
20374
|
try {
|
|
20375
|
-
|
|
20376
|
-
const argumentsBlock = contents.match(
|
|
20377
|
-
/<key>\s*ProgramArguments\s*<\/key>\s*<array>([\s\S]*?)<\/array>/
|
|
20378
|
-
)?.[1];
|
|
20379
|
-
const programArguments = argumentsBlock ? [...argumentsBlock.matchAll(/<string>([^<]*)<\/string>/g)].map((match2) => xmlUnescape(match2[1])) : [];
|
|
20380
|
-
const bunPath = programArguments[0];
|
|
20381
|
-
const supervisorEntry = programArguments[1];
|
|
20382
|
-
const pm2Path = programArguments[2];
|
|
20383
|
-
const pm2Home = plistValue(contents, "PM2_HOME");
|
|
20384
|
-
const managementLock = plistValue(contents, "SUPERTASK_PM2_MANAGEMENT_LOCK");
|
|
20385
|
-
if (!bunPath || !supervisorEntry || !pm2Path || !pm2Home || !managementLock) return false;
|
|
20386
|
-
if (expectedPm2Home && pm2Home !== expectedPm2Home) return false;
|
|
20387
|
-
const expectedManagementLock = expectedRuntime ? managementLockPath(expectedRuntime.env, expectedRuntime.cwd) : process.env.SUPERTASK_PM2_MANAGEMENT_LOCK ? managementLockPath() : join5(expectedPm2Home ?? currentScope().pm2Home, "supertask-gateway.manage.sqlite");
|
|
20388
|
-
if (managementLock !== expectedManagementLock) return false;
|
|
20389
|
-
accessSync(bunPath, constants.X_OK);
|
|
20390
|
-
accessSync(supervisorEntry, constants.R_OK);
|
|
20391
|
-
accessSync(pm2Path, constants.X_OK);
|
|
20392
|
-
if (spawnSync2(bunPath, ["--version"], {
|
|
20393
|
-
stdio: "ignore",
|
|
20394
|
-
timeout: pm2CommandTimeoutMs(),
|
|
20395
|
-
killSignal: "SIGKILL"
|
|
20396
|
-
}).status !== 0) return false;
|
|
20397
|
-
if (spawnSync2(pm2Path, ["--version"], {
|
|
20398
|
-
stdio: "ignore",
|
|
20399
|
-
env: { ...process.env, PM2_HOME: pm2Home },
|
|
20400
|
-
timeout: pm2CommandTimeoutMs(),
|
|
20401
|
-
killSignal: "SIGKILL"
|
|
20402
|
-
}).status !== 0) return false;
|
|
20403
|
-
const loaded = spawnSync2(
|
|
20404
|
-
launchctlBin(),
|
|
20405
|
-
["print", `gui/${process.getuid()}/${MAC_LAUNCH_AGENT_LABEL}`],
|
|
20406
|
-
{
|
|
20407
|
-
encoding: "utf8",
|
|
20408
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
20409
|
-
timeout: pm2CommandTimeoutMs(),
|
|
20410
|
-
killSignal: "SIGKILL"
|
|
20411
|
-
}
|
|
20412
|
-
);
|
|
20413
|
-
if (loaded.status !== 0) return false;
|
|
20414
|
-
if (!loaded.stdout.includes("state = running")) return false;
|
|
20415
|
-
if (!loaded.stdout.includes(`path = ${plistPath}`)) return false;
|
|
20416
|
-
if (!loaded.stdout.includes(`program = ${bunPath}`)) return false;
|
|
20417
|
-
const dumpPath = join5(pm2Home, "dump.pm2");
|
|
20418
|
-
const dump = JSON.parse(readFileSync3(dumpPath, "utf8"));
|
|
20419
|
-
if (!Array.isArray(dump)) return false;
|
|
20420
|
-
const gateway = dump.find((item) => item.name === PROCESS_NAME);
|
|
20421
|
-
const dumpRuntime = gatewayRuntimeFromProcess(gateway);
|
|
20422
|
-
if (!dumpRuntime || !hasRestorableSavedGatewayRuntime(gateway)) return false;
|
|
20423
|
-
return expectedRuntime === void 0 || dumpRuntime.gatewayEntry === expectedRuntime.gatewayEntry && dumpRuntime.bunPath === expectedRuntime.bunPath && dumpRuntime.cwd === expectedRuntime.cwd && scopesMatch(runtimeScope(dumpRuntime), runtimeScope(expectedRuntime));
|
|
20375
|
+
process.kill(pid, "SIGKILL");
|
|
20424
20376
|
} catch {
|
|
20425
|
-
return false;
|
|
20426
20377
|
}
|
|
20427
20378
|
}
|
|
20428
|
-
function
|
|
20429
|
-
|
|
20379
|
+
function diagnosticEntry() {
|
|
20380
|
+
const extension = import.meta.url.endsWith(".ts") ? "ts" : "js";
|
|
20381
|
+
const moduleDir = dirname6(fileURLToPath5(import.meta.url));
|
|
20382
|
+
const candidates = [
|
|
20383
|
+
join5(moduleDir, `../daemon/gateway-diagnostic-runner.${extension}`),
|
|
20384
|
+
join5(moduleDir, `../../src/daemon/gateway-diagnostic-runner.${extension}`)
|
|
20385
|
+
];
|
|
20386
|
+
const entry = candidates.find((candidate) => existsSync6(candidate));
|
|
20387
|
+
if (!entry) throw new Error(`Gateway diagnostic runner \u4E0D\u5B58\u5728\uFF1A${candidates.join(", ")}`);
|
|
20388
|
+
return entry;
|
|
20430
20389
|
}
|
|
20431
|
-
function
|
|
20432
|
-
|
|
20433
|
-
|
|
20434
|
-
|
|
20435
|
-
|
|
20436
|
-
|
|
20437
|
-
|
|
20438
|
-
|
|
20439
|
-
|
|
20440
|
-
|
|
20441
|
-
|
|
20442
|
-
|
|
20443
|
-
|
|
20444
|
-
|
|
20445
|
-
|
|
20446
|
-
|
|
20390
|
+
async function runGatewayDiagnostic() {
|
|
20391
|
+
return new Promise((resolve3, reject) => {
|
|
20392
|
+
const child = spawn3(process.execPath, [diagnosticEntry()], {
|
|
20393
|
+
cwd: process.cwd(),
|
|
20394
|
+
env: process.env,
|
|
20395
|
+
detached: process.platform !== "win32",
|
|
20396
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
20397
|
+
});
|
|
20398
|
+
let stdout = "";
|
|
20399
|
+
let stderr = "";
|
|
20400
|
+
let timedOut = false;
|
|
20401
|
+
if (child.pid) activeProcessGroups.add(child.pid);
|
|
20402
|
+
child.stdout?.on("data", (chunk) => {
|
|
20403
|
+
stdout += chunk.toString();
|
|
20404
|
+
});
|
|
20405
|
+
child.stderr?.on("data", (chunk) => {
|
|
20406
|
+
stderr += chunk.toString();
|
|
20407
|
+
});
|
|
20408
|
+
child.once("error", reject);
|
|
20409
|
+
child.once("close", (exitCode) => {
|
|
20410
|
+
clearTimeout(timer);
|
|
20411
|
+
if (child.pid) {
|
|
20412
|
+
killDiagnosticGroup(child.pid);
|
|
20413
|
+
activeProcessGroups.delete(child.pid);
|
|
20414
|
+
}
|
|
20415
|
+
if (timedOut) {
|
|
20416
|
+
reject(new Error(`Gateway diagnostic runner \u8D85\u8FC7 ${DIAGNOSTIC_TIMEOUT_MS}ms \u672A\u5B8C\u6210`));
|
|
20417
|
+
return;
|
|
20418
|
+
}
|
|
20419
|
+
if (exitCode !== 0) {
|
|
20420
|
+
reject(new Error(stderr.trim() || `Gateway diagnostic runner \u9000\u51FA\u7801 ${exitCode}`));
|
|
20421
|
+
return;
|
|
20422
|
+
}
|
|
20423
|
+
try {
|
|
20424
|
+
resolve3(JSON.parse(stdout));
|
|
20425
|
+
} catch (error) {
|
|
20426
|
+
reject(error);
|
|
20427
|
+
}
|
|
20428
|
+
});
|
|
20429
|
+
const timer = setTimeout(() => {
|
|
20430
|
+
timedOut = true;
|
|
20431
|
+
if (child.pid) killDiagnosticGroup(child.pid);
|
|
20432
|
+
else child.kill("SIGKILL");
|
|
20433
|
+
}, DIAGNOSTIC_TIMEOUT_MS);
|
|
20447
20434
|
});
|
|
20448
|
-
if (contents.status !== 0) return false;
|
|
20449
|
-
const expectedPm2Home = env.PM2_HOME ?? join5(runtimeHome(env), ".pm2");
|
|
20450
|
-
if (!contents.stdout.includes("resurrect") || !contents.stdout.includes(expectedPm2Home)) {
|
|
20451
|
-
return false;
|
|
20452
|
-
}
|
|
20453
|
-
try {
|
|
20454
|
-
const dump = JSON.parse(readFileSync3(join5(expectedPm2Home, "dump.pm2"), "utf8"));
|
|
20455
|
-
if (!Array.isArray(dump)) return false;
|
|
20456
|
-
const gateway = dump.find((item) => item.name === PROCESS_NAME);
|
|
20457
|
-
const runtime = gatewayRuntimeFromProcess(gateway);
|
|
20458
|
-
if (!runtime || !hasRestorableSavedGatewayRuntime(gateway)) return false;
|
|
20459
|
-
return runtime.cwd === resolve3(cwd) && scopesMatch(runtimeScope(runtime), runtimeScope({ cwd, env })) && (expectedRuntime === void 0 || runtime.gatewayEntry === expectedRuntime.gatewayEntry && runtime.bunPath === expectedRuntime.bunPath);
|
|
20460
|
-
} catch {
|
|
20461
|
-
return false;
|
|
20462
|
-
}
|
|
20463
20435
|
}
|
|
20464
|
-
function
|
|
20465
|
-
|
|
20466
|
-
|
|
20467
|
-
|
|
20468
|
-
|
|
20469
|
-
|
|
20470
|
-
killSignal: "SIGKILL"
|
|
20436
|
+
async function getDashboardGatewayDiagnostic(options = {}) {
|
|
20437
|
+
if (!options.fresh && cached && cached.expiresAt > Date.now()) return cached.diagnostic;
|
|
20438
|
+
if (!options.fresh && pending) return pending;
|
|
20439
|
+
const operation = runGatewayDiagnostic().then((diagnostic) => {
|
|
20440
|
+
cached = { expiresAt: Date.now() + CACHE_TTL_MS, diagnostic };
|
|
20441
|
+
return diagnostic;
|
|
20471
20442
|
});
|
|
20472
|
-
|
|
20473
|
-
|
|
20474
|
-
|
|
20475
|
-
const lookup = process.platform === "win32" ? "where" : "which";
|
|
20476
|
-
const result = spawnSync2(lookup, [command], { encoding: "utf8" });
|
|
20477
|
-
return result.status === 0 ? result.stdout.trim().split("\n")[0] || null : null;
|
|
20478
|
-
}
|
|
20479
|
-
function npmPreferredEnvironment() {
|
|
20480
|
-
if (process.platform === "win32") return null;
|
|
20481
|
-
const npm = resolveCommand("npm");
|
|
20482
|
-
const node = resolveCommand("node");
|
|
20483
|
-
if (!npm || !node) return null;
|
|
20484
|
-
const command = resolvePm2Bin();
|
|
20485
|
-
const path = [.../* @__PURE__ */ new Set([
|
|
20486
|
-
dirname6(command),
|
|
20487
|
-
dirname6(npm),
|
|
20488
|
-
dirname6(node),
|
|
20489
|
-
"/usr/local/bin",
|
|
20490
|
-
"/usr/bin",
|
|
20491
|
-
"/bin"
|
|
20492
|
-
])].join(delimiter);
|
|
20493
|
-
return { command, env: { ...process.env, PATH: path } };
|
|
20494
|
-
}
|
|
20495
|
-
function pm2Exec(args, options = {}) {
|
|
20496
|
-
const npmEnvironment = options.preferNpm ? npmPreferredEnvironment() : null;
|
|
20497
|
-
const effectiveEnv = options.env ?? npmEnvironment?.env ?? process.env;
|
|
20498
|
-
const result = spawnSync2(npmEnvironment?.command ?? pm2Bin(effectiveEnv), args, {
|
|
20499
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
20500
|
-
encoding: "utf-8",
|
|
20501
|
-
env: effectiveEnv,
|
|
20502
|
-
shell: process.platform === "win32",
|
|
20503
|
-
timeout: options.timeoutMs ?? pm2CommandTimeoutMs(effectiveEnv),
|
|
20504
|
-
killSignal: "SIGKILL"
|
|
20443
|
+
if (options.fresh) return operation;
|
|
20444
|
+
pending = operation.finally(() => {
|
|
20445
|
+
pending = null;
|
|
20505
20446
|
});
|
|
20506
|
-
|
|
20507
|
-
if (result.error) return { ok: false, output: result.error.message };
|
|
20508
|
-
return { ok: result.status === 0, output };
|
|
20509
|
-
}
|
|
20510
|
-
function requirePm2(args, action, options = {}) {
|
|
20511
|
-
const result = pm2Exec(args, options);
|
|
20512
|
-
if (!result.ok) throw new Error(`[supertask] ${action} failed: ${result.output || "unknown pm2 error"}`);
|
|
20513
|
-
return result.output;
|
|
20514
|
-
}
|
|
20515
|
-
function pm2JsonList(env = process.env) {
|
|
20516
|
-
const output = requirePm2(["jlist"], "pm2 jlist", { env });
|
|
20517
|
-
try {
|
|
20518
|
-
const parsed = JSON.parse(output);
|
|
20519
|
-
if (!Array.isArray(parsed)) throw new Error("result is not an array");
|
|
20520
|
-
return parsed;
|
|
20521
|
-
} catch (error) {
|
|
20522
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
20523
|
-
throw new Error(`[supertask] Invalid pm2 jlist output: ${message}`);
|
|
20524
|
-
}
|
|
20447
|
+
return pending;
|
|
20525
20448
|
}
|
|
20526
|
-
|
|
20527
|
-
|
|
20528
|
-
|
|
20529
|
-
const savedCwd = processInfo?.pm2_env?.pm_cwd ?? processInfo?.pm_cwd;
|
|
20530
|
-
for (const candidate of candidates) {
|
|
20531
|
-
const path = typeof savedCwd === "string" ? runtimePath(candidate, savedCwd) : candidate;
|
|
20532
|
-
if (existsSync6(path)) return resolve3(path);
|
|
20533
|
-
}
|
|
20534
|
-
return null;
|
|
20535
|
-
}
|
|
20536
|
-
function gatewayEnvironmentFromProcess(processInfo) {
|
|
20537
|
-
const saved = processInfo?.pm2_env?.env ?? processInfo?.env;
|
|
20538
|
-
if (!saved) return { ...process.env };
|
|
20539
|
-
const env = {};
|
|
20540
|
-
for (const [key, value] of Object.entries(saved)) {
|
|
20541
|
-
if (typeof value === "string") env[key] = value;
|
|
20542
|
-
}
|
|
20543
|
-
return env;
|
|
20544
|
-
}
|
|
20545
|
-
function gatewayRuntimeFromProcess(processInfo) {
|
|
20546
|
-
const gatewayEntry = gatewayEntryFromProcess(processInfo);
|
|
20547
|
-
if (!gatewayEntry) return null;
|
|
20548
|
-
const savedBunPath = processInfo?.pm2_env?.pm_exec_path ?? processInfo?.pm_exec_path;
|
|
20549
|
-
const savedCwd = processInfo?.pm2_env?.pm_cwd ?? processInfo?.pm_cwd;
|
|
20550
|
-
const savedEnv = gatewayEnvironmentFromProcess(processInfo);
|
|
20551
|
-
if (typeof savedBunPath !== "string" || typeof savedCwd !== "string") return null;
|
|
20552
|
-
try {
|
|
20553
|
-
accessSync(savedBunPath, constants.X_OK);
|
|
20554
|
-
if (!statSync3(savedCwd).isDirectory()) return null;
|
|
20555
|
-
if (spawnSync2(savedBunPath, ["--version"], {
|
|
20556
|
-
stdio: "ignore",
|
|
20557
|
-
env: savedEnv,
|
|
20558
|
-
timeout: pm2CommandTimeoutMs(savedEnv),
|
|
20559
|
-
killSignal: "SIGKILL"
|
|
20560
|
-
}).status !== 0) return null;
|
|
20561
|
-
} catch {
|
|
20562
|
-
return null;
|
|
20563
|
-
}
|
|
20564
|
-
const killTimeout = processInfo?.pm2_env?.kill_timeout ?? processInfo?.kill_timeout;
|
|
20565
|
-
return {
|
|
20566
|
-
gatewayEntry,
|
|
20567
|
-
bunPath: savedBunPath,
|
|
20568
|
-
cwd: resolve3(savedCwd),
|
|
20569
|
-
env: savedEnv,
|
|
20570
|
-
killTimeoutMs: Number.isInteger(killTimeout) && killTimeout >= 5e3 ? killTimeout : void 0
|
|
20571
|
-
};
|
|
20572
|
-
}
|
|
20573
|
-
function hasRestorableSavedGatewayRuntime(processInfo) {
|
|
20574
|
-
return gatewayRuntimeFromProcess(processInfo) !== null;
|
|
20575
|
-
}
|
|
20576
|
-
function databasePath(env = process.env, cwd = process.cwd()) {
|
|
20577
|
-
return env.SUPERTASK_DB_PATH ? runtimePath(env.SUPERTASK_DB_PATH, cwd) : join5(runtimeHome(env), ".local/share/opencode/tasks.db");
|
|
20578
|
-
}
|
|
20579
|
-
function isGatewayReady(expectedPid, path = databasePath()) {
|
|
20580
|
-
if (!existsSync6(path)) return false;
|
|
20581
|
-
let database = null;
|
|
20582
|
-
try {
|
|
20583
|
-
database = new Database5(path, { readonly: true });
|
|
20584
|
-
const row = database.query(
|
|
20585
|
-
"SELECT pid, heartbeat_at, ready_at FROM gateway_lock WHERE id = 1"
|
|
20586
|
-
).get();
|
|
20587
|
-
if (!row || row.ready_at == null) return false;
|
|
20588
|
-
if (expectedPid !== void 0 && row.pid !== expectedPid) return false;
|
|
20589
|
-
const ageMs = Date.now() - row.heartbeat_at;
|
|
20590
|
-
return ageMs >= -5e3 && ageMs < GATEWAY_LOCK_STALE_MS;
|
|
20591
|
-
} catch {
|
|
20592
|
-
return false;
|
|
20593
|
-
} finally {
|
|
20594
|
-
database?.close();
|
|
20595
|
-
}
|
|
20596
|
-
}
|
|
20597
|
-
function gatewayVersionFromLock(expectedPid, path) {
|
|
20598
|
-
if (!existsSync6(path)) return void 0;
|
|
20599
|
-
let database = null;
|
|
20600
|
-
try {
|
|
20601
|
-
database = new Database5(path, { readonly: true });
|
|
20602
|
-
const row = database.query(
|
|
20603
|
-
"SELECT pid, version FROM gateway_lock WHERE id = 1"
|
|
20604
|
-
).get();
|
|
20605
|
-
if (!row || row.pid !== expectedPid) return void 0;
|
|
20606
|
-
return row.version;
|
|
20607
|
-
} catch {
|
|
20608
|
-
return void 0;
|
|
20609
|
-
} finally {
|
|
20610
|
-
database?.close();
|
|
20611
|
-
}
|
|
20612
|
-
}
|
|
20613
|
-
function managementLockPath(env = process.env, cwd = process.cwd()) {
|
|
20614
|
-
const override = env.SUPERTASK_PM2_MANAGEMENT_LOCK;
|
|
20615
|
-
if (override) return runtimePath(override, cwd);
|
|
20616
|
-
return join5(runtimeScope({ env, cwd }).pm2Home, "supertask-gateway.manage.sqlite");
|
|
20617
|
-
}
|
|
20618
|
-
function canonicalManagementLockPath(env = process.env, cwd = process.cwd()) {
|
|
20619
|
-
const home = runtimeHome(env);
|
|
20620
|
-
const pm2Home = env.PM2_HOME ? runtimePath(env.PM2_HOME, cwd) : join5(home, ".pm2");
|
|
20621
|
-
return join5(pm2Home, "supertask-gateway.manage.sqlite");
|
|
20622
|
-
}
|
|
20623
|
-
var __dirname, PROCESS_NAME, MAC_LAUNCH_AGENT_LABEL, GATEWAY_LOCK_STALE_MS;
|
|
20624
|
-
var init_pm2 = __esm({
|
|
20625
|
-
"src/daemon/pm2.ts"() {
|
|
20449
|
+
var CACHE_TTL_MS, DIAGNOSTIC_TIMEOUT_MS, cached, pending, activeProcessGroups;
|
|
20450
|
+
var init_gateway_diagnostic = __esm({
|
|
20451
|
+
"src/web/gateway-diagnostic.ts"() {
|
|
20626
20452
|
"use strict";
|
|
20627
|
-
|
|
20628
|
-
|
|
20629
|
-
|
|
20630
|
-
|
|
20631
|
-
|
|
20632
|
-
|
|
20633
|
-
|
|
20634
|
-
|
|
20453
|
+
CACHE_TTL_MS = 5e3;
|
|
20454
|
+
DIAGNOSTIC_TIMEOUT_MS = 2e4;
|
|
20455
|
+
cached = null;
|
|
20456
|
+
pending = null;
|
|
20457
|
+
activeProcessGroups = /* @__PURE__ */ new Set();
|
|
20458
|
+
process.once("exit", () => {
|
|
20459
|
+
for (const pid of activeProcessGroups) killDiagnosticGroup(pid);
|
|
20460
|
+
});
|
|
20635
20461
|
}
|
|
20636
20462
|
});
|
|
20637
20463
|
|
|
@@ -20721,6 +20547,7 @@ function clientMessages(locale) {
|
|
|
20721
20547
|
"details.copySuccess",
|
|
20722
20548
|
"details.id",
|
|
20723
20549
|
"details.project",
|
|
20550
|
+
"details.variant",
|
|
20724
20551
|
"details.prompt",
|
|
20725
20552
|
"details.result",
|
|
20726
20553
|
"details.category",
|
|
@@ -20821,6 +20648,7 @@ function clientMessages(locale) {
|
|
|
20821
20648
|
"catalog.chooseProject",
|
|
20822
20649
|
"catalog.defaultModel",
|
|
20823
20650
|
"catalog.defaultProvider",
|
|
20651
|
+
"catalog.defaultVariant",
|
|
20824
20652
|
"catalog.loading",
|
|
20825
20653
|
"catalog.loaded",
|
|
20826
20654
|
"catalog.failed",
|
|
@@ -20945,23 +20773,23 @@ function renderLayout(options) {
|
|
|
20945
20773
|
function detailSession(value){if(!value)return text('details.none');const session=String(value);return session.length<=10?session:session.slice(0,6)+'***'+session.slice(-4)}
|
|
20946
20774
|
function detailTaskResult(data){const presentation=data._resultPresentation;if(!presentation)return text('details.none');const parts=[];if(Array.isArray(presentation.errors)&&presentation.errors.length)parts.push(presentation.errors.join('\\n'));if(presentation.text)parts.push(presentation.text);return parts.join('\\n\\n')||text('details.none')}
|
|
20947
20775
|
function detailField(label,value,options={}){const item=document.createElement('div');item.className='detail-item'+(options.wide?' wide':'');const name=document.createElement('div');name.className='detail-label';name.textContent=label;const content=options.long?document.createElement('pre'):document.createElement('div');content.className='detail-value'+(options.mono?' mono':'')+(options.long?' long':'');content.textContent=value===null||value===undefined||value===''?text('details.none'):String(value);item.append(name,content);return item}
|
|
20948
|
-
function renderDetailHistory(runs){const section=document.createElement('section');section.className='detail-history';const title=document.createElement('h3');title.textContent=text('details.history');section.appendChild(title);if(!Array.isArray(runs)||runs.length===0){const empty=document.createElement('div');empty.className='muted small';empty.textContent=text('details.noHistory');section.appendChild(empty);return section}const list=document.createElement('div');list.className='detail-history-list';for(const run of runs){const item=document.createElement('div');item.className='detail-history-item';const primary=document.createElement('strong');primary.textContent='Run #'+run.id+' \xB7 '+detailStatus('run',run.status);const secondary=document.createElement('span');secondary.className='muted';secondary.textContent=detailDate(run.startedAt)+(run.model?' \xB7 '+detailModel(run.model):'');item.append(primary,secondary);list.appendChild(item)}section.appendChild(list);return section}
|
|
20776
|
+
function renderDetailHistory(runs){const section=document.createElement('section');section.className='detail-history';const title=document.createElement('h3');title.textContent=text('details.history');section.appendChild(title);if(!Array.isArray(runs)||runs.length===0){const empty=document.createElement('div');empty.className='muted small';empty.textContent=text('details.noHistory');section.appendChild(empty);return section}const list=document.createElement('div');list.className='detail-history-list';for(const run of runs){const item=document.createElement('div');item.className='detail-history-item';const primary=document.createElement('strong');primary.textContent='Run #'+run.id+' \xB7 '+detailStatus('run',run.status);const secondary=document.createElement('span');secondary.className='muted';secondary.textContent=detailDate(run.startedAt)+(run.model?' \xB7 '+detailModel(run.model):'')+(run.variant?' \xB7 '+run.variant:'');item.append(primary,secondary);list.appendChild(item)}section.appendChild(list);return section}
|
|
20949
20777
|
function detailFields(type,data){if(type==='task')return [
|
|
20950
20778
|
[text('details.id'),'#'+data.id],[text('table.name'),data.name],[text('table.status'),detailStatus('task',data.status)],[text('details.project'),data.cwd,{wide:true,mono:true}],
|
|
20951
|
-
[text('table.agent'),data.agent],[text('table.model'),detailModel(data.model)],[text('details.prompt'),data.prompt,{wide:true,long:true}],
|
|
20779
|
+
[text('table.agent'),data.agent],[text('table.model'),detailModel(data.model)],[text('details.variant'),data.variant||text('details.default')],[text('details.prompt'),data.prompt,{wide:true,long:true}],
|
|
20952
20780
|
[text('details.category'),data.category],[text('details.batch'),data.batchId],[text('details.dependency'),data.dependsOn?'#'+data.dependsOn:text('details.none')],
|
|
20953
20781
|
[text('details.importance'),data.importance],[text('details.urgency'),data.urgency],[text('details.retryCount'),String(data.retryCount??0)+' / '+String(data.maxRetries??0)],
|
|
20954
20782
|
[text('details.retryBackoff'),detailDuration(data.retryBackoffMs)],[text('details.timeout'),detailDuration(data.timeoutMs)],[text('details.scheduledAt'),detailDate(data.scheduledAt)],
|
|
20955
20783
|
[text('details.createdAt'),detailDate(data.createdAt)],[text('details.startedAt'),detailDate(data.startedAt)],[text('details.finishedAt'),detailDate(data.finishedAt)],
|
|
20956
20784
|
[text('details.result'),detailTaskResult(data),{wide:true,long:true}]
|
|
20957
20785
|
];if(type==='run'){const started=data.startedAt?new Date(data.startedAt).getTime():null;const finished=data.finishedAt?new Date(data.finishedAt).getTime():null;return [
|
|
20958
|
-
[text('details.id'),'Run #'+data.id],[text('details.taskId'),'#'+data.taskId],[text('table.status'),detailStatus('run',data.status)],[text('table.model'),detailModel(data.model)],
|
|
20786
|
+
[text('details.id'),'Run #'+data.id],[text('details.taskId'),'#'+data.taskId],[text('table.status'),detailStatus('run',data.status)],[text('table.model'),detailModel(data.model)],[text('details.variant'),data.variant||text('details.default')],
|
|
20959
20787
|
[text('details.session'),detailSession(data.sessionId)],[text('details.startedAt'),detailDate(data.startedAt)],[text('details.finishedAt'),detailDate(data.finishedAt)],
|
|
20960
20788
|
[text('table.duration'),started!==null?detailDuration((finished??Date.now())-started):text('details.none')],[text('details.heartbeat'),detailDate(data.heartbeatAt)],
|
|
20961
20789
|
[text('details.process'),'Worker PID '+String(data.workerPid??'\u2014')+' \xB7 OpenCode PID '+String(data.childPid??'\u2014'),{wide:true,mono:true}]
|
|
20962
20790
|
];}const scheduleRule=data.scheduleType==='cron'?data.cronExpr:data.scheduleType==='recurring'?detailDuration(data.intervalMs):detailDate(data.runAt);return [
|
|
20963
20791
|
[text('details.id'),'#'+data.id],[text('table.name'),data.name],[text('details.enabled'),data.enabled?text('details.enabledYes'):text('details.enabledNo')],[text('details.project'),data.cwd,{wide:true,mono:true}],
|
|
20964
|
-
[text('table.agent'),data.agent],[text('table.model'),detailModel(data.model)],[text('details.prompt'),data.prompt,{wide:true,long:true}],
|
|
20792
|
+
[text('table.agent'),data.agent],[text('table.model'),detailModel(data.model)],[text('details.variant'),data.variant||text('details.default')],[text('details.prompt'),data.prompt,{wide:true,long:true}],
|
|
20965
20793
|
[text('template.scheduleType'),detailScheduleType(data.scheduleType)],[text('details.scheduleRule'),scheduleRule],[text('details.category'),data.category],[text('details.batch'),data.batchId],
|
|
20966
20794
|
[text('details.importance'),data.importance],[text('details.urgency'),data.urgency],[text('details.maxInstances'),data.maxInstances],[text('details.maxRetries'),data.maxRetries??0],
|
|
20967
20795
|
[text('details.retryBackoff'),detailDuration(data.retryBackoffMs)],[text('details.timeout'),detailDuration(data.timeoutMs)],[text('details.lastRun'),detailDate(data.lastRunAt)],[text('details.nextRun'),detailDate(data.nextRunAt)],
|
|
@@ -20978,12 +20806,20 @@ function renderLayout(options) {
|
|
|
20978
20806
|
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')}
|
|
20979
20807
|
const catalogTimers={};const catalogRequests={};const catalogModels={};
|
|
20980
20808
|
function catalogField(prefix,name){return document.getElementById(prefix+'-'+name)}
|
|
20981
|
-
function resetCatalog(prefix){const agent=catalogField(prefix,'agent');const provider=catalogField(prefix,'model-provider');const model=catalogField(prefix,'model');if(!agent||!provider||!model)return;catalogModels[prefix]=[];agent.replaceChildren(new Option(text('catalog.chooseProject'),''));provider.replaceChildren(new Option(text('catalog.defaultProvider'),''));model.replaceChildren(new Option(text('catalog.defaultModel'),'default'));agent.disabled=false;provider.disabled=true;model.disabled=true;catalogField(prefix,'catalog-status').textContent='';}
|
|
20809
|
+
function resetCatalog(prefix){const agent=catalogField(prefix,'agent');const provider=catalogField(prefix,'model-provider');const model=catalogField(prefix,'model');const variant=catalogField(prefix,'variant');if(!agent||!provider||!model||!variant)return;const preferredVariant=variant.dataset.preferred||'';catalogModels[prefix]=[];agent.replaceChildren(new Option(text('catalog.chooseProject'),''));provider.replaceChildren(new Option(text('catalog.defaultProvider'),''));model.replaceChildren(new Option(text('catalog.defaultModel'),'default'));variant.replaceChildren(new Option(text('catalog.defaultVariant'),''));if(preferredVariant)appendCurrentOption(variant,preferredVariant);variant.value=preferredVariant;agent.disabled=false;provider.disabled=true;model.disabled=true;variant.disabled=!preferredVariant;catalogField(prefix,'catalog-status').textContent='';}
|
|
20982
20810
|
function appendCurrentOption(select,value){if(!value||[...select.options].some(option=>option.value===value))return;select.appendChild(new Option(value,value));}
|
|
20811
|
+
function setPreferredVariant(prefix,modelValue,variantValue){const variant=catalogField(prefix,'variant');if(!variant)return;const preferred=variantValue||'';variant.dataset.preferred=preferred;variant.dataset.preferredModel=modelValue||'default';variant.replaceChildren(new Option(text('catalog.defaultVariant'),''));if(preferred)appendCurrentOption(variant,preferred);variant.value=preferred;variant.disabled=!preferred}
|
|
20812
|
+
function invalidateVariantRequest(prefix){const key=prefix+'-variant';catalogRequests[key]=(catalogRequests[key]||0)+1}
|
|
20813
|
+
function invalidateCatalogRequests(prefix){clearTimeout(catalogTimers[prefix]);catalogRequests[prefix]=(catalogRequests[prefix]||0)+1;invalidateVariantRequest(prefix)}
|
|
20814
|
+
function clearVariantPreference(prefix){const variant=catalogField(prefix,'variant');if(!variant)return;invalidateVariantRequest(prefix);variant.dataset.preferred='';variant.dataset.preferredModel='';variant.replaceChildren(new Option(text('catalog.defaultVariant'),''));variant.value='';variant.disabled=true}
|
|
20815
|
+
function handleProviderChange(prefix){clearVariantPreference(prefix);populateModelOptions(prefix)}
|
|
20816
|
+
function handleModelChange(prefix){clearVariantPreference(prefix);populateVariantOptions(prefix)}
|
|
20817
|
+
function handleVariantChange(prefix){const variant=catalogField(prefix,'variant');if(!variant)return;invalidateVariantRequest(prefix);variant.dataset.preferred='';variant.dataset.preferredModel=''}
|
|
20983
20818
|
function modelProvider(value){const slash=value.indexOf('/');return slash>0?value.slice(0,slash):''}
|
|
20984
|
-
function populateModelOptions(prefix,preferredModel=''){const provider=catalogField(prefix,'model-provider');const model=catalogField(prefix,'model');if(!provider||!model)return;const selectedProvider=provider.value;model.replaceChildren();if(!selectedProvider){model.appendChild(new Option(text('catalog.defaultModel'),'default'));model.disabled=true;return}const available=(catalogModels[prefix]||[]).filter(value=>modelProvider(value)===selectedProvider);for(const value of available)model.appendChild(new Option(value,value));if(preferredModel&&available.includes(preferredModel))model.value=preferredModel;model.disabled=available.length===0}
|
|
20819
|
+
function populateModelOptions(prefix,preferredModel=''){const provider=catalogField(prefix,'model-provider');const model=catalogField(prefix,'model');if(!provider||!model)return;const selectedProvider=provider.value;model.replaceChildren();if(!selectedProvider){model.appendChild(new Option(text('catalog.defaultModel'),'default'));model.disabled=true;populateVariantOptions(prefix);return}const available=(catalogModels[prefix]||[]).filter(value=>modelProvider(value)===selectedProvider);for(const value of available)model.appendChild(new Option(value,value));if(preferredModel&&available.includes(preferredModel))model.value=preferredModel;model.disabled=available.length===0;populateVariantOptions(prefix)}
|
|
20820
|
+
async function populateVariantOptions(prefix,preferredVariant=''){const cwd=catalogField(prefix,'cwd')?.value.trim()||'';const model=catalogField(prefix,'model');const variant=catalogField(prefix,'variant');if(!model||!variant)return;const selectedModel=model.value;const preferred=preferredVariant||(variant.dataset.preferredModel===selectedModel?variant.dataset.preferred||'':'');if(!cwd||!selectedModel||selectedModel==='default'){variant.replaceChildren(new Option(text('catalog.defaultVariant'),''));if(preferred)appendCurrentOption(variant,preferred);variant.value=preferred;variant.dataset.preferred='';variant.dataset.preferredModel='';variant.disabled=!preferred;return}if(!preferred){variant.replaceChildren(new Option(text('catalog.defaultVariant'),''));variant.value=''}const requestKey=prefix+'-variant';const request=(catalogRequests[requestKey]||0)+1;catalogRequests[requestKey]=request;variant.disabled=true;try{const data=await readJson(await fetch('/api/opencode/catalog?cwd='+encodeURIComponent(cwd)));if(catalogRequests[requestKey]!==request||catalogField(prefix,'cwd')?.value.trim()!==cwd||model.value!==selectedModel)return;const available=Array.isArray(data.variantsByModel?.[selectedModel])?data.variantsByModel[selectedModel]:[];variant.replaceChildren(new Option(text('catalog.defaultVariant'),''));for(const value of available)variant.appendChild(new Option(value,value));if(preferred)appendCurrentOption(variant,preferred);variant.value=[...variant.options].some(option=>option.value===preferred)?preferred:'';variant.dataset.preferred='';variant.dataset.preferredModel='';variant.disabled=available.length===0&&!preferred}catch(error){if(catalogRequests[requestKey]!==request||catalogField(prefix,'cwd')?.value.trim()!==cwd||model.value!==selectedModel)return;if(preferred)appendCurrentOption(variant,preferred);variant.value=preferred;variant.dataset.preferred=preferred;variant.dataset.preferredModel=selectedModel;variant.disabled=!preferred}}
|
|
20985
20821
|
async function loadCatalog(prefix,preferredAgent='',preferredModel='default',preserveUnavailable=false){const cwd=catalogField(prefix,'cwd')?.value.trim()||'';const status=catalogField(prefix,'catalog-status');const agent=catalogField(prefix,'agent');const provider=catalogField(prefix,'model-provider');const model=catalogField(prefix,'model');if(!cwd||!status||!agent||!provider||!model){if(!cwd)resetCatalog(prefix);return}const request=(catalogRequests[prefix]||0)+1;catalogRequests[prefix]=request;status.dataset.state='loading';status.textContent=text('catalog.loading');agent.disabled=true;provider.disabled=true;model.disabled=true;try{const data=await readJson(await fetch('/api/opencode/catalog?cwd='+encodeURIComponent(cwd)));if(catalogRequests[prefix]!==request||catalogField(prefix,'cwd').value.trim()!==cwd)return;agent.replaceChildren();for(const item of data.agents){const label=item.name+' \u2014 '+text('catalog.'+item.mode);agent.appendChild(new Option(label,item.name))}if(preserveUnavailable)appendCurrentOption(agent,preferredAgent);const defaultAgent=preferredAgent||data.agents.find(item=>item.name==='build')?.name||data.agents.find(item=>item.mode==='primary')?.name||data.agents[0]?.name||'';agent.value=[...agent.options].some(option=>option.value===defaultAgent)?defaultAgent:(agent.options[0]?.value||'');catalogModels[prefix]=[...data.models];if(preserveUnavailable&&preferredModel&&preferredModel!=='default'&&!catalogModels[prefix].includes(preferredModel))catalogModels[prefix].push(preferredModel);provider.replaceChildren(new Option(text('catalog.defaultProvider'),''));for(const name of [...new Set(catalogModels[prefix].map(modelProvider).filter(Boolean))].sort())provider.appendChild(new Option(name,name));const preferredProvider=preferredModel==='default'?'':modelProvider(preferredModel);provider.value=[...provider.options].some(option=>option.value===preferredProvider)?preferredProvider:'';populateModelOptions(prefix,preferredModel);status.dataset.state='ready';status.textContent=text('catalog.loaded',{agents:data.agents.length,models:data.models.length})}catch(error){if(catalogRequests[prefix]!==request)return;resetCatalog(prefix);if(preserveUnavailable){appendCurrentOption(agent,preferredAgent);if(preferredModel&&preferredModel!=='default'){catalogModels[prefix]=[preferredModel];appendCurrentOption(provider,modelProvider(preferredModel));provider.value=modelProvider(preferredModel);populateModelOptions(prefix,preferredModel)}}status.dataset.state='error';status.textContent=text('catalog.failed',{error:error.message})}finally{if(catalogRequests[prefix]===request){agent.disabled=false;provider.disabled=false;if(provider.value)model.disabled=false}}}
|
|
20986
|
-
function scheduleCatalogLoad(prefix){
|
|
20822
|
+
function scheduleCatalogLoad(prefix){invalidateCatalogRequests(prefix);resetCatalog(prefix);catalogTimers[prefix]=setTimeout(()=>loadCatalog(prefix),450)}
|
|
20987
20823
|
let directoryTargetId='';let directoryCurrent='';let directoryEntries=[];let directoryShowHidden=false;
|
|
20988
20824
|
function renderDirectoryEntries(){const list=document.getElementById('directory-list');list.replaceChildren();const entries=directoryEntries.filter(entry=>directoryShowHidden||!entry.hidden);const hidden=document.getElementById('directory-hidden');hidden.textContent=text(directoryShowHidden?'action.hideHidden':'action.showHidden');if(entries.length===0){const empty=document.createElement('div');empty.className='directory-empty';empty.textContent=text('directory.empty');list.appendChild(empty);return}for(const entry of entries){const button=document.createElement('button');button.type='button';button.className='directory-item';button.innerHTML='${icon("folder")}<span></span>';button.querySelector('span').textContent=entry.name;button.onclick=()=>browseDirectory(entry.path);list.appendChild(button)}}
|
|
20989
20825
|
async function browseDirectory(path=''){const choose=document.getElementById('directory-choose');choose.disabled=true;try{const suffix=path?'?path='+encodeURIComponent(path):'';const data=await readJson(await fetch('/api/filesystem/directories'+suffix));directoryCurrent=data.path;directoryEntries=data.directories;document.getElementById('directory-path').textContent=data.path;document.getElementById('directory-up').disabled=data.parent===data.path;document.getElementById('directory-up').onclick=()=>browseDirectory(data.parent);document.getElementById('directory-home').onclick=()=>browseDirectory(data.home);renderDirectoryEntries();choose.disabled=false;return true}catch(error){directoryCurrent='';directoryEntries=[];document.getElementById('directory-path').textContent='';renderDirectoryEntries();showToast(error.message,'error');return false}}
|
|
@@ -20993,16 +20829,16 @@ function renderLayout(options) {
|
|
|
20993
20829
|
function updateDurationControl(id){const preset=document.getElementById(id+'-preset');const custom=document.getElementById(id+'-custom');const input=document.getElementById(id+'-value');const visible=preset.value==='custom';custom.hidden=!visible;input.required=visible;if(visible&&!input.value)input.value='1'}
|
|
20994
20830
|
function readDuration(id){const preset=document.getElementById(id+'-preset').value;if(preset==='')return '';if(preset!=='custom')return preset==='0'?'0':preset+'ms';const value=document.getElementById(id+'-value').value.trim();return value===''?'':value+document.getElementById(id+'-unit').value}
|
|
20995
20831
|
function setDuration(id,milliseconds){const preset=document.getElementById(id+'-preset');const exact=milliseconds==null?'':String(milliseconds);if([...preset.options].some(option=>option.value===exact)){preset.value=exact;updateDurationControl(id);return}preset.value='custom';const input=document.getElementById(id+'-value');const unit=document.getElementById(id+'-unit');const units=[['d',86400000],['h',3600000],['min',60000],['s',1000]];let matched=false;for(const [name,factor] of units){if(milliseconds!=null&&(milliseconds===0||milliseconds%factor===0)){input.value=String(milliseconds/factor);unit.value=name;matched=true;break}}if(!matched){input.value=String((milliseconds??60000)/1000);unit.value='s'}updateDurationControl(id)}
|
|
20996
|
-
function openTaskCreator(){const form=document.getElementById('task-form');form.reset();taskField('id').value='';taskField('cwd').readOnly=false;taskField('cwd-picker').hidden=false;taskField('cwd').value=form.dataset.defaultCwd||'';setDuration('task-retry-backoff',30000);setDuration('task-timeout',null);taskField('dialog-title').textContent=text('task.createTitle');taskField('save').textContent=text('action.saveTask');updateTaskProjectStatus();resetCatalog('task');document.getElementById('task-dialog').showModal();if(taskField('cwd').value)loadCatalog('task');setTimeout(()=>taskField('name').focus(),50)}
|
|
20997
|
-
async function openTaskEditor(id){try{const data=await readJson(await fetch('/api/tasks/'+id));taskField('id').value=String(id);taskField('cwd').value=data.cwd||'';taskField('cwd').readOnly=true;taskField('cwd-picker').hidden=true;taskField('name').value=data.name||'';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);setDuration('task-retry-backoff',data.retryBackoffMs??30000);setDuration('task-timeout',data.timeoutMs);taskField('dialog-title').textContent=text('task.editTitle');taskField('save').textContent=text('action.updateTask');updateTaskProjectStatus();resetCatalog('task');document.getElementById('task-dialog').showModal();loadCatalog('task',data.agent||'',data.model||'default',true);setTimeout(()=>taskField('name').focus(),50)}catch(error){showToast(error.message,'error')}}
|
|
20998
|
-
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:readDuration('task-retry-backoff'),timeout:readDuration('task-timeout')};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}}
|
|
20832
|
+
function openTaskCreator(){invalidateCatalogRequests('task');const form=document.getElementById('task-form');form.reset();taskField('id').value='';setPreferredVariant('task','default','');taskField('cwd').readOnly=false;taskField('cwd-picker').hidden=false;taskField('cwd').value=form.dataset.defaultCwd||'';setDuration('task-retry-backoff',30000);setDuration('task-timeout',null);taskField('dialog-title').textContent=text('task.createTitle');taskField('save').textContent=text('action.saveTask');updateTaskProjectStatus();resetCatalog('task');document.getElementById('task-dialog').showModal();if(taskField('cwd').value)loadCatalog('task');setTimeout(()=>taskField('name').focus(),50)}
|
|
20833
|
+
async function openTaskEditor(id){invalidateCatalogRequests('task');try{const data=await readJson(await fetch('/api/tasks/'+id));document.getElementById('task-form').reset();taskField('id').value=String(id);taskField('cwd').value=data.cwd||'';taskField('cwd').readOnly=true;taskField('cwd-picker').hidden=true;taskField('name').value=data.name||'';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);setDuration('task-retry-backoff',data.retryBackoffMs??30000);setDuration('task-timeout',data.timeoutMs);taskField('dialog-title').textContent=text('task.editTitle');taskField('save').textContent=text('action.updateTask');updateTaskProjectStatus();setPreferredVariant('task',data.model||'default',data.variant||'');resetCatalog('task');document.getElementById('task-dialog').showModal();loadCatalog('task',data.agent||'',data.model||'default',true);setTimeout(()=>taskField('name').focus(),50)}catch(error){showToast(error.message,'error')}}
|
|
20834
|
+
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,variant:taskField('variant').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:readDuration('task-retry-backoff'),timeout:readDuration('task-timeout')};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}}
|
|
20999
20835
|
function localDateTime(milliseconds){const date=new Date(milliseconds);const local=new Date(milliseconds-date.getTimezoneOffset()*60000);return local.toISOString().slice(0,23)}
|
|
21000
20836
|
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;for(const control of node.querySelectorAll('input,select'))control.required=false;const required=name==='recurring'?node.querySelector('[id$="-preset"]'):node.querySelector('input');if(required)required.required=name===type;if(name==='recurring'&&name===type)updateDurationControl('template-interval')}}
|
|
21001
20837
|
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}
|
|
21002
20838
|
function selectedRunAt(){const input=templateField('run-at');return resolveEditedRunAt(input.dataset.originalEpoch?Number(input.dataset.originalEpoch):null,input.dataset.originalLocal||'',input.value)}
|
|
21003
|
-
function openTemplateCreator(){const form=document.getElementById('template-form');form.reset();templateField('id').value='';templateField('dialog-title').textContent=text('template.createTitle');setDuration('template-interval',3600000);setDuration('template-retry-backoff',30000);setDuration('template-timeout',null);setOriginalRunAt(null);templateField('run-at').value=localDateTime(Date.now()+3600000);updateTemplateScheduleFields();resetCatalog('template');document.getElementById('template-dialog').showModal();if(templateField('cwd').value)loadCatalog('template');setTimeout(()=>templateField('name').focus(),50)}
|
|
21004
|
-
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('prompt').value=data.prompt||'';templateField('schedule-type').value=data.scheduleType;templateField('cron').value=data.cronExpr||'';setDuration('template-interval',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);setDuration('template-retry-backoff',data.retryBackoffMs??30000);setDuration('template-timeout',data.timeoutMs);updateTemplateScheduleFields();resetCatalog('template');document.getElementById('template-dialog').showModal();loadCatalog('template',data.agent||'',data.model||'default',true);setTimeout(()=>templateField('name').focus(),50)}catch(error){showToast(error.message,'error')}}
|
|
21005
|
-
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:readDuration('template-interval'),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:readDuration('template-retry-backoff'),timeout:readDuration('template-timeout')};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}}
|
|
20839
|
+
function openTemplateCreator(){invalidateCatalogRequests('template');const form=document.getElementById('template-form');form.reset();templateField('id').value='';setPreferredVariant('template','default','');templateField('dialog-title').textContent=text('template.createTitle');setDuration('template-interval',3600000);setDuration('template-retry-backoff',30000);setDuration('template-timeout',null);setOriginalRunAt(null);templateField('run-at').value=localDateTime(Date.now()+3600000);updateTemplateScheduleFields();resetCatalog('template');document.getElementById('template-dialog').showModal();if(templateField('cwd').value)loadCatalog('template');setTimeout(()=>templateField('name').focus(),50)}
|
|
20840
|
+
async function openTemplateEditor(id){invalidateCatalogRequests('template');try{const data=await readJson(await fetch('/api/templates/'+id));document.getElementById('template-form').reset();templateField('id').value=String(id);templateField('dialog-title').textContent=text('template.editTitle');templateField('name').value=data.name||'';templateField('cwd').value=data.cwd||'';templateField('prompt').value=data.prompt||'';templateField('schedule-type').value=data.scheduleType;templateField('cron').value=data.cronExpr||'';setDuration('template-interval',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);setDuration('template-retry-backoff',data.retryBackoffMs??30000);setDuration('template-timeout',data.timeoutMs);updateTemplateScheduleFields();setPreferredVariant('template',data.model||'default',data.variant||'');resetCatalog('template');document.getElementById('template-dialog').showModal();loadCatalog('template',data.agent||'',data.model||'default',true);setTimeout(()=>templateField('name').focus(),50)}catch(error){showToast(error.message,'error')}}
|
|
20841
|
+
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,variant:templateField('variant').value,prompt:templateField('prompt').value,scheduleType:type,cronExpr:templateField('cron').value,interval:readDuration('template-interval'),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:readDuration('template-retry-backoff'),timeout:readDuration('template-timeout')};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}}
|
|
21006
20842
|
async function enableTmpl(id){try{await readJson(await fetch('/api/templates/'+id+'/enable',{method:'POST'}));location.reload()}catch(error){showToast(error.message,'error')}}
|
|
21007
20843
|
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')}}
|
|
21008
20844
|
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')}}
|
|
@@ -21184,6 +21020,7 @@ var init_ui = __esm({
|
|
|
21184
21020
|
"template.cwdHint": "OpenCode \u4F1A\u5728\u8FD9\u4E2A\u76EE\u5F55\u4E2D\u6267\u884C\u4EFB\u52A1\u3002",
|
|
21185
21021
|
"template.agent": "Agent",
|
|
21186
21022
|
"template.model": "\u6A21\u578B",
|
|
21023
|
+
"template.variant": "\u6A21\u578B Variant",
|
|
21187
21024
|
"template.prompt": "\u63D0\u793A\u8BCD",
|
|
21188
21025
|
"template.scheduleType": "\u6267\u884C\u65B9\u5F0F",
|
|
21189
21026
|
"template.cronExpr": "Cron \u8868\u8FBE\u5F0F",
|
|
@@ -21221,9 +21058,11 @@ var init_ui = __esm({
|
|
|
21221
21058
|
"catalog.chooseProject": "\u8BF7\u5148\u9009\u62E9\u9879\u76EE\u76EE\u5F55",
|
|
21222
21059
|
"catalog.defaultModel": "\u8DDF\u968F Agent / OpenCode \u9ED8\u8BA4\u6A21\u578B",
|
|
21223
21060
|
"catalog.defaultProvider": "\u9ED8\u8BA4\u6A21\u578B",
|
|
21061
|
+
"catalog.defaultVariant": "\u8DDF\u968F Agent / \u6A21\u578B\u9ED8\u8BA4\u8BBE\u7F6E",
|
|
21224
21062
|
"catalog.provider": "\u6A21\u578B\u63D0\u4F9B\u5546",
|
|
21225
21063
|
"catalog.model": "\u5177\u4F53\u6A21\u578B",
|
|
21226
|
-
"catalog.modelHint": "\u5148\u9009\u63D0\u4F9B\u5546\uFF0C\u518D\u9009\u672C\u9879\u76EE opencode models \u8FD4\u56DE\u7684\u6A21\u578B\uFF1B\u9ED8\u8BA4\u9009\u9879\u4E0D\u4F1A\u4F20\u5165 -m\u3002",
|
|
21064
|
+
"catalog.modelHint": "\u5148\u9009\u63D0\u4F9B\u5546\uFF0C\u518D\u9009\u672C\u9879\u76EE opencode models --verbose \u8FD4\u56DE\u7684\u6A21\u578B\uFF1B\u9ED8\u8BA4\u9009\u9879\u4E0D\u4F1A\u4F20\u5165 -m\u3002",
|
|
21065
|
+
"catalog.variantHint": "\u4EC5\u663E\u793A\u6240\u9009\u6A21\u578B\u58F0\u660E\u652F\u6301\u7684 variants\uFF1B\u9ED8\u8BA4\u9009\u9879\u4E0D\u4F1A\u4F20\u5165 --variant\u3002",
|
|
21227
21066
|
"catalog.agentHint": "\u6765\u81EA\u5F53\u524D\u9879\u76EE\u7684 opencode agent list\u3002",
|
|
21228
21067
|
"catalog.loading": "\u6B63\u5728\u8BFB\u53D6\u6B64\u9879\u76EE\u53EF\u7528\u7684 Agent \u548C\u6A21\u578B\u2026",
|
|
21229
21068
|
"catalog.loaded": "\u5DF2\u4ECE\u672C\u673A OpenCode \u8BFB\u53D6 {agents} \u4E2A Agent\u3001{models} \u4E2A\u6A21\u578B\u3002",
|
|
@@ -21254,6 +21093,7 @@ var init_ui = __esm({
|
|
|
21254
21093
|
"details.copySuccess": "\u539F\u59CB\u6570\u636E\u5DF2\u590D\u5236",
|
|
21255
21094
|
"details.id": "\u7F16\u53F7",
|
|
21256
21095
|
"details.project": "\u9879\u76EE\u76EE\u5F55",
|
|
21096
|
+
"details.variant": "\u6A21\u578B Variant",
|
|
21257
21097
|
"details.prompt": "\u63D0\u793A\u8BCD",
|
|
21258
21098
|
"details.result": "\u6267\u884C\u7ED3\u679C / \u5931\u8D25\u539F\u56E0",
|
|
21259
21099
|
"details.category": "\u5206\u7C7B",
|
|
@@ -21286,7 +21126,7 @@ var init_ui = __esm({
|
|
|
21286
21126
|
"details.enabledYes": "\u5DF2\u542F\u7528",
|
|
21287
21127
|
"details.enabledNo": "\u5DF2\u505C\u7528",
|
|
21288
21128
|
"dialog.cancelTask": "\u53D6\u6D88\u4EFB\u52A1 #{id}\uFF1F",
|
|
21289
|
-
"dialog.cancelTaskBody": "\u8FD0\u884C\u4E2D\u7684\u4EFB\u52A1\u4F1A\u5728\u4E0B\u4E00\u4E2A\u8F6E\u8BE2\u5468\u671F\u7EC8\u6B62\u5BF9\u5E94\u8FDB\u7A0B\
|
|
21129
|
+
"dialog.cancelTaskBody": "\u8FD0\u884C\u4E2D\u7684\u4EFB\u52A1\u4F1A\u5728\u4E0B\u4E00\u4E2A\u8F6E\u8BE2\u5468\u671F\u7EC8\u6B62\u5BF9\u5E94\u7684\u53D7\u7BA1\u8FDB\u7A0B\u7EC4\u3002",
|
|
21290
21130
|
"dialog.retryTask": "\u91CD\u8BD5\u4EFB\u52A1 #{id}\uFF1F",
|
|
21291
21131
|
"dialog.retryTaskBody": "\u4EFB\u52A1\u5C06\u56DE\u5230\u5F85\u6267\u884C\u72B6\u6001\uFF0C\u5E76\u91CD\u7F6E\u81EA\u52A8\u91CD\u8BD5\u9884\u7B97\u3002",
|
|
21292
21132
|
"dialog.deleteTask": "\u5220\u9664\u4EFB\u52A1 #{id}\uFF1F",
|
|
@@ -21485,6 +21325,7 @@ var init_ui = __esm({
|
|
|
21485
21325
|
"template.cwdHint": "OpenCode runs the task in this directory.",
|
|
21486
21326
|
"template.agent": "Agent",
|
|
21487
21327
|
"template.model": "Model",
|
|
21328
|
+
"template.variant": "Model variant",
|
|
21488
21329
|
"template.prompt": "Prompt",
|
|
21489
21330
|
"template.scheduleType": "Schedule",
|
|
21490
21331
|
"template.cronExpr": "Cron expression",
|
|
@@ -21522,9 +21363,11 @@ var init_ui = __esm({
|
|
|
21522
21363
|
"catalog.chooseProject": "Choose a project directory first",
|
|
21523
21364
|
"catalog.defaultModel": "Use the Agent / OpenCode default model",
|
|
21524
21365
|
"catalog.defaultProvider": "Default model",
|
|
21366
|
+
"catalog.defaultVariant": "Use the Agent / model default",
|
|
21525
21367
|
"catalog.provider": "Model provider",
|
|
21526
21368
|
"catalog.model": "Model",
|
|
21527
|
-
"catalog.modelHint": "Choose a provider, then a model returned by opencode models for this project. Default does not pass -m.",
|
|
21369
|
+
"catalog.modelHint": "Choose a provider, then a model returned by opencode models --verbose for this project. Default does not pass -m.",
|
|
21370
|
+
"catalog.variantHint": "Only variants declared by the selected model are shown. Default does not pass --variant.",
|
|
21528
21371
|
"catalog.agentHint": "Loaded from opencode agent list for this project.",
|
|
21529
21372
|
"catalog.loading": "Loading Agents and models available to this project\u2026",
|
|
21530
21373
|
"catalog.loaded": "Loaded {agents} Agents and {models} models from local OpenCode.",
|
|
@@ -21555,6 +21398,7 @@ var init_ui = __esm({
|
|
|
21555
21398
|
"details.copySuccess": "Raw data copied",
|
|
21556
21399
|
"details.id": "ID",
|
|
21557
21400
|
"details.project": "Project directory",
|
|
21401
|
+
"details.variant": "Model variant",
|
|
21558
21402
|
"details.prompt": "Prompt",
|
|
21559
21403
|
"details.result": "Result / failure reason",
|
|
21560
21404
|
"details.category": "Category",
|
|
@@ -21587,7 +21431,7 @@ var init_ui = __esm({
|
|
|
21587
21431
|
"details.enabledYes": "Enabled",
|
|
21588
21432
|
"details.enabledNo": "Disabled",
|
|
21589
21433
|
"dialog.cancelTask": "Cancel task #{id}?",
|
|
21590
|
-
"dialog.cancelTaskBody": "A running task will terminate its process
|
|
21434
|
+
"dialog.cancelTaskBody": "A running task will terminate its managed process group on the next worker poll.",
|
|
21591
21435
|
"dialog.retryTask": "Retry task #{id}?",
|
|
21592
21436
|
"dialog.retryTaskBody": "The task returns to pending and its automatic retry budget is reset.",
|
|
21593
21437
|
"dialog.deleteTask": "Delete task #{id}?",
|
|
@@ -22009,14 +21853,14 @@ __export(web_exports, {
|
|
|
22009
21853
|
});
|
|
22010
21854
|
import {
|
|
22011
21855
|
existsSync as existsSync7,
|
|
22012
|
-
mkdirSync as
|
|
22013
|
-
readFileSync as
|
|
21856
|
+
mkdirSync as mkdirSync3,
|
|
21857
|
+
readFileSync as readFileSync3,
|
|
22014
21858
|
readdirSync,
|
|
22015
21859
|
renameSync as renameSync2,
|
|
22016
|
-
statSync as
|
|
22017
|
-
writeFileSync as
|
|
21860
|
+
statSync as statSync3,
|
|
21861
|
+
writeFileSync as writeFileSync2
|
|
22018
21862
|
} from "fs";
|
|
22019
|
-
import { homedir as
|
|
21863
|
+
import { homedir as homedir3 } from "os";
|
|
22020
21864
|
import { basename as basename3, dirname as dirname7, join as join6 } from "path";
|
|
22021
21865
|
function setDashboardRuntimeConfig(config) {
|
|
22022
21866
|
runtimeConfig = config === null ? null : structuredClone(config);
|
|
@@ -22042,10 +21886,13 @@ function resolveDashboardConfigState(runtimeAvailable, restartRequired, managedR
|
|
|
22042
21886
|
function isSafeDashboardRestartTarget(diagnostic, currentPid) {
|
|
22043
21887
|
return diagnostic.pm2Installed && diagnostic.processFound && diagnostic.status === "online" && diagnostic.pid === currentPid && diagnostic.ready && diagnostic.scopeMatches;
|
|
22044
21888
|
}
|
|
22045
|
-
function canRestartCurrentGateway() {
|
|
21889
|
+
async function canRestartCurrentGateway(fresh = false) {
|
|
22046
21890
|
if (runtimeConfig === null) return false;
|
|
22047
21891
|
try {
|
|
22048
|
-
return isSafeDashboardRestartTarget(
|
|
21892
|
+
return isSafeDashboardRestartTarget(
|
|
21893
|
+
await getDashboardGatewayDiagnostic({ fresh }),
|
|
21894
|
+
process.pid
|
|
21895
|
+
);
|
|
22049
21896
|
} catch {
|
|
22050
21897
|
return false;
|
|
22051
21898
|
}
|
|
@@ -22064,7 +21911,7 @@ function listChildDirectories(path) {
|
|
|
22064
21911
|
if (entry.isDirectory()) return [{ name: entry.name, path: entryPath, hidden: entry.name.startsWith(".") }];
|
|
22065
21912
|
if (!entry.isSymbolicLink()) return [];
|
|
22066
21913
|
try {
|
|
22067
|
-
return
|
|
21914
|
+
return statSync3(entryPath).isDirectory() ? [{ name: entry.name, path: entryPath, hidden: entry.name.startsWith(".") }] : [];
|
|
22068
21915
|
} catch {
|
|
22069
21916
|
return [];
|
|
22070
21917
|
}
|
|
@@ -22117,6 +21964,7 @@ function parseTaskPayload(value) {
|
|
|
22117
21964
|
cwd: requiredString("cwd"),
|
|
22118
21965
|
agent: requiredString("agent"),
|
|
22119
21966
|
model: optionalString("model", "default"),
|
|
21967
|
+
variant: Object.hasOwn(input, "variant") ? optionalString("variant") : void 0,
|
|
22120
21968
|
prompt: requiredString("prompt"),
|
|
22121
21969
|
category: optionalString("category", "general"),
|
|
22122
21970
|
batchId: optionalString("batchId"),
|
|
@@ -22132,6 +21980,7 @@ function editableTaskPayload(input) {
|
|
|
22132
21980
|
name: input.name,
|
|
22133
21981
|
agent: input.agent,
|
|
22134
21982
|
model: input.model ?? "default",
|
|
21983
|
+
...input.variant === void 0 ? {} : { variant: input.variant },
|
|
22135
21984
|
prompt: input.prompt,
|
|
22136
21985
|
category: input.category ?? "general",
|
|
22137
21986
|
batchId: input.batchId ?? null,
|
|
@@ -22187,6 +22036,7 @@ function parseTemplatePayload(value) {
|
|
|
22187
22036
|
name: requiredString("name"),
|
|
22188
22037
|
agent: requiredString("agent"),
|
|
22189
22038
|
model: optionalString("model", "default"),
|
|
22039
|
+
variant: Object.hasOwn(input, "variant") ? optionalString("variant") : void 0,
|
|
22190
22040
|
prompt: requiredString("prompt"),
|
|
22191
22041
|
cwd: requiredString("cwd"),
|
|
22192
22042
|
category: optionalString("category", "general"),
|
|
@@ -22299,7 +22149,7 @@ function readCurrentConfig() {
|
|
|
22299
22149
|
const configPath = getConfigPath();
|
|
22300
22150
|
if (!existsSync7(configPath)) return {};
|
|
22301
22151
|
try {
|
|
22302
|
-
return JSON.parse(
|
|
22152
|
+
return JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
22303
22153
|
} catch {
|
|
22304
22154
|
return {};
|
|
22305
22155
|
}
|
|
@@ -22307,9 +22157,9 @@ function readCurrentConfig() {
|
|
|
22307
22157
|
function writeConfig(cfg) {
|
|
22308
22158
|
const configPath = getConfigPath();
|
|
22309
22159
|
const dir = dirname7(configPath);
|
|
22310
|
-
if (!existsSync7(dir))
|
|
22160
|
+
if (!existsSync7(dir)) mkdirSync3(dir, { recursive: true });
|
|
22311
22161
|
const tempPath = `${configPath}.${process.pid}.tmp`;
|
|
22312
|
-
|
|
22162
|
+
writeFileSync2(tempPath, JSON.stringify(cfg, null, 2) + "\n", { mode: 384 });
|
|
22313
22163
|
renameSync2(tempPath, configPath);
|
|
22314
22164
|
}
|
|
22315
22165
|
function statCard(value, label, tone, cardIcon, delay = "") {
|
|
@@ -22361,7 +22211,7 @@ function pagination(locale, basePath, page, pages, total, suffix = "") {
|
|
|
22361
22211
|
const next = page < pages ? `<a class="btn" href="${basePath}?page=${page + 1}${suffix}">${t(locale, "pagination.next")}${icon("chevronRight")}</a>` : "";
|
|
22362
22212
|
return `<div class="pagination">${previous}<span class="summary">${t(locale, "pagination.summary", { page, pages, total })}</span>${next}</div>`;
|
|
22363
22213
|
}
|
|
22364
|
-
var app, LEGACY_PROJECT_FILTER, TASK_STATUSES, SESSION_ID_PATTERN, runtimeConfig, restartScheduled, dashboardApp, web_default;
|
|
22214
|
+
var app, LEGACY_PROJECT_FILTER, TASK_STATUSES, SESSION_ID_PATTERN, LOOPBACK_HOST_PATTERN, runtimeConfig, restartScheduled, dashboardApp, web_default;
|
|
22365
22215
|
var init_web = __esm({
|
|
22366
22216
|
"src/web/index.tsx"() {
|
|
22367
22217
|
"use strict";
|
|
@@ -22378,7 +22228,7 @@ var init_web = __esm({
|
|
|
22378
22228
|
init_config();
|
|
22379
22229
|
init_health();
|
|
22380
22230
|
init_job_templates();
|
|
22381
|
-
|
|
22231
|
+
init_gateway_diagnostic();
|
|
22382
22232
|
init_ui();
|
|
22383
22233
|
app = new Hono2();
|
|
22384
22234
|
LEGACY_PROJECT_FILTER = "__supertask_legacy__";
|
|
@@ -22391,9 +22241,18 @@ var init_web = __esm({
|
|
|
22391
22241
|
"cancelled"
|
|
22392
22242
|
]);
|
|
22393
22243
|
SESSION_ID_PATTERN = /^ses_[A-Za-z0-9_]+$/;
|
|
22244
|
+
LOOPBACK_HOST_PATTERN = /^(?:localhost|127\.0\.0\.1|\[::1\])(?::([1-9]\d{0,4}))?$/i;
|
|
22394
22245
|
runtimeConfig = null;
|
|
22395
22246
|
restartScheduled = false;
|
|
22396
22247
|
app.use("*", async (c, next) => {
|
|
22248
|
+
const requestHostname = new URL(c.req.url).hostname;
|
|
22249
|
+
const hostHeader = c.req.header("Host");
|
|
22250
|
+
const loopbackHosts = ["localhost", "127.0.0.1", "[::1]"];
|
|
22251
|
+
const hostMatch = hostHeader?.match(LOOPBACK_HOST_PATTERN) ?? null;
|
|
22252
|
+
const hostPort = hostMatch?.[1] === void 0 ? null : Number(hostMatch[1]);
|
|
22253
|
+
if (!loopbackHosts.includes(requestHostname) || hostHeader !== void 0 && (!hostMatch || hostPort !== null && hostPort > 65535)) {
|
|
22254
|
+
return c.json({ error: "invalid dashboard host" }, 421);
|
|
22255
|
+
}
|
|
22397
22256
|
await next();
|
|
22398
22257
|
c.header("X-Content-Type-Options", "nosniff");
|
|
22399
22258
|
c.header("X-Frame-Options", "DENY");
|
|
@@ -22425,13 +22284,13 @@ var init_web = __esm({
|
|
|
22425
22284
|
return c.json(health, health.status === "ok" ? 200 : 503);
|
|
22426
22285
|
});
|
|
22427
22286
|
app.get("/api/filesystem/directories", (c) => {
|
|
22428
|
-
const requestedPath = c.req.query("path")?.trim() ||
|
|
22287
|
+
const requestedPath = c.req.query("path")?.trim() || homedir3();
|
|
22429
22288
|
try {
|
|
22430
22289
|
validateTaskWorkingDirectory(requestedPath);
|
|
22431
22290
|
return c.json({
|
|
22432
22291
|
path: requestedPath,
|
|
22433
22292
|
parent: dirname7(requestedPath),
|
|
22434
|
-
home:
|
|
22293
|
+
home: homedir3(),
|
|
22435
22294
|
directories: listChildDirectories(requestedPath)
|
|
22436
22295
|
});
|
|
22437
22296
|
} catch (error) {
|
|
@@ -22520,7 +22379,7 @@ var init_web = __esm({
|
|
|
22520
22379
|
const latestRun = latestRuns.get(task.id);
|
|
22521
22380
|
const executionActive = latestRun?.status === "running";
|
|
22522
22381
|
const batchId = task.batchId?.trim() || null;
|
|
22523
|
-
const searchable = esc(`${task.name} ${task.agent} ${task.prompt} ${task.cwd ?? ""} ${task.batchId ?? ""} ${task.category ?? ""}`);
|
|
22382
|
+
const searchable = esc(`${task.name} ${task.agent} ${task.model ?? ""} ${task.variant ?? ""} ${task.prompt} ${task.cwd ?? ""} ${task.batchId ?? ""} ${task.category ?? ""}`);
|
|
22524
22383
|
return `<tr data-task-row data-search="${searchable}">
|
|
22525
22384
|
<td class="faint" data-label="${t(locale, "table.id")}">#${task.id}</td>
|
|
22526
22385
|
<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>
|
|
@@ -22598,11 +22457,12 @@ var init_web = __esm({
|
|
|
22598
22457
|
<label class="form-field"><span>${t(locale, "template.cwd")}</span><div class="field-action"><input id="task-cwd" required autocomplete="off" list="task-cwd-options" placeholder="/path/to/project" oninput="updateTaskProjectStatus();scheduleCatalogLoad('task')"><button id="task-cwd-picker" type="button" class="btn" onclick="openDirectoryPicker('task-cwd')">${icon("folder")}${t(locale, "action.chooseFolder")}</button></div><small>${t(locale, "template.cwdHint")}</small></label>
|
|
22599
22458
|
<datalist id="task-cwd-options">${projects.map((project) => `<option value="${esc(project.cwd)}"></option>`).join("")}</datalist>
|
|
22600
22459
|
<label class="form-field"><span>${t(locale, "template.agent")}</span><select id="task-agent" required><option value="">${t(locale, "catalog.chooseProject")}</option></select><small>${t(locale, "catalog.agentHint")}</small></label>
|
|
22601
|
-
<label class="form-field"><span>${t(locale, "template.model")}</span><div class="model-selector"><select id="task-model-provider" aria-label="${t(locale, "catalog.provider")}" onchange="
|
|
22460
|
+
<label class="form-field"><span>${t(locale, "template.model")}</span><div class="model-selector"><select id="task-model-provider" aria-label="${t(locale, "catalog.provider")}" onchange="handleProviderChange('task')"><option value="">${t(locale, "catalog.defaultProvider")}</option></select><select id="task-model" required aria-label="${t(locale, "catalog.model")}" onchange="handleModelChange('task')" disabled><option value="default">${t(locale, "catalog.defaultModel")}</option></select></div><small>${t(locale, "catalog.modelHint")}</small></label>
|
|
22461
|
+
<label class="form-field"><span>${t(locale, "template.variant")}</span><select id="task-variant" onchange="handleVariantChange('task')" disabled><option value="">${t(locale, "catalog.defaultVariant")}</option></select><small>${t(locale, "catalog.variantHint")}</small></label>
|
|
22602
22462
|
<label class="form-field form-field-wide"><span>${t(locale, "template.prompt")}</span><textarea id="task-prompt" rows="6" required></textarea></label>
|
|
22603
22463
|
</div>
|
|
22604
22464
|
<p id="task-project-status" class="form-note"></p>
|
|
22605
|
-
<p id="task-catalog-status" class="form-note catalog-status"></p>
|
|
22465
|
+
<p id="task-catalog-status" class="form-note catalog-status" role="status" aria-live="polite"></p>
|
|
22606
22466
|
<details class="advanced-fields">
|
|
22607
22467
|
<summary>${t(locale, "template.advanced")}</summary>
|
|
22608
22468
|
<div class="template-form-grid">
|
|
@@ -22644,7 +22504,7 @@ var init_web = __esm({
|
|
|
22644
22504
|
return `<tr>
|
|
22645
22505
|
<td class="faint" data-label="${t(locale, "table.id")}">#${template.id}</td>
|
|
22646
22506
|
<td data-primary data-label="${t(locale, "table.name")}"><div class="task-name">${esc(template.name)}</div><div class="task-prompt" title="${esc(template.prompt)}">${esc(template.prompt.substring(0, 140))}</div>
|
|
22647
|
-
<div class="actions" style="margin-top:5px"><span class="tag">${esc(template.agent)}</span>${template.model && template.model !== "default" ? `<span class="tag">${esc(template.model)}</span>` : ""}</div></td>
|
|
22507
|
+
<div class="actions" style="margin-top:5px"><span class="tag">${esc(template.agent)}</span>${template.model && template.model !== "default" ? `<span class="tag">${esc(template.model)}</span>` : ""}${template.variant ? `<span class="tag">${esc(template.variant)}</span>` : ""}</div></td>
|
|
22648
22508
|
<td data-label="${t(locale, "table.type")}"><span class="tag t-${scheduleType}">${typeLabel}</span></td>
|
|
22649
22509
|
<td data-label="${t(locale, "table.rule")}" class="m small">${esc(rule)}</td>
|
|
22650
22510
|
<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>
|
|
@@ -22674,14 +22534,15 @@ var init_web = __esm({
|
|
|
22674
22534
|
<label class="form-field"><span>${t(locale, "template.name")}</span><input id="template-name" required maxlength="200" autocomplete="off"></label>
|
|
22675
22535
|
<label class="form-field"><span>${t(locale, "template.cwd")}</span><div class="field-action"><input id="template-cwd" required autocomplete="off" placeholder="/path/to/project" oninput="scheduleCatalogLoad('template')"><button type="button" class="btn" onclick="openDirectoryPicker('template-cwd')">${icon("folder")}${t(locale, "action.chooseFolder")}</button></div><small>${t(locale, "template.cwdHint")}</small></label>
|
|
22676
22536
|
<label class="form-field"><span>${t(locale, "template.agent")}</span><select id="template-agent" required><option value="">${t(locale, "catalog.chooseProject")}</option></select><small>${t(locale, "catalog.agentHint")}</small></label>
|
|
22677
|
-
<label class="form-field"><span>${t(locale, "template.model")}</span><div class="model-selector"><select id="template-model-provider" aria-label="${t(locale, "catalog.provider")}" onchange="
|
|
22537
|
+
<label class="form-field"><span>${t(locale, "template.model")}</span><div class="model-selector"><select id="template-model-provider" aria-label="${t(locale, "catalog.provider")}" onchange="handleProviderChange('template')"><option value="">${t(locale, "catalog.defaultProvider")}</option></select><select id="template-model" required aria-label="${t(locale, "catalog.model")}" onchange="handleModelChange('template')" disabled><option value="default">${t(locale, "catalog.defaultModel")}</option></select></div><small>${t(locale, "catalog.modelHint")}</small></label>
|
|
22538
|
+
<label class="form-field"><span>${t(locale, "template.variant")}</span><select id="template-variant" onchange="handleVariantChange('template')" disabled><option value="">${t(locale, "catalog.defaultVariant")}</option></select><small>${t(locale, "catalog.variantHint")}</small></label>
|
|
22678
22539
|
<label class="form-field form-field-wide"><span>${t(locale, "template.prompt")}</span><textarea id="template-prompt" rows="6" required></textarea></label>
|
|
22679
22540
|
<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>
|
|
22680
22541
|
<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>
|
|
22681
22542
|
<label id="template-interval-field" class="form-field"><span>${t(locale, "template.interval")}</span>${durationControl(locale, "template-interval", 36e5, "interval")}<small>${t(locale, "template.intervalHint")}</small></label>
|
|
22682
22543
|
<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>
|
|
22683
22544
|
</div>
|
|
22684
|
-
<p id="template-catalog-status" class="form-note catalog-status"></p>
|
|
22545
|
+
<p id="template-catalog-status" class="form-note catalog-status" role="status" aria-live="polite"></p>
|
|
22685
22546
|
<details class="advanced-fields">
|
|
22686
22547
|
<summary>${t(locale, "template.advanced")}</summary>
|
|
22687
22548
|
<div class="template-form-grid">
|
|
@@ -22715,6 +22576,7 @@ var init_web = __esm({
|
|
|
22715
22576
|
taskId: taskRuns4.taskId,
|
|
22716
22577
|
sessionId: taskRuns4.sessionId,
|
|
22717
22578
|
model: taskRuns4.model,
|
|
22579
|
+
variant: taskRuns4.variant,
|
|
22718
22580
|
status: taskRuns4.status,
|
|
22719
22581
|
startedAt: taskRuns4.startedAt,
|
|
22720
22582
|
finishedAt: taskRuns4.finishedAt,
|
|
@@ -22735,7 +22597,7 @@ var init_web = __esm({
|
|
|
22735
22597
|
const log = run.log ? renderRunLog(run.id, run.taskName, run.log, locale, run.status !== "done") : "";
|
|
22736
22598
|
return `<tr class="run-summary-row">
|
|
22737
22599
|
<td class="faint" data-label="${t(locale, "table.run")}">#${run.id}</td>
|
|
22738
|
-
<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"
|
|
22600
|
+
<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 || run.variant ? `<div class="actions" style="margin-top:4px">${run.model ? `<span class="tag">${esc(run.model)}</span>` : ""}${run.variant ? `<span class="tag">${esc(run.variant)}</span>` : ""}</div>` : ""}</td>
|
|
22739
22601
|
<td data-label="${t(locale, "table.agent")}"><span class="tag">${esc(run.taskAgent)}</span></td>
|
|
22740
22602
|
<td data-label="${t(locale, "table.session")}" class="m small">${esc(maskSessionId(run.sessionId))}</td>
|
|
22741
22603
|
<td data-label="${t(locale, "table.status")}"><span class="badge b-${status}">${runStatusText(locale, run.status ?? "unknown")}</span></td>
|
|
@@ -22762,7 +22624,7 @@ var init_web = __esm({
|
|
|
22762
22624
|
const config = loadConfig();
|
|
22763
22625
|
const activeConfig = runtimeConfig ?? config;
|
|
22764
22626
|
const restartRequired = runtimeConfig !== null && !configsEqual(config, runtimeConfig);
|
|
22765
|
-
const managedRestart = canRestartCurrentGateway();
|
|
22627
|
+
const managedRestart = await canRestartCurrentGateway();
|
|
22766
22628
|
const configState = resolveDashboardConfigState(
|
|
22767
22629
|
runtimeConfig !== null,
|
|
22768
22630
|
restartRequired,
|
|
@@ -22785,7 +22647,7 @@ var init_web = __esm({
|
|
|
22785
22647
|
const runRows = runningRuns.map((run) => {
|
|
22786
22648
|
const session = maskSessionId(run.sessionId);
|
|
22787
22649
|
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>
|
|
22788
|
-
<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>
|
|
22650
|
+
<td data-label="${t(locale, "table.model")}" class="small">${esc(run.model) || "\u2014"}${run.variant ? ` \xB7 ${esc(run.variant)}` : ""}</td><td data-label="${t(locale, "table.startedAt")}" class="small">${formatDateTime(run.startedAt, locale)}</td>
|
|
22789
22651
|
<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>
|
|
22790
22652
|
<td data-label="${t(locale, "table.duration")}" class="small">${formatDuration(run.startedAt, null)}</td></tr>`;
|
|
22791
22653
|
}).join("");
|
|
@@ -22882,9 +22744,9 @@ var init_web = __esm({
|
|
|
22882
22744
|
if (!isValidSessionId(run.sessionId)) return c.json({ error: "session unavailable" }, 409);
|
|
22883
22745
|
return c.json({ command: sessionCommand(run.sessionId) });
|
|
22884
22746
|
});
|
|
22885
|
-
app.get("/api/gateway/status", (c) => {
|
|
22747
|
+
app.get("/api/gateway/status", async (c) => {
|
|
22886
22748
|
const savedConfig = loadConfig();
|
|
22887
|
-
const managed = canRestartCurrentGateway();
|
|
22749
|
+
const managed = await canRestartCurrentGateway();
|
|
22888
22750
|
return c.json({
|
|
22889
22751
|
pid: process.pid,
|
|
22890
22752
|
managed,
|
|
@@ -23009,7 +22871,7 @@ var init_web = __esm({
|
|
|
23009
22871
|
return c.json({
|
|
23010
22872
|
success: true,
|
|
23011
22873
|
restartRequired: runtimeConfig === null || !configsEqual(savedConfig, runtimeConfig),
|
|
23012
|
-
managed: canRestartCurrentGateway()
|
|
22874
|
+
managed: await canRestartCurrentGateway()
|
|
23013
22875
|
});
|
|
23014
22876
|
} catch (error) {
|
|
23015
22877
|
return c.json({
|
|
@@ -23025,9 +22887,10 @@ var init_web = __esm({
|
|
|
23025
22887
|
return c.json({ error: "confirmation must be RESTART" }, 400);
|
|
23026
22888
|
}
|
|
23027
22889
|
if (restartScheduled) return c.json({ error: "restart already scheduled" }, 409);
|
|
23028
|
-
if (!canRestartCurrentGateway()) {
|
|
22890
|
+
if (!await canRestartCurrentGateway(true)) {
|
|
23029
22891
|
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);
|
|
23030
22892
|
}
|
|
22893
|
+
if (restartScheduled) return c.json({ error: "restart already scheduled" }, 409);
|
|
23031
22894
|
restartScheduled = true;
|
|
23032
22895
|
const previousPid = process.pid;
|
|
23033
22896
|
setTimeout(() => {
|
|
@@ -23091,7 +22954,7 @@ function runCommandContext(executable, args, cwd) {
|
|
|
23091
22954
|
function assertWorkerProcessIsolationSupported(platform = process.platform) {
|
|
23092
22955
|
if (platform === "win32") {
|
|
23093
22956
|
throw new Error(
|
|
23094
|
-
"Windows Worker \u5DF2\u5B89\u5168\u7981\u7528\uFF1A\u5F53\u524D\u8FD0\u884C\u65F6\u65E0\u6CD5\u7528 Job Object \
|
|
22957
|
+
"Windows Worker \u5DF2\u5B89\u5168\u7981\u7528\uFF1A\u5F53\u524D\u8FD0\u884C\u65F6\u65E0\u6CD5\u7528 Job Object \u63D0\u4F9B\u7B49\u4EF7\u7684\u53D7\u7BA1\u8FDB\u7A0B\u9694\u79BB\u4E0E\u6392\u7A7A\u8BC1\u660E"
|
|
23095
22958
|
);
|
|
23096
22959
|
}
|
|
23097
22960
|
}
|
|
@@ -23102,17 +22965,24 @@ var WorkerEngine = class {
|
|
|
23102
22965
|
pollTimer = null;
|
|
23103
22966
|
heartbeatTimer = null;
|
|
23104
22967
|
pollCyclePromise = null;
|
|
22968
|
+
shutdownDeadlineMs = null;
|
|
22969
|
+
settlementRetryWakeups = /* @__PURE__ */ new Set();
|
|
23105
22970
|
cfg;
|
|
23106
22971
|
opencodeBin;
|
|
23107
22972
|
maxOutputChars;
|
|
22973
|
+
settlementRetryDelaysMs;
|
|
22974
|
+
settlementRetryIntervalMs;
|
|
23108
22975
|
constructor(cfg, options = {}) {
|
|
23109
22976
|
this.cfg = cfg.worker;
|
|
23110
22977
|
this.opencodeBin = options.opencodeBin ?? process.env.SUPERTASK_OPENCODE_BIN ?? "opencode";
|
|
23111
22978
|
this.maxOutputChars = options.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
|
|
22979
|
+
this.settlementRetryDelaysMs = options.settlementRetryDelaysMs ?? [250, 1e3, 4e3];
|
|
22980
|
+
this.settlementRetryIntervalMs = options.settlementRetryIntervalMs ?? 5e3;
|
|
23112
22981
|
}
|
|
23113
22982
|
start() {
|
|
23114
22983
|
assertWorkerProcessIsolationSupported();
|
|
23115
22984
|
this.stopped = false;
|
|
22985
|
+
this.shutdownDeadlineMs = null;
|
|
23116
22986
|
markGatewayActivity("worker");
|
|
23117
22987
|
this.poll();
|
|
23118
22988
|
this.heartbeatTimer = setInterval(() => {
|
|
@@ -23121,6 +22991,8 @@ var WorkerEngine = class {
|
|
|
23121
22991
|
}
|
|
23122
22992
|
async stop(gracePeriodMs = 0) {
|
|
23123
22993
|
this.stopped = true;
|
|
22994
|
+
this.shutdownDeadlineMs = Date.now() + Math.max(0, gracePeriodMs);
|
|
22995
|
+
for (const wake of [...this.settlementRetryWakeups]) wake();
|
|
23124
22996
|
if (this.pollTimer) {
|
|
23125
22997
|
clearTimeout(this.pollTimer);
|
|
23126
22998
|
this.pollTimer = null;
|
|
@@ -23190,27 +23062,25 @@ var WorkerEngine = class {
|
|
|
23190
23062
|
if (databaseRunningCount >= this.cfg.maxConcurrency) break;
|
|
23191
23063
|
let task;
|
|
23192
23064
|
try {
|
|
23193
|
-
task = await TaskService.
|
|
23065
|
+
task = await TaskService.claimNext({ excludedBatchIds: [...this.activeBatchIds] });
|
|
23194
23066
|
} catch (err) {
|
|
23195
23067
|
this.logError("task claim failed", err);
|
|
23196
23068
|
throw err;
|
|
23197
23069
|
}
|
|
23198
23070
|
if (!task) break;
|
|
23199
|
-
if (this.stopped) break;
|
|
23200
|
-
if (!await TaskService.start(task.id)) continue;
|
|
23201
|
-
const batchId = normalizeTaskBatchId(task.batchId);
|
|
23202
|
-
if (batchId) this.activeBatchIds.add(batchId);
|
|
23203
23071
|
if (this.stopped) {
|
|
23204
23072
|
await TaskService.resetRunningToPending([task.id]);
|
|
23205
|
-
this.releaseBatch(task);
|
|
23206
23073
|
break;
|
|
23207
23074
|
}
|
|
23075
|
+
const batchId = normalizeTaskBatchId(task.batchId);
|
|
23076
|
+
if (batchId) this.activeBatchIds.add(batchId);
|
|
23208
23077
|
let runId = null;
|
|
23209
23078
|
try {
|
|
23210
23079
|
const launchIdentity = `gateway-${process.pid}:launch:${randomUUID()}`;
|
|
23211
23080
|
const run = await TaskRunService.create({
|
|
23212
23081
|
taskId: task.id,
|
|
23213
23082
|
model: this.resolveModel(task.model),
|
|
23083
|
+
variant: this.resolveVariant(task.variant),
|
|
23214
23084
|
status: "running",
|
|
23215
23085
|
workerPid: process.pid,
|
|
23216
23086
|
lockedAt: Date.now(),
|
|
@@ -23273,8 +23143,10 @@ var WorkerEngine = class {
|
|
|
23273
23143
|
}
|
|
23274
23144
|
async spawnTask(task, runId, launchIdentity) {
|
|
23275
23145
|
const model = this.resolveModel(task.model);
|
|
23146
|
+
const variant = this.resolveVariant(task.variant);
|
|
23276
23147
|
const args = ["run", "--agent", task.agent, "--format", "json"];
|
|
23277
23148
|
if (model) args.push("-m", model);
|
|
23149
|
+
if (variant) args.push("--variant", variant);
|
|
23278
23150
|
args.push(task.prompt);
|
|
23279
23151
|
const cwd = task.cwd || process.cwd();
|
|
23280
23152
|
const child = spawn(process.execPath, [
|
|
@@ -23334,8 +23206,8 @@ var WorkerEngine = class {
|
|
|
23334
23206
|
}
|
|
23335
23207
|
});
|
|
23336
23208
|
let spawnError = null;
|
|
23337
|
-
const spawned = new Promise((
|
|
23338
|
-
child.once("spawn",
|
|
23209
|
+
const spawned = new Promise((resolve3, reject) => {
|
|
23210
|
+
child.once("spawn", resolve3);
|
|
23339
23211
|
child.once("error", (error) => {
|
|
23340
23212
|
spawnError = error;
|
|
23341
23213
|
reject(error);
|
|
@@ -23383,10 +23255,10 @@ var WorkerEngine = class {
|
|
|
23383
23255
|
return;
|
|
23384
23256
|
}
|
|
23385
23257
|
try {
|
|
23386
|
-
await new Promise((
|
|
23258
|
+
await new Promise((resolve3, reject) => {
|
|
23387
23259
|
child.stdin.end("START\n", (error) => {
|
|
23388
23260
|
if (error) reject(error);
|
|
23389
|
-
else
|
|
23261
|
+
else resolve3();
|
|
23390
23262
|
});
|
|
23391
23263
|
});
|
|
23392
23264
|
} catch (error) {
|
|
@@ -23412,7 +23284,7 @@ var WorkerEngine = class {
|
|
|
23412
23284
|
if (!entry.guardianDrained) {
|
|
23413
23285
|
const pid = entry.child.pid;
|
|
23414
23286
|
const presence = pid == null ? spawnError == null ? "unknown" : "not-running" : inspectSpawnedProcessTreePresence(pid);
|
|
23415
|
-
const message = "guardian \u672A\u63D0\u4F9B\u8FDB\u7A0B\
|
|
23287
|
+
const message = "guardian \u672A\u63D0\u4F9B\u53D7\u7BA1\u8FDB\u7A0B\u7EC4\u6392\u7A7A\u8BC1\u660E";
|
|
23416
23288
|
if (presence !== "not-running") {
|
|
23417
23289
|
if (entry.timeoutTimer) {
|
|
23418
23290
|
clearTimeout(entry.timeoutTimer);
|
|
@@ -23433,7 +23305,7 @@ var WorkerEngine = class {
|
|
|
23433
23305
|
);
|
|
23434
23306
|
return;
|
|
23435
23307
|
}
|
|
23436
|
-
const failure = code === 0 ? void 0 : `${spawnError ? "\u65E0\u6CD5\u542F\u52A8 opencode" : "opencode \u9000\u51FA\u7801"} ${spawnError?.message ?? code ?? "null"}${signal ? `\uFF0C\u4FE1\u53F7 ${signal}` : ""}\uFF08agent=${entry.task.agent}\uFF0Cmodel=${this.resolveModel(entry.task.model) ?? "Agent/\u9ED8\u8BA4\u914D\u7F6E"}\uFF0Ccwd=${entry.task.cwd ?? process.cwd()}\uFF09`;
|
|
23308
|
+
const failure = code === 0 ? void 0 : `${spawnError ? "\u65E0\u6CD5\u542F\u52A8 opencode" : "opencode \u9000\u51FA\u7801"} ${spawnError?.message ?? code ?? "null"}${signal ? `\uFF0C\u4FE1\u53F7 ${signal}` : ""}\uFF08agent=${entry.task.agent}\uFF0Cmodel=${this.resolveModel(entry.task.model) ?? "Agent/\u9ED8\u8BA4\u914D\u7F6E"}\uFF0Cvariant=${this.resolveVariant(entry.task.variant) ?? "Agent/\u6A21\u578B\u9ED8\u8BA4\u914D\u7F6E"}\uFF0Ccwd=${entry.task.cwd ?? process.cwd()}\uFF09`;
|
|
23437
23309
|
this.runDetached(
|
|
23438
23310
|
this.settleEntry(entry, code, failure),
|
|
23439
23311
|
"task settlement failed",
|
|
@@ -23448,17 +23320,48 @@ var WorkerEngine = class {
|
|
|
23448
23320
|
clearTimeout(entry.timeoutTimer);
|
|
23449
23321
|
entry.timeoutTimer = null;
|
|
23450
23322
|
}
|
|
23451
|
-
const settlement = this.
|
|
23452
|
-
markGatewayFailure("worker", error);
|
|
23453
|
-
this.logError("task settlement failed", error, entry.task.id);
|
|
23454
|
-
return false;
|
|
23455
|
-
}).finally(() => {
|
|
23323
|
+
const settlement = this.commitEntryWithRetry(entry, code, failure).finally(() => {
|
|
23456
23324
|
this.runningTasks.delete(entry.task.id);
|
|
23457
23325
|
this.releaseBatch(entry.task);
|
|
23458
23326
|
});
|
|
23459
23327
|
entry.settlementPromise = settlement;
|
|
23460
23328
|
return settlement;
|
|
23461
23329
|
}
|
|
23330
|
+
async commitEntryWithRetry(entry, code, failure) {
|
|
23331
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
23332
|
+
try {
|
|
23333
|
+
await this.commitEntry(entry, code, failure);
|
|
23334
|
+
return true;
|
|
23335
|
+
} catch (error) {
|
|
23336
|
+
markGatewayFailure("worker", error);
|
|
23337
|
+
const shortRetryDelayMs = this.settlementRetryDelaysMs[attempt];
|
|
23338
|
+
let retryDelayMs = shortRetryDelayMs ?? this.settlementRetryIntervalMs;
|
|
23339
|
+
if (this.stopped) {
|
|
23340
|
+
const remainingMs = Math.max(0, (this.shutdownDeadlineMs ?? 0) - Date.now());
|
|
23341
|
+
if (remainingMs === 0) return false;
|
|
23342
|
+
retryDelayMs = Math.min(retryDelayMs, remainingMs);
|
|
23343
|
+
}
|
|
23344
|
+
this.logError(
|
|
23345
|
+
`task settlement failed; retrying in ${retryDelayMs}ms`,
|
|
23346
|
+
error,
|
|
23347
|
+
entry.task.id
|
|
23348
|
+
);
|
|
23349
|
+
await this.waitForSettlementRetry(retryDelayMs);
|
|
23350
|
+
}
|
|
23351
|
+
}
|
|
23352
|
+
}
|
|
23353
|
+
waitForSettlementRetry(delayMs) {
|
|
23354
|
+
return new Promise((resolve3) => {
|
|
23355
|
+
let timer;
|
|
23356
|
+
const finish = () => {
|
|
23357
|
+
clearTimeout(timer);
|
|
23358
|
+
this.settlementRetryWakeups.delete(finish);
|
|
23359
|
+
resolve3();
|
|
23360
|
+
};
|
|
23361
|
+
timer = setTimeout(finish, delayMs);
|
|
23362
|
+
this.settlementRetryWakeups.add(finish);
|
|
23363
|
+
});
|
|
23364
|
+
}
|
|
23462
23365
|
async commitEntry(entry, code, failure) {
|
|
23463
23366
|
const termination = entry.termination;
|
|
23464
23367
|
if (termination?.kind === "shutdown") return;
|
|
@@ -23622,6 +23525,9 @@ ${output}` : ""}`;
|
|
|
23622
23525
|
if (!taskModel || taskModel === "default") return null;
|
|
23623
23526
|
return taskModel;
|
|
23624
23527
|
}
|
|
23528
|
+
resolveVariant(taskVariant) {
|
|
23529
|
+
return taskVariant?.trim() || null;
|
|
23530
|
+
}
|
|
23625
23531
|
runDetached(operation, message, taskId) {
|
|
23626
23532
|
operation.catch((error) => {
|
|
23627
23533
|
markGatewayFailure("worker", error);
|
|
@@ -24022,7 +23928,29 @@ init_db2();
|
|
|
24022
23928
|
init_task_service();
|
|
24023
23929
|
init_health();
|
|
24024
23930
|
init_process_control();
|
|
24025
|
-
|
|
23931
|
+
|
|
23932
|
+
// src/core/package-version.ts
|
|
23933
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
23934
|
+
import { dirname as dirname4, join as join4 } from "path";
|
|
23935
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
23936
|
+
function getPackageVersion() {
|
|
23937
|
+
let directory = dirname4(fileURLToPath4(import.meta.url));
|
|
23938
|
+
for (let depth = 0; depth < 5; depth += 1) {
|
|
23939
|
+
const packagePath = join4(directory, "package.json");
|
|
23940
|
+
if (existsSync4(packagePath)) {
|
|
23941
|
+
try {
|
|
23942
|
+
const pkg = JSON.parse(readFileSync2(packagePath, "utf8"));
|
|
23943
|
+
if (typeof pkg.version === "string") return pkg.version;
|
|
23944
|
+
} catch {
|
|
23945
|
+
return "0.0.0";
|
|
23946
|
+
}
|
|
23947
|
+
}
|
|
23948
|
+
directory = dirname4(directory);
|
|
23949
|
+
}
|
|
23950
|
+
return "0.0.0";
|
|
23951
|
+
}
|
|
23952
|
+
|
|
23953
|
+
// src/gateway/index.ts
|
|
24026
23954
|
var STALE_THRESHOLD_MS = 3e4;
|
|
24027
23955
|
async function runGatewayShutdownStep(failures, step, operation) {
|
|
24028
23956
|
try {
|