opencode-supertask 0.1.19 → 0.1.21

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.
@@ -234,7 +234,7 @@ var require_CronField = __commonJS({
234
234
  if (!isValidRange) {
235
235
  throw new Error(`${this.constructor.name} Validation error, got value ${badValue} expected range ${this.min}-${this.max}${charsString}`);
236
236
  }
237
- const duplicate = this.#values.find((value, index) => this.#values.indexOf(value) !== index);
237
+ const duplicate = this.#values.find((value, index2) => this.#values.indexOf(value) !== index2);
238
238
  if (duplicate) {
239
239
  throw new Error(`${this.constructor.name} Validation error, duplicate values found: ${duplicate}`);
240
240
  }
@@ -8079,11 +8079,11 @@ var require_CronFieldCollection = __commonJS({
8079
8079
  throw new Error("Unexpected range end");
8080
8080
  }
8081
8081
  if (step * multiplier > range.end) {
8082
- const mapFn = (_, index) => {
8082
+ const mapFn = (_, index2) => {
8083
8083
  if (typeof range.start !== "number") {
8084
8084
  throw new Error("Unexpected range start");
8085
8085
  }
8086
- return index % step === 0 ? range.start + index : null;
8086
+ return index2 % step === 0 ? range.start + index2 : null;
8087
8087
  };
8088
8088
  if (typeof range.start !== "number") {
8089
8089
  throw new Error("Unexpected range start");
@@ -8975,9 +8975,9 @@ var require_CronExpressionParser = __commonJS({
8975
8975
  if (field === CronUnit.DayOfWeek && max % 7 === 0) {
8976
8976
  stack.push(0);
8977
8977
  }
8978
- for (let index = min; index <= max; index += repeatInterval) {
8979
- if (stack.indexOf(index) === -1) {
8980
- stack.push(index);
8978
+ for (let index2 = min; index2 <= max; index2 += repeatInterval) {
8979
+ if (stack.indexOf(index2) === -1) {
8980
+ stack.push(index2);
8981
8981
  }
8982
8982
  }
8983
8983
  return stack;
@@ -9100,8 +9100,8 @@ var require_CronFileParser = __commonJS({
9100
9100
  * @throws If file cannot be read
9101
9101
  */
9102
9102
  static parseFileSync(filePath) {
9103
- const { readFileSync: readFileSync2 } = __require("fs");
9104
- const data = readFileSync2(filePath, "utf8");
9103
+ const { readFileSync: readFileSync4 } = __require("fs");
9104
+ const data = readFileSync4(filePath, "utf8");
9105
9105
  return _CronFileParser.#parseContent(data);
9106
9106
  }
9107
9107
  /**
@@ -11968,11 +11968,11 @@ var $ZodDate = /* @__PURE__ */ $constructor("$ZodDate", (inst, def) => {
11968
11968
  return payload;
11969
11969
  };
11970
11970
  });
11971
- function handleArrayResult(result, final, index) {
11971
+ function handleArrayResult(result, final, index2) {
11972
11972
  if (result.issues.length) {
11973
- final.issues.push(...prefixIssues(index, result.issues));
11973
+ final.issues.push(...prefixIssues(index2, result.issues));
11974
11974
  }
11975
- final.value[index] = result.value;
11975
+ final.value[index2] = result.value;
11976
11976
  }
11977
11977
  var $ZodArray = /* @__PURE__ */ $constructor("$ZodArray", (inst, def) => {
11978
11978
  $ZodType.init(inst, def);
@@ -12367,14 +12367,14 @@ function mergeValues(a, b) {
12367
12367
  return { valid: false, mergeErrorPath: [] };
12368
12368
  }
12369
12369
  const newArray = [];
12370
- for (let index = 0; index < a.length; index++) {
12371
- const itemA = a[index];
12372
- const itemB = b[index];
12370
+ for (let index2 = 0; index2 < a.length; index2++) {
12371
+ const itemA = a[index2];
12372
+ const itemB = b[index2];
12373
12373
  const sharedValue = mergeValues(itemA, itemB);
12374
12374
  if (!sharedValue.valid) {
12375
12375
  return {
12376
12376
  valid: false,
12377
- mergeErrorPath: [index, ...sharedValue.mergeErrorPath]
12377
+ mergeErrorPath: [index2, ...sharedValue.mergeErrorPath]
12378
12378
  };
12379
12379
  }
12380
12380
  newArray.push(sharedValue.data);
@@ -12466,11 +12466,11 @@ var $ZodTuple = /* @__PURE__ */ $constructor("$ZodTuple", (inst, def) => {
12466
12466
  return payload;
12467
12467
  };
12468
12468
  });
12469
- function handleTupleResult(result, final, index) {
12469
+ function handleTupleResult(result, final, index2) {
12470
12470
  if (result.issues.length) {
12471
- final.issues.push(...prefixIssues(index, result.issues));
12471
+ final.issues.push(...prefixIssues(index2, result.issues));
12472
12472
  }
12473
- final.value[index] = result.value;
12473
+ final.value[index2] = result.value;
12474
12474
  }
12475
12475
  var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
12476
12476
  $ZodType.init(inst, def);
@@ -22653,7 +22653,7 @@ function sql(strings, ...params) {
22653
22653
  return new SQL([new StringChunk(str)]);
22654
22654
  }
22655
22655
  sql2.raw = raw;
22656
- function join3(chunks, separator) {
22656
+ function join5(chunks, separator) {
22657
22657
  const result = [];
22658
22658
  for (const [i, chunk] of chunks.entries()) {
22659
22659
  if (i > 0 && separator !== void 0) {
@@ -22663,7 +22663,7 @@ function sql(strings, ...params) {
22663
22663
  }
22664
22664
  return new SQL(result);
22665
22665
  }
22666
- sql2.join = join3;
22666
+ sql2.join = join5;
22667
22667
  function identifier(value) {
22668
22668
  return new Name(value);
22669
22669
  }
@@ -22822,8 +22822,8 @@ function haveSameKeys(left, right) {
22822
22822
  if (leftKeys.length !== rightKeys.length) {
22823
22823
  return false;
22824
22824
  }
22825
- for (const [index, key] of leftKeys.entries()) {
22826
- if (key !== rightKeys[index]) {
22825
+ for (const [index2, key] of leftKeys.entries()) {
22826
+ if (key !== rightKeys[index2]) {
22827
22827
  return false;
22828
22828
  }
22829
22829
  }
@@ -24389,8 +24389,8 @@ var SQLiteDialect = class {
24389
24389
  }
24390
24390
  const joinsArray = [];
24391
24391
  if (joins) {
24392
- for (const [index, joinMeta] of joins.entries()) {
24393
- if (index === 0) {
24392
+ for (const [index2, joinMeta] of joins.entries()) {
24393
+ if (index2 === 0) {
24394
24394
  joinsArray.push(sql` `);
24395
24395
  }
24396
24396
  const table = joinMeta.table;
@@ -24407,7 +24407,7 @@ var SQLiteDialect = class {
24407
24407
  sql`${sql.raw(joinMeta.joinType)} join ${table} on ${joinMeta.on}`
24408
24408
  );
24409
24409
  }
24410
- if (index < joins.length - 1) {
24410
+ if (index2 < joins.length - 1) {
24411
24411
  joinsArray.push(sql` `);
24412
24412
  }
24413
24413
  }
@@ -24420,9 +24420,9 @@ var SQLiteDialect = class {
24420
24420
  buildOrderBy(orderBy) {
24421
24421
  const orderByList = [];
24422
24422
  if (orderBy) {
24423
- for (const [index, orderByValue] of orderBy.entries()) {
24423
+ for (const [index2, orderByValue] of orderBy.entries()) {
24424
24424
  orderByList.push(orderByValue);
24425
- if (index < orderBy.length - 1) {
24425
+ if (index2 < orderBy.length - 1) {
24426
24426
  orderByList.push(sql`, `);
24427
24427
  }
24428
24428
  }
@@ -24471,9 +24471,9 @@ var SQLiteDialect = class {
24471
24471
  const havingSql = having ? sql` having ${having}` : void 0;
24472
24472
  const groupByList = [];
24473
24473
  if (groupBy) {
24474
- for (const [index, groupByValue] of groupBy.entries()) {
24474
+ for (const [index2, groupByValue] of groupBy.entries()) {
24475
24475
  groupByList.push(groupByValue);
24476
- if (index < groupBy.length - 1) {
24476
+ if (index2 < groupBy.length - 1) {
24477
24477
  groupByList.push(sql`, `);
24478
24478
  }
24479
24479
  }
@@ -24959,7 +24959,7 @@ var SQLiteSelectQueryBuilderBase = class extends TypedQueryBuilder {
24959
24959
  return (table, on) => {
24960
24960
  const baseTableName = this.tableName;
24961
24961
  const tableName = getTableLikeName(table);
24962
- if (typeof tableName === "string" && this.config.joins?.some((join3) => join3.alias === tableName)) {
24962
+ if (typeof tableName === "string" && this.config.joins?.some((join5) => join5.alias === tableName)) {
24963
24963
  throw new Error(`Alias "${tableName}" is already used in this query`);
24964
24964
  }
24965
24965
  if (!this.isPartialSelect) {
@@ -25772,7 +25772,7 @@ var SQLiteUpdateBase = class extends QueryPromise {
25772
25772
  createJoin(joinType) {
25773
25773
  return (table, on) => {
25774
25774
  const tableName = getTableLikeName(table);
25775
- if (typeof tableName === "string" && this.config.joins.some((join3) => join3.alias === tableName)) {
25775
+ if (typeof tableName === "string" && this.config.joins.some((join5) => join5.alias === tableName)) {
25776
25776
  throw new Error(`Alias "${tableName}" is already used in this query`);
25777
25777
  }
25778
25778
  if (typeof on === "function") {
@@ -26401,6 +26401,52 @@ var BaseSQLiteDatabase = class {
26401
26401
  }
26402
26402
  };
26403
26403
 
26404
+ // node_modules/drizzle-orm/sqlite-core/indexes.js
26405
+ var IndexBuilderOn = class {
26406
+ constructor(name, unique) {
26407
+ this.name = name;
26408
+ this.unique = unique;
26409
+ }
26410
+ static [entityKind] = "SQLiteIndexBuilderOn";
26411
+ on(...columns) {
26412
+ return new IndexBuilder(this.name, columns, this.unique);
26413
+ }
26414
+ };
26415
+ var IndexBuilder = class {
26416
+ static [entityKind] = "SQLiteIndexBuilder";
26417
+ /** @internal */
26418
+ config;
26419
+ constructor(name, columns, unique) {
26420
+ this.config = {
26421
+ name,
26422
+ columns,
26423
+ unique,
26424
+ where: void 0
26425
+ };
26426
+ }
26427
+ /**
26428
+ * Condition for partial index.
26429
+ */
26430
+ where(condition) {
26431
+ this.config.where = condition;
26432
+ return this;
26433
+ }
26434
+ /** @internal */
26435
+ build(table) {
26436
+ return new Index(this.config, table);
26437
+ }
26438
+ };
26439
+ var Index = class {
26440
+ static [entityKind] = "SQLiteIndex";
26441
+ config;
26442
+ constructor(config2, table) {
26443
+ this.config = { ...config2, table };
26444
+ }
26445
+ };
26446
+ function index(name) {
26447
+ return new IndexBuilderOn(name, false);
26448
+ }
26449
+
26404
26450
  // node_modules/drizzle-orm/sqlite-core/session.js
26405
26451
  var ExecuteResultSync = class extends QueryPromise {
26406
26452
  constructor(resultCb) {
@@ -26747,12 +26793,17 @@ var tasks = sqliteTable("tasks", {
26747
26793
  resultLog: text("result_log"),
26748
26794
  retryCount: integer2("retry_count").default(0),
26749
26795
  maxRetries: integer2("max_retries").default(3),
26796
+ retryBackoffMs: integer2("retry_backoff_ms").default(3e4),
26750
26797
  // Gateway 扩展字段(毫秒)
26751
26798
  retryAfter: integer2("retry_after"),
26752
26799
  timeoutMs: integer2("timeout_ms"),
26753
26800
  templateId: integer2("template_id"),
26754
26801
  scheduledAt: integer2("scheduled_at")
26755
- });
26802
+ }, (table) => [
26803
+ index("tasks_queue_idx").on(table.status, table.retryAfter, table.urgency, table.importance, table.createdAt, table.id),
26804
+ index("tasks_batch_status_idx").on(table.batchId, table.status),
26805
+ index("tasks_template_status_idx").on(table.templateId, table.status)
26806
+ ]);
26756
26807
  var TASK_CATEGORIES = [
26757
26808
  "translate",
26758
26809
  "generate",
@@ -26762,7 +26813,7 @@ var TASK_CATEGORIES = [
26762
26813
  ];
26763
26814
  var taskRuns = sqliteTable("task_runs", {
26764
26815
  id: integer2("id").primaryKey({ autoIncrement: true }),
26765
- taskId: integer2("task_id").notNull().references(() => tasks.id),
26816
+ taskId: integer2("task_id").notNull().references(() => tasks.id, { onDelete: "cascade" }),
26766
26817
  sessionId: text("session_id"),
26767
26818
  model: text("model"),
26768
26819
  status: text("status").default("running"),
@@ -26775,7 +26826,10 @@ var taskRuns = sqliteTable("task_runs", {
26775
26826
  heartbeatAt: integer2("heartbeat_at"),
26776
26827
  workerPid: integer2("worker_pid"),
26777
26828
  childPid: integer2("child_pid")
26778
- });
26829
+ }, (table) => [
26830
+ index("task_runs_task_started_idx").on(table.taskId, table.startedAt, table.id),
26831
+ index("task_runs_status_heartbeat_idx").on(table.status, table.heartbeatAt)
26832
+ ]);
26779
26833
  var taskTemplates = sqliteTable("task_templates", {
26780
26834
  id: integer2("id").primaryKey({ autoIncrement: true }),
26781
26835
  name: text("name").notNull(),
@@ -26786,6 +26840,7 @@ var taskTemplates = sqliteTable("task_templates", {
26786
26840
  category: text("category").default("general"),
26787
26841
  importance: integer2("importance").default(3),
26788
26842
  urgency: integer2("urgency").default(3),
26843
+ batchId: text("batch_id"),
26789
26844
  scheduleType: text("schedule_type").notNull(),
26790
26845
  cronExpr: text("cron_expr"),
26791
26846
  intervalMs: integer2("interval_ms"),
@@ -26793,12 +26848,15 @@ var taskTemplates = sqliteTable("task_templates", {
26793
26848
  maxInstances: integer2("max_instances").default(1),
26794
26849
  maxRetries: integer2("max_retries").default(3),
26795
26850
  retryBackoffMs: integer2("retry_backoff_ms").default(3e4),
26851
+ timeoutMs: integer2("timeout_ms"),
26796
26852
  lastRunAt: integer2("last_run_at"),
26797
26853
  nextRunAt: integer2("next_run_at"),
26798
26854
  enabled: integer2("enabled", { mode: "boolean" }).default(true),
26799
26855
  createdAt: integer2("created_at").default(0),
26800
26856
  updatedAt: integer2("updated_at").default(0)
26801
- });
26857
+ }, (table) => [
26858
+ index("task_templates_due_idx").on(table.enabled, table.nextRunAt, table.id)
26859
+ ]);
26802
26860
 
26803
26861
  // src/core/db/index.ts
26804
26862
  import { existsSync, mkdirSync } from "fs";
@@ -26812,7 +26870,15 @@ var _db = null;
26812
26870
  var _migrationRan = false;
26813
26871
  function getMigrationsFolder() {
26814
26872
  const __dirname2 = dirname(fileURLToPath(import.meta.url));
26815
- return join(__dirname2, "../../../drizzle");
26873
+ let dir = __dirname2;
26874
+ for (let i = 0; i < 5; i++) {
26875
+ const candidate = join(dir, "drizzle", "meta", "_journal.json");
26876
+ if (existsSync(candidate)) {
26877
+ return join(dir, "drizzle");
26878
+ }
26879
+ dir = dirname(dir);
26880
+ }
26881
+ return join(__dirname2, "../../drizzle");
26816
26882
  }
26817
26883
  function initDb() {
26818
26884
  const dataDir = dirname(DB_FILE_PATH);
@@ -26827,16 +26893,30 @@ function initDb() {
26827
26893
  id INTEGER PRIMARY KEY CHECK (id = 1),
26828
26894
  pid INTEGER NOT NULL,
26829
26895
  acquired_at INTEGER NOT NULL,
26830
- heartbeat_at INTEGER NOT NULL
26896
+ heartbeat_at INTEGER NOT NULL,
26897
+ ready_at INTEGER
26831
26898
  );
26832
26899
  `);
26900
+ const gatewayLockColumns = _sqlite.query("PRAGMA table_info(gateway_lock)").all();
26901
+ if (!gatewayLockColumns.some((column) => column.name === "ready_at")) {
26902
+ _sqlite.exec("ALTER TABLE gateway_lock ADD COLUMN ready_at INTEGER;");
26903
+ }
26833
26904
  _db = drizzle(_sqlite, { schema: schema_exports });
26834
26905
  if (!_migrationRan) {
26835
- _migrationRan = true;
26836
26906
  try {
26837
26907
  migrate(_db, { migrationsFolder: getMigrationsFolder() });
26908
+ _sqlite.exec("PRAGMA foreign_keys = ON;");
26909
+ const violations = _sqlite.query("PRAGMA foreign_key_check;").all();
26910
+ if (violations.length > 0) {
26911
+ throw new Error(`\u68C0\u6D4B\u5230 ${violations.length} \u6761\u5B64\u7ACB\u5173\u8054\u8BB0\u5F55\uFF0C\u8BF7\u5148\u4FEE\u590D\u6570\u636E\u518D\u542F\u52A8`);
26912
+ }
26913
+ _migrationRan = true;
26838
26914
  } catch (err) {
26839
26915
  const msg = err instanceof Error ? err.message : String(err);
26916
+ _migrationRan = false;
26917
+ _sqlite.close();
26918
+ _sqlite = null;
26919
+ _db = null;
26840
26920
  console.error(`[supertask] migration failed: ${msg}`);
26841
26921
  throw new Error(`[supertask] DB migration failed: ${msg}`);
26842
26922
  }
@@ -26875,7 +26955,7 @@ function computeBackoff(retryCount, baseMs = 3e4, maxMs = MAX_BACKOFF_MS) {
26875
26955
  }
26876
26956
 
26877
26957
  // src/core/services/task.service.ts
26878
- var { tasks: tasks2 } = schema_exports;
26958
+ var { tasks: tasks2, taskRuns: taskRuns2 } = schema_exports;
26879
26959
  var TaskService = class {
26880
26960
  static buildScopeWhere(scope) {
26881
26961
  const conditions = [];
@@ -26885,9 +26965,27 @@ var TaskService = class {
26885
26965
  return conditions;
26886
26966
  }
26887
26967
  static async add(data) {
26968
+ this.validateNewTask(data);
26888
26969
  const result = await db.insert(tasks2).values(data).returning();
26889
26970
  return result[0];
26890
26971
  }
26972
+ static validateNewTask(data) {
26973
+ if (!data.name.trim()) throw new Error("name \u4E0D\u80FD\u4E3A\u7A7A");
26974
+ if (!data.agent.trim()) throw new Error("agent \u4E0D\u80FD\u4E3A\u7A7A");
26975
+ if (!data.prompt.trim()) throw new Error("prompt \u4E0D\u80FD\u4E3A\u7A7A");
26976
+ this.validateInteger("importance", data.importance, 1, 5);
26977
+ this.validateInteger("urgency", data.urgency, 1, 5);
26978
+ this.validateInteger("maxRetries", data.maxRetries, 0, 1e3);
26979
+ this.validateInteger("retryBackoffMs", data.retryBackoffMs, 0, 864e5);
26980
+ this.validateInteger("timeoutMs", data.timeoutMs, 1e3, 6048e5);
26981
+ this.validateInteger("dependsOn", data.dependsOn, 1, Number.MAX_SAFE_INTEGER);
26982
+ }
26983
+ static validateInteger(name, value, min, max) {
26984
+ if (value === void 0 || value === null) return;
26985
+ if (!Number.isInteger(value) || value < min || value > max) {
26986
+ throw new Error(`${name} \u5FC5\u987B\u662F ${min} \u5230 ${max} \u4E4B\u95F4\u7684\u6574\u6570`);
26987
+ }
26988
+ }
26891
26989
  static async next(scope = {}) {
26892
26990
  const baseConditions = [...this.buildScopeWhere(scope)];
26893
26991
  const nowMs = Date.now();
@@ -26910,7 +27008,7 @@ var TaskService = class {
26910
27008
  ),
26911
27009
  and(
26912
27010
  eq(tasks2.status, "failed"),
26913
- sql`${tasks2.retryCount} < ${tasks2.maxRetries}`,
27011
+ sql`${tasks2.retryCount} <= ${tasks2.maxRetries}`,
26914
27012
  retryAfterFilter
26915
27013
  )
26916
27014
  );
@@ -26924,7 +27022,8 @@ var TaskService = class {
26924
27022
  const allTasks = await db.select().from(tasks2).where(and(...conditions)).orderBy(
26925
27023
  desc(tasks2.urgency),
26926
27024
  desc(tasks2.importance),
26927
- asc(tasks2.createdAt)
27025
+ asc(tasks2.createdAt),
27026
+ asc(tasks2.id)
26928
27027
  );
26929
27028
  for (const task of allTasks) {
26930
27029
  if (task.dependsOn) {
@@ -26945,7 +27044,7 @@ var TaskService = class {
26945
27044
  eq(tasks2.status, "pending"),
26946
27045
  and(
26947
27046
  eq(tasks2.status, "failed"),
26948
- sql`${tasks2.retryCount} < ${tasks2.maxRetries}`
27047
+ sql`${tasks2.retryCount} <= ${tasks2.maxRetries}`
26949
27048
  )
26950
27049
  ),
26951
27050
  ...this.buildScopeWhere(scope)
@@ -26958,7 +27057,11 @@ var TaskService = class {
26958
27057
  return result[0] || null;
26959
27058
  }
26960
27059
  static async done(id, log, scope = {}) {
26961
- const conditions = [eq(tasks2.id, id), ...this.buildScopeWhere(scope)];
27060
+ const conditions = [
27061
+ eq(tasks2.id, id),
27062
+ eq(tasks2.status, "running"),
27063
+ ...this.buildScopeWhere(scope)
27064
+ ];
26962
27065
  const result = await db.update(tasks2).set({
26963
27066
  status: "done",
26964
27067
  finishedAt: /* @__PURE__ */ new Date(),
@@ -26969,17 +27072,24 @@ var TaskService = class {
26969
27072
  }
26970
27073
  static async fail(id, log, scope = {}, options) {
26971
27074
  const current = await this.getById(id, scope);
26972
- if (!current) return null;
27075
+ if (!current || current.status !== "running") return null;
26973
27076
  const newRetryCount = (current.retryCount ?? 0) + 1;
26974
27077
  const maxRetries = current.maxRetries ?? 3;
26975
- const isDeadLetter = options?.setDeadLetter ?? newRetryCount >= maxRetries;
26976
- const conditions = [eq(tasks2.id, id), ...this.buildScopeWhere(scope)];
27078
+ const isDeadLetter = options?.setDeadLetter ?? newRetryCount > maxRetries;
27079
+ const conditions = [
27080
+ eq(tasks2.id, id),
27081
+ eq(tasks2.status, "running"),
27082
+ ...this.buildScopeWhere(scope)
27083
+ ];
26977
27084
  const result = await db.update(tasks2).set({
26978
27085
  status: isDeadLetter ? "dead_letter" : "failed",
26979
27086
  finishedAt: /* @__PURE__ */ new Date(),
26980
27087
  resultLog: log,
26981
27088
  retryCount: newRetryCount,
26982
- retryAfter: isDeadLetter ? null : options?.retryAfterMs ?? Date.now() + computeBackoff(newRetryCount)
27089
+ retryAfter: isDeadLetter ? null : options?.retryAfterMs ?? Date.now() + computeBackoff(
27090
+ newRetryCount,
27091
+ current.retryBackoffMs ?? 3e4
27092
+ )
26983
27093
  }).where(and(...conditions)).returning();
26984
27094
  return result[0] || null;
26985
27095
  }
@@ -27012,8 +27122,20 @@ var TaskService = class {
27012
27122
  return result.length;
27013
27123
  }
27014
27124
  static async cancel(id, scope = {}) {
27015
- const conditions = [eq(tasks2.id, id), ...this.buildScopeWhere(scope)];
27016
- const result = await db.update(tasks2).set({ status: "cancelled" }).where(and(...conditions)).returning();
27125
+ const conditions = [
27126
+ eq(tasks2.id, id),
27127
+ or(
27128
+ eq(tasks2.status, "pending"),
27129
+ eq(tasks2.status, "running"),
27130
+ eq(tasks2.status, "failed")
27131
+ ),
27132
+ ...this.buildScopeWhere(scope)
27133
+ ];
27134
+ const result = await db.update(tasks2).set({
27135
+ status: "cancelled",
27136
+ finishedAt: /* @__PURE__ */ new Date(),
27137
+ retryAfter: null
27138
+ }).where(and(...conditions)).returning();
27017
27139
  return result[0] || null;
27018
27140
  }
27019
27141
  static async retry(id, scope = {}) {
@@ -27025,7 +27147,8 @@ var TaskService = class {
27025
27147
  status: "pending",
27026
27148
  startedAt: null,
27027
27149
  finishedAt: null,
27028
- retryAfter: null
27150
+ retryAfter: null,
27151
+ retryCount: 0
27029
27152
  }).where(and(...conditions)).returning();
27030
27153
  return result[0] || null;
27031
27154
  }
@@ -27039,7 +27162,8 @@ var TaskService = class {
27039
27162
  status: "pending",
27040
27163
  startedAt: null,
27041
27164
  finishedAt: null,
27042
- retryAfter: null
27165
+ retryAfter: null,
27166
+ retryCount: 0
27043
27167
  }).where(and(...conditions)).returning();
27044
27168
  return result.length;
27045
27169
  }
@@ -27107,6 +27231,9 @@ var TaskService = class {
27107
27231
  }
27108
27232
  static async delete(id, scope = {}) {
27109
27233
  const conditions = [eq(tasks2.id, id), ...this.buildScopeWhere(scope)];
27234
+ const existing = await db.select({ id: tasks2.id }).from(tasks2).where(and(...conditions)).limit(1);
27235
+ if (!existing[0]) return false;
27236
+ await db.delete(taskRuns2).where(eq(taskRuns2.taskId, id));
27110
27237
  const result = await db.delete(tasks2).where(and(...conditions)).returning();
27111
27238
  return result.length > 0;
27112
27239
  }
@@ -27148,6 +27275,7 @@ function isValidCronExpr(expr) {
27148
27275
  var { taskTemplates: taskTemplates2 } = schema_exports;
27149
27276
  var TaskTemplateService = class {
27150
27277
  static async create(data) {
27278
+ this.validate(data);
27151
27279
  const now = Date.now();
27152
27280
  const result = await db.insert(taskTemplates2).values({ ...data, createdAt: now, updatedAt: now }).returning();
27153
27281
  const tmpl = result[0];
@@ -27163,8 +27291,38 @@ var TaskTemplateService = class {
27163
27291
  }
27164
27292
  return tmpl;
27165
27293
  }
27294
+ static validate(data) {
27295
+ if (!data.name.trim()) throw new Error("name \u4E0D\u80FD\u4E3A\u7A7A");
27296
+ if (!data.agent.trim()) throw new Error("agent \u4E0D\u80FD\u4E3A\u7A7A");
27297
+ if (!data.prompt.trim()) throw new Error("prompt \u4E0D\u80FD\u4E3A\u7A7A");
27298
+ const scheduleType = data.scheduleType;
27299
+ if (!["cron", "delayed", "recurring"].includes(scheduleType)) {
27300
+ throw new Error("scheduleType \u5FC5\u987B\u662F cron\u3001delayed \u6216 recurring");
27301
+ }
27302
+ if (scheduleType === "cron" && (!data.cronExpr || !isValidCronExpr(data.cronExpr))) {
27303
+ throw new Error("cronExpr \u7F3A\u5931\u6216\u683C\u5F0F\u65E0\u6548");
27304
+ }
27305
+ if (scheduleType === "recurring" && (!Number.isInteger(data.intervalMs) || (data.intervalMs ?? 0) <= 0)) {
27306
+ throw new Error("intervalMs \u5FC5\u987B\u662F\u6B63\u6574\u6570");
27307
+ }
27308
+ if (scheduleType === "delayed" && (!Number.isInteger(data.runAt) || (data.runAt ?? 0) <= 0)) {
27309
+ throw new Error("runAt \u5FC5\u987B\u662F\u6B63\u6574\u6570\u65F6\u95F4\u6233");
27310
+ }
27311
+ this.validateInteger("importance", data.importance, 1, 5);
27312
+ this.validateInteger("urgency", data.urgency, 1, 5);
27313
+ this.validateInteger("maxInstances", data.maxInstances, 1, 1e3);
27314
+ this.validateInteger("maxRetries", data.maxRetries, 0, 1e3);
27315
+ this.validateInteger("retryBackoffMs", data.retryBackoffMs, 0, 864e5);
27316
+ this.validateInteger("timeoutMs", data.timeoutMs, 1e3, 6048e5);
27317
+ }
27318
+ static validateInteger(name, value, min, max) {
27319
+ if (value === void 0 || value === null) return;
27320
+ if (!Number.isInteger(value) || value < min || value > max) {
27321
+ throw new Error(`${name} \u5FC5\u987B\u662F ${min} \u5230 ${max} \u4E4B\u95F4\u7684\u6574\u6570`);
27322
+ }
27323
+ }
27166
27324
  static async list(limit = 50) {
27167
- return await db.select().from(taskTemplates2).orderBy(desc(taskTemplates2.createdAt)).limit(limit);
27325
+ return await db.select().from(taskTemplates2).orderBy(desc(taskTemplates2.createdAt), desc(taskTemplates2.id)).limit(limit);
27168
27326
  }
27169
27327
  static async getById(id) {
27170
27328
  const result = await db.select().from(taskTemplates2).where(eq(taskTemplates2.id, id));
@@ -27233,85 +27391,261 @@ function parseDuration(input) {
27233
27391
 
27234
27392
  // src/daemon/pm2.ts
27235
27393
  import { execSync, spawnSync } from "child_process";
27236
- import { join as join2, dirname as dirname2 } from "path";
27394
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
27395
+ import { homedir as homedir3 } from "os";
27396
+ import { dirname as dirname2, join as join3 } from "path";
27237
27397
  import { fileURLToPath as fileURLToPath2 } from "url";
27238
- import { readFileSync, writeFileSync, existsSync as existsSync2 } from "fs";
27398
+ import { Database as Database3 } from "bun:sqlite";
27399
+
27400
+ // src/gateway/config.ts
27401
+ import { existsSync as existsSync2, readFileSync } from "fs";
27239
27402
  import { homedir as homedir2 } from "os";
27403
+ import { join as join2 } from "path";
27404
+ var DEFAULT_CONFIG = {
27405
+ configVersion: 2,
27406
+ worker: {
27407
+ maxConcurrency: 2,
27408
+ pollIntervalMs: 1e3,
27409
+ heartbeatIntervalMs: 3e4,
27410
+ taskTimeoutMs: 18e5,
27411
+ shutdownGracePeriodMs: 3e4
27412
+ },
27413
+ scheduler: {
27414
+ enabled: true,
27415
+ checkIntervalMs: 1e3
27416
+ },
27417
+ watchdog: {
27418
+ heartbeatTimeoutMs: 6e5,
27419
+ checkIntervalMs: 6e4,
27420
+ cleanupIntervalMs: 864e5,
27421
+ retentionDays: 30
27422
+ },
27423
+ dashboard: {
27424
+ enabled: true,
27425
+ port: 4680
27426
+ }
27427
+ };
27428
+ var DEFAULT_CONFIG_PATH = join2(homedir2(), ".config/opencode/supertask.json");
27429
+ function getConfigPath() {
27430
+ return process.env.SUPERTASK_CONFIG_PATH ?? DEFAULT_CONFIG_PATH;
27431
+ }
27432
+ function objectAt(value, path) {
27433
+ if (value === void 0) return {};
27434
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
27435
+ throw new Error(`${path} \u5FC5\u987B\u662F\u5BF9\u8C61`);
27436
+ }
27437
+ return value;
27438
+ }
27439
+ function integerAt(source, key, fallback, min, max, path) {
27440
+ const value = source[key];
27441
+ if (value === void 0) return fallback;
27442
+ if (typeof value !== "number" || !Number.isInteger(value) || value < min || value > max) {
27443
+ throw new Error(`${path}.${key} \u5FC5\u987B\u662F ${min} \u5230 ${max} \u4E4B\u95F4\u7684\u6574\u6570`);
27444
+ }
27445
+ return value;
27446
+ }
27447
+ function booleanAt(source, key, fallback, path) {
27448
+ const value = source[key];
27449
+ if (value === void 0) return fallback;
27450
+ if (typeof value !== "boolean") throw new Error(`${path}.${key} \u5FC5\u987B\u662F\u5E03\u5C14\u503C`);
27451
+ return value;
27452
+ }
27453
+ function validateConfig(input) {
27454
+ const root = objectAt(input, "config");
27455
+ const version3 = root.configVersion ?? 1;
27456
+ if (version3 !== 1 && version3 !== 2) {
27457
+ throw new Error("config.configVersion \u53EA\u652F\u6301 1 \u6216 2");
27458
+ }
27459
+ const worker = objectAt(root.worker, "worker");
27460
+ const scheduler = objectAt(root.scheduler, "scheduler");
27461
+ const watchdog = objectAt(root.watchdog, "watchdog");
27462
+ const dashboard = objectAt(root.dashboard, "dashboard");
27463
+ const heartbeatIntervalMs = integerAt(worker, "heartbeatIntervalMs", DEFAULT_CONFIG.worker.heartbeatIntervalMs, 1e3, 36e5, "worker");
27464
+ const heartbeatTimeoutMs = integerAt(watchdog, "heartbeatTimeoutMs", DEFAULT_CONFIG.watchdog.heartbeatTimeoutMs, 1e3, 864e5, "watchdog");
27465
+ let checkIntervalMs;
27466
+ let cleanupIntervalMs;
27467
+ if (version3 === 1 && watchdog.checkIntervalMs === void 0 && watchdog.cleanupIntervalMs !== void 0) {
27468
+ checkIntervalMs = integerAt(watchdog, "cleanupIntervalMs", DEFAULT_CONFIG.watchdog.checkIntervalMs, 1e3, 36e5, "watchdog");
27469
+ cleanupIntervalMs = DEFAULT_CONFIG.watchdog.cleanupIntervalMs;
27470
+ } else {
27471
+ checkIntervalMs = integerAt(watchdog, "checkIntervalMs", DEFAULT_CONFIG.watchdog.checkIntervalMs, 1e3, 36e5, "watchdog");
27472
+ cleanupIntervalMs = integerAt(watchdog, "cleanupIntervalMs", DEFAULT_CONFIG.watchdog.cleanupIntervalMs, 6e4, 6048e5, "watchdog");
27473
+ }
27474
+ if (heartbeatIntervalMs >= heartbeatTimeoutMs) {
27475
+ throw new Error("worker.heartbeatIntervalMs \u5FC5\u987B\u5C0F\u4E8E watchdog.heartbeatTimeoutMs");
27476
+ }
27477
+ if (checkIntervalMs > heartbeatTimeoutMs) {
27478
+ throw new Error("watchdog.checkIntervalMs \u4E0D\u80FD\u5927\u4E8E watchdog.heartbeatTimeoutMs");
27479
+ }
27480
+ return {
27481
+ configVersion: 2,
27482
+ worker: {
27483
+ maxConcurrency: integerAt(worker, "maxConcurrency", DEFAULT_CONFIG.worker.maxConcurrency, 1, 64, "worker"),
27484
+ pollIntervalMs: integerAt(worker, "pollIntervalMs", DEFAULT_CONFIG.worker.pollIntervalMs, 50, 6e4, "worker"),
27485
+ heartbeatIntervalMs,
27486
+ taskTimeoutMs: integerAt(worker, "taskTimeoutMs", DEFAULT_CONFIG.worker.taskTimeoutMs, 1e3, 6048e5, "worker"),
27487
+ shutdownGracePeriodMs: integerAt(worker, "shutdownGracePeriodMs", DEFAULT_CONFIG.worker.shutdownGracePeriodMs, 0, 36e5, "worker")
27488
+ },
27489
+ scheduler: {
27490
+ enabled: booleanAt(scheduler, "enabled", DEFAULT_CONFIG.scheduler.enabled, "scheduler"),
27491
+ checkIntervalMs: integerAt(scheduler, "checkIntervalMs", DEFAULT_CONFIG.scheduler.checkIntervalMs, 100, 6e4, "scheduler")
27492
+ },
27493
+ watchdog: {
27494
+ heartbeatTimeoutMs,
27495
+ checkIntervalMs,
27496
+ cleanupIntervalMs,
27497
+ retentionDays: integerAt(watchdog, "retentionDays", DEFAULT_CONFIG.watchdog.retentionDays, 1, 3650, "watchdog")
27498
+ },
27499
+ dashboard: {
27500
+ enabled: booleanAt(dashboard, "enabled", DEFAULT_CONFIG.dashboard.enabled, "dashboard"),
27501
+ port: integerAt(dashboard, "port", DEFAULT_CONFIG.dashboard.port, 1, 65535, "dashboard")
27502
+ }
27503
+ };
27504
+ }
27505
+ function loadConfig(path = getConfigPath()) {
27506
+ if (!existsSync2(path)) return validateConfig({ configVersion: 2 });
27507
+ try {
27508
+ return validateConfig(JSON.parse(readFileSync(path, "utf-8")));
27509
+ } catch (error45) {
27510
+ const message = error45 instanceof Error ? error45.message : String(error45);
27511
+ throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u914D\u7F6E ${path}: ${message}`);
27512
+ }
27513
+ }
27514
+
27515
+ // src/daemon/pm2.ts
27240
27516
  var __dirname = dirname2(fileURLToPath2(import.meta.url));
27241
- var GATEWAY_ENTRY = join2(__dirname, "../gateway/index.js");
27242
27517
  var PROCESS_NAME = "supertask-gateway";
27243
- var VERSION_FILE = join2(homedir2(), ".local/share/opencode/supertask-gateway-version");
27518
+ var GATEWAY_LOCK_STALE_MS = 3e4;
27244
27519
  function getPackageVersion() {
27520
+ const envVersion = process.env.npm_package_version;
27521
+ if (envVersion) return envVersion;
27245
27522
  try {
27246
- const pkgPath = join2(__dirname, "../package.json");
27247
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
27248
- return pkg.version || "0.0.0";
27523
+ const pkgPath = join3(__dirname, "../../package.json");
27524
+ const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
27525
+ return typeof pkg.version === "string" ? pkg.version : "0.0.0";
27249
27526
  } catch {
27250
27527
  return "0.0.0";
27251
27528
  }
27252
27529
  }
27530
+ function resolveGatewayEntry() {
27531
+ const override = process.env.SUPERTASK_GATEWAY_ENTRY;
27532
+ if (override) {
27533
+ if (!existsSync3(override)) throw new Error(`[supertask] Gateway entry not found: ${override}`);
27534
+ return override;
27535
+ }
27536
+ const candidates = [
27537
+ join3(__dirname, "../gateway/index.js"),
27538
+ join3(__dirname, "../gateway/index.ts")
27539
+ ];
27540
+ const entry = candidates.find((candidate) => existsSync3(candidate));
27541
+ if (!entry) throw new Error(`[supertask] Gateway entry not found. Checked: ${candidates.join(", ")}`);
27542
+ return entry;
27543
+ }
27544
+ function versionFile() {
27545
+ return process.env.SUPERTASK_VERSION_FILE ?? join3(homedir3(), ".local/share/opencode/supertask-gateway-version");
27546
+ }
27253
27547
  function getRunningVersion() {
27254
27548
  try {
27255
- if (!existsSync2(VERSION_FILE)) return null;
27256
- return readFileSync(VERSION_FILE, "utf-8").trim() || null;
27549
+ const path = versionFile();
27550
+ if (!existsSync3(path)) return null;
27551
+ return readFileSync2(path, "utf-8").trim() || null;
27257
27552
  } catch {
27258
27553
  return null;
27259
27554
  }
27260
27555
  }
27261
27556
  function writeRunningVersion(version3) {
27262
- try {
27263
- writeFileSync(VERSION_FILE, version3, "utf-8");
27264
- } catch {
27265
- }
27557
+ const path = versionFile();
27558
+ mkdirSync2(dirname2(path), { recursive: true });
27559
+ writeFileSync(path, version3, "utf-8");
27266
27560
  }
27267
27561
  function pm2Bin() {
27268
- return process.platform === "win32" ? "pm2.cmd" : "pm2";
27562
+ return process.env.SUPERTASK_PM2_BIN ?? (process.platform === "win32" ? "pm2.cmd" : "pm2");
27269
27563
  }
27270
27564
  function isPm2Installed() {
27271
- try {
27272
- const cmd = process.platform === "win32" ? "where pm2" : "which pm2";
27273
- execSync(cmd, { stdio: "pipe" });
27274
- return true;
27275
- } catch {
27276
- return false;
27277
- }
27565
+ const result = spawnSync(pm2Bin(), ["--version"], {
27566
+ stdio: "ignore",
27567
+ shell: process.platform === "win32"
27568
+ });
27569
+ return result.status === 0;
27278
27570
  }
27279
27571
  function pm2Exec(args) {
27280
- const bin = pm2Bin();
27572
+ const result = spawnSync(pm2Bin(), args, {
27573
+ stdio: ["pipe", "pipe", "pipe"],
27574
+ encoding: "utf-8",
27575
+ env: process.env,
27576
+ shell: process.platform === "win32"
27577
+ });
27578
+ const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
27579
+ if (result.error) return { ok: false, output: result.error.message };
27580
+ return { ok: result.status === 0, output };
27581
+ }
27582
+ function requirePm2(args, action) {
27583
+ const result = pm2Exec(args);
27584
+ if (!result.ok) throw new Error(`[supertask] ${action} failed: ${result.output || "unknown pm2 error"}`);
27585
+ return result.output;
27586
+ }
27587
+ function pm2JsonList() {
27588
+ const output = requirePm2(["jlist"], "pm2 jlist");
27281
27589
  try {
27282
- const result = spawnSync(bin, args, {
27283
- stdio: ["pipe", "pipe", "pipe"],
27284
- encoding: "utf-8",
27285
- shell: process.platform === "win32"
27286
- });
27287
- const output = (result.stdout ?? "") + (result.stderr ?? "");
27288
- return { ok: result.status === 0, output };
27289
- } catch (err) {
27290
- return { ok: false, output: err instanceof Error ? err.message : String(err) };
27590
+ const parsed = JSON.parse(output);
27591
+ if (!Array.isArray(parsed)) throw new Error("result is not an array");
27592
+ return parsed;
27593
+ } catch (error45) {
27594
+ const message = error45 instanceof Error ? error45.message : String(error45);
27595
+ throw new Error(`[supertask] Invalid pm2 jlist output: ${message}`);
27291
27596
  }
27292
27597
  }
27293
- function pm2JsonList() {
27294
- const { ok, output } = pm2Exec(["jlist"]);
27295
- if (!ok) return [];
27598
+ function databasePath() {
27599
+ return process.env.SUPERTASK_DB_PATH ?? join3(homedir3(), ".local/share/opencode/tasks.db");
27600
+ }
27601
+ function isGatewayReady(expectedPid) {
27602
+ const path = databasePath();
27603
+ if (!existsSync3(path)) return false;
27604
+ let database = null;
27296
27605
  try {
27297
- return JSON.parse(output);
27606
+ database = new Database3(path, { readonly: true });
27607
+ const row = database.query(
27608
+ "SELECT pid, heartbeat_at, ready_at FROM gateway_lock WHERE id = 1"
27609
+ ).get();
27610
+ if (!row || row.ready_at == null) return false;
27611
+ if (expectedPid !== void 0 && row.pid !== expectedPid) return false;
27612
+ const ageMs = Date.now() - row.heartbeat_at;
27613
+ return ageMs >= -5e3 && ageMs < GATEWAY_LOCK_STALE_MS;
27298
27614
  } catch {
27299
- return [];
27615
+ return false;
27616
+ } finally {
27617
+ database?.close();
27300
27618
  }
27301
27619
  }
27620
+ function readyTimeoutMs() {
27621
+ const value = Number(process.env.SUPERTASK_GATEWAY_READY_TIMEOUT_MS ?? 3e4);
27622
+ return Number.isFinite(value) && value > 0 ? value : 3e4;
27623
+ }
27624
+ function waitForGatewayReady(pid) {
27625
+ const deadline = Date.now() + readyTimeoutMs();
27626
+ const sleeper = new Int32Array(new SharedArrayBuffer(4));
27627
+ while (Date.now() < deadline) {
27628
+ if (isGatewayReady(pid)) return true;
27629
+ Atomics.wait(sleeper, 0, 0, 100);
27630
+ }
27631
+ return isGatewayReady(pid);
27632
+ }
27302
27633
  function findBunPath() {
27634
+ const override = process.env.SUPERTASK_BUN_BIN;
27635
+ if (override) return override;
27303
27636
  try {
27304
- const cmd = process.platform === "win32" ? "where bun" : "which bun";
27305
- return execSync(cmd, { stdio: "pipe" }).toString().trim().split("\n")[0];
27637
+ const command = process.platform === "win32" ? "where bun" : "which bun";
27638
+ return execSync(command, { stdio: "pipe" }).toString().trim().split("\n")[0];
27306
27639
  } catch {
27307
27640
  return process.execPath;
27308
27641
  }
27309
27642
  }
27310
- function pm2StartGateway(version3) {
27311
- const bunPath = findBunPath();
27312
- return pm2Exec([
27643
+ function pm2StartGateway(gatewayEntry = resolveGatewayEntry()) {
27644
+ const configuredKillTimeout = Number(process.env.SUPERTASK_PM2_KILL_TIMEOUT_MS);
27645
+ const killTimeoutMs = Number.isInteger(configuredKillTimeout) && configuredKillTimeout >= 5e3 ? configuredKillTimeout : loadConfig().worker.shutdownGracePeriodMs + 5e3;
27646
+ requirePm2([
27313
27647
  "start",
27314
- bunPath,
27648
+ findBunPath(),
27315
27649
  "--name",
27316
27650
  PROCESS_NAME,
27317
27651
  "--interpreter",
@@ -27320,80 +27654,146 @@ function pm2StartGateway(version3) {
27320
27654
  "5000",
27321
27655
  "--max-restarts",
27322
27656
  "30",
27657
+ "--kill-timeout",
27658
+ String(killTimeoutMs),
27323
27659
  "--",
27324
- GATEWAY_ENTRY
27325
- ]);
27660
+ gatewayEntry
27661
+ ], "pm2 start");
27662
+ const started = pm2JsonList().find((item) => item.name === PROCESS_NAME);
27663
+ if (started?.pm2_env?.status !== "online") {
27664
+ throw new Error(`[supertask] Gateway did not become online (status: ${started?.pm2_env?.status ?? "missing"})`);
27665
+ }
27666
+ if (typeof started.pid !== "number" || !waitForGatewayReady(started.pid)) {
27667
+ throw new Error("[supertask] Gateway \u8FDB\u7A0B online\uFF0C\u4F46\u672A\u5728\u9650\u5B9A\u65F6\u95F4\u5185\u5C31\u7EEA\uFF1B\u8BF7\u67E5\u770B pm2 logs supertask-gateway");
27668
+ }
27669
+ }
27670
+ function savePm2State() {
27671
+ requirePm2(["save"], "pm2 save");
27326
27672
  }
27327
- function upgrade() {
27673
+ function upgrade(target) {
27674
+ if (!isPm2Installed()) {
27675
+ throw new Error("[supertask] pm2 is not installed. Run `supertask install` first.");
27676
+ }
27328
27677
  const before = getRunningVersion();
27329
- const currentVersion = getPackageVersion();
27330
- const list = pm2JsonList();
27331
- const proc = list.find((p) => p.name === PROCESS_NAME);
27332
- if (proc) {
27333
- console.log(`[supertask] Stopping Gateway (version ${before ?? "unknown"})...`);
27334
- pm2Exec(["delete", PROCESS_NAME]);
27335
- }
27336
- console.log(`[supertask] Starting Gateway (version ${currentVersion})...`);
27337
- const { ok } = pm2StartGateway(currentVersion);
27338
- if (ok) {
27678
+ const oldGatewayEntry = resolveGatewayEntry();
27679
+ const currentVersion = target?.version ?? getPackageVersion();
27680
+ const existing = pm2JsonList().find((item) => item.name === PROCESS_NAME);
27681
+ if (existing) requirePm2(["delete", PROCESS_NAME], "pm2 delete old Gateway");
27682
+ try {
27683
+ pm2StartGateway(target?.gatewayEntry ?? oldGatewayEntry);
27339
27684
  writeRunningVersion(currentVersion);
27340
- pm2Exec(["save"]);
27341
- console.log(`[supertask] Gateway upgraded: ${before ?? "unknown"} \u2192 ${currentVersion}`);
27342
- } else {
27343
- throw new Error(`[supertask] Failed to start Gateway after upgrade`);
27685
+ savePm2State();
27686
+ return { before, after: currentVersion, restarted: true };
27687
+ } catch (error45) {
27688
+ const failed = pm2JsonList().find((item) => item.name === PROCESS_NAME);
27689
+ if (failed) requirePm2(["delete", PROCESS_NAME], "pm2 delete failed Gateway");
27690
+ try {
27691
+ pm2StartGateway(oldGatewayEntry);
27692
+ if (before) writeRunningVersion(before);
27693
+ savePm2State();
27694
+ } catch (rollbackError) {
27695
+ const original = error45 instanceof Error ? error45.message : String(error45);
27696
+ const rollback = rollbackError instanceof Error ? rollbackError.message : String(rollbackError);
27697
+ throw new Error(`${original}; \u65E7 Gateway \u56DE\u6EDA\u4E5F\u5931\u8D25: ${rollback}`);
27698
+ }
27699
+ const message = error45 instanceof Error ? error45.message : String(error45);
27700
+ throw new Error(`${message}; \u5DF2\u56DE\u6EDA\u5230\u65E7 Gateway`);
27344
27701
  }
27345
- return { before, after: currentVersion, restarted: true };
27346
27702
  }
27347
27703
  function ensureGateway() {
27704
+ if (!isPm2Installed()) {
27705
+ return { ok: false, reason: "pm2-not-installed" };
27706
+ }
27348
27707
  const currentVersion = getPackageVersion();
27708
+ const processList = pm2JsonList();
27709
+ const existing = processList.find((item) => item.name === PROCESS_NAME);
27710
+ if (existing?.pm2_env?.status === "online" && typeof existing.pid === "number" && isGatewayReady(existing.pid) && getRunningVersion() === currentVersion) {
27711
+ return { ok: true, action: "already-running" };
27712
+ }
27713
+ if (existing) requirePm2(["delete", PROCESS_NAME], "pm2 delete stale Gateway");
27714
+ pm2StartGateway();
27715
+ writeRunningVersion(currentVersion);
27716
+ savePm2State();
27717
+ return { ok: true, action: existing ? "restarted" : "started" };
27718
+ }
27719
+
27720
+ // src/daemon/update.ts
27721
+ import { spawnSync as spawnSync2 } from "child_process";
27722
+ import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync } from "fs";
27723
+ import { homedir as homedir4 } from "os";
27724
+ import { join as join4 } from "path";
27725
+ var PACKAGE_NAME = "opencode-supertask";
27726
+ function pluginAt(packageDir) {
27727
+ const packageJson = join4(packageDir, "package.json");
27728
+ const gatewayEntry = join4(packageDir, "dist/gateway/index.js");
27729
+ if (!existsSync4(packageJson) || !existsSync4(gatewayEntry)) return null;
27349
27730
  try {
27350
- const list = pm2JsonList();
27351
- const proc = list.find((p) => p.name === PROCESS_NAME);
27352
- if (proc && proc.pm2_env?.status === "online") {
27353
- const runningVersion = getRunningVersion();
27354
- if (runningVersion === currentVersion) {
27355
- return;
27356
- }
27357
- console.log(`[supertask] Version changed: ${runningVersion ?? "unknown"} \u2192 ${currentVersion}, reloading Gateway...`);
27358
- pm2Exec(["delete", PROCESS_NAME]);
27359
- const { ok } = pm2StartGateway(currentVersion);
27360
- if (ok) writeRunningVersion(currentVersion);
27361
- pm2Exec(["save"]);
27362
- return;
27363
- }
27731
+ const pkg = JSON.parse(readFileSync3(packageJson, "utf8"));
27732
+ if (pkg.name !== PACKAGE_NAME || typeof pkg.version !== "string") return null;
27733
+ return { packageDir, gatewayEntry, version: pkg.version };
27364
27734
  } catch {
27735
+ return null;
27365
27736
  }
27366
- if (!isPm2Installed()) {
27367
- console.log("[supertask] Installing pm2 for Gateway process management...");
27368
- try {
27369
- execSync("npm install -g pm2", { stdio: "pipe" });
27370
- } catch {
27371
- try {
27372
- execSync("bun install -g pm2", { stdio: "pipe" });
27373
- } catch {
27374
- console.warn("[supertask] Could not install pm2. Gateway will not auto-start. Run `supertask install` manually.");
27375
- return;
27376
- }
27377
- }
27737
+ }
27738
+ function versionParts(version3) {
27739
+ const match = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(version3);
27740
+ return match ? match.slice(1).map(Number) : null;
27741
+ }
27742
+ function compareVersions(left, right) {
27743
+ const a = versionParts(left);
27744
+ const b = versionParts(right);
27745
+ if (!a || !b) return left.localeCompare(right);
27746
+ for (let index2 = 0; index2 < 3; index2 += 1) {
27747
+ if (a[index2] !== b[index2]) return a[index2] - b[index2];
27748
+ }
27749
+ return 0;
27750
+ }
27751
+ function cacheRoot() {
27752
+ return process.env.SUPERTASK_OPENCODE_CACHE_DIR ?? join4(homedir4(), ".cache/opencode/packages");
27753
+ }
27754
+ function resolveInstalledPlugin() {
27755
+ const override = process.env.SUPERTASK_PLUGIN_PACKAGE_DIR;
27756
+ if (override) {
27757
+ const plugin = pluginAt(override);
27758
+ if (!plugin) throw new Error(`[supertask] \u5B89\u88C5\u5305\u65E0\u6548\u6216\u7F3A\u5C11 Gateway \u6784\u5EFA\u4EA7\u7269: ${override}`);
27759
+ return plugin;
27760
+ }
27761
+ const root = cacheRoot();
27762
+ const packageDirs = existsSync4(root) ? readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && (entry.name === PACKAGE_NAME || entry.name.startsWith(`${PACKAGE_NAME}@`))).map((entry) => join4(root, entry.name, "node_modules", PACKAGE_NAME)) : [];
27763
+ const installed = packageDirs.map(pluginAt).filter((plugin) => plugin !== null).sort((left, right) => compareVersions(right.version, left.version));
27764
+ if (!installed[0]) {
27765
+ throw new Error(`[supertask] OpenCode \u63D2\u4EF6\u7F13\u5B58\u4E2D\u627E\u4E0D\u5230\u53EF\u8FD0\u884C\u7684 ${PACKAGE_NAME}`);
27766
+ }
27767
+ return installed[0];
27768
+ }
27769
+ function opencodeBin() {
27770
+ return process.env.SUPERTASK_OPENCODE_BIN ?? "opencode";
27771
+ }
27772
+ function installLatestPlugin() {
27773
+ const result = spawnSync2(opencodeBin(), [
27774
+ "plugin",
27775
+ `${PACKAGE_NAME}@latest`,
27776
+ "--global",
27777
+ "--force"
27778
+ ], {
27779
+ encoding: "utf8",
27780
+ env: process.env,
27781
+ timeout: 12e4
27782
+ });
27783
+ const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
27784
+ if (result.error) {
27785
+ throw new Error(`[supertask] OpenCode \u63D2\u4EF6\u66F4\u65B0\u5931\u8D25: ${result.error.message}`);
27378
27786
  }
27379
- const pm2List = pm2JsonList();
27380
- const existing = pm2List.find((p) => p.name === PROCESS_NAME);
27381
- if (existing) {
27382
- pm2Exec(["restart", PROCESS_NAME]);
27383
- } else {
27384
- const version3 = getPackageVersion();
27385
- const { ok } = pm2StartGateway(version3);
27386
- if (ok) writeRunningVersion(version3);
27787
+ if (result.status !== 0) {
27788
+ throw new Error(`[supertask] OpenCode \u63D2\u4EF6\u66F4\u65B0\u5931\u8D25: ${output || `\u9000\u51FA\u7801 ${result.status}`}`);
27387
27789
  }
27388
- pm2Exec(["save"]);
27790
+ return resolveInstalledPlugin();
27389
27791
  }
27390
27792
 
27391
27793
  // plugin/supertask.ts
27392
- import { homedir as homedir3 } from "os";
27393
27794
  var _initialized = false;
27394
27795
  function ensureInit() {
27395
27796
  if (_initialized) return;
27396
- _initialized = true;
27397
27797
  try {
27398
27798
  getDb();
27399
27799
  } catch (err) {
@@ -27401,65 +27801,23 @@ function ensureInit() {
27401
27801
  return;
27402
27802
  }
27403
27803
  try {
27404
- const lockRow = sqlite.prepare("SELECT pid, heartbeat_at FROM gateway_lock WHERE id = 1").get();
27405
- if (lockRow && Date.now() - lockRow.heartbeat_at < 3e4) {
27804
+ const lockRow = sqlite.prepare("SELECT pid, heartbeat_at, ready_at FROM gateway_lock WHERE id = 1").get();
27805
+ if (lockRow?.ready_at != null && Date.now() - lockRow.heartbeat_at < 3e4) {
27806
+ _initialized = true;
27406
27807
  return;
27407
27808
  }
27408
27809
  } catch {
27409
27810
  }
27410
27811
  try {
27411
- ensureGateway();
27412
- } catch {
27812
+ const gateway = ensureGateway();
27813
+ if (!gateway.ok) {
27814
+ console.warn("[supertask] Gateway \u672A\u81EA\u52A8\u542F\u52A8\uFF1A\u672A\u5B89\u88C5 pm2\u3002\u8FD0\u884C `supertask install` \u542F\u7528\u5E38\u9A7B\u6267\u884C\uFF0C\u6216\u8FD0\u884C `supertask gateway` \u524D\u53F0\u542F\u52A8\u3002");
27815
+ }
27816
+ } catch (error45) {
27817
+ console.error("[supertask] Gateway init failed:", error45 instanceof Error ? error45.message : String(error45));
27413
27818
  }
27819
+ _initialized = true;
27414
27820
  }
27415
- var RUNNER_PROMPT = `\u4F60\u662F **SuperTask \u4EFB\u52A1\u6267\u884C\u5668**\u3002
27416
-
27417
- ## \u5DE5\u4F5C\u6D41\u7A0B
27418
-
27419
- ### 1. \u83B7\u53D6\u4EFB\u52A1
27420
-
27421
- - \u5982\u679C\u7528\u6237\u8F93\u5165\u5305\u542B \`\u6267\u884C\u4EFB\u52A1 ID: <\u6570\u5B57>\`\uFF0C\u7528 \`supertask_get(id)\` \u83B7\u53D6\u8BE5\u4EFB\u52A1
27422
- - \u5426\u5219\u7528 \`supertask_next\` \u83B7\u53D6\u4E0B\u4E00\u4E2A\u5F85\u6267\u884C\u4EFB\u52A1
27423
- - \u5982\u679C\u6CA1\u6709\u4EFB\u52A1\uFF0C\u62A5\u544A"\u961F\u5217\u4E3A\u7A7A"\u5E76\u7ED3\u675F
27424
-
27425
- ### 2. \u6807\u8BB0\u5F00\u59CB
27426
-
27427
- - \u5982\u679C\u4EFB\u52A1\u72B6\u6001\u662F \`pending\`\uFF0C\u8C03\u7528 \`supertask_start(id)\` \u6807\u8BB0\u4E3A running
27428
- - \u5982\u679C\u5DF2\u7ECF\u662F \`running\`\uFF08Worker \u5DF2\u6807\u8BB0\uFF09\uFF0C\u8DF3\u8FC7\u6B64\u6B65
27429
-
27430
- ### 3. \u6267\u884C\u4EFB\u52A1
27431
-
27432
- \u7528 Bash \u5DE5\u5177\u6267\u884C\u5B50 Agent\uFF0C**\u5FC5\u987B\u4F20\u5165 timeout \u53C2\u6570**\uFF1A
27433
-
27434
- **\u5DE5\u5177\u8C03\u7528\u683C\u5F0F**\uFF1A
27435
- \`\`\`
27436
- Bash(
27437
- command: "opencode run --agent \\"<task.agent>\\" -m \\"<model>\\" --format json \\"<task.prompt>\\"",
27438
- workdir: "<task.cwd>",
27439
- timeout: 3600000
27440
- )
27441
- \`\`\`
27442
-
27443
- - **command**\uFF1A\u6267\u884C\u5B50 Agent \u7684\u547D\u4EE4
27444
- - **workdir**\uFF1A\u4F7F\u7528 \`task.cwd\`\uFF08\u82E5\u4E3A\u7A7A\u5219\u7528\u5F53\u524D\u76EE\u5F55\uFF09
27445
- - **model**\uFF1A\u4ECE\u7528\u6237\u8F93\u5165\u89E3\u6790 \`OVERRIDE_MODEL=xxx\`\uFF0C\u7528\u5B83\u4F5C\u4E3A \`-m\` \u53C2\u6570\uFF1B\u89E3\u6790\u4E0D\u5230\u5C31\u4E0D\u4F20 \`-m\`
27446
- - **timeout**\uFF1A**\u5FC5\u987B\u8BBE\u7F6E\u4E3A 3600000\uFF0860 \u5206\u949F\uFF09**
27447
- - **\u5B89\u5168\u68C0\u67E5**\uFF1A\u5982\u679C \`task.agent\` \u662F \`supertask-runner\`\uFF0C\u76F4\u63A5 fail \u5E76\u7ED3\u675F\uFF08\u9632\u6B62\u9012\u5F52\uFF09
27448
-
27449
- ### 4. \u5224\u65AD\u7ED3\u679C\u5E76\u66F4\u65B0\u72B6\u6001
27450
-
27451
- \u770B\u5B50 Agent \u7684\u8F93\u51FA\u5185\u5BB9\uFF0C\u5224\u65AD\u4EFB\u52A1\u662F\u5426\u6210\u529F\u5B8C\u6210\uFF1A
27452
-
27453
- - **\u6210\u529F**\uFF1A\u5B50 Agent \u5B8C\u6210\u4E86\u4EFB\u52A1\u8981\u6C42\u7684\u5DE5\u4F5C \u2192 \u8C03\u7528 \`supertask_done(id, "\u7B80\u8981\u63CF\u8FF0\u5B8C\u6210\u60C5\u51B5")\`
27454
- - **\u5931\u8D25**\uFF1A\u5B50 Agent \u62A5\u9519\u3001\u62D2\u7EDD\u6267\u884C\u3001\u660E\u786E\u8BF4\u65E0\u6CD5\u5B8C\u6210\u3001\u6216\u660E\u663E\u6CA1\u505A\u5B8C \u2192 \u8C03\u7528 \`supertask_fail(id, "\u5931\u8D25\u539F\u56E0")\`
27455
-
27456
- \u7528\u4F60\u7684\u5224\u65AD\u529B\uFF0C\u4E0D\u9700\u8981\u6B7B\u677F\u7684\u89C4\u5219\u3002
27457
-
27458
- ## \u6CE8\u610F\u4E8B\u9879
27459
-
27460
- 1. \u4F60\u662F\u8C03\u5EA6\u5668\uFF0C\u4E0D\u8981\u81EA\u5DF1\u6267\u884C\u4EFB\u52A1\u5185\u5BB9\uFF0C\u5FC5\u987B\u7528 Bash \u8C03\u7528 \`opencode run\`
27461
- 2. \u5B8C\u6574\u4F20\u9012 \`task.prompt\`\uFF0C\u4E0D\u8981\u64C5\u81EA\u4FEE\u6539
27462
- 3. \u4E00\u6B21\u53EA\u5904\u7406\u4E00\u4E2A\u4EFB\u52A1\uFF0C\u5904\u7406\u5B8C\u5C31\u7ED3\u675F`;
27463
27821
  var SYSTEM_INSTRUCTION = `
27464
27822
  ## SuperTask \u4EFB\u52A1\u961F\u5217\u7CFB\u7EDF
27465
27823
 
@@ -27486,18 +27844,7 @@ var SYSTEM_INSTRUCTION = `
27486
27844
  `;
27487
27845
  var SuperTaskPlugin = async () => {
27488
27846
  return {
27489
- async config(cfg) {
27490
- cfg.agent = cfg.agent ?? {};
27491
- cfg.agent["supertask-runner"] = {
27492
- description: "SuperTask \u4EFB\u52A1\u6267\u884C\u5668 - \u4ECE\u4EFB\u52A1\u961F\u5217\u83B7\u53D6\u4EFB\u52A1\u5E76\u6D3E\u53D1\u7ED9\u5B50 Agent \u6267\u884C",
27493
- mode: "all",
27494
- hidden: true,
27495
- prompt: RUNNER_PROMPT,
27496
- temperature: 0.3,
27497
- permission: {
27498
- bash: "allow"
27499
- }
27500
- };
27847
+ async config() {
27501
27848
  ensureInit();
27502
27849
  },
27503
27850
  async "experimental.chat.system.transform"(input, output) {
@@ -27506,7 +27853,7 @@ var SuperTaskPlugin = async () => {
27506
27853
  tool: {
27507
27854
  // 创建任务
27508
27855
  supertask_add: tool({
27509
- description: "\u521B\u5EFA\u65B0\u4EFB\u52A1\u5230\u961F\u5217\u3002\u8FD4\u56DE\u4EFB\u52A1 ID\u3002\u4EFB\u52A1\u5C06\u6309\u4F18\u5148\u7EA7\uFF08importance \xD7 urgency\uFF09\u6392\u5E8F\u6267\u884C\u3002",
27856
+ description: "\u521B\u5EFA\u65B0\u4EFB\u52A1\u5230\u961F\u5217\u3002\u8FD4\u56DE\u4EFB\u52A1 ID\u3002\u4EFB\u52A1\u6309 urgency\u3001importance\u3001createdAt\u3001id \u7684\u987A\u5E8F\u8C03\u5EA6\u3002",
27510
27857
  args: {
27511
27858
  name: tool.schema.string().describe("\u4EFB\u52A1\u540D\u79F0\uFF08\u4EBA\u7C7B\u53EF\u8BFB\uFF09"),
27512
27859
  agent: tool.schema.string().describe("\u6267\u884C\u7684 Agent \u540D\u79F0\uFF0C\u5982 localize-gen, course-gen"),
@@ -27517,6 +27864,9 @@ var SuperTaskPlugin = async () => {
27517
27864
  urgency: tool.schema.number().optional().describe("\u7D27\u6025\u7A0B\u5EA6 1-5\uFF085 \u6700\u7D27\u6025\uFF09"),
27518
27865
  batchId: tool.schema.string().optional().describe("\u6279\u6B21 ID\uFF0C\u7528\u4E8E\u5206\u7EC4\u7BA1\u7406"),
27519
27866
  dependsOn: tool.schema.number().optional().describe("\u4F9D\u8D56\u7684\u4EFB\u52A1 ID\uFF0C\u8BE5\u4EFB\u52A1\u5B8C\u6210\u540E\u624D\u4F1A\u6267\u884C"),
27867
+ max_retries: tool.schema.number().optional().describe("\u9996\u6B21\u6267\u884C\u4E4B\u5916\u5141\u8BB8\u7684\u91CD\u8BD5\u6B21\u6570\uFF0C\u9ED8\u8BA4 3"),
27868
+ retry_backoff_ms: tool.schema.number().optional().describe("\u91CD\u8BD5\u9000\u907F\u57FA\u7840\u95F4\u9694 ms\uFF0C\u9ED8\u8BA4 30000"),
27869
+ timeout_ms: tool.schema.number().optional().describe("\u4EFB\u52A1\u786C\u8D85\u65F6 ms\uFF1B\u672A\u4F20\u5219\u4F7F\u7528 Gateway \u9ED8\u8BA4\u503C"),
27520
27870
  cwd: tool.schema.string().optional().describe(
27521
27871
  "(\u5DF2\u5E9F\u5F03) \u5DE5\u4F5C\u76EE\u5F55\u3002\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u8BB0\u5F55\u63D0\u4EA4\u4EFB\u52A1\u65F6\u7684 opencode run \u542F\u52A8\u76EE\u5F55\u3002"
27522
27872
  )
@@ -27534,7 +27884,10 @@ var SuperTaskPlugin = async () => {
27534
27884
  urgency: args.urgency ?? 3,
27535
27885
  batchId: args.batchId,
27536
27886
  dependsOn: args.dependsOn,
27537
- cwd: submitCwd
27887
+ cwd: submitCwd,
27888
+ maxRetries: args.max_retries,
27889
+ retryBackoffMs: args.retry_backoff_ms,
27890
+ timeoutMs: args.timeout_ms
27538
27891
  });
27539
27892
  return JSON.stringify({ id: task.id, status: "created" });
27540
27893
  } catch (error45) {
@@ -27546,7 +27899,7 @@ var SuperTaskPlugin = async () => {
27546
27899
  }),
27547
27900
  // 获取下一条任务
27548
27901
  supertask_next: tool({
27549
- description: "\u83B7\u53D6\u4E0B\u4E00\u4E2A\u5F85\u6267\u884C\u7684\u4EFB\u52A1\u3002\u4F18\u5148\u5904\u7406\u53EF\u91CD\u8BD5\u7684\u5931\u8D25\u4EFB\u52A1\uFF08retryCount < maxRetries\uFF09\uFF0C\u518D\u5904\u7406 pending \u4EFB\u52A1\uFF0C\u5747\u6309\u521B\u5EFA\u65F6\u95F4\u5347\u5E8F\u6392\u5217\uFF0C\u4F1A\u81EA\u52A8\u8DF3\u8FC7\u4F9D\u8D56\u672A\u5B8C\u6210\u7684\u4EFB\u52A1\u3002",
27902
+ description: "\u83B7\u53D6\u4E0B\u4E00\u4E2A\u53EF\u6267\u884C\u4EFB\u52A1\u3002\u5019\u9009\u5305\u62EC pending \u548C\u9000\u907F\u5230\u671F\u4E14 retryCount <= maxRetries \u7684 failed \u4EFB\u52A1\uFF0C\u6309 urgency\u3001importance\u3001createdAt\u3001id \u6392\u5E8F\uFF0C\u5E76\u8DF3\u8FC7\u4F9D\u8D56\u672A\u5B8C\u6210\u7684\u4EFB\u52A1\u3002",
27550
27903
  args: {
27551
27904
  cwd: tool.schema.string().optional().describe("\u9879\u76EE\u9694\u79BB\uFF1A\u4F20\u5165\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\uFF0C\u53EA\u8FD4\u56DE\u8BE5\u9879\u76EE\u7684\u4EFB\u52A1")
27552
27905
  },
@@ -27567,7 +27920,7 @@ var SuperTaskPlugin = async () => {
27567
27920
  urgency: task.urgency
27568
27921
  });
27569
27922
  } else {
27570
- return JSON.stringify({ id: null, message: "No pending tasks" });
27923
+ return JSON.stringify({ id: null, message: "No executable tasks" });
27571
27924
  }
27572
27925
  } catch (error45) {
27573
27926
  return JSON.stringify({
@@ -27594,7 +27947,7 @@ var SuperTaskPlugin = async () => {
27594
27947
  error: "Task status does not allow start",
27595
27948
  id: existing.id,
27596
27949
  status: existing.status,
27597
- message: `Only pending tasks can be started; current status is '${existing.status}'.`
27950
+ message: `Only pending or retryable failed tasks can be started; current status is '${existing.status}'.`
27598
27951
  });
27599
27952
  } catch (error45) {
27600
27953
  return JSON.stringify({
@@ -27781,7 +28134,8 @@ var SuperTaskPlugin = async () => {
27781
28134
  }).describe("\u8C03\u5EA6\u914D\u7F6E"),
27782
28135
  max_instances: tool.schema.number().optional().describe("\u6700\u5927\u5E76\u53D1\u5B9E\u4F8B\u6570\uFF0C\u9ED8\u8BA4 1"),
27783
28136
  max_retries: tool.schema.number().optional().describe("\u514B\u9686\u7ED9 task \u7684\u6700\u5927\u91CD\u8BD5\u6B21\u6570\uFF0C\u9ED8\u8BA4 3"),
27784
- retry_backoff_ms: tool.schema.number().optional().describe("\u514B\u9686\u7ED9 task \u7684\u9000\u907F\u57FA\u7840\u95F4\u9694 ms\uFF0C\u9ED8\u8BA4 30000")
28137
+ retry_backoff_ms: tool.schema.number().optional().describe("\u514B\u9686\u7ED9 task \u7684\u9000\u907F\u57FA\u7840\u95F4\u9694 ms\uFF0C\u9ED8\u8BA4 30000"),
28138
+ timeout_ms: tool.schema.number().optional().describe("\u514B\u9686\u7ED9 task \u7684\u786C\u8D85\u65F6 ms\uFF1B\u672A\u4F20\u5219\u4F7F\u7528 Gateway \u9ED8\u8BA4\u503C")
27785
28139
  },
27786
28140
  async execute(args) {
27787
28141
  try {
@@ -27813,13 +28167,16 @@ var SuperTaskPlugin = async () => {
27813
28167
  category: args.category ?? "general",
27814
28168
  importance: args.importance ?? 3,
27815
28169
  urgency: args.urgency ?? 3,
28170
+ cwd: process.cwd(),
28171
+ batchId: args.batchId,
27816
28172
  scheduleType,
27817
28173
  cronExpr,
27818
28174
  intervalMs,
27819
28175
  runAt,
27820
28176
  maxInstances: args.max_instances,
27821
28177
  maxRetries: args.max_retries,
27822
- retryBackoffMs: args.retry_backoff_ms
28178
+ retryBackoffMs: args.retry_backoff_ms,
28179
+ timeoutMs: args.timeout_ms
27823
28180
  });
27824
28181
  return JSON.stringify({
27825
28182
  id: tmpl.id,
@@ -27836,27 +28193,22 @@ var SuperTaskPlugin = async () => {
27836
28193
  }
27837
28194
  }),
27838
28195
  supertask_upgrade: tool({
27839
- description: "\u5347\u7EA7 SuperTask \u63D2\u4EF6\u3002\u5148\u901A\u8FC7 npm \u66F4\u65B0\u63D2\u4EF6\u5305\u5230\u6700\u65B0\u7248\u672C\uFF0C\u7136\u540E\u91CD\u542F Gateway \u8FDB\u7A0B\u3002\u5F53\u7528\u6237\u8BF4'\u5347\u7EA7\u63D2\u4EF6'\u3001'\u66F4\u65B0 supertask'\u3001'upgrade'\u65F6\u4F7F\u7528\u3002",
28196
+ description: "\u5347\u7EA7 SuperTask \u63D2\u4EF6\u3002\u901A\u8FC7 OpenCode \u5237\u65B0\u63D2\u4EF6\u7F13\u5B58\uFF0C\u6821\u9A8C\u65B0\u7248\u6784\u5EFA\u4EA7\u7269\u540E\u91CD\u542F Gateway\u3002\u5F53\u7528\u6237\u8BF4'\u5347\u7EA7\u63D2\u4EF6'\u3001'\u66F4\u65B0 supertask'\u3001'upgrade'\u65F6\u4F7F\u7528\u3002",
27840
28197
  args: {},
27841
28198
  async execute() {
27842
28199
  try {
27843
- const { execSync: execSync2 } = await import("child_process");
27844
- const configDir = `${homedir3()}/.config/opencode`;
27845
- console.log("[supertask] Updating npm package...");
28200
+ console.log("[supertask] Updating OpenCode plugin cache...");
28201
+ let installed;
27846
28202
  try {
27847
- execSync2("npm install opencode-supertask@latest", {
27848
- cwd: configDir,
27849
- stdio: "pipe",
27850
- timeout: 6e4
27851
- });
27852
- } catch (npmErr) {
28203
+ installed = installLatestPlugin();
28204
+ } catch (updateError) {
27853
28205
  return JSON.stringify({
27854
28206
  success: false,
27855
- error: `npm install failed: ${npmErr instanceof Error ? npmErr.message : String(npmErr)}`,
27856
- hint: "Try manually: cd ~/.config/opencode && npm install opencode-supertask@latest"
28207
+ error: updateError instanceof Error ? updateError.message : String(updateError),
28208
+ hint: "Try manually: opencode plugin opencode-supertask@latest --global --force"
27857
28209
  });
27858
28210
  }
27859
- const result = upgrade();
28211
+ const result = upgrade(installed);
27860
28212
  return JSON.stringify({
27861
28213
  success: true,
27862
28214
  before: result.before,