opencode-supertask 0.1.34 → 0.1.35
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 +15 -0
- package/README.md +11 -7
- package/dist/cli/index.js +448 -31
- package/dist/cli/index.js.map +1 -1
- package/dist/gateway/index.js +448 -31
- package/dist/gateway/index.js.map +1 -1
- package/dist/web/index.d.ts +12 -1
- package/dist/web/index.js +459 -63
- package/dist/web/index.js.map +1 -1
- package/dist/worker/index.d.ts +3 -1
- package/dist/worker/index.js +18 -3
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
package/dist/web/index.js
CHANGED
|
@@ -12224,7 +12224,7 @@ function sql(strings, ...params) {
|
|
|
12224
12224
|
return new SQL([new StringChunk(str)]);
|
|
12225
12225
|
}
|
|
12226
12226
|
sql2.raw = raw2;
|
|
12227
|
-
function
|
|
12227
|
+
function join5(chunks, separator) {
|
|
12228
12228
|
const result = [];
|
|
12229
12229
|
for (const [i, chunk] of chunks.entries()) {
|
|
12230
12230
|
if (i > 0 && separator !== void 0) {
|
|
@@ -12234,7 +12234,7 @@ function sql(strings, ...params) {
|
|
|
12234
12234
|
}
|
|
12235
12235
|
return new SQL(result);
|
|
12236
12236
|
}
|
|
12237
|
-
sql2.join =
|
|
12237
|
+
sql2.join = join5;
|
|
12238
12238
|
function identifier(value) {
|
|
12239
12239
|
return new Name(value);
|
|
12240
12240
|
}
|
|
@@ -13083,8 +13083,17 @@ function mapRelationalRow(tablesConfig, tableConfig, row, buildQueryResultSelect
|
|
|
13083
13083
|
}
|
|
13084
13084
|
|
|
13085
13085
|
// src/web/index.tsx
|
|
13086
|
-
import {
|
|
13087
|
-
|
|
13086
|
+
import {
|
|
13087
|
+
existsSync as existsSync5,
|
|
13088
|
+
mkdirSync as mkdirSync4,
|
|
13089
|
+
readFileSync as readFileSync3,
|
|
13090
|
+
readdirSync,
|
|
13091
|
+
renameSync as renameSync2,
|
|
13092
|
+
statSync as statSync4,
|
|
13093
|
+
writeFileSync as writeFileSync3
|
|
13094
|
+
} from "fs";
|
|
13095
|
+
import { homedir as homedir4 } from "os";
|
|
13096
|
+
import { basename as basename2, dirname as dirname4, join as join4 } from "path";
|
|
13088
13097
|
|
|
13089
13098
|
// src/core/db/index.ts
|
|
13090
13099
|
import { Database as Database2 } from "bun:sqlite";
|
|
@@ -14571,7 +14580,7 @@ var SQLiteSelectQueryBuilderBase = class extends TypedQueryBuilder {
|
|
|
14571
14580
|
return (table, on) => {
|
|
14572
14581
|
const baseTableName = this.tableName;
|
|
14573
14582
|
const tableName = getTableLikeName(table);
|
|
14574
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
14583
|
+
if (typeof tableName === "string" && this.config.joins?.some((join5) => join5.alias === tableName)) {
|
|
14575
14584
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
14576
14585
|
}
|
|
14577
14586
|
if (!this.isPartialSelect) {
|
|
@@ -15384,7 +15393,7 @@ var SQLiteUpdateBase = class extends QueryPromise {
|
|
|
15384
15393
|
createJoin(joinType) {
|
|
15385
15394
|
return (table, on) => {
|
|
15386
15395
|
const tableName = getTableLikeName(table);
|
|
15387
|
-
if (typeof tableName === "string" && this.config.joins.some((
|
|
15396
|
+
if (typeof tableName === "string" && this.config.joins.some((join5) => join5.alias === tableName)) {
|
|
15388
15397
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
15389
15398
|
}
|
|
15390
15399
|
if (typeof on === "function") {
|
|
@@ -16611,6 +16620,140 @@ function parseDuration(input) {
|
|
|
16611
16620
|
return null;
|
|
16612
16621
|
}
|
|
16613
16622
|
|
|
16623
|
+
// src/core/opencode-catalog.ts
|
|
16624
|
+
import { spawn } from "child_process";
|
|
16625
|
+
|
|
16626
|
+
// src/core/task-working-directory.ts
|
|
16627
|
+
import { statSync } from "fs";
|
|
16628
|
+
import { isAbsolute } from "path";
|
|
16629
|
+
var InvalidTaskWorkingDirectoryError = class extends Error {
|
|
16630
|
+
constructor(message) {
|
|
16631
|
+
super(message);
|
|
16632
|
+
this.name = "InvalidTaskWorkingDirectoryError";
|
|
16633
|
+
}
|
|
16634
|
+
};
|
|
16635
|
+
function validateTaskWorkingDirectory(cwd) {
|
|
16636
|
+
if (cwd == null) return;
|
|
16637
|
+
if (!cwd.trim()) {
|
|
16638
|
+
throw new InvalidTaskWorkingDirectoryError("\u4EFB\u52A1\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u80FD\u4E3A\u7A7A");
|
|
16639
|
+
}
|
|
16640
|
+
if (!isAbsolute(cwd)) {
|
|
16641
|
+
throw new InvalidTaskWorkingDirectoryError(`\u4EFB\u52A1\u5DE5\u4F5C\u76EE\u5F55\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84\uFF1A${cwd}`);
|
|
16642
|
+
}
|
|
16643
|
+
let stat;
|
|
16644
|
+
try {
|
|
16645
|
+
stat = statSync(cwd);
|
|
16646
|
+
} catch (error) {
|
|
16647
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
16648
|
+
throw new InvalidTaskWorkingDirectoryError(`\u4EFB\u52A1\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u65E0\u6CD5\u8BBF\u95EE\uFF1A${cwd}\uFF08${detail}\uFF09`);
|
|
16649
|
+
}
|
|
16650
|
+
if (!stat.isDirectory()) {
|
|
16651
|
+
throw new InvalidTaskWorkingDirectoryError(`\u4EFB\u52A1\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u662F\u76EE\u5F55\uFF1A${cwd}`);
|
|
16652
|
+
}
|
|
16653
|
+
}
|
|
16654
|
+
|
|
16655
|
+
// src/core/opencode-catalog.ts
|
|
16656
|
+
var CATALOG_CACHE_MS = 3e4;
|
|
16657
|
+
var COMMAND_TIMEOUT_MS = 2e4;
|
|
16658
|
+
var MAX_OUTPUT_BYTES = 4 * 1024 * 1024;
|
|
16659
|
+
var ANSI_PATTERN = /\x1B(?:[@-_][0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\))/g;
|
|
16660
|
+
var catalogCache = /* @__PURE__ */ new Map();
|
|
16661
|
+
function cleanOutput(value) {
|
|
16662
|
+
return value.replace(ANSI_PATTERN, "").replace(/\r/g, "");
|
|
16663
|
+
}
|
|
16664
|
+
function runOpenCode(executable, args, cwd, timeoutMs) {
|
|
16665
|
+
return new Promise((resolve3, reject) => {
|
|
16666
|
+
const child = spawn(executable, args, {
|
|
16667
|
+
cwd,
|
|
16668
|
+
env: process.env,
|
|
16669
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
16670
|
+
});
|
|
16671
|
+
let stdout = "";
|
|
16672
|
+
let stderr = "";
|
|
16673
|
+
let failure = null;
|
|
16674
|
+
let finished = false;
|
|
16675
|
+
const append = (current, chunk) => {
|
|
16676
|
+
const next = current + chunk.toString();
|
|
16677
|
+
if (Buffer.byteLength(next) > MAX_OUTPUT_BYTES && failure === null) {
|
|
16678
|
+
failure = new Error(`OpenCode \u8F93\u51FA\u8D85\u8FC7 ${MAX_OUTPUT_BYTES} bytes`);
|
|
16679
|
+
child.kill("SIGTERM");
|
|
16680
|
+
}
|
|
16681
|
+
return next.slice(-MAX_OUTPUT_BYTES);
|
|
16682
|
+
};
|
|
16683
|
+
child.stdout?.on("data", (chunk) => {
|
|
16684
|
+
stdout = append(stdout, chunk);
|
|
16685
|
+
});
|
|
16686
|
+
child.stderr?.on("data", (chunk) => {
|
|
16687
|
+
stderr = append(stderr, chunk);
|
|
16688
|
+
});
|
|
16689
|
+
child.once("error", (error) => {
|
|
16690
|
+
failure ??= error;
|
|
16691
|
+
});
|
|
16692
|
+
const timer = setTimeout(() => {
|
|
16693
|
+
failure ??= new Error(`OpenCode \u547D\u4EE4\u8D85\u8FC7 ${timeoutMs}ms \u672A\u5B8C\u6210`);
|
|
16694
|
+
child.kill("SIGTERM");
|
|
16695
|
+
}, timeoutMs);
|
|
16696
|
+
child.once("close", (code) => {
|
|
16697
|
+
if (finished) return;
|
|
16698
|
+
finished = true;
|
|
16699
|
+
clearTimeout(timer);
|
|
16700
|
+
if (failure) {
|
|
16701
|
+
reject(failure);
|
|
16702
|
+
return;
|
|
16703
|
+
}
|
|
16704
|
+
if (code !== 0) {
|
|
16705
|
+
const detail = cleanOutput(stderr).trim() || `\u9000\u51FA\u7801 ${code ?? "null"}`;
|
|
16706
|
+
reject(new Error(`OpenCode ${args.join(" ")} \u5931\u8D25\uFF1A${detail}`));
|
|
16707
|
+
return;
|
|
16708
|
+
}
|
|
16709
|
+
resolve3(cleanOutput(stdout));
|
|
16710
|
+
});
|
|
16711
|
+
});
|
|
16712
|
+
}
|
|
16713
|
+
function parseOpenCodeModels(output) {
|
|
16714
|
+
return [...new Set(cleanOutput(output).split("\n").map((line) => line.trim()).filter((line) => /^[^\s/]+\/.+/.test(line)))].sort((left, right) => left.localeCompare(right));
|
|
16715
|
+
}
|
|
16716
|
+
function parseOpenCodeAgents(output) {
|
|
16717
|
+
const agents = /* @__PURE__ */ new Map();
|
|
16718
|
+
for (const line of cleanOutput(output).split("\n")) {
|
|
16719
|
+
const match2 = /^([^\s()]+) \((primary|subagent|all)\)$/.exec(line.trim());
|
|
16720
|
+
if (!match2) continue;
|
|
16721
|
+
const name = match2[1];
|
|
16722
|
+
const mode = match2[2];
|
|
16723
|
+
if (name === "supertask-runner") continue;
|
|
16724
|
+
agents.set(name, { name, mode });
|
|
16725
|
+
}
|
|
16726
|
+
const rank = { primary: 0, all: 1, subagent: 2 };
|
|
16727
|
+
return [...agents.values()].sort((left, right) => rank[left.mode] - rank[right.mode] || left.name.localeCompare(right.name));
|
|
16728
|
+
}
|
|
16729
|
+
async function loadOpenCodeCatalog(cwd, options = {}) {
|
|
16730
|
+
validateTaskWorkingDirectory(cwd);
|
|
16731
|
+
const executable = options.executable ?? process.env.SUPERTASK_OPENCODE_BIN ?? "opencode";
|
|
16732
|
+
const timeoutMs = options.timeoutMs ?? COMMAND_TIMEOUT_MS;
|
|
16733
|
+
const cacheKey = `${executable}\0${cwd}`;
|
|
16734
|
+
const cached = catalogCache.get(cacheKey);
|
|
16735
|
+
if (options.useCache !== false && cached && cached.expiresAt > Date.now()) {
|
|
16736
|
+
return cached.result;
|
|
16737
|
+
}
|
|
16738
|
+
const result = Promise.all([
|
|
16739
|
+
runOpenCode(executable, ["models"], cwd, timeoutMs),
|
|
16740
|
+
runOpenCode(executable, ["agent", "list"], cwd, timeoutMs)
|
|
16741
|
+
]).then(([modelsOutput, agentsOutput]) => {
|
|
16742
|
+
const models = parseOpenCodeModels(modelsOutput);
|
|
16743
|
+
const agents = parseOpenCodeAgents(agentsOutput).filter((agent) => agent.mode !== "subagent");
|
|
16744
|
+
if (models.length === 0) throw new Error("OpenCode \u6CA1\u6709\u8FD4\u56DE\u53EF\u7528\u6A21\u578B");
|
|
16745
|
+
if (agents.length === 0) throw new Error("OpenCode \u6CA1\u6709\u8FD4\u56DE\u53EF\u76F4\u63A5\u8FD0\u884C\u7684\u4E3B Agent");
|
|
16746
|
+
return { cwd, models, agents };
|
|
16747
|
+
});
|
|
16748
|
+
if (options.useCache !== false) {
|
|
16749
|
+
catalogCache.set(cacheKey, { expiresAt: Date.now() + CATALOG_CACHE_MS, result });
|
|
16750
|
+
result.catch(() => {
|
|
16751
|
+
if (catalogCache.get(cacheKey)?.result === result) catalogCache.delete(cacheKey);
|
|
16752
|
+
});
|
|
16753
|
+
}
|
|
16754
|
+
return result;
|
|
16755
|
+
}
|
|
16756
|
+
|
|
16614
16757
|
// src/core/services/database-maintenance.service.ts
|
|
16615
16758
|
import { Database as Database3 } from "bun:sqlite";
|
|
16616
16759
|
import { randomUUID } from "crypto";
|
|
@@ -16619,7 +16762,7 @@ import {
|
|
|
16619
16762
|
existsSync as existsSync2,
|
|
16620
16763
|
mkdirSync as mkdirSync2,
|
|
16621
16764
|
renameSync,
|
|
16622
|
-
statSync,
|
|
16765
|
+
statSync as statSync2,
|
|
16623
16766
|
unlinkSync,
|
|
16624
16767
|
writeFileSync
|
|
16625
16768
|
} from "fs";
|
|
@@ -16728,9 +16871,9 @@ var DatabaseMaintenanceService = class {
|
|
|
16728
16871
|
if (!existsSync2(source)) {
|
|
16729
16872
|
throw new Error(`\u5907\u4EFD\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${source}`);
|
|
16730
16873
|
}
|
|
16731
|
-
const sourceStat =
|
|
16874
|
+
const sourceStat = statSync2(source);
|
|
16732
16875
|
if (!sourceStat.isFile()) throw new Error(`\u5907\u4EFD\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6\uFF1A${source}`);
|
|
16733
|
-
const liveStat =
|
|
16876
|
+
const liveStat = statSync2(livePath);
|
|
16734
16877
|
if (sourceStat.dev === liveStat.dev && sourceStat.ino === liveStat.ino) {
|
|
16735
16878
|
throw new Error("\u6062\u590D\u6765\u6E90\u4E0D\u80FD\u662F\u5F53\u524D\u6570\u636E\u5E93\u7684\u7B26\u53F7\u94FE\u63A5\u6216\u786C\u94FE\u63A5\u522B\u540D");
|
|
16736
16879
|
}
|
|
@@ -16971,7 +17114,7 @@ var DatabaseMaintenanceService = class {
|
|
|
16971
17114
|
const counts = missingTables.length === 0 ? this.readCounts(sqlite2) : { tasks: 0, taskRuns: 0, taskTemplates: 0 };
|
|
16972
17115
|
const runningTasks = missingTables.includes("tasks") ? 0 : this.scalar(sqlite2, "SELECT COUNT(*) AS count FROM tasks WHERE status = 'running'");
|
|
16973
17116
|
const runningRuns = missingTables.includes("task_runs") ? 0 : this.scalar(sqlite2, "SELECT COUNT(*) AS count FROM task_runs WHERE status = 'running'");
|
|
16974
|
-
const sizeBytes = path === ":memory:" || !existsSync2(path) ? sqlite2.serialize().byteLength :
|
|
17117
|
+
const sizeBytes = path === ":memory:" || !existsSync2(path) ? sqlite2.serialize().byteLength : statSync2(path).size;
|
|
16975
17118
|
return {
|
|
16976
17119
|
ok: integrityMessages.length === 1 && integrityMessages[0] === "ok" && foreignKeyViolations === 0 && missingTables.length === 0,
|
|
16977
17120
|
path: normalizedPath(path),
|
|
@@ -17008,7 +17151,7 @@ var DatabaseMaintenanceService = class {
|
|
|
17008
17151
|
const check = this.inspectFile(temporary);
|
|
17009
17152
|
if (!check.ok) throw new Error("\u65B0\u5907\u4EFD\u672A\u901A\u8FC7\u5B8C\u6574\u6027\u6821\u9A8C");
|
|
17010
17153
|
renameSync(temporary, output);
|
|
17011
|
-
const finalCheck = { ...check, path: output, sizeBytes:
|
|
17154
|
+
const finalCheck = { ...check, path: output, sizeBytes: statSync2(output).size };
|
|
17012
17155
|
return { path: output, sizeBytes: finalCheck.sizeBytes, check: finalCheck };
|
|
17013
17156
|
} catch (error) {
|
|
17014
17157
|
safeUnlink(temporary);
|
|
@@ -17244,35 +17387,6 @@ function computeBackoff(retryCount, baseMs = 3e4, maxMs = MAX_BACKOFF_MS) {
|
|
|
17244
17387
|
return Math.min(baseMs * Math.pow(2, retryCount - 1), maxMs);
|
|
17245
17388
|
}
|
|
17246
17389
|
|
|
17247
|
-
// src/core/task-working-directory.ts
|
|
17248
|
-
import { statSync as statSync2 } from "fs";
|
|
17249
|
-
import { isAbsolute } from "path";
|
|
17250
|
-
var InvalidTaskWorkingDirectoryError = class extends Error {
|
|
17251
|
-
constructor(message) {
|
|
17252
|
-
super(message);
|
|
17253
|
-
this.name = "InvalidTaskWorkingDirectoryError";
|
|
17254
|
-
}
|
|
17255
|
-
};
|
|
17256
|
-
function validateTaskWorkingDirectory(cwd) {
|
|
17257
|
-
if (cwd == null) return;
|
|
17258
|
-
if (!cwd.trim()) {
|
|
17259
|
-
throw new InvalidTaskWorkingDirectoryError("\u4EFB\u52A1\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u80FD\u4E3A\u7A7A");
|
|
17260
|
-
}
|
|
17261
|
-
if (!isAbsolute(cwd)) {
|
|
17262
|
-
throw new InvalidTaskWorkingDirectoryError(`\u4EFB\u52A1\u5DE5\u4F5C\u76EE\u5F55\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84\uFF1A${cwd}`);
|
|
17263
|
-
}
|
|
17264
|
-
let stat;
|
|
17265
|
-
try {
|
|
17266
|
-
stat = statSync2(cwd);
|
|
17267
|
-
} catch (error) {
|
|
17268
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
17269
|
-
throw new InvalidTaskWorkingDirectoryError(`\u4EFB\u52A1\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u65E0\u6CD5\u8BBF\u95EE\uFF1A${cwd}\uFF08${detail}\uFF09`);
|
|
17270
|
-
}
|
|
17271
|
-
if (!stat.isDirectory()) {
|
|
17272
|
-
throw new InvalidTaskWorkingDirectoryError(`\u4EFB\u52A1\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u662F\u76EE\u5F55\uFF1A${cwd}`);
|
|
17273
|
-
}
|
|
17274
|
-
}
|
|
17275
|
-
|
|
17276
17390
|
// src/core/task-batch.ts
|
|
17277
17391
|
var TASK_BATCH_TRIM_CHARACTERS = " \n\v\f\r\xA0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF";
|
|
17278
17392
|
function normalizeTaskBatchId(batchId) {
|
|
@@ -19058,6 +19172,13 @@ var ZH = {
|
|
|
19058
19172
|
"action.updateTask": "\u4FDD\u5B58\u4FEE\u6539",
|
|
19059
19173
|
"action.createTemplate": "\u65B0\u5EFA\u5B9A\u65F6\u4EFB\u52A1",
|
|
19060
19174
|
"action.saveTemplate": "\u4FDD\u5B58\u5B9A\u65F6\u4EFB\u52A1",
|
|
19175
|
+
"action.chooseFolder": "\u9009\u62E9\u6587\u4EF6\u5939",
|
|
19176
|
+
"action.chooseThisFolder": "\u9009\u62E9\u5F53\u524D\u6587\u4EF6\u5939",
|
|
19177
|
+
"action.home": "\u4E3B\u76EE\u5F55",
|
|
19178
|
+
"action.up": "\u4E0A\u4E00\u7EA7",
|
|
19179
|
+
"action.copyCommand": "\u590D\u5236\u547D\u4EE4",
|
|
19180
|
+
"action.showHidden": "\u663E\u793A\u9690\u85CF\u6587\u4EF6\u5939",
|
|
19181
|
+
"action.hideHidden": "\u9690\u85CF\u9690\u85CF\u6587\u4EF6\u5939",
|
|
19061
19182
|
"status.pending": "\u5F85\u6267\u884C",
|
|
19062
19183
|
"status.running": "\u8FD0\u884C\u4E2D",
|
|
19063
19184
|
"status.done": "\u5DF2\u5B8C\u6210",
|
|
@@ -19124,6 +19245,15 @@ var ZH = {
|
|
|
19124
19245
|
"schedule.hours": "{count} \u5C0F\u65F6",
|
|
19125
19246
|
"schedule.days": "{count} \u5929",
|
|
19126
19247
|
"schedule.overdue": "\u5DF2\u5230\u671F",
|
|
19248
|
+
"duration.unit": "\u65F6\u95F4\u5355\u4F4D",
|
|
19249
|
+
"duration.seconds": "\u79D2",
|
|
19250
|
+
"duration.minutes": "\u5206\u949F",
|
|
19251
|
+
"duration.hours": "\u5C0F\u65F6",
|
|
19252
|
+
"duration.days": "\u5929",
|
|
19253
|
+
"duration.systemDefault": "\u4F7F\u7528 Gateway \u9ED8\u8BA4\u8D85\u65F6",
|
|
19254
|
+
"duration.immediate": "\u7ACB\u5373\u91CD\u8BD5",
|
|
19255
|
+
"duration.custom": "\u81EA\u5B9A\u4E49\u2026",
|
|
19256
|
+
"duration.every": "\u6BCF {duration}",
|
|
19127
19257
|
"system.worker": "\u4EFB\u52A1\u6267\u884C",
|
|
19128
19258
|
"system.scheduler": "\u5B9A\u65F6\u4EFB\u52A1\u670D\u52A1",
|
|
19129
19259
|
"system.watchdog": "\u8FD0\u884C\u76D1\u63A7",
|
|
@@ -19171,6 +19301,9 @@ var ZH = {
|
|
|
19171
19301
|
"template.interval": "\u6267\u884C\u95F4\u9694",
|
|
19172
19302
|
"template.runAt": "\u6267\u884C\u65F6\u95F4",
|
|
19173
19303
|
"template.durationHint": "\u652F\u6301 30s\u30015min\u30011h\u30012d",
|
|
19304
|
+
"template.intervalHint": "\u76F4\u63A5\u9009\u62E9\u5E38\u7528\u9891\u7387\uFF1B\u53EA\u6709\u7279\u6B8A\u9700\u6C42\u624D\u9700\u8981\u81EA\u5B9A\u4E49\u3002",
|
|
19305
|
+
"template.retryBackoffHint": "\u4E00\u6B21\u5931\u8D25\u540E\uFF0C\u7B49\u5F85\u591A\u4E45\u518D\u91CD\u8BD5\u3002",
|
|
19306
|
+
"template.timeoutHint": "\u7559\u7A7A\u8868\u793A\u4F7F\u7528 Gateway \u7684\u9ED8\u8BA4\u4EFB\u52A1\u8D85\u65F6\u3002",
|
|
19174
19307
|
"template.advanced": "\u66F4\u591A\u6267\u884C\u8BBE\u7F6E",
|
|
19175
19308
|
"template.category": "\u5206\u7C7B",
|
|
19176
19309
|
"template.batchId": "\u6279\u6B21 ID",
|
|
@@ -19195,6 +19328,28 @@ var ZH = {
|
|
|
19195
19328
|
"task.batchHint": "\u76F8\u540C\u975E\u7A7A\u6279\u6B21 ID \u7684\u4EFB\u52A1\u4E25\u683C\u4E32\u884C\uFF1B\u7559\u7A7A\u5219\u4E0D\u53D7\u6279\u6B21\u4E32\u884C\u9650\u5236\uFF0C\u4F46\u4ECD\u53D7\u5168\u5C40\u5E76\u53D1\u548C\u4F9D\u8D56\u7EA6\u675F\u3002",
|
|
19196
19329
|
"task.projectExisting": "\u6B64\u9879\u76EE\u73B0\u6709 {total} \u4E2A\u4EFB\u52A1\uFF1A\u8FD0\u884C {running}\uFF0C\u6392\u961F {pending}\uFF0C\u5F02\u5E38 {failed}\u3002",
|
|
19197
19330
|
"task.projectNew": "\u8FD9\u662F\u4E00\u4E2A\u65B0\u9879\u76EE\u5206\u7EC4\uFF1B\u521B\u5EFA\u540E\u4F1A\u51FA\u73B0\u5728\u9879\u76EE\u5217\u8868\u4E2D\u3002",
|
|
19331
|
+
"catalog.chooseProject": "\u8BF7\u5148\u9009\u62E9\u9879\u76EE\u76EE\u5F55",
|
|
19332
|
+
"catalog.defaultModel": "\u8DDF\u968F Agent / OpenCode \u9ED8\u8BA4\u6A21\u578B",
|
|
19333
|
+
"catalog.defaultProvider": "\u9ED8\u8BA4\u6A21\u578B",
|
|
19334
|
+
"catalog.provider": "\u6A21\u578B\u63D0\u4F9B\u5546",
|
|
19335
|
+
"catalog.model": "\u5177\u4F53\u6A21\u578B",
|
|
19336
|
+
"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",
|
|
19337
|
+
"catalog.agentHint": "\u6765\u81EA\u5F53\u524D\u9879\u76EE\u7684 opencode agent list\u3002",
|
|
19338
|
+
"catalog.loading": "\u6B63\u5728\u8BFB\u53D6\u6B64\u9879\u76EE\u53EF\u7528\u7684 Agent \u548C\u6A21\u578B\u2026",
|
|
19339
|
+
"catalog.loaded": "\u5DF2\u4ECE\u672C\u673A OpenCode \u8BFB\u53D6 {agents} \u4E2A Agent\u3001{models} \u4E2A\u6A21\u578B\u3002",
|
|
19340
|
+
"catalog.failed": "\u65E0\u6CD5\u8BFB\u53D6\u6B64\u9879\u76EE\u7684 OpenCode \u914D\u7F6E\uFF1A{error}",
|
|
19341
|
+
"catalog.primary": "\u4E3B Agent",
|
|
19342
|
+
"catalog.subagent": "\u5B50 Agent",
|
|
19343
|
+
"catalog.all": "\u901A\u7528 Agent",
|
|
19344
|
+
"directory.title": "\u9009\u62E9\u9879\u76EE\u76EE\u5F55",
|
|
19345
|
+
"directory.subtitle": "\u9009\u62E9\u540E\uFF0C\u7CFB\u7EDF\u4F1A\u5728\u8BE5\u76EE\u5F55\u8FD0\u884C OpenCode\uFF0C\u5E76\u8BFB\u53D6\u8BE5\u9879\u76EE\u53EF\u7528\u7684 Agent \u548C\u6A21\u578B\u3002",
|
|
19346
|
+
"directory.empty": "\u8FD9\u4E2A\u6587\u4EF6\u5939\u4E2D\u6CA1\u6709\u5B50\u6587\u4EF6\u5939",
|
|
19347
|
+
"logs.command": "\u5B9E\u9645\u6267\u884C\u547D\u4EE4",
|
|
19348
|
+
"logs.output": "Agent \u8F93\u51FA",
|
|
19349
|
+
"logs.error": "\u5931\u8D25\u539F\u56E0",
|
|
19350
|
+
"logs.tools": "\u5DE5\u5177\u8C03\u7528",
|
|
19351
|
+
"logs.raw": "\u67E5\u770B\u539F\u59CB\u6267\u884C\u65E5\u5FD7",
|
|
19352
|
+
"logs.noText": "\u8FD9\u6B21\u6267\u884C\u6CA1\u6709\u4EA7\u751F\u53EF\u5C55\u793A\u7684\u6587\u672C\u8F93\u51FA\uFF0C\u8BF7\u67E5\u770B\u539F\u59CB\u65E5\u5FD7\u3002",
|
|
19198
19353
|
"theme.label": "\u4E3B\u9898",
|
|
19199
19354
|
"theme.system": "\u8DDF\u968F\u7CFB\u7EDF",
|
|
19200
19355
|
"theme.light": "\u6D45\u8272",
|
|
@@ -19232,6 +19387,7 @@ var ZH = {
|
|
|
19232
19387
|
"feedback.templateUpdated": "\u5B9A\u65F6\u4EFB\u52A1\u5DF2\u66F4\u65B0",
|
|
19233
19388
|
"feedback.configSaved": "\u8BBE\u7F6E\u5DF2\u4FDD\u5B58",
|
|
19234
19389
|
"feedback.sessionCommandCopied": "\u4F1A\u8BDD\u547D\u4EE4\u5DF2\u590D\u5236\uFF0C\u53EF\u76F4\u63A5\u7C98\u8D34\u5230\u7EC8\u7AEF\u8FD0\u884C",
|
|
19390
|
+
"feedback.commandCopied": "\u6267\u884C\u547D\u4EE4\u5DF2\u590D\u5236",
|
|
19235
19391
|
"feedback.restarting": "Gateway \u6B63\u5728\u91CD\u542F\uFF0C\u8BF7\u7A0D\u5019\u2026",
|
|
19236
19392
|
"feedback.restartTimeout": "Gateway \u5C1A\u672A\u6062\u590D\uFF0C\u8BF7\u7A0D\u540E\u5237\u65B0\u6216\u68C0\u67E5 PM2 \u72B6\u6001",
|
|
19237
19393
|
"feedback.databaseCleared": "\u6570\u636E\u5E93\u5DF2\u6E05\u7A7A\uFF0C\u5907\u4EFD\u4F4D\u4E8E\uFF1A{path}",
|
|
@@ -19280,6 +19436,13 @@ var EN = {
|
|
|
19280
19436
|
"action.updateTask": "Save changes",
|
|
19281
19437
|
"action.createTemplate": "New scheduled task",
|
|
19282
19438
|
"action.saveTemplate": "Save scheduled task",
|
|
19439
|
+
"action.chooseFolder": "Choose folder",
|
|
19440
|
+
"action.chooseThisFolder": "Choose this folder",
|
|
19441
|
+
"action.home": "Home",
|
|
19442
|
+
"action.up": "Up",
|
|
19443
|
+
"action.copyCommand": "Copy command",
|
|
19444
|
+
"action.showHidden": "Show hidden folders",
|
|
19445
|
+
"action.hideHidden": "Hide hidden folders",
|
|
19283
19446
|
"status.pending": "Pending",
|
|
19284
19447
|
"status.running": "Running",
|
|
19285
19448
|
"status.done": "Done",
|
|
@@ -19346,6 +19509,15 @@ var EN = {
|
|
|
19346
19509
|
"schedule.hours": "{count} hr",
|
|
19347
19510
|
"schedule.days": "{count} days",
|
|
19348
19511
|
"schedule.overdue": "Overdue",
|
|
19512
|
+
"duration.unit": "Time unit",
|
|
19513
|
+
"duration.seconds": "seconds",
|
|
19514
|
+
"duration.minutes": "minutes",
|
|
19515
|
+
"duration.hours": "hours",
|
|
19516
|
+
"duration.days": "days",
|
|
19517
|
+
"duration.systemDefault": "Use Gateway default timeout",
|
|
19518
|
+
"duration.immediate": "Retry immediately",
|
|
19519
|
+
"duration.custom": "Custom\u2026",
|
|
19520
|
+
"duration.every": "Every {duration}",
|
|
19349
19521
|
"system.worker": "Task execution",
|
|
19350
19522
|
"system.scheduler": "Scheduled task service",
|
|
19351
19523
|
"system.watchdog": "Runtime monitor",
|
|
@@ -19393,6 +19565,9 @@ var EN = {
|
|
|
19393
19565
|
"template.interval": "Interval",
|
|
19394
19566
|
"template.runAt": "Run at",
|
|
19395
19567
|
"template.durationHint": "Supports 30s, 5min, 1h, 2d",
|
|
19568
|
+
"template.intervalHint": "Choose a common frequency directly; customize only when necessary.",
|
|
19569
|
+
"template.retryBackoffHint": "How long to wait after a failure before retrying.",
|
|
19570
|
+
"template.timeoutHint": "Leave blank to use the Gateway default task timeout.",
|
|
19396
19571
|
"template.advanced": "More execution settings",
|
|
19397
19572
|
"template.category": "Category",
|
|
19398
19573
|
"template.batchId": "Batch ID",
|
|
@@ -19417,6 +19592,28 @@ var EN = {
|
|
|
19417
19592
|
"task.batchHint": "Tasks with the same non-empty batch ID run serially. Blank removes the batch constraint; global concurrency and dependencies still apply.",
|
|
19418
19593
|
"task.projectExisting": "This project has {total} tasks: {running} running, {pending} queued, and {failed} with issues.",
|
|
19419
19594
|
"task.projectNew": "This is a new project group. It appears in the project list after creation.",
|
|
19595
|
+
"catalog.chooseProject": "Choose a project directory first",
|
|
19596
|
+
"catalog.defaultModel": "Use the Agent / OpenCode default model",
|
|
19597
|
+
"catalog.defaultProvider": "Default model",
|
|
19598
|
+
"catalog.provider": "Model provider",
|
|
19599
|
+
"catalog.model": "Model",
|
|
19600
|
+
"catalog.modelHint": "Choose a provider, then a model returned by opencode models for this project. Default does not pass -m.",
|
|
19601
|
+
"catalog.agentHint": "Loaded from opencode agent list for this project.",
|
|
19602
|
+
"catalog.loading": "Loading Agents and models available to this project\u2026",
|
|
19603
|
+
"catalog.loaded": "Loaded {agents} Agents and {models} models from local OpenCode.",
|
|
19604
|
+
"catalog.failed": "Could not load this project\u2019s OpenCode configuration: {error}",
|
|
19605
|
+
"catalog.primary": "primary Agent",
|
|
19606
|
+
"catalog.subagent": "subagent",
|
|
19607
|
+
"catalog.all": "general Agent",
|
|
19608
|
+
"directory.title": "Choose project directory",
|
|
19609
|
+
"directory.subtitle": "OpenCode runs in this directory, and its project-specific Agents and models are loaded.",
|
|
19610
|
+
"directory.empty": "This folder has no subfolders",
|
|
19611
|
+
"logs.command": "Executed command",
|
|
19612
|
+
"logs.output": "Agent output",
|
|
19613
|
+
"logs.error": "Failure reason",
|
|
19614
|
+
"logs.tools": "Tool calls",
|
|
19615
|
+
"logs.raw": "View raw execution log",
|
|
19616
|
+
"logs.noText": "This run produced no displayable text. Inspect the raw log for details.",
|
|
19420
19617
|
"theme.label": "Theme",
|
|
19421
19618
|
"theme.system": "System",
|
|
19422
19619
|
"theme.light": "Light",
|
|
@@ -19454,6 +19651,7 @@ var EN = {
|
|
|
19454
19651
|
"feedback.templateUpdated": "Scheduled task updated",
|
|
19455
19652
|
"feedback.configSaved": "Settings saved",
|
|
19456
19653
|
"feedback.sessionCommandCopied": "Session command copied. Paste it into a terminal to continue.",
|
|
19654
|
+
"feedback.commandCopied": "Execution command copied",
|
|
19457
19655
|
"feedback.restarting": "Gateway is restarting\u2026",
|
|
19458
19656
|
"feedback.restartTimeout": "Gateway has not recovered yet. Refresh later or check PM2 status.",
|
|
19459
19657
|
"feedback.databaseCleared": "Database cleared. Backup: {path}",
|
|
@@ -19523,7 +19721,8 @@ function icon(name, className = "icon") {
|
|
|
19523
19721
|
check: '<path d="m5 12 4 4L19 6"/>',
|
|
19524
19722
|
alert: '<path d="M12 3 2.8 19h18.4L12 3Z"/><path d="M12 9v4M12 17h.01"/>',
|
|
19525
19723
|
clock: '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>',
|
|
19526
|
-
database: '<ellipse cx="12" cy="5" rx="8" ry="3"/><path d="M4 5v6c0 1.66 3.58 3 8 3s8-1.34 8-3V5M4 11v6c0 1.66 3.58 3 8 3s8-1.34 8-3v-6"/>'
|
|
19724
|
+
database: '<ellipse cx="12" cy="5" rx="8" ry="3"/><path d="M4 5v6c0 1.66 3.58 3 8 3s8-1.34 8-3V5M4 11v6c0 1.66 3.58 3 8 3s8-1.34 8-3v-6"/>',
|
|
19725
|
+
folder: '<path d="M3 7.5A2.5 2.5 0 0 1 5.5 5H10l2 2h6.5A2.5 2.5 0 0 1 21 9.5v7A2.5 2.5 0 0 1 18.5 19h-13A2.5 2.5 0 0 1 3 16.5v-9Z"/>'
|
|
19527
19726
|
};
|
|
19528
19727
|
return `<svg class="${className}" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">${paths[name]}</svg>`;
|
|
19529
19728
|
}
|
|
@@ -19730,6 +19929,18 @@ var STYLES = `
|
|
|
19730
19929
|
.danger-card h2 .icon { width:17px; height:17px; }
|
|
19731
19930
|
.danger-card p { max-width:800px; margin:0 0 14px; color:var(--text-2); font-size:12px; }
|
|
19732
19931
|
.log-panel { margin:12px 0; animation:reveal .18s ease both; }
|
|
19932
|
+
.log-content { display:grid; gap:14px; padding:16px; }
|
|
19933
|
+
.log-section-head { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:7px; }
|
|
19934
|
+
.run-command,.run-output,.run-error,.run-tools { min-width:0; }
|
|
19935
|
+
.run-command strong,.run-output>strong,.run-error>strong,.run-tools>strong { display:block; margin-bottom:7px; font-size:12px; }
|
|
19936
|
+
.command-cwd { margin-bottom:6px; color:var(--text-3); font-family:"SFMono-Regular",Consolas,monospace; font-size:10px; overflow-wrap:anywhere; }
|
|
19937
|
+
.run-command pre,.run-output pre,.run-error pre { margin:0; padding:12px; overflow:auto; border:1px solid var(--border); border-radius:9px;
|
|
19938
|
+
color:var(--text-2); background:var(--surface-2); font-family:"SFMono-Regular",Consolas,monospace; font-size:11px; white-space:pre-wrap; overflow-wrap:anywhere; }
|
|
19939
|
+
.run-output pre { color:var(--text); font-family:inherit; font-size:13px; line-height:1.65; }
|
|
19940
|
+
.run-error pre { color:var(--red); border-color:color-mix(in srgb,var(--red) 28%,var(--border)); background:var(--red-soft); }
|
|
19941
|
+
.raw-log { padding-top:12px; border-top:1px solid var(--border); }
|
|
19942
|
+
.raw-log summary { color:var(--text-2); cursor:pointer; font-size:12px; font-weight:650; }
|
|
19943
|
+
.raw-log .log-box { margin-top:10px; border-radius:9px; }
|
|
19733
19944
|
.log-box { max-height:360px; overflow:auto; padding:16px; color:var(--text-2); background:#0b1018; font-family:"SFMono-Regular",Consolas,monospace;
|
|
19734
19945
|
font-size:12px; white-space:pre-wrap; overflow-wrap:anywhere; }
|
|
19735
19946
|
:root[data-theme="light"] .log-box { color:#dbe5f3; }
|
|
@@ -19748,12 +19959,33 @@ var STYLES = `
|
|
|
19748
19959
|
.form-field-wide { grid-column:1 / -1; }
|
|
19749
19960
|
.form-field input,.form-field select,.form-field textarea { width:100%; min-height:39px; padding:8px 10px; border:1px solid var(--border); border-radius:9px;
|
|
19750
19961
|
outline:none; color:var(--text); background:var(--surface-2); font-weight:450; }
|
|
19962
|
+
.field-action { display:flex; align-items:stretch; gap:7px; }
|
|
19963
|
+
.field-action input { min-width:0; flex:1; }
|
|
19964
|
+
.field-action .btn { flex:0 0 auto; white-space:nowrap; }
|
|
19965
|
+
.model-selector { display:grid; grid-template-columns:minmax(120px,.75fr) minmax(0,1.25fr); gap:7px; }
|
|
19966
|
+
.duration-picker { display:grid; gap:7px; }
|
|
19967
|
+
.duration-control { display:grid; grid-template-columns:minmax(0,1fr) 112px; gap:7px; }
|
|
19968
|
+
.duration-control input,.duration-control select { min-width:0; }
|
|
19751
19969
|
.form-field textarea { resize:vertical; line-height:1.5; }
|
|
19752
19970
|
.form-field input:focus,.form-field select:focus,.form-field textarea:focus { border-color:var(--primary); box-shadow:var(--focus); background:var(--surface); }
|
|
19753
19971
|
.form-field small { color:var(--text-3); font-size:10px; font-weight:450; }
|
|
19754
19972
|
.advanced-fields { margin-top:18px; padding-top:14px; border-top:1px solid var(--border); }
|
|
19755
19973
|
.advanced-fields summary { margin-bottom:14px; color:var(--text-2); cursor:pointer; font-size:12px; font-weight:700; }
|
|
19756
19974
|
.form-note { margin:16px 0 0; color:var(--text-3); font-size:11px; }
|
|
19975
|
+
.catalog-status[data-state="loading"] { color:var(--blue); }
|
|
19976
|
+
.catalog-status[data-state="ready"] { color:var(--green); }
|
|
19977
|
+
.catalog-status[data-state="error"] { color:var(--red); }
|
|
19978
|
+
.directory-dialog { width:min(720px,calc(100% - 32px)); }
|
|
19979
|
+
.directory-toolbar { display:flex; align-items:center; gap:8px; margin-bottom:12px; }
|
|
19980
|
+
.directory-path { min-width:0; flex:1; padding:9px 11px; overflow:hidden; border:1px solid var(--border); border-radius:9px;
|
|
19981
|
+
color:var(--text-2); background:var(--surface-2); font-family:"SFMono-Regular",Consolas,monospace; font-size:11px; text-overflow:ellipsis; white-space:nowrap; }
|
|
19982
|
+
.directory-list { min-height:260px; max-height:48vh; display:grid; align-content:start; gap:6px; overflow:auto; }
|
|
19983
|
+
.directory-item { width:100%; min-height:40px; display:flex; align-items:center; gap:9px; padding:0 11px; border:1px solid transparent; border-radius:9px;
|
|
19984
|
+
color:var(--text-2); background:transparent; cursor:pointer; text-align:left; transition:background-color .15s ease,border-color .15s ease,color .15s ease,transform .12s ease; }
|
|
19985
|
+
.directory-item:hover { color:var(--text); border-color:var(--border); background:var(--surface-2); }
|
|
19986
|
+
.directory-item:active { transform:scale(.99); }
|
|
19987
|
+
.directory-item .icon { width:17px; height:17px; color:var(--primary); flex:0 0 auto; }
|
|
19988
|
+
.directory-empty { min-height:220px; display:grid; place-items:center; color:var(--text-3); }
|
|
19757
19989
|
.json-view { min-height:160px; margin:0; padding:15px; overflow:auto; border:1px solid var(--border); border-radius:10px; color:var(--text-2);
|
|
19758
19990
|
background:var(--surface-2); font-size:12px; white-space:pre-wrap; overflow-wrap:anywhere; }
|
|
19759
19991
|
.confirm-copy { color:var(--text-2); margin:0; }
|
|
@@ -19822,6 +20054,9 @@ var STYLES = `
|
|
|
19822
20054
|
.project-grid { grid-template-columns:1fr; }
|
|
19823
20055
|
.template-form-grid { grid-template-columns:1fr; }
|
|
19824
20056
|
.form-field-wide { grid-column:auto; }
|
|
20057
|
+
.field-action { align-items:stretch; flex-direction:column; }
|
|
20058
|
+
.field-action .btn { width:100%; }
|
|
20059
|
+
.model-selector { grid-template-columns:1fr; }
|
|
19825
20060
|
}
|
|
19826
20061
|
@media (max-width:520px) {
|
|
19827
20062
|
.stats-grid,.stats-grid.three { grid-template-columns:1fr 1fr; }
|
|
@@ -19893,7 +20128,20 @@ function clientMessages(locale) {
|
|
|
19893
20128
|
"feedback.restartTimeout",
|
|
19894
20129
|
"template.createTitle",
|
|
19895
20130
|
"template.editTitle",
|
|
19896
|
-
"filter.noResults"
|
|
20131
|
+
"filter.noResults",
|
|
20132
|
+
"catalog.chooseProject",
|
|
20133
|
+
"catalog.defaultModel",
|
|
20134
|
+
"catalog.defaultProvider",
|
|
20135
|
+
"catalog.loading",
|
|
20136
|
+
"catalog.loaded",
|
|
20137
|
+
"catalog.failed",
|
|
20138
|
+
"catalog.primary",
|
|
20139
|
+
"catalog.subagent",
|
|
20140
|
+
"catalog.all",
|
|
20141
|
+
"directory.empty",
|
|
20142
|
+
"feedback.commandCopied",
|
|
20143
|
+
"action.showHidden",
|
|
20144
|
+
"action.hideHidden"
|
|
19897
20145
|
];
|
|
19898
20146
|
return Object.fromEntries(keys.map((key) => [key, t(locale, key)]));
|
|
19899
20147
|
}
|
|
@@ -19961,6 +20209,11 @@ function renderLayout(options) {
|
|
|
19961
20209
|
<footer>${t(locale, "app.footer")}</footer>
|
|
19962
20210
|
</div>
|
|
19963
20211
|
<div id="toast-region" class="toast-region" role="status" aria-live="polite"></div>
|
|
20212
|
+
<dialog id="directory-dialog" class="directory-dialog">
|
|
20213
|
+
<div class="dialog-head"><div><h2>${t(locale, "directory.title")}</h2><p>${t(locale, "directory.subtitle")}</p></div><button type="button" class="icon-button" onclick="document.getElementById('directory-dialog').close()" aria-label="${t(locale, "action.close")}">${icon("close")}</button></div>
|
|
20214
|
+
<div class="dialog-body"><div class="directory-toolbar"><button id="directory-home" type="button" class="btn">${t(locale, "action.home")}</button><button id="directory-up" type="button" class="btn">${t(locale, "action.up")}</button><button id="directory-hidden" type="button" class="btn">${t(locale, "action.showHidden")}</button><div id="directory-path" class="directory-path"></div></div><div id="directory-list" class="directory-list"></div></div>
|
|
20215
|
+
<div class="dialog-actions"><button type="button" class="btn" onclick="document.getElementById('directory-dialog').close()">${t(locale, "action.cancel")}</button><button id="directory-choose" type="button" class="btn btn-primary">${icon("folder")}${t(locale, "action.chooseThisFolder")}</button></div>
|
|
20216
|
+
</dialog>
|
|
19964
20217
|
<dialog id="detail-dialog">
|
|
19965
20218
|
<div class="dialog-head"><div><h2>${t(locale, "details.title")}</h2><p>${t(locale, "details.subtitle")}</p></div><button class="icon-button" onclick="document.getElementById('detail-dialog').close()" aria-label="${t(locale, "action.close")}">${icon("close")}</button></div>
|
|
19966
20219
|
<div class="dialog-body"><pre id="detail-content" class="json-view"></pre></div>
|
|
@@ -19999,21 +20252,38 @@ function renderLayout(options) {
|
|
|
19999
20252
|
const showDetail=id=>showRecord('/api/tasks/'+id);const showRunDetail=id=>showRecord('/api/runs/'+id);const showTemplateDetail=id=>showRecord('/api/templates/'+id);
|
|
20000
20253
|
async function copyDetails(){try{await navigator.clipboard.writeText(document.getElementById('detail-content').textContent);showToast(text('details.copySuccess'))}catch{showToast(text('feedback.copyFailed'),'error')}}
|
|
20001
20254
|
async function copySessionCommand(id){try{const data=await readJson(await fetch('/api/runs/'+id+'/session-command'));await navigator.clipboard.writeText(data.command);showToast(text('feedback.sessionCommandCopied'))}catch(error){showToast(error.message||text('feedback.copyFailed'),'error')}}
|
|
20255
|
+
async function copyRunCommand(id){try{await navigator.clipboard.writeText(document.getElementById('command-'+id).textContent);showToast(text('feedback.commandCopied'))}catch{showToast(text('feedback.copyFailed'),'error')}}
|
|
20002
20256
|
function taskField(name){return document.getElementById('task-'+name)}
|
|
20257
|
+
function templateField(name){return document.getElementById('template-'+name)}
|
|
20003
20258
|
function taskProjects(){const node=document.getElementById('task-project-data');if(!node)return {};try{return JSON.parse(node.textContent||'{}')}catch{return {}}}
|
|
20004
20259
|
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')}
|
|
20005
|
-
|
|
20006
|
-
|
|
20007
|
-
|
|
20008
|
-
function
|
|
20009
|
-
function
|
|
20260
|
+
const catalogTimers={};const catalogRequests={};const catalogModels={};
|
|
20261
|
+
function catalogField(prefix,name){return document.getElementById(prefix+'-'+name)}
|
|
20262
|
+
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='';}
|
|
20263
|
+
function appendCurrentOption(select,value){if(!value||[...select.options].some(option=>option.value===value))return;select.appendChild(new Option(value,value));}
|
|
20264
|
+
function modelProvider(value){const slash=value.indexOf('/');return slash>0?value.slice(0,slash):''}
|
|
20265
|
+
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}
|
|
20266
|
+
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}}}
|
|
20267
|
+
function scheduleCatalogLoad(prefix){clearTimeout(catalogTimers[prefix]);catalogTimers[prefix]=setTimeout(()=>loadCatalog(prefix),450)}
|
|
20268
|
+
let directoryTargetId='';let directoryCurrent='';let directoryEntries=[];let directoryShowHidden=false;
|
|
20269
|
+
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)}}
|
|
20270
|
+
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}}
|
|
20271
|
+
document.getElementById('directory-hidden').onclick=()=>{directoryShowHidden=!directoryShowHidden;renderDirectoryEntries()};
|
|
20272
|
+
async function openDirectoryPicker(targetId){const input=document.getElementById(targetId);if(!input||input.readOnly)return;directoryTargetId=targetId;directoryShowHidden=false;document.getElementById('directory-dialog').showModal();if(!await browseDirectory(input.value.trim()||''))await browseDirectory('')}
|
|
20273
|
+
document.getElementById('directory-choose').onclick=()=>{const input=document.getElementById(directoryTargetId);if(!input||!directoryCurrent)return;input.value=directoryCurrent;input.dispatchEvent(new Event('input',{bubbles:true}));document.getElementById('directory-dialog').close();const prefix=directoryTargetId.startsWith('task-')?'task':'template';loadCatalog(prefix)};
|
|
20274
|
+
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'}
|
|
20275
|
+
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}
|
|
20276
|
+
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)}
|
|
20277
|
+
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)}
|
|
20278
|
+
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')}}
|
|
20279
|
+
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}}
|
|
20010
20280
|
function localDateTime(milliseconds){const date=new Date(milliseconds);const local=new Date(milliseconds-date.getTimezoneOffset()*60000);return local.toISOString().slice(0,23)}
|
|
20011
|
-
function updateTemplateScheduleFields(){const type=templateField('schedule-type').value;const fields={cron:templateField('cron-field'),recurring:templateField('interval-field'),delayed:templateField('run-at-field')};for(const [name,node] of Object.entries(fields)){node.hidden=name!==type;node.querySelector('input').required=name===type}}
|
|
20281
|
+
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')}}
|
|
20012
20282
|
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}
|
|
20013
20283
|
function selectedRunAt(){const input=templateField('run-at');return resolveEditedRunAt(input.dataset.originalEpoch?Number(input.dataset.originalEpoch):null,input.dataset.originalLocal||'',input.value)}
|
|
20014
|
-
function openTemplateCreator(){const form=document.getElementById('template-form');form.reset();templateField('id').value='';templateField('dialog-title').textContent=text('template.createTitle');setOriginalRunAt(null);templateField('run-at').value=localDateTime(Date.now()+3600000);updateTemplateScheduleFields();document.getElementById('template-dialog').showModal();setTimeout(()=>templateField('name').focus(),50)}
|
|
20015
|
-
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('
|
|
20016
|
-
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:
|
|
20284
|
+
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)}
|
|
20285
|
+
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')}}
|
|
20286
|
+
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}}
|
|
20017
20287
|
async function enableTmpl(id){try{await readJson(await fetch('/api/templates/'+id+'/enable',{method:'POST'}));location.reload()}catch(error){showToast(error.message,'error')}}
|
|
20018
20288
|
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')}}
|
|
20019
20289
|
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')}}
|
|
@@ -20083,6 +20353,23 @@ function parsePositiveInteger(value) {
|
|
|
20083
20353
|
function parseTaskStatus(value) {
|
|
20084
20354
|
return TASK_STATUSES.has(value) ? value : null;
|
|
20085
20355
|
}
|
|
20356
|
+
function listChildDirectories(path) {
|
|
20357
|
+
return readdirSync(path, { withFileTypes: true }).flatMap((entry) => {
|
|
20358
|
+
const entryPath = join4(path, entry.name);
|
|
20359
|
+
if (entry.isDirectory()) return [{ name: entry.name, path: entryPath, hidden: entry.name.startsWith(".") }];
|
|
20360
|
+
if (!entry.isSymbolicLink()) return [];
|
|
20361
|
+
try {
|
|
20362
|
+
return statSync4(entryPath).isDirectory() ? [{ name: entry.name, path: entryPath, hidden: entry.name.startsWith(".") }] : [];
|
|
20363
|
+
} catch {
|
|
20364
|
+
return [];
|
|
20365
|
+
}
|
|
20366
|
+
}).sort((left, right) => {
|
|
20367
|
+
const leftHidden = left.name.startsWith(".");
|
|
20368
|
+
const rightHidden = right.name.startsWith(".");
|
|
20369
|
+
if (leftHidden !== rightHidden) return leftHidden ? 1 : -1;
|
|
20370
|
+
return left.name.localeCompare(right.name);
|
|
20371
|
+
});
|
|
20372
|
+
}
|
|
20086
20373
|
function parseTaskPayload(value) {
|
|
20087
20374
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
20088
20375
|
throw new Error("\u8BF7\u6C42\u5185\u5BB9\u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
@@ -20260,6 +20547,33 @@ app.get("/health", (c) => {
|
|
|
20260
20547
|
const health = getGatewayHealth();
|
|
20261
20548
|
return c.json(health, health.status === "ok" ? 200 : 503);
|
|
20262
20549
|
});
|
|
20550
|
+
app.get("/api/filesystem/directories", (c) => {
|
|
20551
|
+
const requestedPath = c.req.query("path")?.trim() || homedir4();
|
|
20552
|
+
try {
|
|
20553
|
+
validateTaskWorkingDirectory(requestedPath);
|
|
20554
|
+
return c.json({
|
|
20555
|
+
path: requestedPath,
|
|
20556
|
+
parent: dirname4(requestedPath),
|
|
20557
|
+
home: homedir4(),
|
|
20558
|
+
directories: listChildDirectories(requestedPath)
|
|
20559
|
+
});
|
|
20560
|
+
} catch (error) {
|
|
20561
|
+
return c.json({
|
|
20562
|
+
error: error instanceof Error ? error.message : String(error)
|
|
20563
|
+
}, 400);
|
|
20564
|
+
}
|
|
20565
|
+
});
|
|
20566
|
+
app.get("/api/opencode/catalog", async (c) => {
|
|
20567
|
+
const cwd = c.req.query("cwd")?.trim();
|
|
20568
|
+
if (!cwd) return c.json({ error: "cwd \u4E0D\u80FD\u4E3A\u7A7A" }, 400);
|
|
20569
|
+
try {
|
|
20570
|
+
return c.json(await loadOpenCodeCatalog(cwd));
|
|
20571
|
+
} catch (error) {
|
|
20572
|
+
return c.json({
|
|
20573
|
+
error: error instanceof Error ? error.message : String(error)
|
|
20574
|
+
}, 400);
|
|
20575
|
+
}
|
|
20576
|
+
});
|
|
20263
20577
|
function formatDuration(startAt, endAt) {
|
|
20264
20578
|
if (!startAt) return "\u2014";
|
|
20265
20579
|
const start = new Date(startAt).getTime();
|
|
@@ -20270,6 +20584,66 @@ function formatDuration(startAt, endAt) {
|
|
|
20270
20584
|
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
|
|
20271
20585
|
return `${Math.floor(seconds / 3600)}h ${Math.floor(seconds % 3600 / 60)}m`;
|
|
20272
20586
|
}
|
|
20587
|
+
function recordValue(value) {
|
|
20588
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
20589
|
+
}
|
|
20590
|
+
function shellQuote(value) {
|
|
20591
|
+
if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value)) return value;
|
|
20592
|
+
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
20593
|
+
}
|
|
20594
|
+
function presentRunLog(log) {
|
|
20595
|
+
let command = null;
|
|
20596
|
+
const textParts = [];
|
|
20597
|
+
const errors = [];
|
|
20598
|
+
const tools = [];
|
|
20599
|
+
for (const line of log.split("\n")) {
|
|
20600
|
+
const trimmed = line.trim();
|
|
20601
|
+
if (!trimmed) continue;
|
|
20602
|
+
let parsed = null;
|
|
20603
|
+
try {
|
|
20604
|
+
parsed = recordValue(JSON.parse(trimmed));
|
|
20605
|
+
} catch {
|
|
20606
|
+
errors.push(line);
|
|
20607
|
+
continue;
|
|
20608
|
+
}
|
|
20609
|
+
if (!parsed) continue;
|
|
20610
|
+
if (parsed.type === "supertask_command") {
|
|
20611
|
+
const executable = typeof parsed.executable === "string" ? parsed.executable : null;
|
|
20612
|
+
const cwd = typeof parsed.cwd === "string" ? parsed.cwd : null;
|
|
20613
|
+
const args = Array.isArray(parsed.args) && parsed.args.every((item) => typeof item === "string") ? parsed.args : null;
|
|
20614
|
+
if (executable && cwd && args) {
|
|
20615
|
+
command = {
|
|
20616
|
+
cwd,
|
|
20617
|
+
command: `cd ${shellQuote(cwd)} && ${[executable, ...args].map(shellQuote).join(" ")}`
|
|
20618
|
+
};
|
|
20619
|
+
}
|
|
20620
|
+
continue;
|
|
20621
|
+
}
|
|
20622
|
+
const part = recordValue(parsed.part);
|
|
20623
|
+
const eventType = typeof parsed.type === "string" ? parsed.type : "";
|
|
20624
|
+
const partType = typeof part?.type === "string" ? part.type : "";
|
|
20625
|
+
const text2 = typeof part?.text === "string" ? part.text : typeof parsed.text === "string" ? parsed.text : null;
|
|
20626
|
+
if (text2 && (eventType === "text" || partType === "text")) textParts.push(text2);
|
|
20627
|
+
const tool = typeof part?.tool === "string" ? part.tool : typeof parsed.tool === "string" ? parsed.tool : null;
|
|
20628
|
+
if (tool && (eventType === "tool_use" || partType === "tool")) tools.push(tool);
|
|
20629
|
+
const error = typeof parsed.error === "string" ? parsed.error : typeof part?.error === "string" ? part.error : null;
|
|
20630
|
+
if (error) errors.push(error);
|
|
20631
|
+
}
|
|
20632
|
+
return {
|
|
20633
|
+
command,
|
|
20634
|
+
text: textParts.join("\n").trim(),
|
|
20635
|
+
errors: [...new Set(errors)],
|
|
20636
|
+
tools
|
|
20637
|
+
};
|
|
20638
|
+
}
|
|
20639
|
+
function renderRunLog(runId, taskName, log, locale) {
|
|
20640
|
+
const presentation = presentRunLog(log);
|
|
20641
|
+
const command = presentation.command ? `<div class="run-command"><div class="log-section-head"><strong>${t(locale, "logs.command")}</strong><button type="button" class="btn" onclick="copyRunCommand(${runId})">${icon("copy")}${t(locale, "action.copyCommand")}</button></div><div class="command-cwd">${esc(presentation.command.cwd)}</div><pre id="command-${runId}">${esc(presentation.command.command)}</pre></div>` : "";
|
|
20642
|
+
const errors = presentation.errors.length > 0 ? `<div class="run-error"><strong>${t(locale, "logs.error")}</strong><pre>${esc(presentation.errors.join("\n"))}</pre></div>` : "";
|
|
20643
|
+
const tools = presentation.tools.length > 0 ? `<div class="run-tools"><strong>${t(locale, "logs.tools")}</strong><div class="actions">${presentation.tools.map((tool) => `<span class="tag">${esc(tool)}</span>`).join("")}</div></div>` : "";
|
|
20644
|
+
const output = `<div class="run-output"><strong>${t(locale, "logs.output")}</strong><pre>${esc(presentation.text || t(locale, "logs.noText"))}</pre></div>`;
|
|
20645
|
+
return `<section id="log-${runId}" class="panel log-panel" hidden><div class="panel-head"><h3>Run #${runId} \xB7 ${esc(taskName)}</h3></div><div class="log-content">${command}${errors}${output}${tools}<details class="raw-log"><summary>${t(locale, "logs.raw")}</summary><div class="log-box">${esc(log)}</div></details></div></section>`;
|
|
20646
|
+
}
|
|
20273
20647
|
function esc(value) {
|
|
20274
20648
|
if (!value) return "";
|
|
20275
20649
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
@@ -20301,6 +20675,25 @@ function emptyState(title, hint, code = "") {
|
|
|
20301
20675
|
return `<div class="empty-state"><div><div class="empty-icon">${icon("inbox")}</div>
|
|
20302
20676
|
<h3>${title}</h3><p>${hint}</p>${code ? `<code>${code}</code>` : ""}</div></div>`;
|
|
20303
20677
|
}
|
|
20678
|
+
function durationControl(locale, id, value, kind) {
|
|
20679
|
+
const units = [
|
|
20680
|
+
{ value: "s", label: t(locale, "duration.seconds") },
|
|
20681
|
+
{ value: "min", label: t(locale, "duration.minutes") },
|
|
20682
|
+
{ value: "h", label: t(locale, "duration.hours") },
|
|
20683
|
+
{ value: "d", label: t(locale, "duration.days") }
|
|
20684
|
+
];
|
|
20685
|
+
const values = kind === "interval" ? [3e5, 9e5, 18e5, 36e5, 216e5, 432e5, 864e5] : kind === "retry" ? [0, 1e4, 3e4, 6e4, 3e5] : [3e5, 9e5, 18e5, 36e5, 72e5, 144e5];
|
|
20686
|
+
const presets = [
|
|
20687
|
+
...kind === "timeout" ? [{ value: "", label: t(locale, "duration.systemDefault") }] : [],
|
|
20688
|
+
...values.map((milliseconds) => ({
|
|
20689
|
+
value: String(milliseconds),
|
|
20690
|
+
label: milliseconds === 0 ? t(locale, "duration.immediate") : kind === "interval" ? t(locale, "duration.every", { duration: formatInterval(milliseconds, locale) }) : formatInterval(milliseconds, locale)
|
|
20691
|
+
})),
|
|
20692
|
+
{ value: "custom", label: t(locale, "duration.custom") }
|
|
20693
|
+
];
|
|
20694
|
+
const selected = value === null ? "" : String(value);
|
|
20695
|
+
return `<div class="duration-picker"><select id="${id}-preset" onchange="updateDurationControl('${id}')">${presets.map((item) => `<option value="${item.value}" ${item.value === selected ? "selected" : ""}>${item.label}</option>`).join("")}</select><div id="${id}-custom" class="duration-control" hidden><input id="${id}-value" type="number" min="${kind === "retry" ? 0 : 0.1}" step="0.1" inputmode="decimal"><select id="${id}-unit" aria-label="${t(locale, "duration.unit")}">${units.map((item) => `<option value="${item.value}">${item.label}</option>`).join("")}</select></div></div>`;
|
|
20696
|
+
}
|
|
20304
20697
|
function formatInterval(milliseconds, locale) {
|
|
20305
20698
|
const formatter = new Intl.NumberFormat(locale === "en" ? "en-US" : "zh-CN", {
|
|
20306
20699
|
maximumFractionDigits: 1
|
|
@@ -20465,13 +20858,14 @@ app.get("/", async (c) => {
|
|
|
20465
20858
|
<div class="dialog-body">
|
|
20466
20859
|
<div class="template-form-grid">
|
|
20467
20860
|
<label class="form-field"><span>${t(locale, "template.name")}</span><input id="task-name" required maxlength="200" autocomplete="off"></label>
|
|
20468
|
-
<label class="form-field"><span>${t(locale, "template.cwd")}</span><input id="task-cwd" required autocomplete="off" list="task-cwd-options" placeholder="/path/to/project" oninput="updateTaskProjectStatus()"><small>${t(locale, "template.cwdHint")}</small></label>
|
|
20861
|
+
<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>
|
|
20469
20862
|
<datalist id="task-cwd-options">${projects.map((project) => `<option value="${esc(project.cwd)}"></option>`).join("")}</datalist>
|
|
20470
|
-
<label class="form-field"><span>${t(locale, "template.agent")}</span><
|
|
20471
|
-
<label class="form-field"><span>${t(locale, "template.model")}</span><
|
|
20863
|
+
<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>
|
|
20864
|
+
<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="populateModelOptions('task')"><option value="">${t(locale, "catalog.defaultProvider")}</option></select><select id="task-model" required aria-label="${t(locale, "catalog.model")}" disabled><option value="default">${t(locale, "catalog.defaultModel")}</option></select></div><small>${t(locale, "catalog.modelHint")}</small></label>
|
|
20472
20865
|
<label class="form-field form-field-wide"><span>${t(locale, "template.prompt")}</span><textarea id="task-prompt" rows="6" required></textarea></label>
|
|
20473
20866
|
</div>
|
|
20474
20867
|
<p id="task-project-status" class="form-note"></p>
|
|
20868
|
+
<p id="task-catalog-status" class="form-note catalog-status"></p>
|
|
20475
20869
|
<details class="advanced-fields">
|
|
20476
20870
|
<summary>${t(locale, "template.advanced")}</summary>
|
|
20477
20871
|
<div class="template-form-grid">
|
|
@@ -20480,8 +20874,8 @@ app.get("/", async (c) => {
|
|
|
20480
20874
|
<label class="form-field"><span>${t(locale, "template.importance")}</span><input id="task-importance" type="number" min="1" max="5" step="1" value="3" required></label>
|
|
20481
20875
|
<label class="form-field"><span>${t(locale, "template.urgency")}</span><input id="task-urgency" type="number" min="1" max="5" step="1" value="3" required></label>
|
|
20482
20876
|
<label class="form-field"><span>${t(locale, "template.maxRetries")}</span><input id="task-max-retries" type="number" min="0" max="1000" step="1" value="3" required></label>
|
|
20483
|
-
<label class="form-field"><span>${t(locale, "template.retryBackoff")}</span
|
|
20484
|
-
<label class="form-field"><span>${t(locale, "template.timeout")}</span
|
|
20877
|
+
<label class="form-field"><span>${t(locale, "template.retryBackoff")}</span>${durationControl(locale, "task-retry-backoff", 3e4, "retry")}<small>${t(locale, "template.retryBackoffHint")}</small></label>
|
|
20878
|
+
<label class="form-field"><span>${t(locale, "template.timeout")}</span>${durationControl(locale, "task-timeout", null, "timeout")}<small>${t(locale, "template.timeoutHint")}</small></label>
|
|
20485
20879
|
</div>
|
|
20486
20880
|
</details>
|
|
20487
20881
|
</div>
|
|
@@ -20541,15 +20935,16 @@ app.get("/templates", async (c) => {
|
|
|
20541
20935
|
<div class="dialog-body">
|
|
20542
20936
|
<div class="template-form-grid">
|
|
20543
20937
|
<label class="form-field"><span>${t(locale, "template.name")}</span><input id="template-name" required maxlength="200" autocomplete="off"></label>
|
|
20544
|
-
<label class="form-field"><span>${t(locale, "template.cwd")}</span><input id="template-cwd" required autocomplete="off" placeholder="/path/to/project"><small>${t(locale, "template.cwdHint")}</small></label>
|
|
20545
|
-
<label class="form-field"><span>${t(locale, "template.agent")}</span><
|
|
20546
|
-
<label class="form-field"><span>${t(locale, "template.model")}</span><
|
|
20938
|
+
<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>
|
|
20939
|
+
<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>
|
|
20940
|
+
<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="populateModelOptions('template')"><option value="">${t(locale, "catalog.defaultProvider")}</option></select><select id="template-model" required aria-label="${t(locale, "catalog.model")}" disabled><option value="default">${t(locale, "catalog.defaultModel")}</option></select></div><small>${t(locale, "catalog.modelHint")}</small></label>
|
|
20547
20941
|
<label class="form-field form-field-wide"><span>${t(locale, "template.prompt")}</span><textarea id="template-prompt" rows="6" required></textarea></label>
|
|
20548
20942
|
<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>
|
|
20549
20943
|
<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>
|
|
20550
|
-
<label id="template-interval-field" class="form-field"><span>${t(locale, "template.interval")}</span
|
|
20944
|
+
<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>
|
|
20551
20945
|
<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>
|
|
20552
20946
|
</div>
|
|
20947
|
+
<p id="template-catalog-status" class="form-note catalog-status"></p>
|
|
20553
20948
|
<details class="advanced-fields">
|
|
20554
20949
|
<summary>${t(locale, "template.advanced")}</summary>
|
|
20555
20950
|
<div class="template-form-grid">
|
|
@@ -20559,8 +20954,8 @@ app.get("/templates", async (c) => {
|
|
|
20559
20954
|
<label class="form-field"><span>${t(locale, "template.urgency")}</span><input id="template-urgency" type="number" min="1" max="5" step="1" value="3" required></label>
|
|
20560
20955
|
<label class="form-field"><span>${t(locale, "template.maxInstances")}</span><input id="template-max-instances" type="number" min="1" max="1000" step="1" value="1" required><small>${t(locale, "template.maxInstancesHint")}</small></label>
|
|
20561
20956
|
<label class="form-field"><span>${t(locale, "template.maxRetries")}</span><input id="template-max-retries" type="number" min="0" max="1000" step="1" value="3" required></label>
|
|
20562
|
-
<label class="form-field"><span>${t(locale, "template.retryBackoff")}</span
|
|
20563
|
-
<label class="form-field"><span>${t(locale, "template.timeout")}</span
|
|
20957
|
+
<label class="form-field"><span>${t(locale, "template.retryBackoff")}</span>${durationControl(locale, "template-retry-backoff", 3e4, "retry")}<small>${t(locale, "template.retryBackoffHint")}</small></label>
|
|
20958
|
+
<label class="form-field"><span>${t(locale, "template.timeout")}</span>${durationControl(locale, "template-timeout", null, "timeout")}<small>${t(locale, "template.timeoutHint")}</small></label>
|
|
20564
20959
|
</div>
|
|
20565
20960
|
</details>
|
|
20566
20961
|
<p class="form-note">${t(locale, "template.futureOnly")}</p>
|
|
@@ -20602,7 +20997,7 @@ app.get("/runs", async (c) => {
|
|
|
20602
20997
|
const status = safeStatus(run.status);
|
|
20603
20998
|
const resumable = isValidSessionId(run.sessionId);
|
|
20604
20999
|
if (run.log) {
|
|
20605
|
-
logs.push(
|
|
21000
|
+
logs.push(renderRunLog(run.id, run.taskName, run.log, locale));
|
|
20606
21001
|
}
|
|
20607
21002
|
return `<tr>
|
|
20608
21003
|
<td class="faint" data-label="${t(locale, "table.run")}">#${run.id}</td>
|
|
@@ -20929,6 +21324,7 @@ export {
|
|
|
20929
21324
|
dashboardApp,
|
|
20930
21325
|
web_default as default,
|
|
20931
21326
|
isSafeDashboardRestartTarget,
|
|
21327
|
+
presentRunLog,
|
|
20932
21328
|
resolveDashboardConfigState,
|
|
20933
21329
|
setDashboardRuntimeConfig
|
|
20934
21330
|
};
|