opencode-supertask 0.1.20 → 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";
@@ -26835,16 +26893,30 @@ function initDb() {
26835
26893
  id INTEGER PRIMARY KEY CHECK (id = 1),
26836
26894
  pid INTEGER NOT NULL,
26837
26895
  acquired_at INTEGER NOT NULL,
26838
- heartbeat_at INTEGER NOT NULL
26896
+ heartbeat_at INTEGER NOT NULL,
26897
+ ready_at INTEGER
26839
26898
  );
26840
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
+ }
26841
26904
  _db = drizzle(_sqlite, { schema: schema_exports });
26842
26905
  if (!_migrationRan) {
26843
- _migrationRan = true;
26844
26906
  try {
26845
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;
26846
26914
  } catch (err) {
26847
26915
  const msg = err instanceof Error ? err.message : String(err);
26916
+ _migrationRan = false;
26917
+ _sqlite.close();
26918
+ _sqlite = null;
26919
+ _db = null;
26848
26920
  console.error(`[supertask] migration failed: ${msg}`);
26849
26921
  throw new Error(`[supertask] DB migration failed: ${msg}`);
26850
26922
  }
@@ -26883,7 +26955,7 @@ function computeBackoff(retryCount, baseMs = 3e4, maxMs = MAX_BACKOFF_MS) {
26883
26955
  }
26884
26956
 
26885
26957
  // src/core/services/task.service.ts
26886
- var { tasks: tasks2 } = schema_exports;
26958
+ var { tasks: tasks2, taskRuns: taskRuns2 } = schema_exports;
26887
26959
  var TaskService = class {
26888
26960
  static buildScopeWhere(scope) {
26889
26961
  const conditions = [];
@@ -26893,9 +26965,27 @@ var TaskService = class {
26893
26965
  return conditions;
26894
26966
  }
26895
26967
  static async add(data) {
26968
+ this.validateNewTask(data);
26896
26969
  const result = await db.insert(tasks2).values(data).returning();
26897
26970
  return result[0];
26898
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
+ }
26899
26989
  static async next(scope = {}) {
26900
26990
  const baseConditions = [...this.buildScopeWhere(scope)];
26901
26991
  const nowMs = Date.now();
@@ -26918,7 +27008,7 @@ var TaskService = class {
26918
27008
  ),
26919
27009
  and(
26920
27010
  eq(tasks2.status, "failed"),
26921
- sql`${tasks2.retryCount} < ${tasks2.maxRetries}`,
27011
+ sql`${tasks2.retryCount} <= ${tasks2.maxRetries}`,
26922
27012
  retryAfterFilter
26923
27013
  )
26924
27014
  );
@@ -26932,7 +27022,8 @@ var TaskService = class {
26932
27022
  const allTasks = await db.select().from(tasks2).where(and(...conditions)).orderBy(
26933
27023
  desc(tasks2.urgency),
26934
27024
  desc(tasks2.importance),
26935
- asc(tasks2.createdAt)
27025
+ asc(tasks2.createdAt),
27026
+ asc(tasks2.id)
26936
27027
  );
26937
27028
  for (const task of allTasks) {
26938
27029
  if (task.dependsOn) {
@@ -26953,7 +27044,7 @@ var TaskService = class {
26953
27044
  eq(tasks2.status, "pending"),
26954
27045
  and(
26955
27046
  eq(tasks2.status, "failed"),
26956
- sql`${tasks2.retryCount} < ${tasks2.maxRetries}`
27047
+ sql`${tasks2.retryCount} <= ${tasks2.maxRetries}`
26957
27048
  )
26958
27049
  ),
26959
27050
  ...this.buildScopeWhere(scope)
@@ -26966,7 +27057,11 @@ var TaskService = class {
26966
27057
  return result[0] || null;
26967
27058
  }
26968
27059
  static async done(id, log, scope = {}) {
26969
- 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
+ ];
26970
27065
  const result = await db.update(tasks2).set({
26971
27066
  status: "done",
26972
27067
  finishedAt: /* @__PURE__ */ new Date(),
@@ -26977,17 +27072,24 @@ var TaskService = class {
26977
27072
  }
26978
27073
  static async fail(id, log, scope = {}, options) {
26979
27074
  const current = await this.getById(id, scope);
26980
- if (!current) return null;
27075
+ if (!current || current.status !== "running") return null;
26981
27076
  const newRetryCount = (current.retryCount ?? 0) + 1;
26982
27077
  const maxRetries = current.maxRetries ?? 3;
26983
- const isDeadLetter = options?.setDeadLetter ?? newRetryCount >= maxRetries;
26984
- 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
+ ];
26985
27084
  const result = await db.update(tasks2).set({
26986
27085
  status: isDeadLetter ? "dead_letter" : "failed",
26987
27086
  finishedAt: /* @__PURE__ */ new Date(),
26988
27087
  resultLog: log,
26989
27088
  retryCount: newRetryCount,
26990
- 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
+ )
26991
27093
  }).where(and(...conditions)).returning();
26992
27094
  return result[0] || null;
26993
27095
  }
@@ -27020,8 +27122,20 @@ var TaskService = class {
27020
27122
  return result.length;
27021
27123
  }
27022
27124
  static async cancel(id, scope = {}) {
27023
- const conditions = [eq(tasks2.id, id), ...this.buildScopeWhere(scope)];
27024
- 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();
27025
27139
  return result[0] || null;
27026
27140
  }
27027
27141
  static async retry(id, scope = {}) {
@@ -27033,7 +27147,8 @@ var TaskService = class {
27033
27147
  status: "pending",
27034
27148
  startedAt: null,
27035
27149
  finishedAt: null,
27036
- retryAfter: null
27150
+ retryAfter: null,
27151
+ retryCount: 0
27037
27152
  }).where(and(...conditions)).returning();
27038
27153
  return result[0] || null;
27039
27154
  }
@@ -27047,7 +27162,8 @@ var TaskService = class {
27047
27162
  status: "pending",
27048
27163
  startedAt: null,
27049
27164
  finishedAt: null,
27050
- retryAfter: null
27165
+ retryAfter: null,
27166
+ retryCount: 0
27051
27167
  }).where(and(...conditions)).returning();
27052
27168
  return result.length;
27053
27169
  }
@@ -27115,6 +27231,9 @@ var TaskService = class {
27115
27231
  }
27116
27232
  static async delete(id, scope = {}) {
27117
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));
27118
27237
  const result = await db.delete(tasks2).where(and(...conditions)).returning();
27119
27238
  return result.length > 0;
27120
27239
  }
@@ -27156,6 +27275,7 @@ function isValidCronExpr(expr) {
27156
27275
  var { taskTemplates: taskTemplates2 } = schema_exports;
27157
27276
  var TaskTemplateService = class {
27158
27277
  static async create(data) {
27278
+ this.validate(data);
27159
27279
  const now = Date.now();
27160
27280
  const result = await db.insert(taskTemplates2).values({ ...data, createdAt: now, updatedAt: now }).returning();
27161
27281
  const tmpl = result[0];
@@ -27171,8 +27291,38 @@ var TaskTemplateService = class {
27171
27291
  }
27172
27292
  return tmpl;
27173
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
+ }
27174
27324
  static async list(limit = 50) {
27175
- 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);
27176
27326
  }
27177
27327
  static async getById(id) {
27178
27328
  const result = await db.select().from(taskTemplates2).where(eq(taskTemplates2.id, id));
@@ -27241,85 +27391,261 @@ function parseDuration(input) {
27241
27391
 
27242
27392
  // src/daemon/pm2.ts
27243
27393
  import { execSync, spawnSync } from "child_process";
27244
- 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";
27245
27397
  import { fileURLToPath as fileURLToPath2 } from "url";
27246
- 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";
27247
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
27248
27516
  var __dirname = dirname2(fileURLToPath2(import.meta.url));
27249
- var GATEWAY_ENTRY = join2(__dirname, "../gateway/index.js");
27250
27517
  var PROCESS_NAME = "supertask-gateway";
27251
- var VERSION_FILE = join2(homedir2(), ".local/share/opencode/supertask-gateway-version");
27518
+ var GATEWAY_LOCK_STALE_MS = 3e4;
27252
27519
  function getPackageVersion() {
27520
+ const envVersion = process.env.npm_package_version;
27521
+ if (envVersion) return envVersion;
27253
27522
  try {
27254
- const pkgPath = join2(__dirname, "../package.json");
27255
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
27256
- 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";
27257
27526
  } catch {
27258
27527
  return "0.0.0";
27259
27528
  }
27260
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
+ }
27261
27547
  function getRunningVersion() {
27262
27548
  try {
27263
- if (!existsSync2(VERSION_FILE)) return null;
27264
- 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;
27265
27552
  } catch {
27266
27553
  return null;
27267
27554
  }
27268
27555
  }
27269
27556
  function writeRunningVersion(version3) {
27270
- try {
27271
- writeFileSync(VERSION_FILE, version3, "utf-8");
27272
- } catch {
27273
- }
27557
+ const path = versionFile();
27558
+ mkdirSync2(dirname2(path), { recursive: true });
27559
+ writeFileSync(path, version3, "utf-8");
27274
27560
  }
27275
27561
  function pm2Bin() {
27276
- return process.platform === "win32" ? "pm2.cmd" : "pm2";
27562
+ return process.env.SUPERTASK_PM2_BIN ?? (process.platform === "win32" ? "pm2.cmd" : "pm2");
27277
27563
  }
27278
27564
  function isPm2Installed() {
27279
- try {
27280
- const cmd = process.platform === "win32" ? "where pm2" : "which pm2";
27281
- execSync(cmd, { stdio: "pipe" });
27282
- return true;
27283
- } catch {
27284
- return false;
27285
- }
27565
+ const result = spawnSync(pm2Bin(), ["--version"], {
27566
+ stdio: "ignore",
27567
+ shell: process.platform === "win32"
27568
+ });
27569
+ return result.status === 0;
27286
27570
  }
27287
27571
  function pm2Exec(args) {
27288
- 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");
27289
27589
  try {
27290
- const result = spawnSync(bin, args, {
27291
- stdio: ["pipe", "pipe", "pipe"],
27292
- encoding: "utf-8",
27293
- shell: process.platform === "win32"
27294
- });
27295
- const output = (result.stdout ?? "") + (result.stderr ?? "");
27296
- return { ok: result.status === 0, output };
27297
- } catch (err) {
27298
- 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}`);
27299
27596
  }
27300
27597
  }
27301
- function pm2JsonList() {
27302
- const { ok, output } = pm2Exec(["jlist"]);
27303
- 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;
27304
27605
  try {
27305
- 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;
27306
27614
  } catch {
27307
- return [];
27615
+ return false;
27616
+ } finally {
27617
+ database?.close();
27308
27618
  }
27309
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
+ }
27310
27633
  function findBunPath() {
27634
+ const override = process.env.SUPERTASK_BUN_BIN;
27635
+ if (override) return override;
27311
27636
  try {
27312
- const cmd = process.platform === "win32" ? "where bun" : "which bun";
27313
- 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];
27314
27639
  } catch {
27315
27640
  return process.execPath;
27316
27641
  }
27317
27642
  }
27318
- function pm2StartGateway(version3) {
27319
- const bunPath = findBunPath();
27320
- 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([
27321
27647
  "start",
27322
- bunPath,
27648
+ findBunPath(),
27323
27649
  "--name",
27324
27650
  PROCESS_NAME,
27325
27651
  "--interpreter",
@@ -27328,80 +27654,146 @@ function pm2StartGateway(version3) {
27328
27654
  "5000",
27329
27655
  "--max-restarts",
27330
27656
  "30",
27657
+ "--kill-timeout",
27658
+ String(killTimeoutMs),
27331
27659
  "--",
27332
- GATEWAY_ENTRY
27333
- ]);
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");
27334
27672
  }
27335
- function upgrade() {
27673
+ function upgrade(target) {
27674
+ if (!isPm2Installed()) {
27675
+ throw new Error("[supertask] pm2 is not installed. Run `supertask install` first.");
27676
+ }
27336
27677
  const before = getRunningVersion();
27337
- const currentVersion = getPackageVersion();
27338
- const list = pm2JsonList();
27339
- const proc = list.find((p) => p.name === PROCESS_NAME);
27340
- if (proc) {
27341
- console.log(`[supertask] Stopping Gateway (version ${before ?? "unknown"})...`);
27342
- pm2Exec(["delete", PROCESS_NAME]);
27343
- }
27344
- console.log(`[supertask] Starting Gateway (version ${currentVersion})...`);
27345
- const { ok } = pm2StartGateway(currentVersion);
27346
- 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);
27347
27684
  writeRunningVersion(currentVersion);
27348
- pm2Exec(["save"]);
27349
- console.log(`[supertask] Gateway upgraded: ${before ?? "unknown"} \u2192 ${currentVersion}`);
27350
- } else {
27351
- 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`);
27352
27701
  }
27353
- return { before, after: currentVersion, restarted: true };
27354
27702
  }
27355
27703
  function ensureGateway() {
27704
+ if (!isPm2Installed()) {
27705
+ return { ok: false, reason: "pm2-not-installed" };
27706
+ }
27356
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;
27357
27730
  try {
27358
- const list = pm2JsonList();
27359
- const proc = list.find((p) => p.name === PROCESS_NAME);
27360
- if (proc && proc.pm2_env?.status === "online") {
27361
- const runningVersion = getRunningVersion();
27362
- if (runningVersion === currentVersion) {
27363
- return;
27364
- }
27365
- console.log(`[supertask] Version changed: ${runningVersion ?? "unknown"} \u2192 ${currentVersion}, reloading Gateway...`);
27366
- pm2Exec(["delete", PROCESS_NAME]);
27367
- const { ok } = pm2StartGateway(currentVersion);
27368
- if (ok) writeRunningVersion(currentVersion);
27369
- pm2Exec(["save"]);
27370
- return;
27371
- }
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 };
27372
27734
  } catch {
27735
+ return null;
27373
27736
  }
27374
- if (!isPm2Installed()) {
27375
- console.log("[supertask] Installing pm2 for Gateway process management...");
27376
- try {
27377
- execSync("npm install -g pm2", { stdio: "pipe" });
27378
- } catch {
27379
- try {
27380
- execSync("bun install -g pm2", { stdio: "pipe" });
27381
- } catch {
27382
- console.warn("[supertask] Could not install pm2. Gateway will not auto-start. Run `supertask install` manually.");
27383
- return;
27384
- }
27385
- }
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}`);
27386
27786
  }
27387
- const pm2List = pm2JsonList();
27388
- const existing = pm2List.find((p) => p.name === PROCESS_NAME);
27389
- if (existing) {
27390
- pm2Exec(["restart", PROCESS_NAME]);
27391
- } else {
27392
- const version3 = getPackageVersion();
27393
- const { ok } = pm2StartGateway(version3);
27394
- 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}`}`);
27395
27789
  }
27396
- pm2Exec(["save"]);
27790
+ return resolveInstalledPlugin();
27397
27791
  }
27398
27792
 
27399
27793
  // plugin/supertask.ts
27400
- import { homedir as homedir3 } from "os";
27401
27794
  var _initialized = false;
27402
27795
  function ensureInit() {
27403
27796
  if (_initialized) return;
27404
- _initialized = true;
27405
27797
  try {
27406
27798
  getDb();
27407
27799
  } catch (err) {
@@ -27409,65 +27801,23 @@ function ensureInit() {
27409
27801
  return;
27410
27802
  }
27411
27803
  try {
27412
- const lockRow = sqlite.prepare("SELECT pid, heartbeat_at FROM gateway_lock WHERE id = 1").get();
27413
- 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;
27414
27807
  return;
27415
27808
  }
27416
27809
  } catch {
27417
27810
  }
27418
27811
  try {
27419
- ensureGateway();
27420
- } 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));
27421
27818
  }
27819
+ _initialized = true;
27422
27820
  }
27423
- var RUNNER_PROMPT = `\u4F60\u662F **SuperTask \u4EFB\u52A1\u6267\u884C\u5668**\u3002
27424
-
27425
- ## \u5DE5\u4F5C\u6D41\u7A0B
27426
-
27427
- ### 1. \u83B7\u53D6\u4EFB\u52A1
27428
-
27429
- - \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
27430
- - \u5426\u5219\u7528 \`supertask_next\` \u83B7\u53D6\u4E0B\u4E00\u4E2A\u5F85\u6267\u884C\u4EFB\u52A1
27431
- - \u5982\u679C\u6CA1\u6709\u4EFB\u52A1\uFF0C\u62A5\u544A"\u961F\u5217\u4E3A\u7A7A"\u5E76\u7ED3\u675F
27432
-
27433
- ### 2. \u6807\u8BB0\u5F00\u59CB
27434
-
27435
- - \u5982\u679C\u4EFB\u52A1\u72B6\u6001\u662F \`pending\`\uFF0C\u8C03\u7528 \`supertask_start(id)\` \u6807\u8BB0\u4E3A running
27436
- - \u5982\u679C\u5DF2\u7ECF\u662F \`running\`\uFF08Worker \u5DF2\u6807\u8BB0\uFF09\uFF0C\u8DF3\u8FC7\u6B64\u6B65
27437
-
27438
- ### 3. \u6267\u884C\u4EFB\u52A1
27439
-
27440
- \u7528 Bash \u5DE5\u5177\u6267\u884C\u5B50 Agent\uFF0C**\u5FC5\u987B\u4F20\u5165 timeout \u53C2\u6570**\uFF1A
27441
-
27442
- **\u5DE5\u5177\u8C03\u7528\u683C\u5F0F**\uFF1A
27443
- \`\`\`
27444
- Bash(
27445
- command: "opencode run --agent \\"<task.agent>\\" -m \\"<model>\\" --format json \\"<task.prompt>\\"",
27446
- workdir: "<task.cwd>",
27447
- timeout: 3600000
27448
- )
27449
- \`\`\`
27450
-
27451
- - **command**\uFF1A\u6267\u884C\u5B50 Agent \u7684\u547D\u4EE4
27452
- - **workdir**\uFF1A\u4F7F\u7528 \`task.cwd\`\uFF08\u82E5\u4E3A\u7A7A\u5219\u7528\u5F53\u524D\u76EE\u5F55\uFF09
27453
- - **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\`
27454
- - **timeout**\uFF1A**\u5FC5\u987B\u8BBE\u7F6E\u4E3A 3600000\uFF0860 \u5206\u949F\uFF09**
27455
- - **\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
27456
-
27457
- ### 4. \u5224\u65AD\u7ED3\u679C\u5E76\u66F4\u65B0\u72B6\u6001
27458
-
27459
- \u770B\u5B50 Agent \u7684\u8F93\u51FA\u5185\u5BB9\uFF0C\u5224\u65AD\u4EFB\u52A1\u662F\u5426\u6210\u529F\u5B8C\u6210\uFF1A
27460
-
27461
- - **\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")\`
27462
- - **\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")\`
27463
-
27464
- \u7528\u4F60\u7684\u5224\u65AD\u529B\uFF0C\u4E0D\u9700\u8981\u6B7B\u677F\u7684\u89C4\u5219\u3002
27465
-
27466
- ## \u6CE8\u610F\u4E8B\u9879
27467
-
27468
- 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\`
27469
- 2. \u5B8C\u6574\u4F20\u9012 \`task.prompt\`\uFF0C\u4E0D\u8981\u64C5\u81EA\u4FEE\u6539
27470
- 3. \u4E00\u6B21\u53EA\u5904\u7406\u4E00\u4E2A\u4EFB\u52A1\uFF0C\u5904\u7406\u5B8C\u5C31\u7ED3\u675F`;
27471
27821
  var SYSTEM_INSTRUCTION = `
27472
27822
  ## SuperTask \u4EFB\u52A1\u961F\u5217\u7CFB\u7EDF
27473
27823
 
@@ -27494,18 +27844,7 @@ var SYSTEM_INSTRUCTION = `
27494
27844
  `;
27495
27845
  var SuperTaskPlugin = async () => {
27496
27846
  return {
27497
- async config(cfg) {
27498
- cfg.agent = cfg.agent ?? {};
27499
- cfg.agent["supertask-runner"] = {
27500
- description: "SuperTask \u4EFB\u52A1\u6267\u884C\u5668 - \u4ECE\u4EFB\u52A1\u961F\u5217\u83B7\u53D6\u4EFB\u52A1\u5E76\u6D3E\u53D1\u7ED9\u5B50 Agent \u6267\u884C",
27501
- mode: "all",
27502
- hidden: true,
27503
- prompt: RUNNER_PROMPT,
27504
- temperature: 0.3,
27505
- permission: {
27506
- bash: "allow"
27507
- }
27508
- };
27847
+ async config() {
27509
27848
  ensureInit();
27510
27849
  },
27511
27850
  async "experimental.chat.system.transform"(input, output) {
@@ -27514,7 +27853,7 @@ var SuperTaskPlugin = async () => {
27514
27853
  tool: {
27515
27854
  // 创建任务
27516
27855
  supertask_add: tool({
27517
- 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",
27518
27857
  args: {
27519
27858
  name: tool.schema.string().describe("\u4EFB\u52A1\u540D\u79F0\uFF08\u4EBA\u7C7B\u53EF\u8BFB\uFF09"),
27520
27859
  agent: tool.schema.string().describe("\u6267\u884C\u7684 Agent \u540D\u79F0\uFF0C\u5982 localize-gen, course-gen"),
@@ -27525,6 +27864,9 @@ var SuperTaskPlugin = async () => {
27525
27864
  urgency: tool.schema.number().optional().describe("\u7D27\u6025\u7A0B\u5EA6 1-5\uFF085 \u6700\u7D27\u6025\uFF09"),
27526
27865
  batchId: tool.schema.string().optional().describe("\u6279\u6B21 ID\uFF0C\u7528\u4E8E\u5206\u7EC4\u7BA1\u7406"),
27527
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"),
27528
27870
  cwd: tool.schema.string().optional().describe(
27529
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"
27530
27872
  )
@@ -27542,7 +27884,10 @@ var SuperTaskPlugin = async () => {
27542
27884
  urgency: args.urgency ?? 3,
27543
27885
  batchId: args.batchId,
27544
27886
  dependsOn: args.dependsOn,
27545
- cwd: submitCwd
27887
+ cwd: submitCwd,
27888
+ maxRetries: args.max_retries,
27889
+ retryBackoffMs: args.retry_backoff_ms,
27890
+ timeoutMs: args.timeout_ms
27546
27891
  });
27547
27892
  return JSON.stringify({ id: task.id, status: "created" });
27548
27893
  } catch (error45) {
@@ -27554,7 +27899,7 @@ var SuperTaskPlugin = async () => {
27554
27899
  }),
27555
27900
  // 获取下一条任务
27556
27901
  supertask_next: tool({
27557
- 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",
27558
27903
  args: {
27559
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")
27560
27905
  },
@@ -27575,7 +27920,7 @@ var SuperTaskPlugin = async () => {
27575
27920
  urgency: task.urgency
27576
27921
  });
27577
27922
  } else {
27578
- return JSON.stringify({ id: null, message: "No pending tasks" });
27923
+ return JSON.stringify({ id: null, message: "No executable tasks" });
27579
27924
  }
27580
27925
  } catch (error45) {
27581
27926
  return JSON.stringify({
@@ -27602,7 +27947,7 @@ var SuperTaskPlugin = async () => {
27602
27947
  error: "Task status does not allow start",
27603
27948
  id: existing.id,
27604
27949
  status: existing.status,
27605
- 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}'.`
27606
27951
  });
