opencode-supertask 0.1.33 → 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/dist/cli/index.js CHANGED
@@ -4506,7 +4506,7 @@ var init_sql = __esm({
4506
4506
  return new SQL([new StringChunk(str)]);
4507
4507
  }
4508
4508
  sql2.raw = raw2;
4509
- function join7(chunks, separator) {
4509
+ function join8(chunks, separator) {
4510
4510
  const result = [];
4511
4511
  for (const [i, chunk] of chunks.entries()) {
4512
4512
  if (i > 0 && separator !== void 0) {
@@ -4516,7 +4516,7 @@ var init_sql = __esm({
4516
4516
  }
4517
4517
  return new SQL(result);
4518
4518
  }
4519
- sql2.join = join7;
4519
+ sql2.join = join8;
4520
4520
  function identifier(value) {
4521
4521
  return new Name(value);
4522
4522
  }
@@ -7132,7 +7132,7 @@ var init_select2 = __esm({
7132
7132
  return (table, on) => {
7133
7133
  const baseTableName = this.tableName;
7134
7134
  const tableName = getTableLikeName(table);
7135
- if (typeof tableName === "string" && this.config.joins?.some((join7) => join7.alias === tableName)) {
7135
+ if (typeof tableName === "string" && this.config.joins?.some((join8) => join8.alias === tableName)) {
7136
7136
  throw new Error(`Alias "${tableName}" is already used in this query`);
7137
7137
  }
7138
7138
  if (!this.isPartialSelect) {
@@ -7974,7 +7974,7 @@ var init_update = __esm({
7974
7974
  createJoin(joinType) {
7975
7975
  return (table, on) => {
7976
7976
  const tableName = getTableLikeName(table);
7977
- if (typeof tableName === "string" && this.config.joins.some((join7) => join7.alias === tableName)) {
7977
+ if (typeof tableName === "string" && this.config.joins.some((join8) => join8.alias === tableName)) {
7978
7978
  throw new Error(`Alias "${tableName}" is already used in this query`);
7979
7979
  }
7980
7980
  if (typeof on === "function") {
@@ -12696,8 +12696,8 @@ var require_luxon = __commonJS({
12696
12696
  * Set whether Luxon will throw when it encounters invalid DateTimes, Durations, or Intervals
12697
12697
  * @type {boolean}
12698
12698
  */
12699
- static set throwOnInvalid(t2) {
12700
- throwOnInvalid = t2;
12699
+ static set throwOnInvalid(t3) {
12700
+ throwOnInvalid = t3;
12701
12701
  }
12702
12702
  /**
12703
12703
  * Reset Luxon's global caches. Should only be necessary in testing scenarios.
@@ -13590,7 +13590,7 @@ var require_luxon = __commonJS({
13590
13590
  }, tokens = _Formatter.parseFormat(fmt), realTokens = tokens.reduce((found, {
13591
13591
  literal,
13592
13592
  val
13593
- }) => literal ? found : found.concat(val), []), collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter((t2) => t2)), durationInfo = {
13593
+ }) => literal ? found : found.concat(val), []), collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter((t3) => t3)), durationInfo = {
13594
13594
  isNegativeDuration: collapsed < 0,
13595
13595
  // this relies on "collapsed" being based on "shiftTo", which builds up the object
13596
13596
  // in order
@@ -15500,15 +15500,15 @@ var require_luxon = __commonJS({
15500
15500
  return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
15501
15501
  }
15502
15502
  function unitForToken(token, loc) {
15503
- const one = digitRegex(loc), two = digitRegex(loc, "{2}"), three = digitRegex(loc, "{3}"), four = digitRegex(loc, "{4}"), six = digitRegex(loc, "{6}"), oneOrTwo = digitRegex(loc, "{1,2}"), oneToThree = digitRegex(loc, "{1,3}"), oneToSix = digitRegex(loc, "{1,6}"), oneToNine = digitRegex(loc, "{1,9}"), twoToFour = digitRegex(loc, "{2,4}"), fourToSix = digitRegex(loc, "{4,6}"), literal = (t2) => ({
15504
- regex: RegExp(escapeToken(t2.val)),
15503
+ const one = digitRegex(loc), two = digitRegex(loc, "{2}"), three = digitRegex(loc, "{3}"), four = digitRegex(loc, "{4}"), six = digitRegex(loc, "{6}"), oneOrTwo = digitRegex(loc, "{1,2}"), oneToThree = digitRegex(loc, "{1,3}"), oneToSix = digitRegex(loc, "{1,6}"), oneToNine = digitRegex(loc, "{1,9}"), twoToFour = digitRegex(loc, "{2,4}"), fourToSix = digitRegex(loc, "{4,6}"), literal = (t3) => ({
15504
+ regex: RegExp(escapeToken(t3.val)),
15505
15505
  deser: ([s2]) => s2,
15506
15506
  literal: true
15507
- }), unitate = (t2) => {
15507
+ }), unitate = (t3) => {
15508
15508
  if (token.literal) {
15509
- return literal(t2);
15509
+ return literal(t3);
15510
15510
  }
15511
- switch (t2.val) {
15511
+ switch (t3.val) {
15512
15512
  // era
15513
15513
  case "G":
15514
15514
  return oneOf(loc.eras("short"), 0);
@@ -15623,7 +15623,7 @@ var require_luxon = __commonJS({
15623
15623
  case " ":
15624
15624
  return simple(/[^\S\n\r]/);
15625
15625
  default:
15626
- return literal(t2);
15626
+ return literal(t3);
15627
15627
  }
15628
15628
  };
15629
15629
  const unit = unitate(token) || {
@@ -15825,15 +15825,15 @@ var require_luxon = __commonJS({
15825
15825
  return tokens;
15826
15826
  }
15827
15827
  function expandMacroTokens(tokens, locale) {
15828
- return Array.prototype.concat(...tokens.map((t2) => maybeExpandMacroToken(t2, locale)));
15828
+ return Array.prototype.concat(...tokens.map((t3) => maybeExpandMacroToken(t3, locale)));
15829
15829
  }
15830
15830
  var TokenParser = class {
15831
15831
  constructor(locale, format) {
15832
15832
  this.locale = locale;
15833
15833
  this.format = format;
15834
15834
  this.tokens = expandMacroTokens(Formatter.parseFormat(format), locale);
15835
- this.units = this.tokens.map((t2) => unitForToken(t2, locale));
15836
- this.disqualifyingUnit = this.units.find((t2) => t2.invalidReason);
15835
+ this.units = this.tokens.map((t3) => unitForToken(t3, locale));
15836
+ this.disqualifyingUnit = this.units.find((t3) => t3.invalidReason);
15837
15837
  if (!this.disqualifyingUnit) {
15838
15838
  const [regexString, handlers] = buildRegex(this.units);
15839
15839
  this.regex = RegExp(regexString, "i");
@@ -16658,7 +16658,7 @@ var require_luxon = __commonJS({
16658
16658
  */
16659
16659
  static parseFormatForOpts(formatOpts, localeOpts = {}) {
16660
16660
  const tokenList = formatOptsToTokens(formatOpts, Locale.fromObject(localeOpts));
16661
- return !tokenList ? null : tokenList.map((t2) => t2 ? t2.val : null).join("");
16661
+ return !tokenList ? null : tokenList.map((t3) => t3 ? t3.val : null).join("");
16662
16662
  }
16663
16663
  /**
16664
16664
  * Produce the the fully expanded format token for the locale
@@ -16669,7 +16669,7 @@ var require_luxon = __commonJS({
16669
16669
  */
16670
16670
  static expandFormat(fmt, localeOpts = {}) {
16671
16671
  const expanded = expandMacroTokens(Formatter.parseFormat(fmt), Locale.fromObject(localeOpts));
16672
- return expanded.map((t2) => t2.val).join("");
16672
+ return expanded.map((t3) => t3.val).join("");
16673
16673
  }
16674
16674
  static resetCache() {
16675
16675
  zoneOffsetTs = void 0;
@@ -19526,10 +19526,10 @@ var require_random = __commonJS({
19526
19526
  }
19527
19527
  function mulberry32(seed) {
19528
19528
  return () => {
19529
- let t2 = seed += 1831565813;
19530
- t2 = Math.imul(t2 ^ t2 >>> 15, t2 | 1);
19531
- t2 ^= t2 + Math.imul(t2 ^ t2 >>> 7, t2 | 61);
19532
- return ((t2 ^ t2 >>> 14) >>> 0) / 4294967296;
19529
+ let t3 = seed += 1831565813;
19530
+ t3 = Math.imul(t3 ^ t3 >>> 15, t3 | 1);
19531
+ t3 ^= t3 + Math.imul(t3 ^ t3 >>> 7, t3 | 61);
19532
+ return ((t3 ^ t3 >>> 14) >>> 0) / 4294967296;
19533
19533
  };
19534
19534
  }
19535
19535
  function seededRandom(str) {
@@ -21542,6 +21542,25 @@ function currentGatewayRuntime(gatewayEntry = resolveGatewayEntry()) {
21542
21542
  env: { ...process.env }
21543
21543
  };
21544
21544
  }
21545
+ function refreshGatewayExecutionEnvironment(runtime, gatewayEntry) {
21546
+ const env = { ...process.env };
21547
+ for (const key of /* @__PURE__ */ new Set([
21548
+ ...Object.keys(runtime.env).filter((name) => name.startsWith("SUPERTASK_")),
21549
+ ...Object.keys(env).filter((name) => name.startsWith("SUPERTASK_")),
21550
+ "HOME",
21551
+ "PATH",
21552
+ "PM2_HOME"
21553
+ ])) {
21554
+ const value = runtime.env[key];
21555
+ if (value === void 0) delete env[key];
21556
+ else env[key] = value;
21557
+ }
21558
+ return {
21559
+ ...runtime,
21560
+ gatewayEntry: resolve3(gatewayEntry),
21561
+ env
21562
+ };
21563
+ }
21545
21564
  function isGatewayRunning() {
21546
21565
  if (!isPm2Installed()) return false;
21547
21566
  const proc = pm2JsonList().find((item) => item.name === PROCESS_NAME);
@@ -21894,7 +21913,7 @@ function installUnlocked() {
21894
21913
  }
21895
21914
  const existing = pm2JsonList().find((item) => item.name === PROCESS_NAME);
21896
21915
  const oldRuntime = gatewayRuntimeFromProcess(existing);
21897
- const targetRuntime = oldRuntime ? { ...oldRuntime, gatewayEntry: resolve3(resolveGatewayEntry()) } : currentGatewayRuntime();
21916
+ const targetRuntime = oldRuntime ? refreshGatewayExecutionEnvironment(oldRuntime, resolveGatewayEntry()) : currentGatewayRuntime();
21898
21917
  const before = getRunningVersion(oldRuntime?.env ?? process.env, oldRuntime?.cwd);
21899
21918
  if (oldRuntime && !scopesMatch(currentScope(), runtimeScope(oldRuntime))) {
21900
21919
  throw new Error("[supertask] \u5F53\u524D CLI \u4E0E PM2 Gateway \u7684\u6570\u636E\u5E93/\u914D\u7F6E/OpenCode \u8FD0\u884C\u4F5C\u7528\u57DF\u4E0D\u4E00\u81F4\uFF0C\u5DF2\u62D2\u7EDD\u8986\u76D6");
@@ -22007,7 +22026,10 @@ function upgradeUnlocked(target) {
22007
22026
  const oldRuntime = gatewayRuntimeFromProcess(existing) ?? (process.env.SUPERTASK_GATEWAY_ENTRY ? currentGatewayRuntime(resolveGatewayEntry()) : null);
22008
22027
  const before = getRunningVersion(oldRuntime?.env ?? process.env, oldRuntime?.cwd);
22009
22028
  const currentVersion = target?.version ?? getPackageVersion();
22010
- const targetRuntime = oldRuntime ? { ...oldRuntime, gatewayEntry: resolve3(target?.gatewayEntry ?? oldRuntime.gatewayEntry) } : currentGatewayRuntime(target?.gatewayEntry ?? resolveGatewayEntry());
22029
+ const targetRuntime = oldRuntime ? refreshGatewayExecutionEnvironment(
22030
+ oldRuntime,
22031
+ target?.gatewayEntry ?? oldRuntime.gatewayEntry
22032
+ ) : currentGatewayRuntime(target?.gatewayEntry ?? resolveGatewayEntry());
22011
22033
  assertRuntimeCanControlPm2(oldRuntime, existing);
22012
22034
  gatewayKillTimeoutMs(targetRuntime);
22013
22035
  gatewayTerminationCommandTimeoutMs(targetRuntime);
@@ -22163,17 +22185,19 @@ var init_semver = __esm({
22163
22185
  // src/daemon/update.ts
22164
22186
  var update_exports = {};
22165
22187
  __export(update_exports, {
22188
+ getGlobalCliDiagnostic: () => getGlobalCliDiagnostic,
22166
22189
  getOpenCodePluginDiagnostic: () => getOpenCodePluginDiagnostic,
22167
22190
  installLatestPlugin: () => installLatestPlugin,
22168
22191
  installPluginVersion: () => installPluginVersion,
22169
22192
  resolveConfiguredPluginSpec: () => resolveConfiguredPluginSpec,
22170
22193
  resolveInstalledPlugin: () => resolveInstalledPlugin,
22171
- resolveInstalledPluginVersion: () => resolveInstalledPluginVersion
22194
+ resolveInstalledPluginVersion: () => resolveInstalledPluginVersion,
22195
+ updateGlobalCli: () => updateGlobalCli
22172
22196
  });
22173
22197
  import { spawnSync as spawnSync3 } from "child_process";
22174
- import { existsSync as existsSync6, readFileSync as readFileSync4, readdirSync } from "fs";
22198
+ import { existsSync as existsSync6, readFileSync as readFileSync4, readdirSync, realpathSync } from "fs";
22175
22199
  import { homedir as homedir4 } from "os";
22176
- import { join as join5 } from "path";
22200
+ import { dirname as dirname7, join as join5, resolve as resolve4 } from "path";
22177
22201
  function pluginAt(packageDir) {
22178
22202
  const packageJson = join5(packageDir, "package.json");
22179
22203
  const gatewayEntry = join5(packageDir, "dist/gateway/index.js");
@@ -22216,6 +22240,131 @@ function opencodeBin() {
22216
22240
  function npmBin() {
22217
22241
  return process.env.SUPERTASK_NPM_BIN ?? "npm";
22218
22242
  }
22243
+ function bunBin() {
22244
+ return process.env.SUPERTASK_BUN_BIN ?? "bun";
22245
+ }
22246
+ function resolveGlobalCliExecutable() {
22247
+ const override = process.env.SUPERTASK_CLI_BIN;
22248
+ if (override) return existsSync6(override) ? resolve4(override) : null;
22249
+ const result = spawnSync3(process.platform === "win32" ? "where" : "which", ["supertask"], {
22250
+ encoding: "utf8",
22251
+ env: process.env,
22252
+ timeout: 1e4
22253
+ });
22254
+ if (result.status !== 0) return null;
22255
+ const executable = `${result.stdout ?? ""}`.trim().split(/\r?\n/)[0];
22256
+ return executable && existsSync6(executable) ? resolve4(executable) : null;
22257
+ }
22258
+ function cliPackageDir(executable) {
22259
+ try {
22260
+ let directory = dirname7(realpathSync(executable));
22261
+ for (let depth = 0; depth < 6; depth += 1) {
22262
+ const packagePath = join5(directory, "package.json");
22263
+ if (existsSync6(packagePath)) {
22264
+ const pkg = JSON.parse(readFileSync4(packagePath, "utf8"));
22265
+ if (pkg.name === PACKAGE_NAME) return directory;
22266
+ }
22267
+ const parent = dirname7(directory);
22268
+ if (parent === directory) break;
22269
+ directory = parent;
22270
+ }
22271
+ } catch {
22272
+ }
22273
+ return null;
22274
+ }
22275
+ function packageVersion(packageDir) {
22276
+ try {
22277
+ const pkg = JSON.parse(readFileSync4(join5(packageDir, "package.json"), "utf8"));
22278
+ return pkg.name === PACKAGE_NAME && typeof pkg.version === "string" ? pkg.version : null;
22279
+ } catch {
22280
+ return null;
22281
+ }
22282
+ }
22283
+ function npmGlobalRoot() {
22284
+ const result = spawnSync3(npmBin(), ["root", "-g"], {
22285
+ encoding: "utf8",
22286
+ env: process.env,
22287
+ timeout: 3e4
22288
+ });
22289
+ if (result.status !== 0) return null;
22290
+ const root = `${result.stdout ?? ""}`.trim();
22291
+ if (!root) return null;
22292
+ try {
22293
+ return realpathSync(root);
22294
+ } catch {
22295
+ return resolve4(root);
22296
+ }
22297
+ }
22298
+ function globalCliPackageManager(packageDir) {
22299
+ const normalized = packageDir.replaceAll("\\", "/");
22300
+ if (normalized.includes("/install/global/node_modules/")) return "bun";
22301
+ const npmRoot = npmGlobalRoot();
22302
+ if (npmRoot && resolve4(packageDir) === join5(npmRoot, PACKAGE_NAME)) return "npm";
22303
+ return null;
22304
+ }
22305
+ function getGlobalCliDiagnostic() {
22306
+ const executable = resolveGlobalCliExecutable();
22307
+ if (!executable) {
22308
+ return {
22309
+ installed: false,
22310
+ executable: null,
22311
+ packageDir: null,
22312
+ version: null,
22313
+ packageManager: null
22314
+ };
22315
+ }
22316
+ const packageDir = cliPackageDir(executable);
22317
+ if (!packageDir) {
22318
+ return {
22319
+ installed: true,
22320
+ executable,
22321
+ packageDir: null,
22322
+ version: null,
22323
+ packageManager: null
22324
+ };
22325
+ }
22326
+ return {
22327
+ installed: true,
22328
+ executable,
22329
+ packageDir,
22330
+ version: packageVersion(packageDir),
22331
+ packageManager: globalCliPackageManager(packageDir)
22332
+ };
22333
+ }
22334
+ function updateGlobalCli(version2) {
22335
+ if (!isSemanticVersion(version2)) {
22336
+ throw new Error(`[supertask] \u5168\u5C40 CLI \u76EE\u6807\u7248\u672C\u65E0\u6548: ${version2}`);
22337
+ }
22338
+ const before = getGlobalCliDiagnostic();
22339
+ if (!before.installed) return { ...before, action: "not-installed" };
22340
+ if (before.version === version2) return { ...before, action: "already-current" };
22341
+ if (!before.packageManager) {
22342
+ throw new Error(
22343
+ `[supertask] \u65E0\u6CD5\u786E\u8BA4\u5168\u5C40 CLI \u7684\u5305\u7BA1\u7406\u5668\uFF08\u5F53\u524D ${before.version ?? "unknown"}\uFF0C\u76EE\u6807 ${version2}\uFF09\uFF1B\u8BF7\u6267\u884C npm install -g ${PACKAGE_NAME}@${version2} \u6216 bun add -g ${PACKAGE_NAME}@${version2}`
22344
+ );
22345
+ }
22346
+ const command = before.packageManager === "npm" ? npmBin() : bunBin();
22347
+ const args = before.packageManager === "npm" ? ["install", "-g", `${PACKAGE_NAME}@${version2}`] : ["add", "-g", `${PACKAGE_NAME}@${version2}`];
22348
+ const result = spawnSync3(command, args, {
22349
+ encoding: "utf8",
22350
+ env: process.env,
22351
+ timeout: 12e4
22352
+ });
22353
+ const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
22354
+ if (result.error) {
22355
+ throw new Error(`[supertask] \u5168\u5C40 CLI \u66F4\u65B0\u5931\u8D25: ${result.error.message}`);
22356
+ }
22357
+ if (result.status !== 0) {
22358
+ throw new Error(`[supertask] \u5168\u5C40 CLI \u66F4\u65B0\u5931\u8D25: ${output || `\u9000\u51FA\u7801 ${result.status}`}`);
22359
+ }
22360
+ const after = getGlobalCliDiagnostic();
22361
+ if (after.version !== version2) {
22362
+ throw new Error(
22363
+ `[supertask] \u5168\u5C40 CLI \u66F4\u65B0\u540E\u7248\u672C\u4E0D\u5339\u914D\uFF1A\u671F\u671B ${version2}\uFF0C\u5B9E\u9645 ${after.version ?? "unknown"}`
22364
+ );
22365
+ }
22366
+ return { ...after, action: "updated" };
22367
+ }
22219
22368
  function latestVersion() {
22220
22369
  const result = spawnSync3(npmBin(), [
22221
22370
  "view",
@@ -22515,8 +22664,16 @@ var init_health = __esm({
22515
22664
  import { spawn } from "child_process";
22516
22665
  import { fileURLToPath as fileURLToPath5 } from "url";
22517
22666
  import { existsSync as existsSync7 } from "fs";
22518
- import { dirname as dirname7, join as join6 } from "path";
22667
+ import { dirname as dirname8, join as join6 } from "path";
22519
22668
  import { randomUUID as randomUUID2 } from "crypto";
22669
+ function runCommandContext(executable, args, cwd) {
22670
+ return JSON.stringify({
22671
+ type: "supertask_command",
22672
+ executable,
22673
+ args,
22674
+ cwd
22675
+ });
22676
+ }
22520
22677
  function assertWorkerProcessIsolationSupported(platform = process.platform) {
22521
22678
  if (platform === "win32") {
22522
22679
  throw new Error(
@@ -22704,7 +22861,7 @@ var init_worker = __esm({
22704
22861
  }
22705
22862
  launcherEntry() {
22706
22863
  const extension = import.meta.url.endsWith(".ts") ? "ts" : "js";
22707
- const moduleDir = dirname7(fileURLToPath5(import.meta.url));
22864
+ const moduleDir = dirname8(fileURLToPath5(import.meta.url));
22708
22865
  const candidates = [
22709
22866
  join6(moduleDir, `launcher.${extension}`),
22710
22867
  join6(moduleDir, `../worker/launcher.${extension}`)
@@ -22738,6 +22895,7 @@ var init_worker = __esm({
22738
22895
  runId,
22739
22896
  launchIdentity,
22740
22897
  child,
22898
+ commandContext: runCommandContext(this.opencodeBin, args, task.cwd || process.cwd()),
22741
22899
  output: "",
22742
22900
  sessionId: null,
22743
22901
  timeoutTimer: null,
@@ -22773,8 +22931,8 @@ var init_worker = __esm({
22773
22931
  }
22774
22932
  });
22775
22933
  let spawnError = null;
22776
- const spawned = new Promise((resolve4, reject) => {
22777
- child.once("spawn", resolve4);
22934
+ const spawned = new Promise((resolve5, reject) => {
22935
+ child.once("spawn", resolve5);
22778
22936
  child.once("error", (error) => {
22779
22937
  spawnError = error;
22780
22938
  reject(error);
@@ -22822,10 +22980,10 @@ var init_worker = __esm({
22822
22980
  return;
22823
22981
  }
22824
22982
  try {
22825
- await new Promise((resolve4, reject) => {
22983
+ await new Promise((resolve5, reject) => {
22826
22984
  child.stdin.end("START\n", (error) => {
22827
22985
  if (error) reject(error);
22828
- else resolve4();
22986
+ else resolve5();
22829
22987
  });
22830
22988
  });
22831
22989
  } catch (error) {
@@ -22872,7 +23030,7 @@ var init_worker = __esm({
22872
23030
  );
22873
23031
  return;
22874
23032
  }
22875
- 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}` : ""}`;
23033
+ 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`;
22876
23034
  this.runDetached(
22877
23035
  this.settleEntry(entry, code, failure),
22878
23036
  "task settlement failed",
@@ -22902,7 +23060,7 @@ var init_worker = __esm({
22902
23060
  const termination = entry.termination;
22903
23061
  if (termination?.kind === "shutdown") return;
22904
23062
  if (termination?.kind === "cancel") {
22905
- const output2 = entry.output.trim();
23063
+ const output2 = this.outputWithCommand(entry);
22906
23064
  const log2 = `${termination.message}${output2 ? `
22907
23065
  ${output2}` : ""}`;
22908
23066
  await TaskRunService.fail(entry.runId, log2);
@@ -22916,7 +23074,7 @@ ${output2}` : ""}`;
22916
23074
  }
22917
23075
  const currentRun = await TaskRunService.getById(entry.runId);
22918
23076
  if (!currentRun || currentRun.status !== "running") return;
22919
- const output = entry.output.trim();
23077
+ const output = this.outputWithCommand(entry);
22920
23078
  const log = failure ? `${failure}${output ? `
22921
23079
  ${output}` : ""}` : output;
22922
23080
  if (code === 0 && !failure) {
@@ -23052,6 +23210,11 @@ ${output}` : ""}` : output;
23052
23210
  const batchId = normalizeTaskBatchId(task.batchId);
23053
23211
  if (batchId) this.activeBatchIds.delete(batchId);
23054
23212
  }
23213
+ outputWithCommand(entry) {
23214
+ const output = entry.output.trim();
23215
+ return `${entry.commandContext}${output ? `
23216
+ ${output}` : ""}`;
23217
+ }
23055
23218
  resolveModel(taskModel) {
23056
23219
  if (!taskModel || taskModel === "default") return null;
23057
23220
  return taskModel;
@@ -25828,6 +25991,116 @@ var init_dist = __esm({
25828
25991
  }
25829
25992
  });
25830
25993
 
25994
+ // src/core/opencode-catalog.ts
25995
+ import { spawn as spawn2 } from "child_process";
25996
+ function cleanOutput(value) {
25997
+ return value.replace(ANSI_PATTERN, "").replace(/\r/g, "");
25998
+ }
25999
+ function runOpenCode(executable, args, cwd, timeoutMs) {
26000
+ return new Promise((resolve5, reject) => {
26001
+ const child = spawn2(executable, args, {
26002
+ cwd,
26003
+ env: process.env,
26004
+ stdio: ["ignore", "pipe", "pipe"]
26005
+ });
26006
+ let stdout = "";
26007
+ let stderr = "";
26008
+ let failure = null;
26009
+ let finished = false;
26010
+ const append = (current, chunk) => {
26011
+ const next = current + chunk.toString();
26012
+ if (Buffer.byteLength(next) > MAX_OUTPUT_BYTES && failure === null) {
26013
+ failure = new Error(`OpenCode \u8F93\u51FA\u8D85\u8FC7 ${MAX_OUTPUT_BYTES} bytes`);
26014
+ child.kill("SIGTERM");
26015
+ }
26016
+ return next.slice(-MAX_OUTPUT_BYTES);
26017
+ };
26018
+ child.stdout?.on("data", (chunk) => {
26019
+ stdout = append(stdout, chunk);
26020
+ });
26021
+ child.stderr?.on("data", (chunk) => {
26022
+ stderr = append(stderr, chunk);
26023
+ });
26024
+ child.once("error", (error) => {
26025
+ failure ??= error;
26026
+ });
26027
+ const timer = setTimeout(() => {
26028
+ failure ??= new Error(`OpenCode \u547D\u4EE4\u8D85\u8FC7 ${timeoutMs}ms \u672A\u5B8C\u6210`);
26029
+ child.kill("SIGTERM");
26030
+ }, timeoutMs);
26031
+ child.once("close", (code) => {
26032
+ if (finished) return;
26033
+ finished = true;
26034
+ clearTimeout(timer);
26035
+ if (failure) {
26036
+ reject(failure);
26037
+ return;
26038
+ }
26039
+ if (code !== 0) {
26040
+ const detail = cleanOutput(stderr).trim() || `\u9000\u51FA\u7801 ${code ?? "null"}`;
26041
+ reject(new Error(`OpenCode ${args.join(" ")} \u5931\u8D25\uFF1A${detail}`));
26042
+ return;
26043
+ }
26044
+ resolve5(cleanOutput(stdout));
26045
+ });
26046
+ });
26047
+ }
26048
+ function parseOpenCodeModels(output) {
26049
+ return [...new Set(cleanOutput(output).split("\n").map((line) => line.trim()).filter((line) => /^[^\s/]+\/.+/.test(line)))].sort((left, right) => left.localeCompare(right));
26050
+ }
26051
+ function parseOpenCodeAgents(output) {
26052
+ const agents = /* @__PURE__ */ new Map();
26053
+ for (const line of cleanOutput(output).split("\n")) {
26054
+ const match2 = /^([^\s()]+) \((primary|subagent|all)\)$/.exec(line.trim());
26055
+ if (!match2) continue;
26056
+ const name = match2[1];
26057
+ const mode = match2[2];
26058
+ if (name === "supertask-runner") continue;
26059
+ agents.set(name, { name, mode });
26060
+ }
26061
+ const rank = { primary: 0, all: 1, subagent: 2 };
26062
+ return [...agents.values()].sort((left, right) => rank[left.mode] - rank[right.mode] || left.name.localeCompare(right.name));
26063
+ }
26064
+ async function loadOpenCodeCatalog(cwd, options = {}) {
26065
+ validateTaskWorkingDirectory(cwd);
26066
+ const executable = options.executable ?? process.env.SUPERTASK_OPENCODE_BIN ?? "opencode";
26067
+ const timeoutMs = options.timeoutMs ?? COMMAND_TIMEOUT_MS;
26068
+ const cacheKey = `${executable}\0${cwd}`;
26069
+ const cached = catalogCache.get(cacheKey);
26070
+ if (options.useCache !== false && cached && cached.expiresAt > Date.now()) {
26071
+ return cached.result;
26072
+ }
26073
+ const result = Promise.all([
26074
+ runOpenCode(executable, ["models"], cwd, timeoutMs),
26075
+ runOpenCode(executable, ["agent", "list"], cwd, timeoutMs)
26076
+ ]).then(([modelsOutput, agentsOutput]) => {
26077
+ const models = parseOpenCodeModels(modelsOutput);
26078
+ const agents = parseOpenCodeAgents(agentsOutput).filter((agent) => agent.mode !== "subagent");
26079
+ if (models.length === 0) throw new Error("OpenCode \u6CA1\u6709\u8FD4\u56DE\u53EF\u7528\u6A21\u578B");
26080
+ if (agents.length === 0) throw new Error("OpenCode \u6CA1\u6709\u8FD4\u56DE\u53EF\u76F4\u63A5\u8FD0\u884C\u7684\u4E3B Agent");
26081
+ return { cwd, models, agents };
26082
+ });
26083
+ if (options.useCache !== false) {
26084
+ catalogCache.set(cacheKey, { expiresAt: Date.now() + CATALOG_CACHE_MS, result });
26085
+ result.catch(() => {
26086
+ if (catalogCache.get(cacheKey)?.result === result) catalogCache.delete(cacheKey);
26087
+ });
26088
+ }
26089
+ return result;
26090
+ }
26091
+ var CATALOG_CACHE_MS, COMMAND_TIMEOUT_MS, MAX_OUTPUT_BYTES, ANSI_PATTERN, catalogCache;
26092
+ var init_opencode_catalog = __esm({
26093
+ "src/core/opencode-catalog.ts"() {
26094
+ "use strict";
26095
+ init_task_working_directory();
26096
+ CATALOG_CACHE_MS = 3e4;
26097
+ COMMAND_TIMEOUT_MS = 2e4;
26098
+ MAX_OUTPUT_BYTES = 4 * 1024 * 1024;
26099
+ ANSI_PATTERN = /\x1B(?:[@-_][0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\))/g;
26100
+ catalogCache = /* @__PURE__ */ new Map();
26101
+ }
26102
+ });
26103
+
25831
26104
  // src/web/ui.ts
25832
26105
  function t(locale, key, values = {}) {
25833
26106
  const template = (locale === "en" ? EN : ZH)[key];
@@ -25891,7 +26164,8 @@ function icon(name, className = "icon") {
25891
26164
  check: '<path d="m5 12 4 4L19 6"/>',
25892
26165
  alert: '<path d="M12 3 2.8 19h18.4L12 3Z"/><path d="M12 9v4M12 17h.01"/>',
25893
26166
  clock: '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>',
25894
- 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"/>'
26167
+ 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"/>',
26168
+ 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"/>'
25895
26169
  };
25896
26170
  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>`;
25897
26171
  }
@@ -25946,7 +26220,20 @@ function clientMessages(locale) {
25946
26220
  "feedback.restartTimeout",
25947
26221
  "template.createTitle",
25948
26222
  "template.editTitle",
25949
- "filter.noResults"
26223
+ "filter.noResults",
26224
+ "catalog.chooseProject",
26225
+ "catalog.defaultModel",
26226
+ "catalog.defaultProvider",
26227
+ "catalog.loading",
26228
+ "catalog.loaded",
26229
+ "catalog.failed",
26230
+ "catalog.primary",
26231
+ "catalog.subagent",
26232
+ "catalog.all",
26233
+ "directory.empty",
26234
+ "feedback.commandCopied",
26235
+ "action.showHidden",
26236
+ "action.hideHidden"
25950
26237
  ];
25951
26238
  return Object.fromEntries(keys.map((key) => [key, t(locale, key)]));
25952
26239
  }
@@ -26014,6 +26301,11 @@ function renderLayout(options) {
26014
26301
  <footer>${t(locale, "app.footer")}</footer>
26015
26302
  </div>
26016
26303
  <div id="toast-region" class="toast-region" role="status" aria-live="polite"></div>
26304
+ <dialog id="directory-dialog" class="directory-dialog">
26305
+ <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>
26306
+ <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>
26307
+ <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>
26308
+ </dialog>
26017
26309
  <dialog id="detail-dialog">
26018
26310
  <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>
26019
26311
  <div class="dialog-body"><pre id="detail-content" class="json-view"></pre></div>
@@ -26052,21 +26344,38 @@ function renderLayout(options) {
26052
26344
  const showDetail=id=>showRecord('/api/tasks/'+id);const showRunDetail=id=>showRecord('/api/runs/'+id);const showTemplateDetail=id=>showRecord('/api/templates/'+id);
26053
26345
  async function copyDetails(){try{await navigator.clipboard.writeText(document.getElementById('detail-content').textContent);showToast(text('details.copySuccess'))}catch{showToast(text('feedback.copyFailed'),'error')}}
26054
26346
  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')}}
26347
+ async function copyRunCommand(id){try{await navigator.clipboard.writeText(document.getElementById('command-'+id).textContent);showToast(text('feedback.commandCopied'))}catch{showToast(text('feedback.copyFailed'),'error')}}
26055
26348
  function taskField(name){return document.getElementById('task-'+name)}
26349
+ function templateField(name){return document.getElementById('template-'+name)}
26056
26350
  function taskProjects(){const node=document.getElementById('task-project-data');if(!node)return {};try{return JSON.parse(node.textContent||'{}')}catch{return {}}}
26057
26351
  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')}
26058
- function openTaskCreator(){const form=document.getElementById('task-form');form.reset();taskField('id').value='';taskField('cwd').readOnly=false;taskField('cwd').value=form.dataset.defaultCwd||'';taskField('dialog-title').textContent=text('task.createTitle');taskField('save').textContent=text('action.saveTask');updateTaskProjectStatus();document.getElementById('task-dialog').showModal();setTimeout(()=>taskField('name').focus(),50)}
26059
- async function openTaskEditor(id){try{const data=await readJson(await fetch('/api/tasks/'+id));taskField('id').value=String(id);taskField('name').value=data.name||'';taskField('cwd').value=data.cwd||'';taskField('cwd').readOnly=true;taskField('agent').value=data.agent||'';taskField('model').value=data.model||'default';taskField('prompt').value=data.prompt||'';taskField('category').value=data.category||'general';taskField('batch').value=data.batchId||'';taskField('importance').value=String(data.importance??3);taskField('urgency').value=String(data.urgency??3);taskField('max-retries').value=String(data.maxRetries??3);taskField('retry-backoff').value=durationInput(data.retryBackoffMs??30000);taskField('timeout').value=durationInput(data.timeoutMs);taskField('dialog-title').textContent=text('task.editTitle');taskField('save').textContent=text('action.updateTask');updateTaskProjectStatus();document.getElementById('task-dialog').showModal();setTimeout(()=>taskField('name').focus(),50)}catch(error){showToast(error.message,'error')}}
26060
- async function saveTask(event){event.preventDefault();const form=document.getElementById('task-form');if(!form.reportValidity())return;const id=taskField('id').value;const body={name:taskField('name').value,cwd:taskField('cwd').value,agent:taskField('agent').value,model:taskField('model').value,prompt:taskField('prompt').value,category:taskField('category').value,batchId:taskField('batch').value,importance:Number(taskField('importance').value),urgency:Number(taskField('urgency').value),maxRetries:Number(taskField('max-retries').value),retryBackoff:taskField('retry-backoff').value,timeout:taskField('timeout').value};const button=taskField('save');button.disabled=true;try{const data=await readJson(await fetch(id?'/api/tasks/'+id:'/api/tasks',{method:id?'PUT':'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}));showToast(text(id?'feedback.taskUpdated':'feedback.taskCreated',{id:data.task.id}));document.getElementById('task-dialog').close();setTimeout(()=>location.assign(id?location.href:'/?cwd='+encodeURIComponent(data.task.cwd||'')),450)}catch(error){showToast(error.message,'error')}finally{button.disabled=false}}
26061
- function templateField(name){return document.getElementById('template-'+name)}
26062
- function durationInput(milliseconds){if(milliseconds==null)return '';if(milliseconds===0)return '0';const units=[['d',86400000],['h',3600000],['min',60000],['s',1000],['ms',1]];for(const [unit,factor] of units){if(milliseconds%factor===0)return String(milliseconds/factor)+unit}return String(milliseconds)+'ms'}
26352
+ const catalogTimers={};const catalogRequests={};const catalogModels={};
26353
+ function catalogField(prefix,name){return document.getElementById(prefix+'-'+name)}
26354
+ 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='';}
26355
+ function appendCurrentOption(select,value){if(!value||[...select.options].some(option=>option.value===value))return;select.appendChild(new Option(value,value));}
26356
+ function modelProvider(value){const slash=value.indexOf('/');return slash>0?value.slice(0,slash):''}
26357
+ 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}
26358
+ 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}}}
26359
+ function scheduleCatalogLoad(prefix){clearTimeout(catalogTimers[prefix]);catalogTimers[prefix]=setTimeout(()=>loadCatalog(prefix),450)}
26360
+ let directoryTargetId='';let directoryCurrent='';let directoryEntries=[];let directoryShowHidden=false;
26361
+ 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)}}
26362
+ 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}}
26363
+ document.getElementById('directory-hidden').onclick=()=>{directoryShowHidden=!directoryShowHidden;renderDirectoryEntries()};
26364
+ 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('')}
26365
+ 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)};
26366
+ 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'}
26367
+ 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}
26368
+ 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)}
26369
+ 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)}
26370
+ 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')}}
26371
+ 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}}
26063
26372
  function localDateTime(milliseconds){const date=new Date(milliseconds);const local=new Date(milliseconds-date.getTimezoneOffset()*60000);return local.toISOString().slice(0,23)}
26064
- 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}}
26373
+ 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')}}
26065
26374
  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}
26066
26375
  function selectedRunAt(){const input=templateField('run-at');return resolveEditedRunAt(input.dataset.originalEpoch?Number(input.dataset.originalEpoch):null,input.dataset.originalLocal||'',input.value)}
26067
- 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)}
26068
- async function openTemplateEditor(id){try{const data=await readJson(await fetch('/api/templates/'+id));templateField('id').value=String(id);templateField('dialog-title').textContent=text('template.editTitle');templateField('name').value=data.name||'';templateField('cwd').value=data.cwd||'';templateField('agent').value=data.agent||'';templateField('model').value=data.model||'default';templateField('prompt').value=data.prompt||'';templateField('schedule-type').value=data.scheduleType;templateField('cron').value=data.cronExpr||'';templateField('interval').value=durationInput(data.intervalMs);setOriginalRunAt(data.runAt||null);if(!data.runAt)templateField('run-at').value=localDateTime(Date.now()+3600000);templateField('category').value=data.category||'general';templateField('batch').value=data.batchId||'';templateField('importance').value=String(data.importance??3);templateField('urgency').value=String(data.urgency??3);templateField('max-instances').value=String(data.maxInstances??1);templateField('max-retries').value=String(data.maxRetries??3);templateField('retry-backoff').value=durationInput(data.retryBackoffMs??30000);templateField('timeout').value=durationInput(data.timeoutMs);updateTemplateScheduleFields();document.getElementById('template-dialog').showModal();setTimeout(()=>templateField('name').focus(),50)}catch(error){showToast(error.message,'error')}}
26069
- async function saveTemplate(event){event.preventDefault();const form=document.getElementById('template-form');if(!form.reportValidity())return;const id=templateField('id').value;const type=templateField('schedule-type').value;const body={name:templateField('name').value,cwd:templateField('cwd').value,agent:templateField('agent').value,model:templateField('model').value,prompt:templateField('prompt').value,scheduleType:type,cronExpr:templateField('cron').value,interval:templateField('interval').value,runAt:type==='delayed'?selectedRunAt():null,category:templateField('category').value,batchId:templateField('batch').value,importance:Number(templateField('importance').value),urgency:Number(templateField('urgency').value),maxInstances:Number(templateField('max-instances').value),maxRetries:Number(templateField('max-retries').value),retryBackoff:templateField('retry-backoff').value,timeout:templateField('timeout').value};const button=templateField('save');button.disabled=true;try{await readJson(await fetch(id?'/api/templates/'+id:'/api/templates',{method:id?'PUT':'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}));showToast(text(id?'feedback.templateUpdated':'feedback.templateCreated'));document.getElementById('template-dialog').close();setTimeout(()=>location.assign(id?location.href:'/templates'),450)}catch(error){showToast(error.message,'error')}finally{button.disabled=false}}
26376
+ 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)}
26377
+ 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')}}
26378
+ 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}}
26070
26379
  async function enableTmpl(id){try{await readJson(await fetch('/api/templates/'+id+'/enable',{method:'POST'}));location.reload()}catch(error){showToast(error.message,'error')}}
26071
26380
  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')}}
26072
26381
  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')}}
@@ -26126,6 +26435,13 @@ var init_ui = __esm({
26126
26435
  "action.updateTask": "\u4FDD\u5B58\u4FEE\u6539",
26127
26436
  "action.createTemplate": "\u65B0\u5EFA\u5B9A\u65F6\u4EFB\u52A1",
26128
26437
  "action.saveTemplate": "\u4FDD\u5B58\u5B9A\u65F6\u4EFB\u52A1",
26438
+ "action.chooseFolder": "\u9009\u62E9\u6587\u4EF6\u5939",
26439
+ "action.chooseThisFolder": "\u9009\u62E9\u5F53\u524D\u6587\u4EF6\u5939",
26440
+ "action.home": "\u4E3B\u76EE\u5F55",
26441
+ "action.up": "\u4E0A\u4E00\u7EA7",
26442
+ "action.copyCommand": "\u590D\u5236\u547D\u4EE4",
26443
+ "action.showHidden": "\u663E\u793A\u9690\u85CF\u6587\u4EF6\u5939",
26444
+ "action.hideHidden": "\u9690\u85CF\u9690\u85CF\u6587\u4EF6\u5939",
26129
26445
  "status.pending": "\u5F85\u6267\u884C",
26130
26446
  "status.running": "\u8FD0\u884C\u4E2D",
26131
26447
  "status.done": "\u5DF2\u5B8C\u6210",
@@ -26192,6 +26508,15 @@ var init_ui = __esm({
26192
26508
  "schedule.hours": "{count} \u5C0F\u65F6",
26193
26509
  "schedule.days": "{count} \u5929",
26194
26510
  "schedule.overdue": "\u5DF2\u5230\u671F",
26511
+ "duration.unit": "\u65F6\u95F4\u5355\u4F4D",
26512
+ "duration.seconds": "\u79D2",
26513
+ "duration.minutes": "\u5206\u949F",
26514
+ "duration.hours": "\u5C0F\u65F6",
26515
+ "duration.days": "\u5929",
26516
+ "duration.systemDefault": "\u4F7F\u7528 Gateway \u9ED8\u8BA4\u8D85\u65F6",
26517
+ "duration.immediate": "\u7ACB\u5373\u91CD\u8BD5",
26518
+ "duration.custom": "\u81EA\u5B9A\u4E49\u2026",
26519
+ "duration.every": "\u6BCF {duration}",
26195
26520
  "system.worker": "\u4EFB\u52A1\u6267\u884C",
26196
26521
  "system.scheduler": "\u5B9A\u65F6\u4EFB\u52A1\u670D\u52A1",
26197
26522
  "system.watchdog": "\u8FD0\u884C\u76D1\u63A7",
@@ -26239,6 +26564,9 @@ var init_ui = __esm({
26239
26564
  "template.interval": "\u6267\u884C\u95F4\u9694",
26240
26565
  "template.runAt": "\u6267\u884C\u65F6\u95F4",
26241
26566
  "template.durationHint": "\u652F\u6301 30s\u30015min\u30011h\u30012d",
26567
+ "template.intervalHint": "\u76F4\u63A5\u9009\u62E9\u5E38\u7528\u9891\u7387\uFF1B\u53EA\u6709\u7279\u6B8A\u9700\u6C42\u624D\u9700\u8981\u81EA\u5B9A\u4E49\u3002",
26568
+ "template.retryBackoffHint": "\u4E00\u6B21\u5931\u8D25\u540E\uFF0C\u7B49\u5F85\u591A\u4E45\u518D\u91CD\u8BD5\u3002",
26569
+ "template.timeoutHint": "\u7559\u7A7A\u8868\u793A\u4F7F\u7528 Gateway \u7684\u9ED8\u8BA4\u4EFB\u52A1\u8D85\u65F6\u3002",
26242
26570
  "template.advanced": "\u66F4\u591A\u6267\u884C\u8BBE\u7F6E",
26243
26571
  "template.category": "\u5206\u7C7B",
26244
26572
  "template.batchId": "\u6279\u6B21 ID",
@@ -26263,6 +26591,28 @@ var init_ui = __esm({
26263
26591
  "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",
26264
26592
  "task.projectExisting": "\u6B64\u9879\u76EE\u73B0\u6709 {total} \u4E2A\u4EFB\u52A1\uFF1A\u8FD0\u884C {running}\uFF0C\u6392\u961F {pending}\uFF0C\u5F02\u5E38 {failed}\u3002",
26265
26593
  "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",
26594
+ "catalog.chooseProject": "\u8BF7\u5148\u9009\u62E9\u9879\u76EE\u76EE\u5F55",
26595
+ "catalog.defaultModel": "\u8DDF\u968F Agent / OpenCode \u9ED8\u8BA4\u6A21\u578B",
26596
+ "catalog.defaultProvider": "\u9ED8\u8BA4\u6A21\u578B",
26597
+ "catalog.provider": "\u6A21\u578B\u63D0\u4F9B\u5546",
26598
+ "catalog.model": "\u5177\u4F53\u6A21\u578B",
26599
+ "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",
26600
+ "catalog.agentHint": "\u6765\u81EA\u5F53\u524D\u9879\u76EE\u7684 opencode agent list\u3002",
26601
+ "catalog.loading": "\u6B63\u5728\u8BFB\u53D6\u6B64\u9879\u76EE\u53EF\u7528\u7684 Agent \u548C\u6A21\u578B\u2026",
26602
+ "catalog.loaded": "\u5DF2\u4ECE\u672C\u673A OpenCode \u8BFB\u53D6 {agents} \u4E2A Agent\u3001{models} \u4E2A\u6A21\u578B\u3002",
26603
+ "catalog.failed": "\u65E0\u6CD5\u8BFB\u53D6\u6B64\u9879\u76EE\u7684 OpenCode \u914D\u7F6E\uFF1A{error}",
26604
+ "catalog.primary": "\u4E3B Agent",
26605
+ "catalog.subagent": "\u5B50 Agent",
26606
+ "catalog.all": "\u901A\u7528 Agent",
26607
+ "directory.title": "\u9009\u62E9\u9879\u76EE\u76EE\u5F55",
26608
+ "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",
26609
+ "directory.empty": "\u8FD9\u4E2A\u6587\u4EF6\u5939\u4E2D\u6CA1\u6709\u5B50\u6587\u4EF6\u5939",
26610
+ "logs.command": "\u5B9E\u9645\u6267\u884C\u547D\u4EE4",
26611
+ "logs.output": "Agent \u8F93\u51FA",
26612
+ "logs.error": "\u5931\u8D25\u539F\u56E0",
26613
+ "logs.tools": "\u5DE5\u5177\u8C03\u7528",
26614
+ "logs.raw": "\u67E5\u770B\u539F\u59CB\u6267\u884C\u65E5\u5FD7",
26615
+ "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",
26266
26616
  "theme.label": "\u4E3B\u9898",
26267
26617
  "theme.system": "\u8DDF\u968F\u7CFB\u7EDF",
26268
26618
  "theme.light": "\u6D45\u8272",
@@ -26300,6 +26650,7 @@ var init_ui = __esm({
26300
26650
  "feedback.templateUpdated": "\u5B9A\u65F6\u4EFB\u52A1\u5DF2\u66F4\u65B0",
26301
26651
  "feedback.configSaved": "\u8BBE\u7F6E\u5DF2\u4FDD\u5B58",
26302
26652
  "feedback.sessionCommandCopied": "\u4F1A\u8BDD\u547D\u4EE4\u5DF2\u590D\u5236\uFF0C\u53EF\u76F4\u63A5\u7C98\u8D34\u5230\u7EC8\u7AEF\u8FD0\u884C",
26653
+ "feedback.commandCopied": "\u6267\u884C\u547D\u4EE4\u5DF2\u590D\u5236",
26303
26654
  "feedback.restarting": "Gateway \u6B63\u5728\u91CD\u542F\uFF0C\u8BF7\u7A0D\u5019\u2026",
26304
26655
  "feedback.restartTimeout": "Gateway \u5C1A\u672A\u6062\u590D\uFF0C\u8BF7\u7A0D\u540E\u5237\u65B0\u6216\u68C0\u67E5 PM2 \u72B6\u6001",
26305
26656
  "feedback.databaseCleared": "\u6570\u636E\u5E93\u5DF2\u6E05\u7A7A\uFF0C\u5907\u4EFD\u4F4D\u4E8E\uFF1A{path}",
@@ -26348,6 +26699,13 @@ var init_ui = __esm({
26348
26699
  "action.updateTask": "Save changes",
26349
26700
  "action.createTemplate": "New scheduled task",
26350
26701
  "action.saveTemplate": "Save scheduled task",
26702
+ "action.chooseFolder": "Choose folder",
26703
+ "action.chooseThisFolder": "Choose this folder",
26704
+ "action.home": "Home",
26705
+ "action.up": "Up",
26706
+ "action.copyCommand": "Copy command",
26707
+ "action.showHidden": "Show hidden folders",
26708
+ "action.hideHidden": "Hide hidden folders",
26351
26709
  "status.pending": "Pending",
26352
26710
  "status.running": "Running",
26353
26711
  "status.done": "Done",
@@ -26414,6 +26772,15 @@ var init_ui = __esm({
26414
26772
  "schedule.hours": "{count} hr",
26415
26773
  "schedule.days": "{count} days",
26416
26774
  "schedule.overdue": "Overdue",
26775
+ "duration.unit": "Time unit",
26776
+ "duration.seconds": "seconds",
26777
+ "duration.minutes": "minutes",
26778
+ "duration.hours": "hours",
26779
+ "duration.days": "days",
26780
+ "duration.systemDefault": "Use Gateway default timeout",
26781
+ "duration.immediate": "Retry immediately",
26782
+ "duration.custom": "Custom\u2026",
26783
+ "duration.every": "Every {duration}",
26417
26784
  "system.worker": "Task execution",
26418
26785
  "system.scheduler": "Scheduled task service",
26419
26786
  "system.watchdog": "Runtime monitor",
@@ -26461,6 +26828,9 @@ var init_ui = __esm({
26461
26828
  "template.interval": "Interval",
26462
26829
  "template.runAt": "Run at",
26463
26830
  "template.durationHint": "Supports 30s, 5min, 1h, 2d",
26831
+ "template.intervalHint": "Choose a common frequency directly; customize only when necessary.",
26832
+ "template.retryBackoffHint": "How long to wait after a failure before retrying.",
26833
+ "template.timeoutHint": "Leave blank to use the Gateway default task timeout.",
26464
26834
  "template.advanced": "More execution settings",
26465
26835
  "template.category": "Category",
26466
26836
  "template.batchId": "Batch ID",
@@ -26485,6 +26855,28 @@ var init_ui = __esm({
26485
26855
  "task.batchHint": "Tasks with the same non-empty batch ID run serially. Blank removes the batch constraint; global concurrency and dependencies still apply.",
26486
26856
  "task.projectExisting": "This project has {total} tasks: {running} running, {pending} queued, and {failed} with issues.",
26487
26857
  "task.projectNew": "This is a new project group. It appears in the project list after creation.",
26858
+ "catalog.chooseProject": "Choose a project directory first",
26859
+ "catalog.defaultModel": "Use the Agent / OpenCode default model",
26860
+ "catalog.defaultProvider": "Default model",
26861
+ "catalog.provider": "Model provider",
26862
+ "catalog.model": "Model",
26863
+ "catalog.modelHint": "Choose a provider, then a model returned by opencode models for this project. Default does not pass -m.",
26864
+ "catalog.agentHint": "Loaded from opencode agent list for this project.",
26865
+ "catalog.loading": "Loading Agents and models available to this project\u2026",
26866
+ "catalog.loaded": "Loaded {agents} Agents and {models} models from local OpenCode.",
26867
+ "catalog.failed": "Could not load this project\u2019s OpenCode configuration: {error}",
26868
+ "catalog.primary": "primary Agent",
26869
+ "catalog.subagent": "subagent",
26870
+ "catalog.all": "general Agent",
26871
+ "directory.title": "Choose project directory",
26872
+ "directory.subtitle": "OpenCode runs in this directory, and its project-specific Agents and models are loaded.",
26873
+ "directory.empty": "This folder has no subfolders",
26874
+ "logs.command": "Executed command",
26875
+ "logs.output": "Agent output",
26876
+ "logs.error": "Failure reason",
26877
+ "logs.tools": "Tool calls",
26878
+ "logs.raw": "View raw execution log",
26879
+ "logs.noText": "This run produced no displayable text. Inspect the raw log for details.",
26488
26880
  "theme.label": "Theme",
26489
26881
  "theme.system": "System",
26490
26882
  "theme.light": "Light",
@@ -26522,6 +26914,7 @@ var init_ui = __esm({
26522
26914
  "feedback.templateUpdated": "Scheduled task updated",
26523
26915
  "feedback.configSaved": "Settings saved",
26524
26916
  "feedback.sessionCommandCopied": "Session command copied. Paste it into a terminal to continue.",
26917
+ "feedback.commandCopied": "Execution command copied",
26525
26918
  "feedback.restarting": "Gateway is restarting\u2026",
26526
26919
  "feedback.restartTimeout": "Gateway has not recovered yet. Refresh later or check PM2 status.",
26527
26920
  "feedback.databaseCleared": "Database cleared. Backup: {path}",
@@ -26732,6 +27125,18 @@ var init_ui = __esm({
26732
27125
  .danger-card h2 .icon { width:17px; height:17px; }
26733
27126
  .danger-card p { max-width:800px; margin:0 0 14px; color:var(--text-2); font-size:12px; }
26734
27127
  .log-panel { margin:12px 0; animation:reveal .18s ease both; }
27128
+ .log-content { display:grid; gap:14px; padding:16px; }
27129
+ .log-section-head { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:7px; }
27130
+ .run-command,.run-output,.run-error,.run-tools { min-width:0; }
27131
+ .run-command strong,.run-output>strong,.run-error>strong,.run-tools>strong { display:block; margin-bottom:7px; font-size:12px; }
27132
+ .command-cwd { margin-bottom:6px; color:var(--text-3); font-family:"SFMono-Regular",Consolas,monospace; font-size:10px; overflow-wrap:anywhere; }
27133
+ .run-command pre,.run-output pre,.run-error pre { margin:0; padding:12px; overflow:auto; border:1px solid var(--border); border-radius:9px;
27134
+ color:var(--text-2); background:var(--surface-2); font-family:"SFMono-Regular",Consolas,monospace; font-size:11px; white-space:pre-wrap; overflow-wrap:anywhere; }
27135
+ .run-output pre { color:var(--text); font-family:inherit; font-size:13px; line-height:1.65; }
27136
+ .run-error pre { color:var(--red); border-color:color-mix(in srgb,var(--red) 28%,var(--border)); background:var(--red-soft); }
27137
+ .raw-log { padding-top:12px; border-top:1px solid var(--border); }
27138
+ .raw-log summary { color:var(--text-2); cursor:pointer; font-size:12px; font-weight:650; }
27139
+ .raw-log .log-box { margin-top:10px; border-radius:9px; }
26735
27140
  .log-box { max-height:360px; overflow:auto; padding:16px; color:var(--text-2); background:#0b1018; font-family:"SFMono-Regular",Consolas,monospace;
26736
27141
  font-size:12px; white-space:pre-wrap; overflow-wrap:anywhere; }
26737
27142
  :root[data-theme="light"] .log-box { color:#dbe5f3; }
@@ -26750,12 +27155,33 @@ var init_ui = __esm({
26750
27155
  .form-field-wide { grid-column:1 / -1; }
26751
27156
  .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;
26752
27157
  outline:none; color:var(--text); background:var(--surface-2); font-weight:450; }
27158
+ .field-action { display:flex; align-items:stretch; gap:7px; }
27159
+ .field-action input { min-width:0; flex:1; }
27160
+ .field-action .btn { flex:0 0 auto; white-space:nowrap; }
27161
+ .model-selector { display:grid; grid-template-columns:minmax(120px,.75fr) minmax(0,1.25fr); gap:7px; }
27162
+ .duration-picker { display:grid; gap:7px; }
27163
+ .duration-control { display:grid; grid-template-columns:minmax(0,1fr) 112px; gap:7px; }
27164
+ .duration-control input,.duration-control select { min-width:0; }
26753
27165
  .form-field textarea { resize:vertical; line-height:1.5; }
26754
27166
  .form-field input:focus,.form-field select:focus,.form-field textarea:focus { border-color:var(--primary); box-shadow:var(--focus); background:var(--surface); }
26755
27167
  .form-field small { color:var(--text-3); font-size:10px; font-weight:450; }
26756
27168
  .advanced-fields { margin-top:18px; padding-top:14px; border-top:1px solid var(--border); }
26757
27169
  .advanced-fields summary { margin-bottom:14px; color:var(--text-2); cursor:pointer; font-size:12px; font-weight:700; }
26758
27170
  .form-note { margin:16px 0 0; color:var(--text-3); font-size:11px; }
27171
+ .catalog-status[data-state="loading"] { color:var(--blue); }
27172
+ .catalog-status[data-state="ready"] { color:var(--green); }
27173
+ .catalog-status[data-state="error"] { color:var(--red); }
27174
+ .directory-dialog { width:min(720px,calc(100% - 32px)); }
27175
+ .directory-toolbar { display:flex; align-items:center; gap:8px; margin-bottom:12px; }
27176
+ .directory-path { min-width:0; flex:1; padding:9px 11px; overflow:hidden; border:1px solid var(--border); border-radius:9px;
27177
+ color:var(--text-2); background:var(--surface-2); font-family:"SFMono-Regular",Consolas,monospace; font-size:11px; text-overflow:ellipsis; white-space:nowrap; }
27178
+ .directory-list { min-height:260px; max-height:48vh; display:grid; align-content:start; gap:6px; overflow:auto; }
27179
+ .directory-item { width:100%; min-height:40px; display:flex; align-items:center; gap:9px; padding:0 11px; border:1px solid transparent; border-radius:9px;
27180
+ 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; }
27181
+ .directory-item:hover { color:var(--text); border-color:var(--border); background:var(--surface-2); }
27182
+ .directory-item:active { transform:scale(.99); }
27183
+ .directory-item .icon { width:17px; height:17px; color:var(--primary); flex:0 0 auto; }
27184
+ .directory-empty { min-height:220px; display:grid; place-items:center; color:var(--text-3); }
26759
27185
  .json-view { min-height:160px; margin:0; padding:15px; overflow:auto; border:1px solid var(--border); border-radius:10px; color:var(--text-2);
26760
27186
  background:var(--surface-2); font-size:12px; white-space:pre-wrap; overflow-wrap:anywhere; }
26761
27187
  .confirm-copy { color:var(--text-2); margin:0; }
@@ -26824,6 +27250,9 @@ var init_ui = __esm({
26824
27250
  .project-grid { grid-template-columns:1fr; }
26825
27251
  .template-form-grid { grid-template-columns:1fr; }
26826
27252
  .form-field-wide { grid-column:auto; }
27253
+ .field-action { align-items:stretch; flex-direction:column; }
27254
+ .field-action .btn { width:100%; }
27255
+ .model-selector { grid-template-columns:1fr; }
26827
27256
  }
26828
27257
  @media (max-width:520px) {
26829
27258
  .stats-grid,.stats-grid.three { grid-template-columns:1fr 1fr; }
@@ -26853,11 +27282,21 @@ __export(web_exports, {
26853
27282
  dashboardApp: () => dashboardApp,
26854
27283
  default: () => web_default,
26855
27284
  isSafeDashboardRestartTarget: () => isSafeDashboardRestartTarget,
27285
+ presentRunLog: () => presentRunLog,
26856
27286
  resolveDashboardConfigState: () => resolveDashboardConfigState,
26857
27287
  setDashboardRuntimeConfig: () => setDashboardRuntimeConfig
26858
27288
  });
26859
- import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync5, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
26860
- import { basename as basename3, dirname as dirname8 } from "path";
27289
+ import {
27290
+ existsSync as existsSync8,
27291
+ mkdirSync as mkdirSync5,
27292
+ readFileSync as readFileSync5,
27293
+ readdirSync as readdirSync2,
27294
+ renameSync as renameSync2,
27295
+ statSync as statSync4,
27296
+ writeFileSync as writeFileSync3
27297
+ } from "fs";
27298
+ import { homedir as homedir5 } from "os";
27299
+ import { basename as basename3, dirname as dirname9, join as join7 } from "path";
26861
27300
  function setDashboardRuntimeConfig(config) {
26862
27301
  runtimeConfig = config === null ? null : structuredClone(config);
26863
27302
  }
@@ -26898,6 +27337,23 @@ function parsePositiveInteger2(value) {
26898
27337
  function parseTaskStatus2(value) {
26899
27338
  return TASK_STATUSES2.has(value) ? value : null;
26900
27339
  }
27340
+ function listChildDirectories(path) {
27341
+ return readdirSync2(path, { withFileTypes: true }).flatMap((entry) => {
27342
+ const entryPath = join7(path, entry.name);
27343
+ if (entry.isDirectory()) return [{ name: entry.name, path: entryPath, hidden: entry.name.startsWith(".") }];
27344
+ if (!entry.isSymbolicLink()) return [];
27345
+ try {
27346
+ return statSync4(entryPath).isDirectory() ? [{ name: entry.name, path: entryPath, hidden: entry.name.startsWith(".") }] : [];
27347
+ } catch {
27348
+ return [];
27349
+ }
27350
+ }).sort((left, right) => {
27351
+ const leftHidden = left.name.startsWith(".");
27352
+ const rightHidden = right.name.startsWith(".");
27353
+ if (leftHidden !== rightHidden) return leftHidden ? 1 : -1;
27354
+ return left.name.localeCompare(right.name);
27355
+ });
27356
+ }
26901
27357
  function parseTaskPayload(value) {
26902
27358
  if (!value || typeof value !== "object" || Array.isArray(value)) {
26903
27359
  throw new Error("\u8BF7\u6C42\u5185\u5BB9\u5FC5\u987B\u662F\u5BF9\u8C61");
@@ -27054,6 +27510,66 @@ function formatDuration(startAt, endAt) {
27054
27510
  if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
27055
27511
  return `${Math.floor(seconds / 3600)}h ${Math.floor(seconds % 3600 / 60)}m`;
27056
27512
  }
27513
+ function recordValue(value) {
27514
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
27515
+ }
27516
+ function shellQuote(value) {
27517
+ if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value)) return value;
27518
+ return `'${value.replace(/'/g, `'"'"'`)}'`;
27519
+ }
27520
+ function presentRunLog(log) {
27521
+ let command = null;
27522
+ const textParts = [];
27523
+ const errors = [];
27524
+ const tools = [];
27525
+ for (const line of log.split("\n")) {
27526
+ const trimmed = line.trim();
27527
+ if (!trimmed) continue;
27528
+ let parsed = null;
27529
+ try {
27530
+ parsed = recordValue(JSON.parse(trimmed));
27531
+ } catch {
27532
+ errors.push(line);
27533
+ continue;
27534
+ }
27535
+ if (!parsed) continue;
27536
+ if (parsed.type === "supertask_command") {
27537
+ const executable = typeof parsed.executable === "string" ? parsed.executable : null;
27538
+ const cwd = typeof parsed.cwd === "string" ? parsed.cwd : null;
27539
+ const args = Array.isArray(parsed.args) && parsed.args.every((item) => typeof item === "string") ? parsed.args : null;
27540
+ if (executable && cwd && args) {
27541
+ command = {
27542
+ cwd,
27543
+ command: `cd ${shellQuote(cwd)} && ${[executable, ...args].map(shellQuote).join(" ")}`
27544
+ };
27545
+ }
27546
+ continue;
27547
+ }
27548
+ const part = recordValue(parsed.part);
27549
+ const eventType = typeof parsed.type === "string" ? parsed.type : "";
27550
+ const partType = typeof part?.type === "string" ? part.type : "";
27551
+ const text2 = typeof part?.text === "string" ? part.text : typeof parsed.text === "string" ? parsed.text : null;
27552
+ if (text2 && (eventType === "text" || partType === "text")) textParts.push(text2);
27553
+ const tool = typeof part?.tool === "string" ? part.tool : typeof parsed.tool === "string" ? parsed.tool : null;
27554
+ if (tool && (eventType === "tool_use" || partType === "tool")) tools.push(tool);
27555
+ const error = typeof parsed.error === "string" ? parsed.error : typeof part?.error === "string" ? part.error : null;
27556
+ if (error) errors.push(error);
27557
+ }
27558
+ return {
27559
+ command,
27560
+ text: textParts.join("\n").trim(),
27561
+ errors: [...new Set(errors)],
27562
+ tools
27563
+ };
27564
+ }
27565
+ function renderRunLog(runId, taskName, log, locale) {
27566
+ const presentation = presentRunLog(log);
27567
+ 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>` : "";
27568
+ const errors = presentation.errors.length > 0 ? `<div class="run-error"><strong>${t(locale, "logs.error")}</strong><pre>${esc(presentation.errors.join("\n"))}</pre></div>` : "";
27569
+ 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>` : "";
27570
+ const output = `<div class="run-output"><strong>${t(locale, "logs.output")}</strong><pre>${esc(presentation.text || t(locale, "logs.noText"))}</pre></div>`;
27571
+ 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>`;
27572
+ }
27057
27573
  function esc(value) {
27058
27574
  if (!value) return "";
27059
27575
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
@@ -27069,7 +27585,7 @@ function readCurrentConfig() {
27069
27585
  }
27070
27586
  function writeConfig(cfg) {
27071
27587
  const configPath = getConfigPath();
27072
- const dir = dirname8(configPath);
27588
+ const dir = dirname9(configPath);
27073
27589
  if (!existsSync8(dir)) mkdirSync5(dir, { recursive: true });
27074
27590
  const tempPath = `${configPath}.${process.pid}.tmp`;
27075
27591
  writeFileSync3(tempPath, JSON.stringify(cfg, null, 2) + "\n", { mode: 384 });
@@ -27085,6 +27601,25 @@ function emptyState(title, hint, code = "") {
27085
27601
  return `<div class="empty-state"><div><div class="empty-icon">${icon("inbox")}</div>
27086
27602
  <h3>${title}</h3><p>${hint}</p>${code ? `<code>${code}</code>` : ""}</div></div>`;
27087
27603
  }
27604
+ function durationControl(locale, id, value, kind) {
27605
+ const units = [
27606
+ { value: "s", label: t(locale, "duration.seconds") },
27607
+ { value: "min", label: t(locale, "duration.minutes") },
27608
+ { value: "h", label: t(locale, "duration.hours") },
27609
+ { value: "d", label: t(locale, "duration.days") }
27610
+ ];
27611
+ const values = kind === "interval" ? [3e5, 9e5, 18e5, 36e5, 216e5, 432e5, 864e5] : kind === "retry" ? [0, 1e4, 3e4, 6e4, 3e5] : [3e5, 9e5, 18e5, 36e5, 72e5, 144e5];
27612
+ const presets = [
27613
+ ...kind === "timeout" ? [{ value: "", label: t(locale, "duration.systemDefault") }] : [],
27614
+ ...values.map((milliseconds) => ({
27615
+ value: String(milliseconds),
27616
+ label: milliseconds === 0 ? t(locale, "duration.immediate") : kind === "interval" ? t(locale, "duration.every", { duration: formatInterval(milliseconds, locale) }) : formatInterval(milliseconds, locale)
27617
+ })),
27618
+ { value: "custom", label: t(locale, "duration.custom") }
27619
+ ];
27620
+ const selected = value === null ? "" : String(value);
27621
+ 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>`;
27622
+ }
27088
27623
  function formatInterval(milliseconds, locale) {
27089
27624
  const formatter = new Intl.NumberFormat(locale === "en" ? "en-US" : "zh-CN", {
27090
27625
  maximumFractionDigits: 1
@@ -27113,6 +27648,8 @@ var init_web = __esm({
27113
27648
  init_drizzle_orm();
27114
27649
  init_db2();
27115
27650
  init_duration();
27651
+ init_opencode_catalog();
27652
+ init_task_working_directory();
27116
27653
  init_database_maintenance_service();
27117
27654
  init_task_run_service();
27118
27655
  init_task_service();
@@ -27166,6 +27703,33 @@ var init_web = __esm({
27166
27703
  const health = getGatewayHealth();
27167
27704
  return c.json(health, health.status === "ok" ? 200 : 503);
27168
27705
  });
27706
+ app.get("/api/filesystem/directories", (c) => {
27707
+ const requestedPath = c.req.query("path")?.trim() || homedir5();
27708
+ try {
27709
+ validateTaskWorkingDirectory(requestedPath);
27710
+ return c.json({
27711
+ path: requestedPath,
27712
+ parent: dirname9(requestedPath),
27713
+ home: homedir5(),
27714
+ directories: listChildDirectories(requestedPath)
27715
+ });
27716
+ } catch (error) {
27717
+ return c.json({
27718
+ error: error instanceof Error ? error.message : String(error)
27719
+ }, 400);
27720
+ }
27721
+ });
27722
+ app.get("/api/opencode/catalog", async (c) => {
27723
+ const cwd = c.req.query("cwd")?.trim();
27724
+ if (!cwd) return c.json({ error: "cwd \u4E0D\u80FD\u4E3A\u7A7A" }, 400);
27725
+ try {
27726
+ return c.json(await loadOpenCodeCatalog(cwd));
27727
+ } catch (error) {
27728
+ return c.json({
27729
+ error: error instanceof Error ? error.message : String(error)
27730
+ }, 400);
27731
+ }
27732
+ });
27169
27733
  app.get("/", async (c) => {
27170
27734
  const locale = resolveLocale(c);
27171
27735
  const page = parsePositiveInteger2(c.req.query("page") || "1");
@@ -27310,13 +27874,14 @@ var init_web = __esm({
27310
27874
  <div class="dialog-body">
27311
27875
  <div class="template-form-grid">
27312
27876
  <label class="form-field"><span>${t(locale, "template.name")}</span><input id="task-name" required maxlength="200" autocomplete="off"></label>
27313
- <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>
27877
+ <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>
27314
27878
  <datalist id="task-cwd-options">${projects.map((project) => `<option value="${esc(project.cwd)}"></option>`).join("")}</datalist>
27315
- <label class="form-field"><span>${t(locale, "template.agent")}</span><input id="task-agent" required autocomplete="off" value="build" placeholder="build"></label>
27316
- <label class="form-field"><span>${t(locale, "template.model")}</span><input id="task-model" required autocomplete="off" value="default" placeholder="default"></label>
27879
+ <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>
27880
+ <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>
27317
27881
  <label class="form-field form-field-wide"><span>${t(locale, "template.prompt")}</span><textarea id="task-prompt" rows="6" required></textarea></label>
27318
27882
  </div>
27319
27883
  <p id="task-project-status" class="form-note"></p>
27884
+ <p id="task-catalog-status" class="form-note catalog-status"></p>
27320
27885
  <details class="advanced-fields">
27321
27886
  <summary>${t(locale, "template.advanced")}</summary>
27322
27887
  <div class="template-form-grid">
@@ -27325,8 +27890,8 @@ var init_web = __esm({
27325
27890
  <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>
27326
27891
  <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>
27327
27892
  <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>
27328
- <label class="form-field"><span>${t(locale, "template.retryBackoff")}</span><input id="task-retry-backoff" autocomplete="off" value="30s"><small>${t(locale, "template.durationHint")}</small></label>
27329
- <label class="form-field"><span>${t(locale, "template.timeout")}</span><input id="task-timeout" autocomplete="off" placeholder="30min"><small>${t(locale, "template.optional")}</small></label>
27893
+ <label class="form-field"><span>${t(locale, "template.retryBackoff")}</span>${durationControl(locale, "task-retry-backoff", 3e4, "retry")}<small>${t(locale, "template.retryBackoffHint")}</small></label>
27894
+ <label class="form-field"><span>${t(locale, "template.timeout")}</span>${durationControl(locale, "task-timeout", null, "timeout")}<small>${t(locale, "template.timeoutHint")}</small></label>
27330
27895
  </div>
27331
27896
  </details>
27332
27897
  </div>
@@ -27386,15 +27951,16 @@ var init_web = __esm({
27386
27951
  <div class="dialog-body">
27387
27952
  <div class="template-form-grid">
27388
27953
  <label class="form-field"><span>${t(locale, "template.name")}</span><input id="template-name" required maxlength="200" autocomplete="off"></label>
27389
- <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>
27390
- <label class="form-field"><span>${t(locale, "template.agent")}</span><input id="template-agent" required autocomplete="off" placeholder="build"></label>
27391
- <label class="form-field"><span>${t(locale, "template.model")}</span><input id="template-model" required autocomplete="off" value="default" placeholder="default"></label>
27954
+ <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>
27955
+ <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>
27956
+ <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>
27392
27957
  <label class="form-field form-field-wide"><span>${t(locale, "template.prompt")}</span><textarea id="template-prompt" rows="6" required></textarea></label>
27393
27958
  <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>
27394
27959
  <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>
27395
- <label id="template-interval-field" class="form-field"><span>${t(locale, "template.interval")}</span><input id="template-interval" autocomplete="off" value="1h" placeholder="30s / 5min / 1h"><small>${t(locale, "template.durationHint")}</small></label>
27960
+ <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>
27396
27961
  <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>
27397
27962
  </div>
27963
+ <p id="template-catalog-status" class="form-note catalog-status"></p>
27398
27964
  <details class="advanced-fields">
27399
27965
  <summary>${t(locale, "template.advanced")}</summary>
27400
27966
  <div class="template-form-grid">
@@ -27404,8 +27970,8 @@ var init_web = __esm({
27404
27970
  <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>
27405
27971
  <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>
27406
27972
  <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>
27407
- <label class="form-field"><span>${t(locale, "template.retryBackoff")}</span><input id="template-retry-backoff" autocomplete="off" value="30s"><small>${t(locale, "template.durationHint")}</small></label>
27408
- <label class="form-field"><span>${t(locale, "template.timeout")}</span><input id="template-timeout" autocomplete="off" placeholder="30min"><small>${t(locale, "template.optional")}</small></label>
27973
+ <label class="form-field"><span>${t(locale, "template.retryBackoff")}</span>${durationControl(locale, "template-retry-backoff", 3e4, "retry")}<small>${t(locale, "template.retryBackoffHint")}</small></label>
27974
+ <label class="form-field"><span>${t(locale, "template.timeout")}</span>${durationControl(locale, "template-timeout", null, "timeout")}<small>${t(locale, "template.timeoutHint")}</small></label>
27409
27975
  </div>
27410
27976
  </details>
27411
27977
  <p class="form-note">${t(locale, "template.futureOnly")}</p>
@@ -27447,7 +28013,7 @@ var init_web = __esm({
27447
28013
  const status = safeStatus(run.status);
27448
28014
  const resumable = isValidSessionId(run.sessionId);
27449
28015
  if (run.log) {
27450
- logs.push(`<section id="log-${run.id}" class="panel log-panel" hidden><div class="panel-head"><h3>Run #${run.id} \xB7 ${esc(run.taskName)}</h3></div><div class="log-box">${esc(run.log)}</div></section>`);
28016
+ logs.push(renderRunLog(run.id, run.taskName, run.log, locale));
27451
28017
  }
27452
28018
  return `<tr>
27453
28019
  <td class="faint" data-label="${t(locale, "table.run")}">#${run.id}</td>
@@ -28084,6 +28650,26 @@ init_pm2();
28084
28650
  init_config();
28085
28651
  import { spawnSync as spawnSync4 } from "child_process";
28086
28652
 
28653
+ // src/cli/i18n.ts
28654
+ function requestedLanguage(argv) {
28655
+ for (let index2 = 2; index2 < argv.length; index2 += 1) {
28656
+ const argument = argv[index2];
28657
+ if (argument === "--lang") return argv[index2 + 1];
28658
+ if (argument.startsWith("--lang=")) return argument.slice("--lang=".length);
28659
+ }
28660
+ return void 0;
28661
+ }
28662
+ function resolveCliLocale(argv = process.argv, env = process.env) {
28663
+ const requested = requestedLanguage(argv) ?? env.SUPERTASK_LANG ?? "auto";
28664
+ if (requested === "zh" || requested === "zh-CN") return "zh-CN";
28665
+ if (requested === "en") return "en";
28666
+ const systemLocale = env.LC_ALL || env.LC_MESSAGES || env.LANG || "";
28667
+ return /^zh(?:[_-]|$)/i.test(systemLocale) ? "zh-CN" : "en";
28668
+ }
28669
+ function cliText(locale, zh, en) {
28670
+ return locale === "zh-CN" ? zh : en;
28671
+ }
28672
+
28087
28673
  // src/cli/database-output.ts
28088
28674
  function useJson(options) {
28089
28675
  const isTTY = options.isTTY ?? process.stdout.isTTY === true;
@@ -28100,23 +28686,52 @@ function formatBytes(bytes) {
28100
28686
  const rendered = Number.isInteger(value) ? String(value) : value.toFixed(1);
28101
28687
  return `${rendered} ${units[unit]}`;
28102
28688
  }
28103
- function formatCounts(counts) {
28104
- return `\u4EFB\u52A1 ${counts.tasks} \xB7 \u6267\u884C\u8BB0\u5F55 ${counts.taskRuns} \xB7 \u8C03\u5EA6\u6A21\u677F ${counts.taskTemplates}`;
28105
- }
28106
- function formatGateway(gateway) {
28107
- if (gateway.restarted) return "\u5DF2\u81EA\u52A8\u505C\u6B62\u3001\u91CD\u542F\u5E76\u6062\u590D\u5C31\u7EEA";
28108
- if (gateway.keptStopped) return "\u5DF2\u81EA\u52A8\u505C\u6B62\uFF0C\u6309\u8981\u6C42\u4FDD\u6301\u505C\u6B62";
28109
- if (!gateway.wasRunning) return "\u65E0\u9700\u81EA\u52A8\u505C\u542F\uFF08\u672A\u53D1\u73B0\u5339\u914D\u5F53\u524D\u6570\u636E\u5E93\u7684 PM2 Gateway\uFF09";
28110
- return "\u539F Gateway \u672A\u6062\u590D\u8FD0\u884C";
28689
+ function formatCounts(counts, locale) {
28690
+ return cliText(
28691
+ locale,
28692
+ `\u4EFB\u52A1 ${counts.tasks} \xB7 \u6267\u884C\u8BB0\u5F55 ${counts.taskRuns} \xB7 \u5B9A\u65F6\u4EFB\u52A1\u6A21\u677F ${counts.taskTemplates}`,
28693
+ `tasks ${counts.tasks} \xB7 runs ${counts.taskRuns} \xB7 scheduled templates ${counts.taskTemplates}`
28694
+ );
28111
28695
  }
28112
- function formatCheck(result) {
28696
+ function formatGateway(gateway, locale) {
28697
+ if (gateway.restarted) return cliText(locale, "\u5DF2\u81EA\u52A8\u505C\u6B62\u3001\u91CD\u542F\u5E76\u6062\u590D\u5C31\u7EEA", "stopped, restarted, and ready");
28698
+ if (gateway.keptStopped) return cliText(locale, "\u5DF2\u81EA\u52A8\u505C\u6B62\uFF0C\u6309\u8981\u6C42\u4FDD\u6301\u505C\u6B62", "stopped and left stopped as requested");
28699
+ if (!gateway.wasRunning) {
28700
+ return cliText(
28701
+ locale,
28702
+ "\u65E0\u9700\u81EA\u52A8\u505C\u542F\uFF08\u672A\u53D1\u73B0\u5339\u914D\u5F53\u524D\u6570\u636E\u5E93\u7684 PM2 Gateway\uFF09",
28703
+ "no matching PM2 Gateway was running"
28704
+ );
28705
+ }
28706
+ return cliText(locale, "\u539F Gateway \u672A\u6062\u590D\u8FD0\u884C", "the previous Gateway was not restored");
28707
+ }
28708
+ function formatCheck(result, locale) {
28709
+ if (locale === "en") {
28710
+ const lines2 = [
28711
+ result.ok ? "\u2713 Database check passed" : "\u2717 Database check failed",
28712
+ "",
28713
+ `Database: ${result.path}`,
28714
+ `Size: ${formatBytes(result.sizeBytes)}`,
28715
+ `Journal mode: ${result.journalMode}`,
28716
+ `Counts: ${formatCounts(result.counts, locale)}`,
28717
+ `Running: tasks ${result.runningTasks} \xB7 runs ${result.runningRuns}`
28718
+ ];
28719
+ if (!result.ok) {
28720
+ lines2.push(
28721
+ `Integrity: ${result.integrityMessages.join("; ") || "no result"}`,
28722
+ `Foreign-key violations: ${result.foreignKeyViolations}`,
28723
+ `Missing tables: ${result.missingTables.join(", ") || "none"}`
28724
+ );
28725
+ }
28726
+ return lines2.join("\n");
28727
+ }
28113
28728
  const lines = [
28114
28729
  result.ok ? "\u2713 \u6570\u636E\u5E93\u68C0\u67E5\u901A\u8FC7" : "\u2717 \u6570\u636E\u5E93\u68C0\u67E5\u672A\u901A\u8FC7",
28115
28730
  "",
28116
28731
  `\u6570\u636E\u5E93\uFF1A${result.path}`,
28117
28732
  `\u5927\u5C0F\uFF1A${formatBytes(result.sizeBytes)}`,
28118
28733
  `\u65E5\u5FD7\u6A21\u5F0F\uFF1A${result.journalMode}`,
28119
- `\u6570\u636E\u7EDF\u8BA1\uFF1A${formatCounts(result.counts)}`,
28734
+ `\u6570\u636E\u7EDF\u8BA1\uFF1A${formatCounts(result.counts, locale)}`,
28120
28735
  `\u8FD0\u884C\u4E2D\uFF1A\u4EFB\u52A1 ${result.runningTasks} \xB7 \u6267\u884C\u8BB0\u5F55 ${result.runningRuns}`
28121
28736
  ];
28122
28737
  if (!result.ok) {
@@ -28128,49 +28743,81 @@ function formatCheck(result) {
28128
28743
  }
28129
28744
  return lines.join("\n");
28130
28745
  }
28131
- function formatHuman(operation, result) {
28746
+ function formatHuman(operation, result, locale) {
28132
28747
  switch (operation) {
28133
28748
  case "check":
28134
- return formatCheck(result);
28749
+ return formatCheck(result, locale);
28135
28750
  case "backup": {
28136
28751
  const backup = result;
28752
+ if (locale === "en") {
28753
+ return [
28754
+ "\u2713 Database backup completed",
28755
+ "",
28756
+ `Backup: ${backup.path}`,
28757
+ `Size: ${formatBytes(backup.sizeBytes)}`,
28758
+ `Counts: ${formatCounts(backup.check.counts, locale)}`,
28759
+ `Integrity: ${backup.check.ok ? "passed" : "failed"}`
28760
+ ].join("\n");
28761
+ }
28137
28762
  return [
28138
28763
  "\u2713 \u6570\u636E\u5E93\u5907\u4EFD\u5B8C\u6210",
28139
28764
  "",
28140
28765
  `\u5907\u4EFD\u6587\u4EF6\uFF1A${backup.path}`,
28141
28766
  `\u5927\u5C0F\uFF1A${formatBytes(backup.sizeBytes)}`,
28142
- `\u6570\u636E\u7EDF\u8BA1\uFF1A${formatCounts(backup.check.counts)}`,
28767
+ `\u6570\u636E\u7EDF\u8BA1\uFF1A${formatCounts(backup.check.counts, locale)}`,
28143
28768
  `\u6570\u636E\u5E93\u5B8C\u6574\u6027\uFF1A${backup.check.ok ? "\u901A\u8FC7" : "\u672A\u901A\u8FC7"}`
28144
28769
  ].join("\n");
28145
28770
  }
28146
28771
  case "clear": {
28147
28772
  const cleared = result;
28773
+ if (locale === "en") {
28774
+ return [
28775
+ "\u2713 Database safely cleared",
28776
+ "",
28777
+ `Deleted: ${formatCounts(cleared.deleted, locale)}`,
28778
+ `Safety backup: ${cleared.backupPath}`,
28779
+ `Gateway: ${formatGateway(cleared.gateway, locale)}`,
28780
+ `Integrity: ${cleared.check.ok ? "passed" : "failed"}`
28781
+ ].join("\n");
28782
+ }
28148
28783
  return [
28149
28784
  "\u2713 \u6570\u636E\u5E93\u5DF2\u5B89\u5168\u6E05\u7A7A",
28150
28785
  "",
28151
- `\u5DF2\u5220\u9664\uFF1A${formatCounts(cleared.deleted)}`,
28786
+ `\u5DF2\u5220\u9664\uFF1A${formatCounts(cleared.deleted, locale)}`,
28152
28787
  `\u5B89\u5168\u5907\u4EFD\uFF1A${cleared.backupPath}`,
28153
- `Gateway\uFF1A${formatGateway(cleared.gateway)}`,
28788
+ `Gateway\uFF1A${formatGateway(cleared.gateway, locale)}`,
28154
28789
  `\u6570\u636E\u5E93\u5B8C\u6574\u6027\uFF1A${cleared.check.ok ? "\u901A\u8FC7" : "\u672A\u901A\u8FC7"}`
28155
28790
  ].join("\n");
28156
28791
  }
28157
28792
  case "restore": {
28158
28793
  const restored = result;
28794
+ if (locale === "en") {
28795
+ return [
28796
+ "\u2713 Database restore completed",
28797
+ "",
28798
+ `Source: ${restored.sourcePath}`,
28799
+ `Pre-restore safety backup: ${restored.safetyBackupPath}`,
28800
+ `Recovered runtime state: tasks ${restored.recoveredRunningTasks} \xB7 runs ${restored.closedRunningRuns}`,
28801
+ `Counts: ${formatCounts(restored.check.counts, locale)}`,
28802
+ `Gateway: ${formatGateway(restored.gateway, locale)}`,
28803
+ `Integrity: ${restored.check.ok ? "passed" : "failed"}`
28804
+ ].join("\n");
28805
+ }
28159
28806
  return [
28160
28807
  "\u2713 \u6570\u636E\u5E93\u6062\u590D\u5B8C\u6210",
28161
28808
  "",
28162
28809
  `\u6062\u590D\u6765\u6E90\uFF1A${restored.sourcePath}`,
28163
28810
  `\u6062\u590D\u524D\u5B89\u5168\u5907\u4EFD\uFF1A${restored.safetyBackupPath}`,
28164
28811
  `\u8FD0\u884C\u6001\u6536\u655B\uFF1A\u4EFB\u52A1 ${restored.recoveredRunningTasks} \xB7 \u6267\u884C\u8BB0\u5F55 ${restored.closedRunningRuns}`,
28165
- `\u6570\u636E\u7EDF\u8BA1\uFF1A${formatCounts(restored.check.counts)}`,
28166
- `Gateway\uFF1A${formatGateway(restored.gateway)}`,
28812
+ `\u6570\u636E\u7EDF\u8BA1\uFF1A${formatCounts(restored.check.counts, locale)}`,
28813
+ `Gateway\uFF1A${formatGateway(restored.gateway, locale)}`,
28167
28814
  `\u6570\u636E\u5E93\u5B8C\u6574\u6027\uFF1A${restored.check.ok ? "\u901A\u8FC7" : "\u672A\u901A\u8FC7"}`
28168
28815
  ].join("\n");
28169
28816
  }
28170
28817
  }
28171
28818
  }
28172
28819
  function renderDatabaseResult(operation, result, options = {}) {
28173
- return useJson(options) ? JSON.stringify(result, null, 2) : formatHuman(operation, result);
28820
+ return useJson(options) ? JSON.stringify(result, null, 2) : formatHuman(operation, result, options.locale ?? "zh-CN");
28174
28821
  }
28175
28822
  function renderDatabaseError(error, options = {}) {
28176
28823
  const message = error instanceof Error ? error.message : String(error);
@@ -28214,6 +28861,8 @@ function parseTaskStatus(value) {
28214
28861
 
28215
28862
  // src/cli/index.ts
28216
28863
  init_update2();
28864
+ var cliLocale = resolveCliLocale();
28865
+ var t2 = (zh, en) => cliText(cliLocale, zh, en);
28217
28866
  async function withDb(fn, formatError = (error) => JSON.stringify({
28218
28867
  error: error instanceof Error ? error.message : String(error)
28219
28868
  })) {
@@ -28239,8 +28888,13 @@ function runDestructiveDatabaseMaintenance(keepStopped, operation) {
28239
28888
  };
28240
28889
  }
28241
28890
  var program2 = new Command();
28242
- program2.name("supertask").description("\u901A\u7528\u4EFB\u52A1\u7BA1\u7406\u7CFB\u7EDF - AI Agent \u4EFB\u52A1\u8C03\u5EA6\u5668").version(getPackageVersion());
28243
- program2.command("add").description("\u521B\u5EFA\u65B0\u4EFB\u52A1").requiredOption("-n, --name <name>", "\u4EFB\u52A1\u540D\u79F0").requiredOption("-a, --agent <agent>", "\u4E3B Agent \u540D\u79F0").requiredOption("-p, --prompt <prompt>", "\u63D0\u793A\u8BCD").option("-m, --model <model>", "\u6A21\u578B").option("-c, --category <category>", "\u5206\u7C7B", "general").option("-i, --importance <number>", "\u91CD\u8981\u7A0B\u5EA6 (1-5)", "3").option("-u, --urgency <number>", "\u7D27\u6025\u7A0B\u5EA6 (1-5)", "3").option("-b, --batch <batchId>", "\u6279\u6B21 ID").option("-d, --depends <taskId>", "\u4F9D\u8D56\u7684\u4EFB\u52A1 ID").option("--max-retries <number>", "\u9996\u6B21\u6267\u884C\u4E4B\u5916\u5141\u8BB8\u7684\u91CD\u8BD5\u6B21\u6570", "3").option("--retry-backoff <duration>", "\u91CD\u8BD5\u9000\u907F\u57FA\u7840\u95F4\u9694\uFF0C\u5982 30s / 5min", "30s").option("--timeout <duration>", "\u4EFB\u52A1\u786C\u8D85\u65F6\uFF0C\u5982 30min / 2h").option("-w, --cwd <path>", "(\u5DF2\u5E9F\u5F03) \u5DE5\u4F5C\u76EE\u5F55\u3002\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u8BB0\u5F55\u63D0\u4EA4\u4EFB\u52A1\u65F6\u7684\u5F53\u524D\u76EE\u5F55").action(async (options) => withDb(async () => {
28891
+ program2.name("supertask").description(t2("\u9762\u5411 OpenCode Agent \u7684\u6301\u4E45\u5316\u4EFB\u52A1\u961F\u5217\u4E0E\u5B9A\u65F6\u4EFB\u52A1\u7CFB\u7EDF", "Durable task queue and scheduler for OpenCode agents")).addOption(new Option("--lang <language>", t2("CLI \u8BED\u8A00\uFF1Aauto / zh-CN / en", "CLI language: auto / zh-CN / en")).choices(["auto", "zh-CN", "en"]).default("auto")).helpOption("-h, --help", t2("\u663E\u793A\u5E2E\u52A9", "display help for command")).addHelpCommand("help [command]", t2("\u663E\u793A\u547D\u4EE4\u5E2E\u52A9", "display help for command")).version(getPackageVersion(), "-V, --version", t2("\u663E\u793A\u7248\u672C\u53F7", "output the version number"));
28892
+ if (cliLocale === "zh-CN") {
28893
+ program2.configureOutput({
28894
+ writeOut: (value) => process.stdout.write(value.replace(/^Usage:/gm, "\u7528\u6CD5\uFF1A").replace(/^Options:/gm, "\u9009\u9879\uFF1A").replace(/^Commands:/gm, "\u547D\u4EE4\uFF1A").replace(/^Arguments:/gm, "\u53C2\u6570\uFF1A"))
28895
+ });
28896
+ }
28897
+ program2.command("add").description(t2("\u521B\u5EFA\u65B0\u4EFB\u52A1", "create a queued task")).requiredOption("-n, --name <name>", t2("\u4EFB\u52A1\u540D\u79F0", "task name")).requiredOption("-a, --agent <agent>", t2("\u4E3B Agent \u540D\u79F0", "primary agent name")).requiredOption("-p, --prompt <prompt>", t2("\u63D0\u793A\u8BCD", "prompt")).option("-m, --model <model>", t2("\u6A21\u578B", "model")).option("-c, --category <category>", t2("\u5206\u7C7B", "category"), "general").option("-i, --importance <number>", t2("\u91CD\u8981\u7A0B\u5EA6 (1-5)", "importance (1-5)"), "3").option("-u, --urgency <number>", t2("\u7D27\u6025\u7A0B\u5EA6 (1-5)", "urgency (1-5)"), "3").option("-b, --batch <batchId>", t2("\u6279\u6B21 ID", "batch ID")).option("-d, --depends <taskId>", t2("\u4F9D\u8D56\u7684\u4EFB\u52A1 ID", "dependency task ID")).option("--max-retries <number>", t2("\u9996\u6B21\u6267\u884C\u4E4B\u5916\u5141\u8BB8\u7684\u91CD\u8BD5\u6B21\u6570", "retries allowed after the first attempt"), "3").option("--retry-backoff <duration>", t2("\u91CD\u8BD5\u9000\u907F\u57FA\u7840\u95F4\u9694\uFF0C\u5982 30s / 5min", "retry backoff base, e.g. 30s / 5min"), "30s").option("--timeout <duration>", t2("\u4EFB\u52A1\u786C\u8D85\u65F6\uFF0C\u5982 30min / 2h", "hard timeout, e.g. 30min / 2h")).option("-w, --cwd <path>", t2("(\u5DF2\u5E9F\u5F03) \u7CFB\u7EDF\u4F1A\u81EA\u52A8\u8BB0\u5F55\u63D0\u4EA4\u65F6\u7684\u5F53\u524D\u76EE\u5F55", "(deprecated) the submission directory is recorded automatically")).action(async (options) => withDb(async () => {
28244
28898
  const submitCwd = process.cwd();
28245
28899
  const retryBackoffMs = parseDuration(options.retryBackoff);
28246
28900
  const timeoutMs = options.timeout ? parseDuration(options.timeout) : null;
@@ -28264,7 +28918,7 @@ program2.command("add").description("\u521B\u5EFA\u65B0\u4EFB\u52A1").requiredOp
28264
28918
  });
28265
28919
  console.log(JSON.stringify({ id: task.id, status: "created" }, null, 2));
28266
28920
  }));
28267
- program2.command("edit").description("\u4FEE\u6539\u5F53\u524D\u9879\u76EE\u4E2D\u5C1A\u672A\u5B8C\u6210\u7684\u4EFB\u52A1").requiredOption("--id <id>", "\u4EFB\u52A1 ID").option("-n, --name <name>", "\u4EFB\u52A1\u540D\u79F0").option("-a, --agent <agent>", "Agent \u540D\u79F0").option("-m, --model <model>", "\u6A21\u578B").option("-p, --prompt <prompt>", "\u63D0\u793A\u8BCD").option("-c, --category <category>", "\u5206\u7C7B").option("-i, --importance <number>", "\u91CD\u8981\u7A0B\u5EA6 (1-5)").option("-u, --urgency <number>", "\u7D27\u6025\u7A0B\u5EA6 (1-5)").option("-b, --batch <batchId>", "\u6279\u6B21 ID").option("--clear-batch", "\u6E05\u7A7A\u6279\u6B21 ID").option("--max-retries <number>", "\u9996\u6B21\u6267\u884C\u4E4B\u5916\u5141\u8BB8\u7684\u91CD\u8BD5\u6B21\u6570").option("--retry-backoff <duration>", "\u91CD\u8BD5\u9000\u907F\u57FA\u7840\u95F4\u9694\uFF0C\u5982 30s / 5min").option("--timeout <duration>", "\u4EFB\u52A1\u786C\u8D85\u65F6\uFF0C\u5982 30min / 2h").option("--clear-timeout", "\u6E05\u7A7A\u4EFB\u52A1\u7EA7\u8D85\u65F6\uFF0C\u6539\u7528 Gateway \u9ED8\u8BA4\u503C").action(async (options) => withDb(async () => {
28921
+ program2.command("edit").description(t2("\u4FEE\u6539\u5F53\u524D\u9879\u76EE\u4E2D\u5C1A\u672A\u5B8C\u6210\u7684\u4EFB\u52A1", "edit an unfinished task in the current project")).requiredOption("--id <id>", t2("\u4EFB\u52A1 ID", "task ID")).option("-n, --name <name>", t2("\u4EFB\u52A1\u540D\u79F0", "task name")).option("-a, --agent <agent>", t2("Agent \u540D\u79F0", "agent name")).option("-m, --model <model>", t2("\u6A21\u578B", "model")).option("-p, --prompt <prompt>", t2("\u63D0\u793A\u8BCD", "prompt")).option("-c, --category <category>", t2("\u5206\u7C7B", "category")).option("-i, --importance <number>", t2("\u91CD\u8981\u7A0B\u5EA6 (1-5)", "importance (1-5)")).option("-u, --urgency <number>", t2("\u7D27\u6025\u7A0B\u5EA6 (1-5)", "urgency (1-5)")).option("-b, --batch <batchId>", t2("\u6279\u6B21 ID", "batch ID")).option("--clear-batch", t2("\u6E05\u7A7A\u6279\u6B21 ID", "clear the batch ID")).option("--max-retries <number>", t2("\u9996\u6B21\u6267\u884C\u4E4B\u5916\u5141\u8BB8\u7684\u91CD\u8BD5\u6B21\u6570", "retries allowed after the first attempt")).option("--retry-backoff <duration>", t2("\u91CD\u8BD5\u9000\u907F\u57FA\u7840\u95F4\u9694\uFF0C\u5982 30s / 5min", "retry backoff base, e.g. 30s / 5min")).option("--timeout <duration>", t2("\u4EFB\u52A1\u786C\u8D85\u65F6\uFF0C\u5982 30min / 2h", "hard timeout, e.g. 30min / 2h")).option("--clear-timeout", t2("\u6E05\u7A7A\u4EFB\u52A1\u7EA7\u8D85\u65F6\uFF0C\u6539\u7528 Gateway \u9ED8\u8BA4\u503C", "clear the task timeout and use the Gateway default")).action(async (options) => withDb(async () => {
28268
28922
  if (options.batch !== void 0 && options.clearBatch) {
28269
28923
  throw new Error("batch \u548C clear-batch \u4E0D\u80FD\u540C\u65F6\u4F7F\u7528");
28270
28924
  }
@@ -28302,7 +28956,7 @@ program2.command("edit").description("\u4FEE\u6539\u5F53\u524D\u9879\u76EE\u4E2D
28302
28956
  if (!task) throw new Error(`\u4EFB\u52A1 #${id} \u4E0D\u5B58\u5728\u4E8E\u5F53\u524D\u9879\u76EE\uFF0C\u6216\u5176\u72B6\u6001\u4E0D\u5141\u8BB8\u7F16\u8F91`);
28303
28957
  console.log(JSON.stringify({ id: task.id, status: task.status, updated: true }, null, 2));
28304
28958
  }));
28305
- program2.command("next").description("\u83B7\u53D6\u4E0B\u4E00\u4E2A\u5F85\u6267\u884C\u7684\u4EFB\u52A1").action(async () => withDb(async () => {
28959
+ program2.command("next").description(t2("\u83B7\u53D6\u4E0B\u4E00\u4E2A\u5F85\u6267\u884C\u7684\u4EFB\u52A1", "show the next runnable task")).action(async () => withDb(async () => {
28306
28960
  const task = await TaskService.next({ cwd: process.cwd() });
28307
28961
  if (task) {
28308
28962
  console.log(JSON.stringify({
@@ -28320,7 +28974,7 @@ program2.command("next").description("\u83B7\u53D6\u4E0B\u4E00\u4E2A\u5F85\u6267
28320
28974
  console.log(JSON.stringify({ id: null, message: "No executable tasks" }));
28321
28975
  }
28322
28976
  }));
28323
- program2.command("cancel").description("\u53D6\u6D88\u4EFB\u52A1").requiredOption("--id <id>", "\u4EFB\u52A1 ID").action(async (options) => withDb(async () => {
28977
+ program2.command("cancel").description(t2("\u53D6\u6D88\u4EFB\u52A1", "cancel a task")).requiredOption("--id <id>", t2("\u4EFB\u52A1 ID", "task ID")).action(async (options) => withDb(async () => {
28324
28978
  const task = await TaskService.cancel(parsePositiveInteger(options.id, "id"), { cwd: process.cwd() });
28325
28979
  if (task) {
28326
28980
  console.log(JSON.stringify({ id: task.id, status: task.status }));
@@ -28329,8 +28983,8 @@ program2.command("cancel").description("\u53D6\u6D88\u4EFB\u52A1").requiredOptio
28329
28983
  process.exit(1);
28330
28984
  }
28331
28985
  }));
28332
- var runCommand = new Command("run").description("\u7BA1\u7406\u9694\u79BB\u7684\u6267\u884C\u8BB0\u5F55");
28333
- runCommand.command("abandon").description("\u4EBA\u5DE5\u5173\u95ED\u5DF2\u786E\u8BA4\u4E0D\u5B58\u5728\u9057\u7559\u8FDB\u7A0B\u7684\u65E7\u7248\u65E0 PID \u9694\u79BB\u8BB0\u5F55").requiredOption("--id <id>", "\u6267\u884C\u8BB0\u5F55 run ID").option("--confirm <word>", "\u5371\u9669\u64CD\u4F5C\u786E\u8BA4\uFF0C\u5FC5\u987B\u586B\u5199 ABANDON").action(async (options) => withDb(async () => {
28986
+ var runCommand = new Command("run").description(t2("\u7BA1\u7406\u9694\u79BB\u7684\u6267\u884C\u8BB0\u5F55", "manage quarantined task runs"));
28987
+ runCommand.command("abandon").description(t2("\u4EBA\u5DE5\u5173\u95ED\u5DF2\u786E\u8BA4\u4E0D\u5B58\u5728\u9057\u7559\u8FDB\u7A0B\u7684\u65E7\u7248\u65E0 PID \u9694\u79BB\u8BB0\u5F55", "close a legacy no-PID quarantine after confirming no process remains")).requiredOption("--id <id>", t2("\u6267\u884C\u8BB0\u5F55 run ID", "run ID")).option("--confirm <word>", t2("\u5371\u9669\u64CD\u4F5C\u786E\u8BA4\uFF0C\u5FC5\u987B\u586B\u5199 ABANDON", "confirmation word; must be ABANDON")).action(async (options) => withDb(async () => {
28334
28988
  if (options.confirm !== "ABANDON") {
28335
28989
  throw new Error("\u5173\u95ED\u65E7\u7248\u9694\u79BB run \u5FC5\u987B\u663E\u5F0F\u4F20\u5165 --confirm ABANDON");
28336
28990
  }
@@ -28340,7 +28994,7 @@ runCommand.command("abandon").description("\u4EBA\u5DE5\u5173\u95ED\u5DF2\u786E\
28340
28994
  console.log(JSON.stringify(result, null, 2));
28341
28995
  }));
28342
28996
  program2.addCommand(runCommand);
28343
- program2.command("retry").description("\u91CD\u8BD5\u5931\u8D25\u7684\u4EFB\u52A1").option("--id <id>", "\u4EFB\u52A1 ID").option("-b, --batch <batchId>", "\u6279\u6B21 ID\uFF08\u6279\u91CF\u91CD\u8BD5\uFF09").action(async (options) => withDb(async () => {
28997
+ program2.command("retry").description(t2("\u91CD\u8BD5\u5931\u8D25\u7684\u4EFB\u52A1", "retry failed tasks")).option("--id <id>", t2("\u4EFB\u52A1 ID", "task ID")).option("-b, --batch <batchId>", t2("\u6279\u6B21 ID\uFF08\u6279\u91CF\u91CD\u8BD5\uFF09", "batch ID for bulk retry")).action(async (options) => withDb(async () => {
28344
28998
  if (options.id) {
28345
28999
  const task = await TaskService.retry(parsePositiveInteger(options.id, "id"), { cwd: process.cwd() });
28346
29000
  if (task) {
@@ -28357,11 +29011,11 @@ program2.command("retry").description("\u91CD\u8BD5\u5931\u8D25\u7684\u4EFB\u52A
28357
29011
  process.exit(1);
28358
29012
  }
28359
29013
  }));
28360
- program2.command("status").description("\u67E5\u770B\u4EFB\u52A1\u7EDF\u8BA1").option("-b, --batch <batchId>", "\u6309\u6279\u6B21\u7EDF\u8BA1").action(async (options) => withDb(async () => {
29014
+ program2.command("status").description(t2("\u67E5\u770B\u4EFB\u52A1\u7EDF\u8BA1", "show task counts")).option("-b, --batch <batchId>", t2("\u6309\u6279\u6B21\u7EDF\u8BA1", "filter counts by batch")).action(async (options) => withDb(async () => {
28361
29015
  const stats = await TaskService.stats({ batchId: options.batch, cwd: process.cwd() });
28362
29016
  console.log(JSON.stringify(stats, null, 2));
28363
29017
  }));
28364
- program2.command("list").description("\u5217\u51FA\u4EFB\u52A1").option("-s, --status <status>", "\u6309\u72B6\u6001\u7B5B\u9009").option("-b, --batch <batchId>", "\u6309\u6279\u6B21\u7B5B\u9009").option("-c, --category <category>", "\u6309\u5206\u7C7B\u7B5B\u9009").option("-l, --limit <number>", "\u9650\u5236\u6570\u91CF", "20").action(async (options) => withDb(async () => {
29018
+ program2.command("list").description(t2("\u5217\u51FA\u4EFB\u52A1", "list tasks")).option("-s, --status <status>", t2("\u6309\u72B6\u6001\u7B5B\u9009", "filter by status")).option("-b, --batch <batchId>", t2("\u6309\u6279\u6B21\u7B5B\u9009", "filter by batch")).option("-c, --category <category>", t2("\u6309\u5206\u7C7B\u7B5B\u9009", "filter by category")).option("-l, --limit <number>", t2("\u9650\u5236\u6570\u91CF", "maximum rows"), "20").action(async (options) => withDb(async () => {
28365
29019
  const tasks4 = await TaskService.list({
28366
29020
  status: parseTaskStatus(options.status),
28367
29021
  batchId: options.batch,
@@ -28371,7 +29025,7 @@ program2.command("list").description("\u5217\u51FA\u4EFB\u52A1").option("-s, --s
28371
29025
  });
28372
29026
  console.log(JSON.stringify(tasks4, null, 2));
28373
29027
  }));
28374
- program2.command("get").description("\u83B7\u53D6\u5355\u4E2A\u4EFB\u52A1\u8BE6\u60C5").requiredOption("--id <id>", "\u4EFB\u52A1 ID").action(async (options) => withDb(async () => {
29028
+ program2.command("get").description(t2("\u83B7\u53D6\u5355\u4E2A\u4EFB\u52A1\u8BE6\u60C5", "show one task")).requiredOption("--id <id>", t2("\u4EFB\u52A1 ID", "task ID")).action(async (options) => withDb(async () => {
28375
29029
  const task = await TaskService.getById(parsePositiveInteger(options.id, "id"), { cwd: process.cwd() });
28376
29030
  if (task) {
28377
29031
  console.log(JSON.stringify(task, null, 2));
@@ -28380,13 +29034,13 @@ program2.command("get").description("\u83B7\u53D6\u5355\u4E2A\u4EFB\u52A1\u8BE6\
28380
29034
  process.exit(1);
28381
29035
  }
28382
29036
  }));
28383
- program2.command("delete").description("\u5220\u9664\u4EFB\u52A1").requiredOption("--id <id>", "\u4EFB\u52A1 ID").action(async (options) => withDb(async () => {
29037
+ program2.command("delete").description(t2("\u5220\u9664\u4EFB\u52A1", "delete a task")).requiredOption("--id <id>", t2("\u4EFB\u52A1 ID", "task ID")).action(async (options) => withDb(async () => {
28384
29038
  const id = parsePositiveInteger(options.id, "id");
28385
29039
  const deleted = await TaskService.delete(id, { cwd: process.cwd() });
28386
29040
  console.log(JSON.stringify({ deleted, id }));
28387
29041
  }));
28388
- program2.command("template").description("\u7BA1\u7406\u4EFB\u52A1\u8C03\u5EA6\u6A21\u677F").addCommand(
28389
- new Command("add").description("\u521B\u5EFA\u8C03\u5EA6\u6A21\u677F").requiredOption("-n, --name <name>", "\u6A21\u677F\u540D\u79F0").requiredOption("-a, --agent <agent>", "Agent \u540D\u79F0").requiredOption("-p, --prompt <prompt>", "\u63D0\u793A\u8BCD").requiredOption("-t, --type <type>", "\u8C03\u5EA6\u7C7B\u578B\uFF1Acron/delayed/recurring").option("--cron <expr>", "cron \u8868\u8FBE\u5F0F\uFF08cron \u7C7B\u578B\u5FC5\u586B\uFF09").option("--delay <duration>", "\u5EF6\u8FDF\u65F6\u95F4\uFF08delayed \u7C7B\u578B\u5FC5\u586B\uFF09\uFF0C\u5982 30s / 5min / 1h / 2d").option("--interval <duration>", "\u5FAA\u73AF\u95F4\u9694\uFF08recurring \u7C7B\u578B\u5FC5\u586B\uFF09\uFF0C\u5982 1h / 30min / 5s").option("-m, --model <model>", "\u6A21\u578B").option("-c, --category <category>", "\u5206\u7C7B", "general").option("-i, --importance <number>", "\u91CD\u8981\u7A0B\u5EA6 1-5", "3").option("-u, --urgency <number>", "\u7D27\u6025\u7A0B\u5EA6 1-5", "3").option("-b, --batch <batchId>", "\u6A21\u677F\u751F\u6210\u4EFB\u52A1\u7684\u6279\u6B21 ID").option("--max-instances <number>", "\u81EA\u52A8\u8C03\u5EA6\u6D3B\u8DC3\u5B9E\u4F8B\u4E0A\u9650\uFF08\u624B\u52A8\u89E6\u53D1\u4E0D\u53D7\u9650\uFF09", "1").option("--max-retries <number>", "\u6700\u5927\u91CD\u8BD5\u6B21\u6570", "3").option("--retry-backoff <duration>", "\u9000\u907F\u57FA\u7840\u95F4\u9694\uFF0C\u5982 30s / 5min", "30s").option("--timeout <duration>", "\u6BCF\u6B21\u4EFB\u52A1\u786C\u8D85\u65F6\uFF0C\u5982 30min / 2h").action(async (options) => withDb(async () => {
29042
+ program2.command("template").description(t2("\u7BA1\u7406\u5B9A\u65F6\u4EFB\u52A1\u6A21\u677F", "manage scheduled task templates")).addCommand(
29043
+ new Command("add").description(t2("\u521B\u5EFA\u5B9A\u65F6\u4EFB\u52A1\u6A21\u677F", "create a scheduled task template")).requiredOption("-n, --name <name>", t2("\u6A21\u677F\u540D\u79F0", "template name")).requiredOption("-a, --agent <agent>", t2("Agent \u540D\u79F0", "agent name")).requiredOption("-p, --prompt <prompt>", t2("\u63D0\u793A\u8BCD", "prompt")).requiredOption("-t, --type <type>", t2("\u5B9A\u65F6\u7C7B\u578B\uFF1Acron/delayed/recurring", "schedule type: cron/delayed/recurring")).option("--cron <expr>", t2("cron \u8868\u8FBE\u5F0F\uFF08cron \u7C7B\u578B\u5FC5\u586B\uFF09", "cron expression (required for cron)")).option("--delay <duration>", t2("\u5EF6\u8FDF\u65F6\u95F4\uFF08delayed \u5FC5\u586B\uFF09\uFF0C\u5982 30s / 5min / 1h / 2d", "delay (required for delayed), e.g. 30s / 5min / 1h / 2d")).option("--interval <duration>", t2("\u5FAA\u73AF\u95F4\u9694\uFF08recurring \u5FC5\u586B\uFF09\uFF0C\u5982 1h / 30min / 5s", "interval (required for recurring), e.g. 1h / 30min / 5s")).option("-m, --model <model>", t2("\u6A21\u578B", "model")).option("-c, --category <category>", t2("\u5206\u7C7B", "category"), "general").option("-i, --importance <number>", t2("\u91CD\u8981\u7A0B\u5EA6 1-5", "importance 1-5"), "3").option("-u, --urgency <number>", t2("\u7D27\u6025\u7A0B\u5EA6 1-5", "urgency 1-5"), "3").option("-b, --batch <batchId>", t2("\u6A21\u677F\u751F\u6210\u4EFB\u52A1\u7684\u6279\u6B21 ID", "batch ID for generated tasks")).option("--max-instances <number>", t2("\u81EA\u52A8\u5B9A\u65F6\u4EFB\u52A1\u7684\u6D3B\u8DC3\u5B9E\u4F8B\u4E0A\u9650\uFF08\u7ACB\u5373\u89E6\u53D1\u4E0D\u53D7\u9650\uFF09", "active instance limit for automatic scheduling (Run now is unrestricted)"), "1").option("--max-retries <number>", t2("\u6700\u5927\u91CD\u8BD5\u6B21\u6570", "maximum retries"), "3").option("--retry-backoff <duration>", t2("\u9000\u907F\u57FA\u7840\u95F4\u9694\uFF0C\u5982 30s / 5min", "retry backoff base, e.g. 30s / 5min"), "30s").option("--timeout <duration>", t2("\u6BCF\u6B21\u4EFB\u52A1\u786C\u8D85\u65F6\uFF0C\u5982 30min / 2h", "hard timeout per task, e.g. 30min / 2h")).action(async (options) => withDb(async () => {
28390
29044
  let intervalMs = null;
28391
29045
  let runAt = null;
28392
29046
  const retryBackoffMs = parseDuration(options.retryBackoff);
@@ -28431,12 +29085,12 @@ program2.command("template").description("\u7BA1\u7406\u4EFB\u52A1\u8C03\u5EA6\u
28431
29085
  console.log(JSON.stringify({ id: tmpl.id, status: "created", nextRunAt: tmpl.nextRunAt }, null, 2));
28432
29086
  }))
28433
29087
  ).addCommand(
28434
- new Command("list").description("\u5217\u51FA\u8C03\u5EA6\u6A21\u677F").action(async () => withDb(async () => {
29088
+ new Command("list").description(t2("\u5217\u51FA\u5B9A\u65F6\u4EFB\u52A1\u6A21\u677F", "list scheduled task templates")).action(async () => withDb(async () => {
28435
29089
  const templates = await TaskTemplateService.list();
28436
29090
  console.log(JSON.stringify(templates, null, 2));
28437
29091
  }))
28438
29092
  ).addCommand(
28439
- new Command("enable").description("\u542F\u7528\u6A21\u677F").requiredOption("--id <id>", "\u6A21\u677F ID").action(async (options) => withDb(async () => {
29093
+ new Command("enable").description(t2("\u542F\u7528\u6A21\u677F", "enable a template")).requiredOption("--id <id>", t2("\u6A21\u677F ID", "template ID")).action(async (options) => withDb(async () => {
28440
29094
  const tmpl = await TaskTemplateService.enable(parsePositiveInteger(options.id, "id"));
28441
29095
  if (tmpl) {
28442
29096
  console.log(JSON.stringify({ id: tmpl.id, enabled: true }));
@@ -28446,7 +29100,7 @@ program2.command("template").description("\u7BA1\u7406\u4EFB\u52A1\u8C03\u5EA6\u
28446
29100
  }
28447
29101
  }))
28448
29102
  ).addCommand(
28449
- new Command("disable").description("\u7981\u7528\u6A21\u677F").requiredOption("--id <id>", "\u6A21\u677F ID").action(async (options) => withDb(async () => {
29103
+ new Command("disable").description(t2("\u7981\u7528\u6A21\u677F", "disable a template")).requiredOption("--id <id>", t2("\u6A21\u677F ID", "template ID")).action(async (options) => withDb(async () => {
28450
29104
  const tmpl = await TaskTemplateService.disable(parsePositiveInteger(options.id, "id"));
28451
29105
  if (tmpl) {
28452
29106
  console.log(JSON.stringify({ id: tmpl.id, enabled: false }));
@@ -28456,23 +29110,23 @@ program2.command("template").description("\u7BA1\u7406\u4EFB\u52A1\u8C03\u5EA6\u
28456
29110
  }
28457
29111
  }))
28458
29112
  ).addCommand(
28459
- new Command("delete").description("\u5220\u9664\u6A21\u677F").requiredOption("--id <id>", "\u6A21\u677F ID").action(async (options) => withDb(async () => {
29113
+ new Command("delete").description(t2("\u5220\u9664\u6A21\u677F", "delete a template")).requiredOption("--id <id>", t2("\u6A21\u677F ID", "template ID")).action(async (options) => withDb(async () => {
28460
29114
  const id = parsePositiveInteger(options.id, "id");
28461
29115
  const deleted = await TaskTemplateService.delete(id);
28462
29116
  console.log(JSON.stringify({ deleted, id }));
28463
29117
  }))
28464
29118
  );
28465
- var databaseCommand = new Command("db").description("\u6570\u636E\u5E93\u68C0\u67E5\u3001\u5907\u4EFD\u3001\u6E05\u7A7A\u4E0E\u6062\u590D");
28466
- databaseCommand.command("check").description("\u68C0\u67E5\u6570\u636E\u5E93\u5B8C\u6574\u6027\u3001\u5916\u952E\u548C\u4E1A\u52A1\u8868\u7EDF\u8BA1").option("--json", "\u5F3A\u5236\u8F93\u51FA JSON\uFF08\u975E\u4EA4\u4E92\u8C03\u7528\u9ED8\u8BA4\u5DF2\u8F93\u51FA JSON\uFF09").action(async (options) => withDb(async () => {
29119
+ var databaseCommand = new Command("db").description(t2("\u6570\u636E\u5E93\u68C0\u67E5\u3001\u5907\u4EFD\u3001\u6E05\u7A7A\u4E0E\u6062\u590D", "check, back up, clear, and restore the database"));
29120
+ databaseCommand.command("check").description(t2("\u68C0\u67E5\u6570\u636E\u5E93\u5B8C\u6574\u6027\u3001\u5916\u952E\u548C\u4E1A\u52A1\u8868\u7EDF\u8BA1", "check integrity, foreign keys, and table counts")).option("--json", t2("\u5F3A\u5236\u8F93\u51FA JSON\uFF08\u975E\u4EA4\u4E92\u8C03\u7528\u9ED8\u8BA4\u5DF2\u8F93\u51FA JSON\uFF09", "force JSON output (already the default when non-interactive)")).action(async (options) => withDb(async () => {
28467
29121
  const result = DatabaseMaintenanceService.check();
28468
- console.log(renderDatabaseResult("check", result, { forceJson: options.json }));
29122
+ console.log(renderDatabaseResult("check", result, { forceJson: options.json, locale: cliLocale }));
28469
29123
  if (!result.ok) process.exitCode = 1;
28470
29124
  }, (error) => renderDatabaseError(error, { forceJson: options.json })));
28471
- databaseCommand.command("backup").description("\u521B\u5EFA\u7ECF\u8FC7\u5B8C\u6574\u6027\u6821\u9A8C\u7684\u4E00\u81F4\u6027\u5907\u4EFD").option("-o, --output <path>", "\u5907\u4EFD\u6587\u4EF6\u8DEF\u5F84\uFF08\u9ED8\u8BA4\u5199\u5165\u6570\u636E\u5E93\u76EE\u5F55\uFF09").option("--json", "\u5F3A\u5236\u8F93\u51FA JSON\uFF08\u975E\u4EA4\u4E92\u8C03\u7528\u9ED8\u8BA4\u5DF2\u8F93\u51FA JSON\uFF09").action(async (options) => withDb(async () => {
29125
+ databaseCommand.command("backup").description(t2("\u521B\u5EFA\u7ECF\u8FC7\u5B8C\u6574\u6027\u6821\u9A8C\u7684\u4E00\u81F4\u6027\u5907\u4EFD", "create a consistent, integrity-checked backup")).option("-o, --output <path>", t2("\u5907\u4EFD\u6587\u4EF6\u8DEF\u5F84\uFF08\u9ED8\u8BA4\u5199\u5165\u6570\u636E\u5E93\u76EE\u5F55\uFF09", "backup path (defaults to the database directory)")).option("--json", t2("\u5F3A\u5236\u8F93\u51FA JSON\uFF08\u975E\u4EA4\u4E92\u8C03\u7528\u9ED8\u8BA4\u5DF2\u8F93\u51FA JSON\uFF09", "force JSON output (already the default when non-interactive)")).action(async (options) => withDb(async () => {
28472
29126
  const result = DatabaseMaintenanceService.backup(options.output);
28473
- console.log(renderDatabaseResult("backup", result, { forceJson: options.json }));
29127
+ console.log(renderDatabaseResult("backup", result, { forceJson: options.json, locale: cliLocale }));
28474
29128
  }, (error) => renderDatabaseError(error, { forceJson: options.json })));
28475
- databaseCommand.command("clear").description("\u5907\u4EFD\u540E\u4E8B\u52A1\u6027\u6E05\u7A7A\u4EFB\u52A1\u3001\u6267\u884C\u8BB0\u5F55\u548C\u8C03\u5EA6\u6A21\u677F").option("--confirm <word>", "\u5371\u9669\u64CD\u4F5C\u786E\u8BA4\uFF0C\u5FC5\u987B\u586B\u5199 CLEAR").option("--keep-stopped", "\u7EF4\u62A4\u7ED3\u675F\u540E\u4E0D\u91CD\u542F\u539F\u672C\u7531 PM2 \u7BA1\u7406\u7684 Gateway").option("--json", "\u5F3A\u5236\u8F93\u51FA JSON\uFF08\u975E\u4EA4\u4E92\u8C03\u7528\u9ED8\u8BA4\u5DF2\u8F93\u51FA JSON\uFF09").action(async (options) => withDb(async () => {
29129
+ databaseCommand.command("clear").description(t2("\u5907\u4EFD\u540E\u4E8B\u52A1\u6027\u6E05\u7A7A\u4EFB\u52A1\u3001\u6267\u884C\u8BB0\u5F55\u548C\u5B9A\u65F6\u4EFB\u52A1\u6A21\u677F", "back up, then transactionally clear tasks, runs, and scheduled templates")).option("--confirm <word>", t2("\u5371\u9669\u64CD\u4F5C\u786E\u8BA4\uFF0C\u5FC5\u987B\u586B\u5199 CLEAR", "confirmation word; must be CLEAR")).option("--keep-stopped", t2("\u7EF4\u62A4\u7ED3\u675F\u540E\u4E0D\u91CD\u542F\u539F\u672C\u7531 PM2 \u7BA1\u7406\u7684 Gateway", "leave a previously managed Gateway stopped")).option("--json", t2("\u5F3A\u5236\u8F93\u51FA JSON\uFF08\u975E\u4EA4\u4E92\u8C03\u7528\u9ED8\u8BA4\u5DF2\u8F93\u51FA JSON\uFF09", "force JSON output (already the default when non-interactive)")).action(async (options) => withDb(async () => {
28476
29130
  if (options.confirm !== "CLEAR") {
28477
29131
  throw new Error("\u6E05\u7A7A\u6570\u636E\u5E93\u5FC5\u987B\u663E\u5F0F\u4F20\u5165 --confirm CLEAR");
28478
29132
  }
@@ -28480,9 +29134,9 @@ databaseCommand.command("clear").description("\u5907\u4EFD\u540E\u4E8B\u52A1\u60
28480
29134
  options.keepStopped ?? false,
28481
29135
  () => DatabaseMaintenanceService.clear()
28482
29136
  );
28483
- console.log(renderDatabaseResult("clear", result, { forceJson: options.json }));
29137
+ console.log(renderDatabaseResult("clear", result, { forceJson: options.json, locale: cliLocale }));
28484
29138
  }, (error) => renderDatabaseError(error, { forceJson: options.json })));
28485
- databaseCommand.command("restore").description("\u81EA\u52A8\u5907\u4EFD\u5F53\u524D\u5E93\u540E\uFF0C\u4ECE\u6307\u5B9A\u5907\u4EFD\u6062\u590D\u6570\u636E\u5E93").requiredOption("--from <path>", "\u8981\u6062\u590D\u7684 SQLite \u5907\u4EFD\u6587\u4EF6").option("--confirm <word>", "\u5371\u9669\u64CD\u4F5C\u786E\u8BA4\uFF0C\u5FC5\u987B\u586B\u5199 RESTORE").option("--keep-stopped", "\u7EF4\u62A4\u7ED3\u675F\u540E\u4E0D\u91CD\u542F\u539F\u672C\u7531 PM2 \u7BA1\u7406\u7684 Gateway").option("--json", "\u5F3A\u5236\u8F93\u51FA JSON\uFF08\u975E\u4EA4\u4E92\u8C03\u7528\u9ED8\u8BA4\u5DF2\u8F93\u51FA JSON\uFF09").action(async (options) => withDb(async () => {
29139
+ databaseCommand.command("restore").description(t2("\u81EA\u52A8\u5907\u4EFD\u5F53\u524D\u5E93\u540E\uFF0C\u4ECE\u6307\u5B9A\u5907\u4EFD\u6062\u590D\u6570\u636E\u5E93", "back up the live database, then restore from a backup")).requiredOption("--from <path>", t2("\u8981\u6062\u590D\u7684 SQLite \u5907\u4EFD\u6587\u4EF6", "SQLite backup to restore")).option("--confirm <word>", t2("\u5371\u9669\u64CD\u4F5C\u786E\u8BA4\uFF0C\u5FC5\u987B\u586B\u5199 RESTORE", "confirmation word; must be RESTORE")).option("--keep-stopped", t2("\u7EF4\u62A4\u7ED3\u675F\u540E\u4E0D\u91CD\u542F\u539F\u672C\u7531 PM2 \u7BA1\u7406\u7684 Gateway", "leave a previously managed Gateway stopped")).option("--json", t2("\u5F3A\u5236\u8F93\u51FA JSON\uFF08\u975E\u4EA4\u4E92\u8C03\u7528\u9ED8\u8BA4\u5DF2\u8F93\u51FA JSON\uFF09", "force JSON output (already the default when non-interactive)")).action(async (options) => withDb(async () => {
28486
29140
  if (options.confirm !== "RESTORE") {
28487
29141
  throw new Error("\u6062\u590D\u6570\u636E\u5E93\u5FC5\u987B\u663E\u5F0F\u4F20\u5165 --confirm RESTORE");
28488
29142
  }
@@ -28490,16 +29144,16 @@ databaseCommand.command("restore").description("\u81EA\u52A8\u5907\u4EFD\u5F53\u
28490
29144
  options.keepStopped ?? false,
28491
29145
  () => DatabaseMaintenanceService.restore(options.from)
28492
29146
  );
28493
- console.log(renderDatabaseResult("restore", result, { forceJson: options.json }));
29147
+ console.log(renderDatabaseResult("restore", result, { forceJson: options.json, locale: cliLocale }));
28494
29148
  }, (error) => renderDatabaseError(error, { forceJson: options.json })));
28495
29149
  program2.addCommand(databaseCommand);
28496
- program2.command("init").description("Initialize SuperTask (create config + run migrations)").action(async () => withDb(async () => {
29150
+ program2.command("init").description(t2("\u521D\u59CB\u5316 SuperTask\uFF08\u521B\u5EFA\u914D\u7F6E\u5E76\u6267\u884C\u8FC1\u79FB\uFF09", "initialize SuperTask (create config and run migrations)")).action(async () => withDb(async () => {
28497
29151
  const { existsSync: existsSync9, mkdirSync: mkdirSync6, writeFileSync: writeFileSync4 } = await import("fs");
28498
- const { dirname: dirname9 } = await import("path");
29152
+ const { dirname: dirname10 } = await import("path");
28499
29153
  const { getConfigPath: getConfigPath2 } = await Promise.resolve().then(() => (init_config(), config_exports));
28500
29154
  const configPath = getConfigPath2();
28501
29155
  if (!existsSync9(configPath)) {
28502
- const dir = dirname9(configPath);
29156
+ const dir = dirname10(configPath);
28503
29157
  if (!existsSync9(dir)) mkdirSync6(dir, { recursive: true });
28504
29158
  writeFileSync4(configPath, JSON.stringify({
28505
29159
  configVersion: 2,
@@ -28514,16 +29168,16 @@ program2.command("init").description("Initialize SuperTask (create config + run
28514
29168
  getDb2();
28515
29169
  console.log(JSON.stringify({ migrated: true }));
28516
29170
  }));
28517
- program2.command("migrate").description("Run database migrations").action(async () => withDb(async () => {
29171
+ program2.command("migrate").description(t2("\u6267\u884C\u6570\u636E\u5E93\u8FC1\u79FB", "run database migrations")).action(async () => withDb(async () => {
28518
29172
  const { getDb: getDb2 } = await Promise.resolve().then(() => (init_db2(), db_exports));
28519
29173
  getDb2();
28520
29174
  console.log(JSON.stringify({ migrated: true }));
28521
29175
  }));
28522
- program2.command("gateway").description("Start the Gateway process (foreground)").action(async () => {
29176
+ program2.command("gateway").description(t2("\u5728\u524D\u53F0\u542F\u52A8 Gateway", "start the Gateway in the foreground")).action(async () => {
28523
29177
  const { main: main2 } = await Promise.resolve().then(() => (init_gateway(), gateway_exports));
28524
29178
  await main2();
28525
29179
  });
28526
- program2.command("ui").description("Open Web Dashboard (embedded in Gateway)").action(async () => {
29180
+ program2.command("ui").description(t2("\u6253\u5F00 Gateway \u5185\u7F6E\u7684 Web \u7BA1\u7406\u754C\u9762", "open the Web Dashboard embedded in the Gateway")).action(async () => {
28527
29181
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
28528
29182
  const cfg = loadConfig2();
28529
29183
  const url = `http://localhost:${cfg.dashboard.port}`;
@@ -28535,19 +29189,19 @@ program2.command("ui").description("Open Web Dashboard (embedded in Gateway)").a
28535
29189
  } catch {
28536
29190
  }
28537
29191
  });
28538
- program2.command("config").description("Show current configuration").action(async () => {
29192
+ program2.command("config").description(t2("\u663E\u793A\u5F53\u524D\u914D\u7F6E", "show the current configuration")).action(async () => {
28539
29193
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
28540
29194
  const cfg = loadConfig2();
28541
29195
  console.log(JSON.stringify(cfg, null, 2));
28542
29196
  });
28543
- program2.command("doctor").description("\u68C0\u67E5 OpenCode\u3001\u6570\u636E\u5E93\u3001Gateway\u3001Dashboard \u548C\u65E5\u5FD7\u8F6E\u8F6C").option("--json", "\u5F3A\u5236\u8F93\u51FA JSON").action(async (options) => withDb(async () => {
29197
+ program2.command("doctor").description(t2("\u68C0\u67E5 OpenCode\u3001\u6570\u636E\u5E93\u3001Gateway\u3001Web \u754C\u9762\u548C\u65E5\u5FD7\u8F6E\u8F6C", "diagnose OpenCode, database, Gateway, Dashboard, and log rotation")).option("--json", t2("\u5F3A\u5236\u8F93\u51FA JSON", "force JSON output")).action(async (options) => withDb(async () => {
28544
29198
  const config = loadConfig();
28545
29199
  const database = DatabaseMaintenanceService.check();
28546
29200
  const legacyQuarantinedRuns = await TaskRunService.listLegacyQuarantinedRuns(
28547
29201
  config.watchdog.heartbeatTimeoutMs
28548
29202
  );
28549
29203
  const gateway = getGatewayDiagnostic();
28550
- const packageVersion = getPackageVersion();
29204
+ const packageVersion2 = getPackageVersion();
28551
29205
  const opencodeBin2 = process.env.SUPERTASK_OPENCODE_BIN ?? "opencode";
28552
29206
  const opencodeResult = spawnSync4(opencodeBin2, ["--version"], {
28553
29207
  encoding: "utf8",
@@ -28557,7 +29211,7 @@ program2.command("doctor").description("\u68C0\u67E5 OpenCode\u3001\u6570\u636E\
28557
29211
  ok: opencodeResult.status === 0,
28558
29212
  executable: opencodeBin2,
28559
29213
  version: opencodeResult.status === 0 ? opencodeResult.stdout.trim() : null,
28560
- error: opencodeResult.status === 0 ? null : opencodeResult.error?.message || opencodeResult.stderr.trim() || `\u9000\u51FA\u7801 ${opencodeResult.status}`
29214
+ error: opencodeResult.status === 0 ? null : opencodeResult.error?.message || opencodeResult.stderr.trim() || t2(`\u9000\u51FA\u7801 ${opencodeResult.status}`, `exit code ${opencodeResult.status}`)
28561
29215
  };
28562
29216
  const plugin = getOpenCodePluginDiagnostic();
28563
29217
  let dashboard = {
@@ -28582,44 +29236,68 @@ program2.command("doctor").description("\u68C0\u67E5 OpenCode\u3001\u6570\u636E\
28582
29236
  const gatewayEntryPinned = gateway.gatewayEntry === null || !/[\\/]opencode-supertask@(latest|next)[\\/]/.test(gateway.gatewayEntry);
28583
29237
  const gatewayVersionMatchesPackage = gateway.gatewayPackageVersion !== null && gateway.runningVersion === gateway.gatewayPackageVersion;
28584
29238
  const configuredVersionsMatch = plugin.ok && plugin.version === gateway.gatewayPackageVersion;
29239
+ const cliVersionMatchesPlugin = plugin.ok && plugin.version === packageVersion2;
28585
29240
  if (!plugin.ok && plugin.error) {
28586
29241
  warnings.push(plugin.error);
28587
29242
  }
28588
- if (plugin.version !== null && plugin.version !== packageVersion) {
28589
- warnings.push(`\u5F53\u524D CLI v${packageVersion} \u4E0E OpenCode \u63D2\u4EF6 v${plugin.version} \u4E0D\u4E00\u81F4\uFF1B\u8BF7\u7528\u539F\u5305\u7BA1\u7406\u5668\u5168\u5C40\u5B89\u88C5 opencode-supertask@${plugin.version}\uFF08npm install -g \u6216 bun add -g\uFF09`);
29243
+ if (plugin.version !== null && plugin.version !== packageVersion2) {
29244
+ warnings.push(t2(
29245
+ `\u5F53\u524D CLI v${packageVersion2} \u4E0E OpenCode \u63D2\u4EF6 v${plugin.version} \u4E0D\u4E00\u81F4\uFF1B\u6267\u884C supertask upgrade \u8BA9 CLI\u3001\u63D2\u4EF6\u548C Gateway \u6536\u655B\u5230\u540C\u4E00\u7CBE\u786E\u7248\u672C`,
29246
+ `CLI v${packageVersion2} does not match OpenCode plugin v${plugin.version}; run supertask upgrade to converge the CLI, plugin, and Gateway on one exact version`
29247
+ ));
28590
29248
  }
28591
29249
  if (!gatewayEntryPinned) {
28592
- warnings.push(`PM2 Gateway \u4ECD\u4ECE\u6D6E\u52A8\u7F13\u5B58\u8DEF\u5F84\u542F\u52A8\uFF1A${gateway.gatewayEntry}`);
29250
+ warnings.push(t2(
29251
+ `PM2 Gateway \u4ECD\u4ECE\u6D6E\u52A8\u7F13\u5B58\u8DEF\u5F84\u542F\u52A8\uFF1A${gateway.gatewayEntry}`,
29252
+ `The PM2 Gateway still starts from a floating cache path: ${gateway.gatewayEntry}`
29253
+ ));
28593
29254
  }
28594
29255
  if (gateway.processFound && gateway.gatewayPackageVersion === null) {
28595
- warnings.push(`\u65E0\u6CD5\u4ECE PM2 Gateway \u5165\u53E3\u786E\u8BA4 opencode-supertask \u5305\u7248\u672C\uFF1A${gateway.gatewayEntry ?? "unknown"}`);
29256
+ warnings.push(t2(
29257
+ `\u65E0\u6CD5\u4ECE PM2 Gateway \u5165\u53E3\u786E\u8BA4 opencode-supertask \u5305\u7248\u672C\uFF1A${gateway.gatewayEntry ?? "unknown"}`,
29258
+ `Could not determine the opencode-supertask package version from the PM2 Gateway entry: ${gateway.gatewayEntry ?? "unknown"}`
29259
+ ));
28596
29260
  } else if (gateway.processFound && !gatewayVersionMatchesPackage) {
28597
- warnings.push(`Gateway ready \u9501\u7248\u672C ${gateway.runningVersion ?? "unknown"} \u4E0E\u5165\u53E3\u5305\u7248\u672C ${gateway.gatewayPackageVersion ?? "unknown"} \u4E0D\u4E00\u81F4`);
29261
+ warnings.push(t2(
29262
+ `Gateway ready \u9501\u7248\u672C ${gateway.runningVersion ?? "unknown"} \u4E0E\u5165\u53E3\u5305\u7248\u672C ${gateway.gatewayPackageVersion ?? "unknown"} \u4E0D\u4E00\u81F4`,
29263
+ `Gateway ready-lock version ${gateway.runningVersion ?? "unknown"} does not match entry package version ${gateway.gatewayPackageVersion ?? "unknown"}`
29264
+ ));
28598
29265
  }
28599
29266
  if (plugin.version !== null && gateway.gatewayPackageVersion !== null && plugin.version !== gateway.gatewayPackageVersion) {
28600
- warnings.push(`OpenCode \u63D2\u4EF6 v${plugin.version} \u4E0E PM2 Gateway v${gateway.gatewayPackageVersion} \u4E0D\u4E00\u81F4\uFF1B\u6267\u884C supertask upgrade`);
29267
+ warnings.push(t2(
29268
+ `OpenCode \u63D2\u4EF6 v${plugin.version} \u4E0E PM2 Gateway v${gateway.gatewayPackageVersion} \u4E0D\u4E00\u81F4\uFF1B\u6267\u884C supertask upgrade`,
29269
+ `OpenCode plugin v${plugin.version} does not match PM2 Gateway v${gateway.gatewayPackageVersion}; run supertask upgrade`
29270
+ ));
28601
29271
  }
28602
29272
  if (gateway.pm2Installed && !gateway.logRotationInstalled) {
28603
- warnings.push("\u672A\u68C0\u6D4B\u5230 pm2-logrotate\uFF1B\u957F\u671F\u8FD0\u884C\u524D\u5EFA\u8BAE\u5B89\u88C5\u5E76\u9650\u5236\u65E5\u5FD7\u4FDD\u7559\u91CF");
29273
+ warnings.push(t2(
29274
+ "\u672A\u68C0\u6D4B\u5230 pm2-logrotate\uFF1B\u957F\u671F\u8FD0\u884C\u524D\u5EFA\u8BAE\u5B89\u88C5\u5E76\u9650\u5236\u65E5\u5FD7\u4FDD\u7559\u91CF",
29275
+ "pm2-logrotate was not detected; install it and limit log retention before long-running use"
29276
+ ));
28604
29277
  }
28605
29278
  if (gateway.startupConfigured === false) {
28606
- warnings.push(process.platform === "linux" ? "\u672A\u68C0\u6D4B\u5230\u5DF2\u542F\u7528\u4E14\u5305\u542B\u53EF\u6062\u590D PM2 dump \u7684 systemd \u81EA\u542F\u670D\u52A1" : "\u672A\u68C0\u6D4B\u5230\u6B63\u5728\u8FD0\u884C\u4E14\u5305\u542B\u53EF\u6062\u590D PM2 dump \u7684 macOS LaunchAgent");
29279
+ warnings.push(process.platform === "linux" ? t2("\u672A\u68C0\u6D4B\u5230\u5DF2\u542F\u7528\u4E14\u5305\u542B\u53EF\u6062\u590D PM2 dump \u7684 systemd \u81EA\u542F\u670D\u52A1", "No enabled systemd startup service with a recoverable PM2 dump was detected") : t2("\u672A\u68C0\u6D4B\u5230\u6B63\u5728\u8FD0\u884C\u4E14\u5305\u542B\u53EF\u6062\u590D PM2 dump \u7684 macOS LaunchAgent", "No running macOS LaunchAgent with a recoverable PM2 dump was detected"));
28607
29280
  }
28608
29281
  if (gateway.processFound && !gateway.scopeMatches) {
28609
- warnings.push("\u5F53\u524D CLI/OpenCode \u4E0E PM2 Gateway \u7684\u6570\u636E\u5E93\u3001\u914D\u7F6E\u6216 OpenCode \u53EF\u6267\u884C\u6587\u4EF6\u4F5C\u7528\u57DF\u4E0D\u4E00\u81F4");
29282
+ warnings.push(t2(
29283
+ "\u5F53\u524D CLI/OpenCode \u4E0E PM2 Gateway \u7684\u6570\u636E\u5E93\u3001\u914D\u7F6E\u6216 OpenCode \u53EF\u6267\u884C\u6587\u4EF6\u4F5C\u7528\u57DF\u4E0D\u4E00\u81F4",
29284
+ "The current CLI/OpenCode database, config, or OpenCode executable scope does not match the PM2 Gateway"
29285
+ ));
28610
29286
  }
28611
29287
  for (const run of legacyQuarantinedRuns) {
28612
- const cwdHint = run.taskCwd == null ? "\uFF08\u65E7\u4EFB\u52A1\u6CA1\u6709 cwd\uFF0C\u8BF7\u5148\u5728 Dashboard \u53D6\u6D88\uFF09" : `\uFF08\u5728 ${run.taskCwd} \u6267\u884C\uFF09`;
28613
- const cancel = run.taskStatus === "cancelled" ? "" : `\u5148${cwdHint} supertask cancel --id ${run.taskId}\uFF1B`;
28614
- const owner = run.ownerAlive ? `owner PID ${run.workerPid} \u4ECD\u5B58\u6D3B\uFF0C\u5148\u786E\u8BA4\u5E76\u505C\u6B62\u5BF9\u5E94\u8FDB\u7A0B\uFF1B` : "";
28615
- warnings.push(
28616
- `\u65E7\u7248\u9694\u79BB run #${run.runId}\uFF1A${owner}${cancel}\u786E\u8BA4\u6CA1\u6709\u9057\u7559 OpenCode \u8FDB\u7A0B\u540E\u6267\u884C supertask run abandon --id ${run.runId} --confirm ABANDON`
28617
- );
28618
- }
28619
- const ok = opencode.ok && plugin.ok && database.ok && legacyQuarantinedRuns.length === 0 && gateway.pm2Installed && gateway.status === "online" && gateway.ready && gatewayEntryPinned && gatewayVersionMatchesPackage && configuredVersionsMatch && gateway.scopeMatches && gateway.logRotationInstalled && gateway.startupConfigured !== false && dashboard.ok;
29288
+ const cwdHint = run.taskCwd == null ? t2("\uFF08\u65E7\u4EFB\u52A1\u6CA1\u6709 cwd\uFF0C\u8BF7\u5148\u5728 Dashboard \u53D6\u6D88\uFF09", " (the legacy task has no cwd; cancel it in the Dashboard first)") : t2(`\uFF08\u5728 ${run.taskCwd} \u6267\u884C\uFF09`, ` (run in ${run.taskCwd})`);
29289
+ const cancel = run.taskStatus === "cancelled" ? "" : t2(`\u5148${cwdHint} supertask cancel --id ${run.taskId}\uFF1B`, `first${cwdHint}: supertask cancel --id ${run.taskId}; `);
29290
+ const owner = run.ownerAlive ? t2(`owner PID ${run.workerPid} \u4ECD\u5B58\u6D3B\uFF0C\u5148\u786E\u8BA4\u5E76\u505C\u6B62\u5BF9\u5E94\u8FDB\u7A0B\uFF1B`, `owner PID ${run.workerPid} is still alive; confirm and stop it first; `) : "";
29291
+ warnings.push(t2(
29292
+ `\u65E7\u7248\u9694\u79BB run #${run.runId}\uFF1A${owner}${cancel}\u786E\u8BA4\u6CA1\u6709\u9057\u7559 OpenCode \u8FDB\u7A0B\u540E\u6267\u884C supertask run abandon --id ${run.runId} --confirm ABANDON`,
29293
+ `Legacy quarantined run #${run.runId}: ${owner}${cancel}after confirming no OpenCode process remains, run supertask run abandon --id ${run.runId} --confirm ABANDON`
29294
+ ));
29295
+ }
29296
+ const ok = opencode.ok && plugin.ok && database.ok && legacyQuarantinedRuns.length === 0 && gateway.pm2Installed && gateway.status === "online" && gateway.ready && gatewayEntryPinned && gatewayVersionMatchesPackage && configuredVersionsMatch && cliVersionMatchesPlugin && gateway.scopeMatches && gateway.logRotationInstalled && gateway.startupConfigured !== false && dashboard.ok;
28620
29297
  const report = {
28621
29298
  ok,
28622
- packageVersion,
29299
+ packageVersion: packageVersion2,
29300
+ cliVersionMatchesPlugin,
28623
29301
  configPath: getConfigPath(),
28624
29302
  opencode,
28625
29303
  plugin,
@@ -28634,17 +29312,17 @@ program2.command("doctor").description("\u68C0\u67E5 OpenCode\u3001\u6570\u636E\
28634
29312
  console.log(JSON.stringify(report, null, 2));
28635
29313
  } else {
28636
29314
  const mark = (value) => value ? "\u2713" : "\u2717";
28637
- console.log(`SuperTask doctor: ${ok ? "\u6B63\u5E38" : "\u5F02\u5E38"}`);
28638
- console.log(`${mark(opencode.ok)} OpenCode ${opencode.version ?? opencode.error ?? "\u4E0D\u53EF\u7528"}`);
28639
- console.log(`${mark(plugin.ok)} OpenCode \u63D2\u4EF6 ${plugin.spec || plugin.error || "\u672A\u914D\u7F6E"}${plugin.cachedVersion ? `\uFF08\u7F13\u5B58 v${plugin.cachedVersion}\uFF09` : ""}`);
28640
- console.log(`${mark(database.ok)} \u6570\u636E\u5E93 ${database.path}\uFF08\u4EFB\u52A1 ${database.counts.tasks}\uFF0C\u8FD0\u884C\u4E2D ${database.runningTasks}\uFF09`);
29315
+ console.log(`SuperTask doctor: ${ok ? t2("\u6B63\u5E38", "healthy") : t2("\u5F02\u5E38", "unhealthy")}`);
29316
+ console.log(`${mark(opencode.ok)} OpenCode ${opencode.version ?? opencode.error ?? t2("\u4E0D\u53EF\u7528", "unavailable")}`);
29317
+ console.log(`${mark(plugin.ok)} ${t2("OpenCode \u63D2\u4EF6", "OpenCode plugin")} ${plugin.spec || plugin.error || t2("\u672A\u914D\u7F6E", "not configured")}${plugin.cachedVersion ? t2(`\uFF08\u7F13\u5B58 v${plugin.cachedVersion}\uFF09`, ` (cached v${plugin.cachedVersion})`) : ""}`);
29318
+ console.log(`${mark(database.ok)} ${t2("\u6570\u636E\u5E93", "Database")} ${database.path}${t2(`\uFF08\u4EFB\u52A1 ${database.counts.tasks}\uFF0C\u8FD0\u884C\u4E2D ${database.runningTasks}\uFF09`, ` (tasks ${database.counts.tasks}, running ${database.runningTasks})`)}`);
28641
29319
  console.log(`${mark(gateway.status === "online" && gateway.ready && gatewayEntryPinned && gatewayVersionMatchesPackage)} Gateway ${gateway.status ?? "missing"}${gateway.pid ? `\uFF0CPID ${gateway.pid}` : ""}${gateway.runningVersion ? `\uFF0Cv${gateway.runningVersion}` : ""}${gateway.gatewayEntry ? `\uFF0C${gateway.gatewayEntry}` : ""}`);
28642
- console.log(`${mark(dashboard.ok)} Dashboard ${dashboard.enabled ? dashboard.url : "\u5DF2\u7981\u7528"}`);
29320
+ console.log(`${mark(dashboard.ok)} Dashboard ${dashboard.enabled ? dashboard.url : t2("\u5DF2\u7981\u7528", "disabled")}`);
28643
29321
  for (const warning of warnings) console.log(`! ${warning}`);
28644
29322
  }
28645
29323
  if (!ok) process.exitCode = 1;
28646
29324
  }));
28647
- program2.command("install").description("Install Gateway as pm2 service (auto-start, crash recovery, log rotation)").action(async () => {
29325
+ program2.command("install").description(t2("\u7528 PM2 \u5B89\u88C5 Gateway\uFF08\u5F00\u673A\u542F\u52A8\u3001\u5D29\u6E83\u6062\u590D\u3001\u65E5\u5FD7\u8F6E\u8F6C\uFF09", "install the Gateway with PM2 (startup, crash recovery, log rotation)")).action(async () => {
28648
29326
  try {
28649
29327
  const { install: pm2Install } = await Promise.resolve().then(() => (init_pm2(), pm2_exports));
28650
29328
  pm2Install();
@@ -28653,7 +29331,7 @@ program2.command("install").description("Install Gateway as pm2 service (auto-st
28653
29331
  process.exit(1);
28654
29332
  }
28655
29333
  });
28656
- program2.command("uninstall").description("Stop and remove Gateway pm2 service").action(async () => {
29334
+ program2.command("uninstall").description(t2("\u505C\u6B62\u5E76\u79FB\u9664 PM2 Gateway", "stop and remove the PM2 Gateway")).action(async () => {
28657
29335
  try {
28658
29336
  const { uninstall: pm2Uninstall } = await Promise.resolve().then(() => (init_pm2(), pm2_exports));
28659
29337
  pm2Uninstall();
@@ -28662,15 +29340,15 @@ program2.command("uninstall").description("Stop and remove Gateway pm2 service")
28662
29340
  process.exit(1);
28663
29341
  }
28664
29342
  });
28665
- program2.command("upgrade").description("Update OpenCode plugin cache and restart Gateway").action(async () => {
28666
- console.log("Updating opencode-supertask...");
29343
+ program2.command("upgrade").description(t2("\u66F4\u65B0 OpenCode \u63D2\u4EF6\u7F13\u5B58\u5E76\u91CD\u542F Gateway", "update the OpenCode plugin cache and restart the Gateway")).action(async () => {
29344
+ console.log(t2("\u6B63\u5728\u66F4\u65B0 opencode-supertask...", "Updating opencode-supertask..."));
28667
29345
  let installed;
28668
29346
  let previousVersion;
28669
29347
  try {
28670
29348
  const { resolveInstalledPlugin: resolveInstalledPlugin2 } = await Promise.resolve().then(() => (init_update2(), update_exports));
28671
29349
  previousVersion = resolveInstalledPlugin2().version;
28672
29350
  } catch (error) {
28673
- console.error(`\u65E0\u6CD5\u786E\u8BA4\u5F53\u524D OpenCode \u63D2\u4EF6\u7248\u672C\uFF0C\u5DF2\u53D6\u6D88\u5347\u7EA7: ${error instanceof Error ? error.message : String(error)}`);
29351
+ console.error(t2("\u65E0\u6CD5\u786E\u8BA4\u5F53\u524D OpenCode \u63D2\u4EF6\u7248\u672C\uFF0C\u5DF2\u53D6\u6D88\u5347\u7EA7\uFF1A", "Could not determine the current OpenCode plugin version; upgrade cancelled: ") + (error instanceof Error ? error.message : String(error)));
28674
29352
  process.exit(1);
28675
29353
  }
28676
29354
  try {
@@ -28685,17 +29363,40 @@ program2.command("upgrade").description("Update OpenCode plugin cache and restar
28685
29363
  detail += `; OpenCode \u63D2\u4EF6\u56DE\u6EDA\u5931\u8D25: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`;
28686
29364
  }
28687
29365
  console.error(detail);
28688
- console.error("Try manually: query npm dist-tags.latest, then install that exact version with opencode plugin.");
29366
+ console.error(t2(
29367
+ "\u53EF\u4EBA\u5DE5\u67E5\u8BE2 npm dist-tags.latest\uFF0C\u518D\u7528 opencode plugin \u5B89\u88C5\u8BE5\u7CBE\u786E\u7248\u672C\u3002",
29368
+ "Try manually: query npm dist-tags.latest, then install that exact version with opencode plugin."
29369
+ ));
28689
29370
  process.exit(1);
28690
29371
  }
28691
29372
  try {
28692
29373
  const { upgrade: pm2Upgrade } = await Promise.resolve().then(() => (init_pm2(), pm2_exports));
28693
29374
  const result = pm2Upgrade(installed);
28694
- console.log(`
28695
- SuperTask upgraded: ${result.before ?? "unknown"} \u2192 ${result.after}`);
28696
- console.log("Gateway restarted. Please restart opencode to load the new plugin.");
28697
- if (getPackageVersion() !== installed.version) {
28698
- console.log(`Global CLI remains v${getPackageVersion()}. Update it with your original package manager to opencode-supertask@${installed.version} (npm install -g or bun add -g).`);
29375
+ console.log(t2(
29376
+ `
29377
+ SuperTask \u5DF2\u5347\u7EA7\uFF1A${result.before ?? "unknown"} \u2192 ${result.after}`,
29378
+ `
29379
+ SuperTask upgraded: ${result.before ?? "unknown"} \u2192 ${result.after}`
29380
+ ));
29381
+ console.log(t2("Gateway \u5DF2\u91CD\u542F\u3002\u8BF7\u91CD\u542F OpenCode \u52A0\u8F7D\u65B0\u63D2\u4EF6\u3002", "Gateway restarted. Restart OpenCode to load the new plugin."));
29382
+ try {
29383
+ const { updateGlobalCli: updateGlobalCli2 } = await Promise.resolve().then(() => (init_update2(), update_exports));
29384
+ const cli = updateGlobalCli2(installed.version);
29385
+ if (cli.action === "updated") {
29386
+ console.log(t2(
29387
+ `\u5DF2\u4F7F\u7528 ${cli.packageManager} \u5C06\u5168\u5C40 CLI \u66F4\u65B0\u5230 v${installed.version}\u3002`,
29388
+ `Global CLI updated to v${installed.version} with ${cli.packageManager}.`
29389
+ ));
29390
+ } else if (cli.action === "not-installed") {
29391
+ console.log(t2("\u672A\u627E\u5230\u5168\u5C40 CLI\uFF1B\u63D2\u4EF6\u548C Gateway \u5347\u7EA7\u5DF2\u5B8C\u6210\u3002", "No global CLI installation was found; plugin and Gateway upgrade is complete."));
29392
+ }
29393
+ } catch (cliError) {
29394
+ console.error(t2("\u63D2\u4EF6\u548C Gateway \u5DF2\u5347\u7EA7\uFF0C\u4F46\u5168\u5C40 CLI \u672A\u66F4\u65B0\uFF1A", "Plugin and Gateway were upgraded, but the global CLI was not: ") + (cliError instanceof Error ? cliError.message : String(cliError)));
29395
+ console.error(t2(
29396
+ `\u6267\u884C npm install -g opencode-supertask@${installed.version} \u6216 bun add -g opencode-supertask@${installed.version}\uFF0C\u518D\u8FD0\u884C supertask doctor\u3002`,
29397
+ `Run npm install -g opencode-supertask@${installed.version} or bun add -g opencode-supertask@${installed.version}, then run supertask doctor.`
29398
+ ));
29399
+ process.exit(1);
28699
29400
  }
28700
29401
  } catch (err) {
28701
29402
  let detail = err instanceof Error ? err.message : String(err);
@@ -28707,7 +29408,7 @@ SuperTask upgraded: ${result.before ?? "unknown"} \u2192 ${result.after}`);
28707
29408
  } catch (rollbackError) {
28708
29409
  detail += `; Gateway \u5DF2\u56DE\u6EDA\uFF0C\u4F46 OpenCode \u63D2\u4EF6\u56DE\u6EDA\u5931\u8D25: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`;
28709
29410
  }
28710
- console.error("Gateway restart failed:", detail);
29411
+ console.error(t2("Gateway \u91CD\u542F\u5931\u8D25\uFF1A", "Gateway restart failed:"), detail);
28711
29412
  process.exit(1);
28712
29413
  }
28713
29414
  });