opencode-supertask 0.1.20 → 0.1.22

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
  }
@@ -27019,9 +27121,38 @@ var TaskService = class {
27019
27121
  ).returning();
27020
27122
  return result.length;
27021
27123
  }
27124
+ static async resetOrphanRunningToPending() {
27125
+ const result = await db.update(tasks2).set({
27126
+ status: "pending",
27127
+ startedAt: null,
27128
+ finishedAt: null
27129
+ }).where(
27130
+ and(
27131
+ eq(tasks2.status, "running"),
27132
+ sql`NOT EXISTS (
27133
+ SELECT 1 FROM ${taskRuns2}
27134
+ WHERE ${taskRuns2.taskId} = ${tasks2.id}
27135
+ AND ${taskRuns2.status} = 'running'
27136
+ )`
27137
+ )
27138
+ ).returning();
27139
+ return result.length;
27140
+ }
27022
27141
  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();
27142
+ const conditions = [
27143
+ eq(tasks2.id, id),
27144
+ or(
27145
+ eq(tasks2.status, "pending"),
27146
+ eq(tasks2.status, "running"),
27147
+ eq(tasks2.status, "failed")
27148
+ ),
27149
+ ...this.buildScopeWhere(scope)
27150
+ ];
27151
+ const result = await db.update(tasks2).set({
27152
+ status: "cancelled",
27153
+ finishedAt: /* @__PURE__ */ new Date(),
27154
+ retryAfter: null
27155
+ }).where(and(...conditions)).returning();
27025
27156
  return result[0] || null;
27026
27157
  }
27027
27158
  static async retry(id, scope = {}) {
@@ -27033,7 +27164,8 @@ var TaskService = class {
27033
27164
  status: "pending",
27034
27165
  startedAt: null,
27035
27166
  finishedAt: null,
27036
- retryAfter: null
27167
+ retryAfter: null,
27168
+ retryCount: 0
27037
27169
  }).where(and(...conditions)).returning();
27038
27170
  return result[0] || null;
27039
27171
  }
@@ -27047,7 +27179,8 @@ var TaskService = class {
27047
27179
  status: "pending",
27048
27180
  startedAt: null,
27049
27181
  finishedAt: null,
27050
- retryAfter: null
27182
+ retryAfter: null,
27183
+ retryCount: 0
27051
27184
  }).where(and(...conditions)).returning();
27052
27185
  return result.length;
27053
27186
  }
@@ -27115,6 +27248,9 @@ var TaskService = class {
27115
27248
  }
27116
27249
  static async delete(id, scope = {}) {
27117
27250
  const conditions = [eq(tasks2.id, id), ...this.buildScopeWhere(scope)];
27251
+ const existing = await db.select({ id: tasks2.id }).from(tasks2).where(and(...conditions)).limit(1);
27252
+ if (!existing[0]) return false;
27253
+ await db.delete(taskRuns2).where(eq(taskRuns2.taskId, id));
27118
27254
  const result = await db.delete(tasks2).where(and(...conditions)).returning();
27119
27255
  return result.length > 0;
27120
27256
  }