27607
27952
  } catch (error45) {
27608
27953
  return JSON.stringify({
@@ -27789,7 +28134,8 @@ var SuperTaskPlugin = async () => {
27789
28134
  }).describe("\u8C03\u5EA6\u914D\u7F6E"),
27790
28135
  max_instances: tool.schema.number().optional().describe("\u6700\u5927\u5E76\u53D1\u5B9E\u4F8B\u6570\uFF0C\u9ED8\u8BA4 1"),
27791
28136
  max_retries: tool.schema.number().optional().describe("\u514B\u9686\u7ED9 task \u7684\u6700\u5927\u91CD\u8BD5\u6B21\u6570\uFF0C\u9ED8\u8BA4 3"),
27792
- 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")
27793
28139
  },
27794
28140
  async execute(args) {
27795
28141
  try {
@@ -27821,13 +28167,16 @@ var SuperTaskPlugin = async () => {
27821
28167
  category: args.category ?? "general",
27822
28168
  importance: args.importance ?? 3,
27823
28169
  urgency: args.urgency ?? 3,
28170
+ cwd: process.cwd(),
28171
+ batchId: args.batchId,
27824
28172
  scheduleType,
27825
28173
  cronExpr,
27826
28174
  intervalMs,
27827
28175
  runAt,
27828
28176
  maxInstances: args.max_instances,
27829
28177
  maxRetries: args.max_retries,
27830
- retryBackoffMs: args.retry_backoff_ms
28178
+ retryBackoffMs: args.retry_backoff_ms,
28179
+ timeoutMs: args.timeout_ms
27831
28180
  });
27832
28181
  return JSON.stringify({
27833
28182
  id: tmpl.id,
@@ -27844,27 +28193,22 @@ var SuperTaskPlugin = async () => {
27844
28193
  }
27845
28194
  }),
27846
28195
  supertask_upgrade: tool({
27847
- 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",
27848
28197
  args: {},
27849
28198
  async execute() {
27850
28199
  try {
27851
- const { execSync: execSync2 } = await import("child_process");
27852
- const configDir = `${homedir3()}/.config/opencode`;
27853
- console.log("[supertask] Updating npm package...");
28200
+ console.log("[supertask] Updating OpenCode plugin cache...");
28201
+ let installed;
27854
28202
  try {
27855
- execSync2("npm install opencode-supertask@latest", {
27856
- cwd: configDir,
27857
- stdio: "pipe",
27858
- timeout: 6e4
27859
- });
27860
- } catch (npmErr) {
28203
+ installed = installLatestPlugin();
28204
+ } catch (updateError) {
27861
28205
  return JSON.stringify({
27862
28206
  success: false,
27863
- error: `npm install failed: ${npmErr instanceof Error ? npmErr.message : String(npmErr)}`,
27864
- 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"
27865
28209
  });
27866
28210
  }
27867
- const result = upgrade();
28211
+ const result = upgrade(installed);
27868
28212
  return JSON.stringify({
27869
28213
  success: true,
27870
28214
  before: result.before,