@@ -27156,6 +27292,7 @@ function isValidCronExpr(expr) {
27156
27292
  var { taskTemplates: taskTemplates2 } = schema_exports;
27157
27293
  var TaskTemplateService = class {
27158
27294
  static async create(data) {
27295
+ this.validate(data);
27159
27296
  const now = Date.now();
27160
27297
  const result = await db.insert(taskTemplates2).values({ ...data, createdAt: now, updatedAt: now }).returning();
27161
27298
  const tmpl = result[0];
@@ -27171,8 +27308,38 @@ var TaskTemplateService = class {
27171
27308
  }
27172
27309
  return tmpl;
27173
27310
  }
27311
+ static validate(data) {
27312
+ if (!data.name.trim()) throw new Error("name \u4E0D\u80FD\u4E3A\u7A7A");
27313
+ if (!data.agent.trim()) throw new Error("agent \u4E0D\u80FD\u4E3A\u7A7A");
27314
+ if (!data.prompt.trim()) throw new Error("prompt \u4E0D\u80FD\u4E3A\u7A7A");
27315
+ const scheduleType = data.scheduleType;
27316
+ if (!["cron", "delayed", "recurring"].includes(scheduleType)) {
27317
+ throw new Error("scheduleType \u5FC5\u987B\u662F cron\u3001delayed \u6216 recurring");
27318
+ }
27319
+ if (scheduleType === "cron" && (!data.cronExpr || !isValidCronExpr(data.cronExpr))) {
27320
+ throw new Error("cronExpr \u7F3A\u5931\u6216\u683C\u5F0F\u65E0\u6548");
27321
+ }
27322
+ if (scheduleType === "recurring" && (!Number.isInteger(data.intervalMs) || (data.intervalMs ?? 0) <= 0)) {
27323
+ throw new Error("intervalMs \u5FC5\u987B\u662F\u6B63\u6574\u6570");
27324
+ }
27325
+ if (scheduleType === "delayed" && (!Number.isInteger(data.runAt) || (data.runAt ?? 0) <= 0)) {
27326
+ throw new Error("runAt \u5FC5\u987B\u662F\u6B63\u6574\u6570\u65F6\u95F4\u6233");
27327
+ }
27328
+ this.validateInteger("importance", data.importance, 1, 5);
27329
+ this.validateInteger("urgency", data.urgency, 1, 5);
27330
+ this.validateInteger("maxInstances", data.maxInstances, 1, 1e3);
27331
+ this.validateInteger("maxRetries", data.maxRetries, 0, 1e3);
27332
+ this.validateInteger("retryBackoffMs", data.retryBackoffMs, 0, 864e5);
27333
+ this.validateInteger("timeoutMs", data.timeoutMs, 1e3, 6048e5);
27334
+ }
27335
+ static validateInteger(name, value, min, max) {
27336
+ if (value === void 0 || value === null) return;
27337
+ if (!Number.isInteger(value) || value < min || value > max) {
27338
+ throw new Error(`${name} \u5FC5\u987B\u662F ${min} \u5230 ${max} \u4E4B\u95F4\u7684\u6574\u6570`);
27339
+ }
27340
+ }
27174
27341
  static async list(limit = 50) {
27175
- return await db.select().from(taskTemplates2).orderBy(desc(taskTemplates2.createdAt)).limit(limit);
27342
+ return await db.select().from(taskTemplates2).orderBy(desc(taskTemplates2.createdAt), desc(taskTemplates2.id)).limit(limit);
27176
27343
  }
27177
27344
  static async getById(id) {
27178
27345
  const result = await db.select().from(taskTemplates2).where(eq(taskTemplates2.id, id));
@@ -27241,85 +27408,261 @@ function parseDuration(input) {
27241
27408
 
27242
27409
  // src/daemon/pm2.ts
27243
27410
  import { execSync, spawnSync } from "child_process";
27244
- import { join as join2, dirname as dirname2 } from "path";
27411
+ import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
27412
+ import { homedir as homedir3 } from "os";
27413
+ import { dirname as dirname2, isAbsolute, join as join3 } from "path";
27245
27414
  import { fileURLToPath as fileURLToPath2 } from "url";
27246
- import { readFileSync, writeFileSync, existsSync as existsSync2 } from "fs";
27415
+ import { Database as Database3 } from "bun:sqlite";
27416
+
27417
+ // src/gateway/config.ts
27418
+ import { existsSync as existsSync2, readFileSync } from "fs";
27247
27419
  import { homedir as homedir2 } from "os";
27420
+ import { join as join2 } from "path";
27421
+ var DEFAULT_CONFIG = {
27422
+ configVersion: 2,
27423
+ worker: {
27424
+ maxConcurrency: 2,
27425
+ pollIntervalMs: 1e3,
27426
+ heartbeatIntervalMs: 3e4,
27427
+ taskTimeoutMs: 18e5,
27428
+ shutdownGracePeriodMs: 3e4
27429
+ },
27430
+ scheduler: {
27431
+ enabled: true,
27432
+ checkIntervalMs: 1e3
27433
+ },
27434
+ watchdog: {
27435
+ heartbeatTimeoutMs: 6e5,
27436
+ checkIntervalMs: 6e4,
27437
+ cleanupIntervalMs: 864e5,
27438
+ retentionDays: 30
27439
+ },
27440
+ dashboard: {
27441
+ enabled: true,
27442
+ port: 4680
27443
+ }
27444
+ };
27445
+ var DEFAULT_CONFIG_PATH = join2(homedir2(), ".config/opencode/supertask.json");
27446
+ function getConfigPath() {
27447
+ return process.env.SUPERTASK_CONFIG_PATH ?? DEFAULT_CONFIG_PATH;
27448
+ }
27449
+ function objectAt(value, path) {
27450
+ if (value === void 0) return {};
27451
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
27452
+ throw new Error(`${path} \u5FC5\u987B\u662F\u5BF9\u8C61`);
27453
+ }
27454
+ return value;
27455
+ }
27456
+ function integerAt(source, key, fallback, min, max, path) {
27457
+ const value = source[key];
27458
+ if (value === void 0) return fallback;
27459
+ if (typeof value !== "number" || !Number.isInteger(value) || value < min || value > max) {
27460
+ throw new Error(`${path}.${key} \u5FC5\u987B\u662F ${min} \u5230 ${max} \u4E4B\u95F4\u7684\u6574\u6570`);
27461
+ }
27462
+ return value;
27463
+ }
27464
+ function booleanAt(source, key, fallback, path) {
27465
+ const value = source[key];
27466
+ if (value === void 0) return fallback;
27467
+ if (typeof value !== "boolean") throw new Error(`${path}.${key} \u5FC5\u987B\u662F\u5E03\u5C14\u503C`);
27468
+ return value;
27469
+ }
27470
+ function validateConfig(input) {
27471
+ const root = objectAt(input, "config");
27472
+ const version3 = root.configVersion ?? 1;
27473
+ if (version3 !== 1 && version3 !== 2) {
27474
+ throw new Error("config.configVersion \u53EA\u652F\u6301 1 \u6216 2");
27475
+ }
27476
+ const worker = objectAt(root.worker, "worker");
27477
+ const scheduler = objectAt(root.scheduler, "scheduler");
27478
+ const watchdog = objectAt(root.watchdog, "watchdog");
27479
+ const dashboard = objectAt(root.dashboard, "dashboard");
27480
+ const heartbeatIntervalMs = integerAt(worker, "heartbeatIntervalMs", DEFAULT_CONFIG.worker.heartbeatIntervalMs, 1e3, 36e5, "worker");
27481
+ const heartbeatTimeoutMs = integerAt(watchdog, "heartbeatTimeoutMs", DEFAULT_CONFIG.watchdog.heartbeatTimeoutMs, 1e3, 864e5, "watchdog");
27482
+ let checkIntervalMs;
27483
+ let cleanupIntervalMs;
27484
+ if (version3 === 1 && watchdog.checkIntervalMs === void 0 && watchdog.cleanupIntervalMs !== void 0) {
27485
+ checkIntervalMs = integerAt(watchdog, "cleanupIntervalMs", DEFAULT_CONFIG.watchdog.checkIntervalMs, 1e3, 36e5, "watchdog");
27486
+ cleanupIntervalMs = DEFAULT_CONFIG.watchdog.cleanupIntervalMs;
27487
+ } else {
27488
+ checkIntervalMs = integerAt(watchdog, "checkIntervalMs", DEFAULT_CONFIG.watchdog.checkIntervalMs, 1e3, 36e5, "watchdog");
27489
+ cleanupIntervalMs = integerAt(watchdog, "cleanupIntervalMs", DEFAULT_CONFIG.watchdog.cleanupIntervalMs, 6e4, 6048e5, "watchdog");
27490
+ }
27491
+ if (heartbeatIntervalMs >= heartbeatTimeoutMs) {
27492
+ throw new Error("worker.heartbeatIntervalMs \u5FC5\u987B\u5C0F\u4E8E watchdog.heartbeatTimeoutMs");
27493
+ }
27494
+ if (checkIntervalMs > heartbeatTimeoutMs) {
27495
+ throw new Error("watchdog.checkIntervalMs \u4E0D\u80FD\u5927\u4E8E watchdog.heartbeatTimeoutMs");
27496
+ }
27497
+ return {
27498
+ configVersion: 2,
27499
+ worker: {
27500
+ maxConcurrency: integerAt(worker, "maxConcurrency", DEFAULT_CONFIG.worker.maxConcurrency, 1, 64, "worker"),
27501
+ pollIntervalMs: integerAt(worker, "pollIntervalMs", DEFAULT_CONFIG.worker.pollIntervalMs, 50, 6e4, "worker"),
27502
+ heartbeatIntervalMs,
27503
+ taskTimeoutMs: integerAt(worker, "taskTimeoutMs", DEFAULT_CONFIG.worker.taskTimeoutMs, 1e3, 6048e5, "worker"),
27504
+ shutdownGracePeriodMs: integerAt(worker, "shutdownGracePeriodMs", DEFAULT_CONFIG.worker.shutdownGracePeriodMs, 0, 36e5, "worker")
27505
+ },
27506
+ scheduler: {
27507
+ enabled: booleanAt(scheduler, "enabled", DEFAULT_CONFIG.scheduler.enabled, "scheduler"),
27508
+ checkIntervalMs: integerAt(scheduler, "checkIntervalMs", DEFAULT_CONFIG.scheduler.checkIntervalMs, 100, 6e4, "scheduler")
27509
+ },
27510
+ watchdog: {
27511
+ heartbeatTimeoutMs,
27512
+ checkIntervalMs,
27513
+ cleanupIntervalMs,
27514
+ retentionDays: integerAt(watchdog, "retentionDays", DEFAULT_CONFIG.watchdog.retentionDays, 1, 3650, "watchdog")
27515
+ },
27516
+ dashboard: {
27517
+ enabled: booleanAt(dashboard, "enabled", DEFAULT_CONFIG.dashboard.enabled, "dashboard"),
27518
+ port: integerAt(dashboard, "port", DEFAULT_CONFIG.dashboard.port, 1, 65535, "dashboard")
27519
+ }
27520
+ };
27521
+ }
27522
+ function loadConfig(path = getConfigPath()) {
27523
+ if (!existsSync2(path)) return validateConfig({ configVersion: 2 });
27524
+ try {
27525
+ return validateConfig(JSON.parse(readFileSync(path, "utf-8")));
27526
+ } catch (error45) {
27527
+ const message = error45 instanceof Error ? error45.message : String(error45);
27528
+ throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u914D\u7F6E ${path}: ${message}`);
27529
+ }
27530
+ }
27531
+
27532
+ // src/daemon/pm2.ts
27248
27533
  var __dirname = dirname2(fileURLToPath2(import.meta.url));
27249
- var GATEWAY_ENTRY = join2(__dirname, "../gateway/index.js");
27250
27534
  var PROCESS_NAME = "supertask-gateway";
27251
- var VERSION_FILE = join2(homedir2(), ".local/share/opencode/supertask-gateway-version");
27535
+ var GATEWAY_LOCK_STALE_MS = 3e4;
27252
27536
  function getPackageVersion() {
27537
+ const envVersion = process.env.npm_package_version;
27538
+ if (envVersion) return envVersion;
27253
27539
  try {
27254
- const pkgPath = join2(__dirname, "../package.json");
27255
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
27256
- return pkg.version || "0.0.0";
27540
+ const pkgPath = join3(__dirname, "../../package.json");
27541
+ const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
27542
+ return typeof pkg.version === "string" ? pkg.version : "0.0.0";
27257
27543
  } catch {
27258
27544
  return "0.0.0";
27259
27545
  }
27260
27546
  }
27547
+ function resolveGatewayEntry() {
27548
+ const override = process.env.SUPERTASK_GATEWAY_ENTRY;
27549
+ if (override) {
27550
+ if (!existsSync3(override)) throw new Error(`[supertask] Gateway entry not found: ${override}`);
27551
+ return override;
27552
+ }
27553
+ const candidates = [
27554
+ join3(__dirname, "../gateway/index.js"),
27555
+ join3(__dirname, "../gateway/index.ts")
27556
+ ];
27557
+ const entry = candidates.find((candidate) => existsSync3(candidate));
27558
+ if (!entry) throw new Error(`[supertask] Gateway entry not found. Checked: ${candidates.join(", ")}`);
27559
+ return entry;
27560
+ }
27561
+ function versionFile() {
27562
+ return process.env.SUPERTASK_VERSION_FILE ?? join3(homedir3(), ".local/share/opencode/supertask-gateway-version");
27563
+ }
27261
27564
  function getRunningVersion() {
27262
27565
  try {
27263
- if (!existsSync2(VERSION_FILE)) return null;
27264
- return readFileSync(VERSION_FILE, "utf-8").trim() || null;
27566
+ const path = versionFile();
27567
+ if (!existsSync3(path)) return null;
27568
+ return readFileSync2(path, "utf-8").trim() || null;
27265
27569
  } catch {
27266
27570
  return null;
27267
27571
  }
27268
27572
  }
27269
27573
  function writeRunningVersion(version3) {
27270
- try {
27271
- writeFileSync(VERSION_FILE, version3, "utf-8");
27272
- } catch {
27273
- }
27574
+ const path = versionFile();
27575
+ mkdirSync2(dirname2(path), { recursive: true });
27576
+ writeFileSync(path, version3, "utf-8");
27274
27577
  }
27275
27578
  function pm2Bin() {
27276
- return process.platform === "win32" ? "pm2.cmd" : "pm2";
27579
+ return process.env.SUPERTASK_PM2_BIN ?? (process.platform === "win32" ? "pm2.cmd" : "pm2");
27277
27580
  }
27278
27581
  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
- }
27582
+ const result = spawnSync(pm2Bin(), ["--version"], {
27583
+ stdio: "ignore",
27584
+ shell: process.platform === "win32"
27585
+ });
27586
+ return result.status === 0;
27286
27587
  }
27287
27588
  function pm2Exec(args) {
27288
- const bin = pm2Bin();
27589
+ const result = spawnSync(pm2Bin(), args, {
27590
+ stdio: ["pipe", "pipe", "pipe"],
27591
+ encoding: "utf-8",
27592
+ env: process.env,
27593
+ shell: process.platform === "win32"
27594
+ });
27595
+ const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
27596
+ if (result.error) return { ok: false, output: result.error.message };
27597
+ return { ok: result.status === 0, output };
27598
+ }
27599
+ function requirePm2(args, action) {
27600
+ const result = pm2Exec(args);
27601
+ if (!result.ok) throw new Error(`[supertask] ${action} failed: ${result.output || "unknown pm2 error"}`);
27602
+ return result.output;
27603
+ }
27604
+ function pm2JsonList() {
27605
+ const output = requirePm2(["jlist"], "pm2 jlist");
27289
27606
  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) };
27607
+ const parsed = JSON.parse(output);
27608
+ if (!Array.isArray(parsed)) throw new Error("result is not an array");
27609
+ return parsed;
27610
+ } catch (error45) {
27611
+ const message = error45 instanceof Error ? error45.message : String(error45);
27612
+ throw new Error(`[supertask] Invalid pm2 jlist output: ${message}`);
27299
27613
  }
27300
27614
  }
27301
- function pm2JsonList() {
27302
- const { ok, output } = pm2Exec(["jlist"]);
27303
- if (!ok) return [];
27615
+ function databasePath() {
27616
+ return process.env.SUPERTASK_DB_PATH ?? join3(homedir3(), ".local/share/opencode/tasks.db");
27617
+ }
27618
+ function isGatewayReady(expectedPid) {
27619
+ const path = databasePath();
27620
+ if (!existsSync3(path)) return false;
27621
+ let database = null;
27304
27622
  try {
27305
- return JSON.parse(output);
27623
+ database = new Database3(path, { readonly: true });
27624
+ const row = database.query(
27625
+ "SELECT pid, heartbeat_at, ready_at FROM gateway_lock WHERE id = 1"
27626
+ ).get();
27627
+ if (!row || row.ready_at == null) return false;
27628
+ if (expectedPid !== void 0 && row.pid !== expectedPid) return false;
27629
+ const ageMs = Date.now() - row.heartbeat_at;
27630
+ return ageMs >= -5e3 && ageMs < GATEWAY_LOCK_STALE_MS;
27306
27631
  } catch {
27307
- return [];
27632
+ return false;
27633
+ } finally {
27634
+ database?.close();
27308
27635
  }
27309
27636
  }
27637
+ function readyTimeoutMs() {
27638
+ const value = Number(process.env.SUPERTASK_GATEWAY_READY_TIMEOUT_MS ?? 3e4);
27639
+ return Number.isFinite(value) && value > 0 ? value : 3e4;
27640
+ }
27641
+ function waitForGatewayReady(pid) {
27642
+ const deadline = Date.now() + readyTimeoutMs();
27643
+ const sleeper = new Int32Array(new SharedArrayBuffer(4));
27644
+ while (Date.now() < deadline) {
27645
+ if (isGatewayReady(pid)) return true;
27646
+ Atomics.wait(sleeper, 0, 0, 100);
27647
+ }
27648
+ return isGatewayReady(pid);
27649
+ }
27310
27650
  function findBunPath() {
27651
+ const override = process.env.SUPERTASK_BUN_BIN;
27652
+ if (override) return override;
27311
27653
  try {
27312
- const cmd = process.platform === "win32" ? "where bun" : "which bun";
27313
- return execSync(cmd, { stdio: "pipe" }).toString().trim().split("\n")[0];
27654
+ const command = process.platform === "win32" ? "where bun" : "which bun";
27655
+ return execSync(command, { stdio: "pipe" }).toString().trim().split("\n")[0];
27314
27656
  } catch {
27315
27657
  return process.execPath;
27316
27658
  }
27317
27659
  }
27318
- function pm2StartGateway(version3) {
27319
- const bunPath = findBunPath();
27320
- return pm2Exec([
27660
+ function pm2StartGateway(gatewayEntry = resolveGatewayEntry()) {
27661
+ const configuredKillTimeout = Number(process.env.SUPERTASK_PM2_KILL_TIMEOUT_MS);
27662
+ const killTimeoutMs = Number.isInteger(configuredKillTimeout) && configuredKillTimeout >= 5e3 ? configuredKillTimeout : loadConfig().worker.shutdownGracePeriodMs + 5e3;
27663
+ requirePm2([
27321
27664
  "start",
27322
- bunPath,
27665
+ findBunPath(),
27323
27666
  "--name",
27324
27667
  PROCESS_NAME,
27325
27668
  "--interpreter",
@@ -27328,80 +27671,146 @@ function pm2StartGateway(version3) {
27328
27671
  "5000",
27329
27672
  "--max-restarts",
27330
27673
  "30",
27674
+ "--kill-timeout",
27675
+ String(killTimeoutMs),
27331
27676
  "--",
27332
- GATEWAY_ENTRY
27333
- ]);
27677
+ gatewayEntry
27678
+ ], "pm2 start");
27679
+ const started = pm2JsonList().find((item) => item.name === PROCESS_NAME);
27680
+ if (started?.pm2_env?.status !== "online") {
27681
+ throw new Error(`[supertask] Gateway did not become online (status: ${started?.pm2_env?.status ?? "missing"})`);
27682
+ }
27683
+ if (typeof started.pid !== "number" || !waitForGatewayReady(started.pid)) {
27684
+ 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");
27685
+ }
27686
+ }
27687
+ function savePm2State() {
27688
+ requirePm2(["save"], "pm2 save");
27334
27689
  }
27335
- function upgrade() {
27690
+ function upgrade(target) {
27691
+ if (!isPm2Installed()) {
27692
+ throw new Error("[supertask] pm2 is not installed. Run `supertask install` first.");
27693
+ }
27336
27694
  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) {
27695
+ const oldGatewayEntry = resolveGatewayEntry();
27696
+ const currentVersion = target?.version ?? getPackageVersion();
27697
+ const existing = pm2JsonList().find((item) => item.name === PROCESS_NAME);
27698
+ if (existing) requirePm2(["delete", PROCESS_NAME], "pm2 delete old Gateway");
27699
+ try {
27700
+ pm2StartGateway(target?.gatewayEntry ?? oldGatewayEntry);
27347
27701
  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`);
27702
+ savePm2State();
27703
+ return { before, after: currentVersion, restarted: true };
27704
+ } catch (error45) {
27705
+ const failed = pm2JsonList().find((item) => item.name === PROCESS_NAME);
27706
+ if (failed) requirePm2(["delete", PROCESS_NAME], "pm2 delete failed Gateway");
27707
+ try {
27708
+ pm2StartGateway(oldGatewayEntry);
27709
+ if (before) writeRunningVersion(before);
27710
+ savePm2State();
27711
+ } catch (rollbackError) {
27712
+ const original = error45 instanceof Error ? error45.message : String(error45);
27713
+ const rollback = rollbackError instanceof Error ? rollbackError.message : String(rollbackError);
27714
+ throw new Error(`${original}; \u65E7 Gateway \u56DE\u6EDA\u4E5F\u5931\u8D25: ${rollback}`);
27715
+ }
27716
+ const message = error45 instanceof Error ? error45.message : String(error45);
27717
+ throw new Error(`${message}; \u5DF2\u56DE\u6EDA\u5230\u65E7 Gateway`);
27352
27718
  }
27353
- return { before, after: currentVersion, restarted: true };
27354
27719
  }
27355
27720
  function ensureGateway() {
27721
+ if (!isPm2Installed()) {
27722
+ return { ok: false, reason: "pm2-not-installed" };
27723
+ }
27356
27724
  const currentVersion = getPackageVersion();
27725
+ const processList = pm2JsonList();
27726
+ const existing = processList.find((item) => item.name === PROCESS_NAME);
27727
+ if (existing?.pm2_env?.status === "online" && typeof existing.pid === "number" && isGatewayReady(existing.pid) && getRunningVersion() === currentVersion) {
27728
+ return { ok: true, action: "already-running" };
27729
+ }
27730
+ if (existing) requirePm2(["delete", PROCESS_NAME], "pm2 delete stale Gateway");
27731
+ pm2StartGateway();
27732
+ writeRunningVersion(currentVersion);
27733
+ savePm2State();
27734
+ return { ok: true, action: existing ? "restarted" : "started" };
27735
+ }
27736
+
27737
+ // src/daemon/update.ts
27738
+ import { spawnSync as spawnSync2 } from "child_process";
27739
+ import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync } from "fs";
27740
+ import { homedir as homedir4 } from "os";
27741
+ import { join as join4 } from "path";
27742
+ var PACKAGE_NAME = "opencode-supertask";
27743
+ function pluginAt(packageDir) {
27744
+ const packageJson = join4(packageDir, "package.json");
27745
+ const gatewayEntry = join4(packageDir, "dist/gateway/index.js");
27746
+ if (!existsSync4(packageJson) || !existsSync4(gatewayEntry)) return null;
27357
27747
  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
- }
27748
+ const pkg = JSON.parse(readFileSync3(packageJson, "utf8"));
27749
+ if (pkg.name !== PACKAGE_NAME || typeof pkg.version !== "string") return null;
27750
+ return { packageDir, gatewayEntry, version: pkg.version };
27372
27751
  } catch {
27752
+ return null;
27373
27753
  }
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
- }
27754
+ }
27755
+ function versionParts(version3) {
27756
+ const match = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(version3);
27757
+ return match ? match.slice(1).map(Number) : null;
27758
+ }
27759
+ function compareVersions(left, right) {
27760
+ const a = versionParts(left);
27761
+ const b = versionParts(right);
27762
+ if (!a || !b) return left.localeCompare(right);
27763
+ for (let index2 = 0; index2 < 3; index2 += 1) {
27764
+ if (a[index2] !== b[index2]) return a[index2] - b[index2];
27765
+ }
27766
+ return 0;
27767
+ }
27768
+ function cacheRoot() {
27769
+ return process.env.SUPERTASK_OPENCODE_CACHE_DIR ?? join4(homedir4(), ".cache/opencode/packages");
27770
+ }
27771
+ function resolveInstalledPlugin() {
27772
+ const override = process.env.SUPERTASK_PLUGIN_PACKAGE_DIR;
27773
+ if (override) {
27774
+ const plugin = pluginAt(override);
27775
+ if (!plugin) throw new Error(`[supertask] \u5B89\u88C5\u5305\u65E0\u6548\u6216\u7F3A\u5C11 Gateway \u6784\u5EFA\u4EA7\u7269: ${override}`);
27776
+ return plugin;
27777
+ }
27778
+ const root = cacheRoot();
27779
+ 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)) : [];
27780
+ const installed = packageDirs.map(pluginAt).filter((plugin) => plugin !== null).sort((left, right) => compareVersions(right.version, left.version));
27781
+ if (!installed[0]) {
27782
+ throw new Error(`[supertask] OpenCode \u63D2\u4EF6\u7F13\u5B58\u4E2D\u627E\u4E0D\u5230\u53EF\u8FD0\u884C\u7684 ${PACKAGE_NAME}`);
27783
+ }
27784
+ return installed[0];
27785
+ }
27786
+ function opencodeBin() {
27787
+ return process.env.SUPERTASK_OPENCODE_BIN ?? "opencode";
27788
+ }
27789
+ function installLatestPlugin() {
27790
+ const result = spawnSync2(opencodeBin(), [
27791
+ "plugin",
27792
+ `${PACKAGE_NAME}@latest`,
27793
+ "--global",
27794
+ "--force"
27795
+ ], {
27796
+ encoding: "utf8",
27797
+ env: process.env,
27798
+ timeout: 12e4
27799
+ });
27800
+ const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
27801
+ if (result.error) {
27802
+ throw new Error(`[supertask] OpenCode \u63D2\u4EF6\u66F4\u65B0\u5931\u8D25: ${result.error.message}`);
27386
27803
  }
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);
27804
+ if (result.status !== 0) {
27805
+ throw new Error(`[supertask] OpenCode \u63D2\u4EF6\u66F4\u65B0\u5931\u8D25: ${output || `\u9000\u51FA\u7801 ${result.status}`}`);
27395
27806
  }
27396
- pm2Exec(["save"]);
27807
+ return resolveInstalledPlugin();
27397
27808
  }
27398
27809
 
27399
27810
  // plugin/supertask.ts
27400
- import { homedir as homedir3 } from "os";
27401
27811
  var _initialized = false;
27402
27812
  function ensureInit() {
27403
27813
  if (_initialized) return;
27404
- _initialized = true;
27405
27814
  try {
27406
27815
  getDb();
27407
27816
  } catch (err) {
@@ -27409,65 +27818,23 @@ function ensureInit() {
27409
27818
  return;
27410
27819
  }
27411
27820
  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) {
27821
+ const lockRow = sqlite.prepare("SELECT pid, heartbeat_at, ready_at FROM gateway_lock WHERE id = 1").get();
27822
+ if (lockRow?.ready_at != null && Date.now() - lockRow.heartbeat_at < 3e4) {
27823
+ _initialized = true;
27414
27824
  return;
27415
27825
  }
27416
27826
  } catch {
27417
27827
  }
27418
27828
  try {
27419
- ensureGateway();
27420
- } catch {
27829
+ const gateway = ensureGateway();
27830
+ if (!gateway.ok) {
27831
+ 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");
27832
+ }
27833
+ } catch (error45) {
27834
+ console.error("[supertask] Gateway init failed:", error45 instanceof Error ? error45.message : String(error45));
27421
27835
  }
27836
+ _initialized = true;
27422
27837
  }
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
27838
  var SYSTEM_INSTRUCTION = `
27472
27839
  ## SuperTask \u4EFB\u52A1\u961F\u5217\u7CFB\u7EDF
27473
27840
 
@@ -27494,18 +27861,7 @@ var SYSTEM_INSTRUCTION = `
27494
27861
  `;
27495
27862
  var SuperTaskPlugin = async () => {
27496
27863
  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
- };
27864
+ async config() {
27509
27865
  ensureInit();
27510
27866
  },
27511
27867
  async "experimental.chat.system.transform"(input, output) {
@@ -27514,7 +27870,7 @@ var SuperTaskPlugin = async () => {
27514
27870
  tool: {
27515
27871
  // 创建任务
27516
27872
  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",
27873
+ 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
27874
  args: {
27519
27875
  name: tool.schema.string().describe("\u4EFB\u52A1\u540D\u79F0\uFF08\u4EBA\u7C7B\u53EF\u8BFB\uFF09"),
27520
27876
  agent: tool.schema.string().describe("\u6267\u884C\u7684 Agent \u540D\u79F0\uFF0C\u5982 localize-gen, course-gen"),
@@ -27525,6 +27881,9 @@ var SuperTaskPlugin = async () => {
27525
27881
  urgency: tool.schema.number().optional().describe("\u7D27\u6025\u7A0B\u5EA6 1-5\uFF085 \u6700\u7D27\u6025\uFF09"),
27526
27882
  batchId: tool.schema.string().optional().describe("\u6279\u6B21 ID\uFF0C\u7528\u4E8E\u5206\u7EC4\u7BA1\u7406"),
27527
27883
  dependsOn: tool.schema.number().optional().describe("\u4F9D\u8D56\u7684\u4EFB\u52A1 ID\uFF0C\u8BE5\u4EFB\u52A1\u5B8C\u6210\u540E\u624D\u4F1A\u6267\u884C"),
27884
+ max_retries: tool.schema.number().optional().describe("\u9996\u6B21\u6267\u884C\u4E4B\u5916\u5141\u8BB8\u7684\u91CD\u8BD5\u6B21\u6570\uFF0C\u9ED8\u8BA4 3"),
27885
+ retry_backoff_ms: tool.schema.number().optional().describe("\u91CD\u8BD5\u9000\u907F\u57FA\u7840\u95F4\u9694 ms\uFF0C\u9ED8\u8BA4 30000"),
27886
+ timeout_ms: tool.schema.number().optional().describe("\u4EFB\u52A1\u786C\u8D85\u65F6 ms\uFF1B\u672A\u4F20\u5219\u4F7F\u7528 Gateway \u9ED8\u8BA4\u503C"),
27528
27887
  cwd: tool.schema.string().optional().describe(
27529
27888
  "(\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
27889
  )
@@ -27542,7 +27901,10 @@ var SuperTaskPlugin = async () => {
27542
27901
  urgency: args.urgency ?? 3,
27543
27902
  batchId: args.batchId,
27544
27903
  dependsOn: args.dependsOn,
27545
- cwd: submitCwd
27904
+ cwd: submitCwd,
27905
+ maxRetries: args.max_retries,
27906
+ retryBackoffMs: args.retry_backoff_ms,
27907
+ timeoutMs: args.timeout_ms
27546
27908
  });
27547
27909
  return JSON.stringify({ id: task.id, status: "created" });
27548
27910
  } catch (error45) {
@@ -27554,7 +27916,7 @@ var SuperTaskPlugin = async () => {
27554
27916
  }),
27555
27917
  // 获取下一条任务
27556
27918
  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",
27919
+ 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
27920
  args: {
27559
27921
  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
27922
  },
@@ -27575,7 +27937,7 @@ var SuperTaskPlugin = async () => {
27575
27937
  urgency: task.urgency
27576
27938
  });
27577
27939
  } else {
27578
- return JSON.stringify({ id: null, message: "No pending tasks" });
27940
+ return JSON.stringify({ id: null, message: "No executable tasks" });
27579
27941
  }
27580
27942
  } catch (error45) {
27581
27943
  return JSON.stringify({
@@ -27602,7 +27964,7 @@ var SuperTaskPlugin = async () => {
27602
27964
  error: "Task status does not allow start",
27603
27965
  id: existing.id,
27604
27966
  status: existing.status,
27605
- message: `Only pending tasks can be started; current status is '${existing.status}'.`
27967
+ message: `Only pending or retryable failed tasks can be started; current status is '${existing.status}'.`
27606
27968
  });
27607
27969
  } catch (error45) {
27608
27970
  return JSON.stringify({
@@ -27789,7 +28151,8 @@ var SuperTaskPlugin = async () => {
27789
28151
  }).describe("\u8C03\u5EA6\u914D\u7F6E"),
27790
28152
  max_instances: tool.schema.number().optional().describe("\u6700\u5927\u5E76\u53D1\u5B9E\u4F8B\u6570\uFF0C\u9ED8\u8BA4 1"),
27791
28153
  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")
28154
+ retry_backoff_ms: tool.schema.number().optional().describe("\u514B\u9686\u7ED9 task \u7684\u9000\u907F\u57FA\u7840\u95F4\u9694 ms\uFF0C\u9ED8\u8BA4 30000"),
28155
+ 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
28156
  },
27794
28157
  async execute(args) {
27795
28158
  try {
@@ -27821,13 +28184,16 @@ var SuperTaskPlugin = async () => {
27821
28184
  category: args.category ?? "general",
27822
28185
  importance: args.importance ?? 3,
27823
28186
  urgency: args.urgency ?? 3,
28187
+ cwd: process.cwd(),
28188
+ batchId: args.batchId,
27824
28189
  scheduleType,
27825
28190
  cronExpr,
27826
28191
  intervalMs,
27827
28192
  runAt,
27828
28193
  maxInstances: args.max_instances,
27829
28194
  maxRetries: args.max_retries,
27830
- retryBackoffMs: args.retry_backoff_ms
28195
+ retryBackoffMs: args.retry_backoff_ms,
28196
+ timeoutMs: args.timeout_ms
27831
28197
  });
27832
28198
  return JSON.stringify({
27833
28199
  id: tmpl.id,
@@ -27844,27 +28210,22 @@ var SuperTaskPlugin = async () => {
27844
28210
  }
27845
28211
  }),
27846
28212
  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",
28213
+ 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
28214
  args: {},
27849
28215
  async execute() {
27850
28216
  try {
27851
- const { execSync: execSync2 } = await import("child_process");
27852
- const configDir = `${homedir3()}/.config/opencode`;
27853
- console.log("[supertask] Updating npm package...");
28217
+ console.log("[supertask] Updating OpenCode plugin cache...");
28218
+ let installed;
27854
28219
  try {
27855
- execSync2("npm install opencode-supertask@latest", {
27856
- cwd: configDir,
27857
- stdio: "pipe",
27858
- timeout: 6e4
27859
- });
27860
- } catch (npmErr) {
28220
+ installed = installLatestPlugin();
28221
+ } catch (updateError) {
27861
28222
  return JSON.stringify({
27862
28223
  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"
28224
+ error: updateError instanceof Error ? updateError.message : String(updateError),
28225
+ hint: "Try manually: opencode plugin opencode-supertask@latest --global --force"
27865
28226
  });
27866
28227
  }
27867
- const result = upgrade();
28228
+ const result = upgrade(installed);
27868
28229
  return JSON.stringify({
27869
28230
  success: true,
27870
28231
  before: result.before,