opencode-supertask 0.1.31 → 0.1.32-rc.2

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.
@@ -26976,6 +26976,42 @@ function computeBackoff(retryCount, baseMs = 3e4, maxMs = MAX_BACKOFF_MS) {
26976
26976
  return Math.min(baseMs * Math.pow(2, retryCount - 1), maxMs);
26977
26977
  }
26978
26978
 
26979
+ // src/core/task-working-directory.ts
26980
+ import { statSync } from "fs";
26981
+ import { isAbsolute } from "path";
26982
+ var InvalidTaskWorkingDirectoryError = class extends Error {
26983
+ constructor(message) {
26984
+ super(message);
26985
+ this.name = "InvalidTaskWorkingDirectoryError";
26986
+ }
26987
+ };
26988
+ function validateTaskWorkingDirectory(cwd) {
26989
+ if (cwd == null) return;
26990
+ if (!cwd.trim()) {
26991
+ throw new InvalidTaskWorkingDirectoryError("\u4EFB\u52A1\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u80FD\u4E3A\u7A7A");
26992
+ }
26993
+ if (!isAbsolute(cwd)) {
26994
+ throw new InvalidTaskWorkingDirectoryError(`\u4EFB\u52A1\u5DE5\u4F5C\u76EE\u5F55\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84\uFF1A${cwd}`);
26995
+ }
26996
+ let stat;
26997
+ try {
26998
+ stat = statSync(cwd);
26999
+ } catch (error45) {
27000
+ const detail = error45 instanceof Error ? error45.message : String(error45);
27001
+ throw new InvalidTaskWorkingDirectoryError(`\u4EFB\u52A1\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u65E0\u6CD5\u8BBF\u95EE\uFF1A${cwd}\uFF08${detail}\uFF09`);
27002
+ }
27003
+ if (!stat.isDirectory()) {
27004
+ throw new InvalidTaskWorkingDirectoryError(`\u4EFB\u52A1\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u662F\u76EE\u5F55\uFF1A${cwd}`);
27005
+ }
27006
+ }
27007
+
27008
+ // src/core/task-batch.ts
27009
+ var TASK_BATCH_TRIM_CHARACTERS = " \n\v\f\r\xA0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF";
27010
+ function normalizeTaskBatchId(batchId) {
27011
+ if (batchId == null) return batchId;
27012
+ return batchId.trim() || null;
27013
+ }
27014
+
26979
27015
  // src/core/services/task.service.ts
26980
27016
  var { tasks: tasks2, taskRuns: taskRuns2 } = schema_exports;
26981
27017
  var cleanupInvocation = 0;
@@ -27039,34 +27075,75 @@ var TaskService = class {
27039
27075
  return conditions;
27040
27076
  }
27041
27077
  static async add(data) {
27042
- this.validateNewTask(data);
27078
+ const normalizedData = {
27079
+ ...data,
27080
+ batchId: normalizeTaskBatchId(data.batchId)
27081
+ };
27082
+ this.validateNewTask(normalizedData);
27043
27083
  return db.transaction((tx) => {
27044
- if (data.dependsOn != null) {
27084
+ if (normalizedData.dependsOn != null) {
27045
27085
  const dependency = tx.select({
27046
27086
  id: tasks2.id,
27047
27087
  cwd: tasks2.cwd,
27048
27088
  status: tasks2.status,
27049
27089
  retryCount: tasks2.retryCount,
27050
27090
  maxRetries: tasks2.maxRetries
27051
- }).from(tasks2).where(eq(tasks2.id, data.dependsOn)).get();
27091
+ }).from(tasks2).where(eq(tasks2.id, normalizedData.dependsOn)).get();
27052
27092
  if (!dependency) {
27053
- throw new Error(`dependsOn \u6307\u5411\u7684\u4EFB\u52A1 #${data.dependsOn} \u4E0D\u5B58\u5728`);
27093
+ throw new Error(`dependsOn \u6307\u5411\u7684\u4EFB\u52A1 #${normalizedData.dependsOn} \u4E0D\u5B58\u5728`);
27054
27094
  }
27055
- if ((dependency.cwd ?? null) !== (data.cwd ?? null)) {
27095
+ if ((dependency.cwd ?? null) !== (normalizedData.cwd ?? null)) {
27056
27096
  throw new Error("dependsOn \u5FC5\u987B\u6307\u5411\u540C\u4E00 cwd \u7684\u4EFB\u52A1");
27057
27097
  }
27058
27098
  const dependencyIsRecoverable = dependency.status === "pending" || dependency.status === "running" || dependency.status === "done" || dependency.status === "failed" && (dependency.retryCount ?? 0) <= (dependency.maxRetries ?? 3);
27059
27099
  if (!dependencyIsRecoverable) {
27060
- throw new Error(`dependsOn \u6307\u5411\u7684\u4EFB\u52A1 #${data.dependsOn} \u5DF2\u8FDB\u5165\u4E0D\u53EF\u6062\u590D\u7EC8\u6001`);
27100
+ throw new Error(`dependsOn \u6307\u5411\u7684\u4EFB\u52A1 #${normalizedData.dependsOn} \u5DF2\u8FDB\u5165\u4E0D\u53EF\u6062\u590D\u7EC8\u6001`);
27061
27101
  }
27062
27102
  }
27063
- return tx.insert(tasks2).values(data).returning().get();
27103
+ return tx.insert(tasks2).values(normalizedData).returning().get();
27104
+ }, { behavior: "immediate" });
27105
+ }
27106
+ static async update(id, data, scope = {}) {
27107
+ if (Object.keys(data).length === 0) throw new Error("\u81F3\u5C11\u63D0\u4F9B\u4E00\u4E2A\u8981\u4FEE\u6539\u7684\u5B57\u6BB5");
27108
+ const normalizedData = data.batchId === void 0 ? data : { ...data, batchId: normalizeTaskBatchId(data.batchId) ?? null };
27109
+ return db.transaction((tx) => {
27110
+ const task = tx.select().from(tasks2).where(and(
27111
+ eq(tasks2.id, id),
27112
+ sql`${tasks2.status} IN ('pending', 'failed', 'dead_letter')`,
27113
+ ...this.buildScopeWhere(scope)
27114
+ )).get();
27115
+ if (!task) return null;
27116
+ this.validateNewTask({
27117
+ name: normalizedData.name ?? task.name,
27118
+ agent: normalizedData.agent ?? task.agent,
27119
+ model: normalizedData.model ?? task.model,
27120
+ prompt: normalizedData.prompt ?? task.prompt,
27121
+ cwd: task.cwd,
27122
+ category: normalizedData.category ?? task.category,
27123
+ importance: normalizedData.importance ?? task.importance,
27124
+ urgency: normalizedData.urgency ?? task.urgency,
27125
+ batchId: normalizedData.batchId === void 0 ? task.batchId : normalizedData.batchId,
27126
+ maxRetries: normalizedData.maxRetries ?? task.maxRetries,
27127
+ retryBackoffMs: normalizedData.retryBackoffMs ?? task.retryBackoffMs,
27128
+ timeoutMs: normalizedData.timeoutMs === void 0 ? task.timeoutMs : normalizedData.timeoutMs,
27129
+ dependsOn: task.dependsOn
27130
+ });
27131
+ const maxRetries = normalizedData.maxRetries ?? task.maxRetries ?? 3;
27132
+ const exhausted = task.status === "failed" && (task.retryCount ?? 0) > maxRetries;
27133
+ return tx.update(tasks2).set({
27134
+ ...normalizedData,
27135
+ ...exhausted ? {
27136
+ status: "dead_letter",
27137
+ retryAfter: null
27138
+ } : {}
27139
+ }).where(eq(tasks2.id, id)).returning().get() ?? null;
27064
27140
  }, { behavior: "immediate" });
27065
27141
  }
27066
27142
  static validateNewTask(data) {
27067
27143
  if (!data.name.trim()) throw new Error("name \u4E0D\u80FD\u4E3A\u7A7A");
27068
27144
  if (!data.agent.trim()) throw new Error("agent \u4E0D\u80FD\u4E3A\u7A7A");
27069
27145
  if (!data.prompt.trim()) throw new Error("prompt \u4E0D\u80FD\u4E3A\u7A7A");
27146
+ validateTaskWorkingDirectory(data.cwd);
27070
27147
  this.validateInteger("importance", data.importance, 1, 5);
27071
27148
  this.validateInteger("urgency", data.urgency, 1, 5);
27072
27149
  this.validateInteger("maxRetries", data.maxRetries, 0, 1e3);
@@ -27087,12 +27164,14 @@ var TaskService = class {
27087
27164
  isNull(tasks2.retryAfter),
27088
27165
  sql`${tasks2.retryAfter} <= ${nowMs}`
27089
27166
  );
27090
- const hasExcludedBatches = scope.excludedBatchIds && scope.excludedBatchIds.length > 0;
27167
+ const excludedBatchIds = [...new Set((scope.excludedBatchIds ?? []).map((batchId) => normalizeTaskBatchId(batchId)).filter((batchId) => Boolean(batchId)))];
27168
+ const hasExcludedBatches = excludedBatchIds.length > 0;
27091
27169
  let batchFilter;
27092
27170
  if (hasExcludedBatches) {
27093
27171
  batchFilter = or(
27094
27172
  isNull(tasks2.batchId),
27095
- sql`${tasks2.batchId} NOT IN ${scope.excludedBatchIds}`
27173
+ sql`trim(${tasks2.batchId}, ${TASK_BATCH_TRIM_CHARACTERS}) = ''`,
27174
+ sql`trim(${tasks2.batchId}, ${TASK_BATCH_TRIM_CHARACTERS}) NOT IN ${excludedBatchIds}`
27096
27175
  );
27097
27176
  }
27098
27177
  const statusConditions = or(
@@ -27126,9 +27205,11 @@ var TaskService = class {
27126
27205
  ),
27127
27206
  or(
27128
27207
  isNull(tasks2.batchId),
27208
+ sql`trim(${tasks2.batchId}, ${TASK_BATCH_TRIM_CHARACTERS}) = ''`,
27129
27209
  sql`NOT EXISTS (
27130
27210
  SELECT 1 FROM tasks AS running_batch_task
27131
- WHERE running_batch_task.batch_id = ${tasks2.batchId}
27211
+ WHERE trim(running_batch_task.batch_id, ${TASK_BATCH_TRIM_CHARACTERS})
27212
+ = trim(${tasks2.batchId}, ${TASK_BATCH_TRIM_CHARACTERS})
27132
27213
  AND (
27133
27214
  running_batch_task.status = 'running'
27134
27215
  OR EXISTS (
@@ -27152,12 +27233,18 @@ var TaskService = class {
27152
27233
  ).limit(1);
27153
27234
  return result[0] ?? null;
27154
27235
  }
27155
- static async countRunning() {
27236
+ static async countRunning(scope = {}) {
27237
+ const scopeConditions = scope.legacyCwd ? [sql`${tasks2.cwd} IS NULL OR trim(${tasks2.cwd}) = ''`] : this.buildScopeWhere(scope);
27238
+ if (scope.batchId !== void 0) {
27239
+ const batchId = normalizeTaskBatchId(scope.batchId);
27240
+ scopeConditions.push(batchId ? sql`trim(${tasks2.batchId}, ${TASK_BATCH_TRIM_CHARACTERS}) = ${batchId}` : sql`0`);
27241
+ }
27156
27242
  return db.transaction((tx) => {
27157
- const runningTasks = tx.select({ count: sql`count(*)` }).from(tasks2).where(eq(tasks2.status, "running")).get();
27243
+ const runningTasks = tx.select({ count: sql`count(*)` }).from(tasks2).where(and(eq(tasks2.status, "running"), ...scopeConditions)).get();
27158
27244
  const runsWithoutRunningTask = tx.select({ count: sql`count(DISTINCT ${taskRuns2.taskId})` }).from(taskRuns2).innerJoin(tasks2, eq(tasks2.id, taskRuns2.taskId)).where(and(
27159
27245
  eq(taskRuns2.status, "running"),
27160
- sql`${tasks2.status} <> 'running'`
27246
+ sql`${tasks2.status} <> 'running'`,
27247
+ ...scopeConditions
27161
27248
  )).get();
27162
27249
  return Number(runningTasks?.count ?? 0) + Number(runsWithoutRunningTask?.count ?? 0);
27163
27250
  }, { behavior: "deferred" });
@@ -27490,15 +27577,17 @@ var TaskService = class {
27490
27577
  }).where(and(...conditions, hasViableDependency())).returning().get() ?? null, { behavior: "immediate" });
27491
27578
  }
27492
27579
  static async retryBatch(batchId, scope = {}) {
27580
+ const normalizedBatchId = normalizeTaskBatchId(batchId);
27581
+ if (!normalizedBatchId) return 0;
27493
27582
  const sqlite2 = getSqlite();
27494
27583
  const scopeFilter = scope.cwd === void 0 ? "" : "AND candidate.cwd = ?";
27495
- const parameters = scope.cwd === void 0 ? [batchId] : [batchId, scope.cwd];
27584
+ const parameters = scope.cwd === void 0 ? [TASK_BATCH_TRIM_CHARACTERS, normalizedBatchId] : [TASK_BATCH_TRIM_CHARACTERS, normalizedBatchId, scope.cwd];
27496
27585
  return db.transaction(() => sqlite2.query(`
27497
27586
  WITH RECURSIVE
27498
27587
  candidate(id, cwd, depends_on) AS MATERIALIZED (
27499
27588
  SELECT candidate.id, candidate.cwd, candidate.depends_on
27500
27589
  FROM tasks AS candidate
27501
- WHERE candidate.batch_id = ?
27590
+ WHERE trim(candidate.batch_id, ?) = ?
27502
27591
  AND candidate.status IN ('failed', 'dead_letter')
27503
27592
  ${scopeFilter}
27504
27593
  ),
@@ -27552,16 +27641,28 @@ var TaskService = class {
27552
27641
  static async list(options = {}) {
27553
27642
  let query = db.select().from(tasks2).$dynamic();
27554
27643
  const conditions = [];
27555
- if (options.status) {
27644
+ if (options.activeExecution) {
27645
+ conditions.push(or(
27646
+ eq(tasks2.status, "running"),
27647
+ sql`EXISTS (
27648
+ SELECT 1 FROM task_runs AS active_list_run
27649
+ WHERE active_list_run.task_id = ${tasks2.id}
27650
+ AND active_list_run.status = 'running'
27651
+ )`
27652
+ ));
27653
+ } else if (options.status) {
27556
27654
  conditions.push(eq(tasks2.status, options.status));
27557
27655
  }
27558
- if (options.batchId) {
27559
- conditions.push(eq(tasks2.batchId, options.batchId));
27656
+ if (options.batchId !== void 0) {
27657
+ const batchId = normalizeTaskBatchId(options.batchId);
27658
+ conditions.push(batchId ? sql`trim(${tasks2.batchId}, ${TASK_BATCH_TRIM_CHARACTERS}) = ${batchId}` : sql`0`);
27560
27659
  }
27561
27660
  if (options.category) {
27562
27661
  conditions.push(eq(tasks2.category, options.category));
27563
27662
  }
27564
- if (options.cwd !== void 0) {
27663
+ if (options.legacyCwd) {
27664
+ conditions.push(sql`${tasks2.cwd} IS NULL OR trim(${tasks2.cwd}) = ''`);
27665
+ } else if (options.cwd !== void 0) {
27565
27666
  conditions.push(eq(tasks2.cwd, options.cwd));
27566
27667
  }
27567
27668
  if (conditions.length > 0) {
@@ -27579,9 +27680,12 @@ var TaskService = class {
27579
27680
  static async stats(options = {}) {
27580
27681
  const conditions = [];
27581
27682
  if (options.batchId !== void 0) {
27582
- conditions.push(eq(tasks2.batchId, options.batchId));
27683
+ const batchId = normalizeTaskBatchId(options.batchId);
27684
+ conditions.push(batchId ? sql`trim(${tasks2.batchId}, ${TASK_BATCH_TRIM_CHARACTERS}) = ${batchId}` : sql`0`);
27583
27685
  }
27584
- if (options.cwd !== void 0) {
27686
+ if (options.legacyCwd) {
27687
+ conditions.push(sql`${tasks2.cwd} IS NULL OR trim(${tasks2.cwd}) = ''`);
27688
+ } else if (options.cwd !== void 0) {
27585
27689
  conditions.push(eq(tasks2.cwd, options.cwd));
27586
27690
  }
27587
27691
  const whereCondition = conditions.length > 0 ? and(...conditions) : void 0;
@@ -27606,6 +27710,33 @@ var TaskService = class {
27606
27710
  }
27607
27711
  return stats;
27608
27712
  }
27713
+ static async projectSummaries(limit = 100) {
27714
+ this.validateInteger("limit", limit, 1, 1e3);
27715
+ const lastCreatedAt = sql`max(${tasks2.createdAt})`;
27716
+ const lastTaskId = sql`max(${tasks2.id})`;
27717
+ const rows = await db.select({
27718
+ cwd: tasks2.cwd,
27719
+ total: sql`count(*)`,
27720
+ pending: sql`sum(CASE WHEN ${tasks2.status} = 'pending' THEN 1 ELSE 0 END)`,
27721
+ running: sql`sum(CASE WHEN ${tasks2.status} = 'running' OR EXISTS (
27722
+ SELECT 1 FROM task_runs AS active_project_run
27723
+ WHERE active_project_run.task_id = ${tasks2.id}
27724
+ AND active_project_run.status = 'running'
27725
+ ) THEN 1 ELSE 0 END)`,
27726
+ failed: sql`sum(CASE WHEN ${tasks2.status} IN ('failed', 'dead_letter') THEN 1 ELSE 0 END)`,
27727
+ done: sql`sum(CASE WHEN ${tasks2.status} = 'done' THEN 1 ELSE 0 END)`,
27728
+ lastCreatedAt
27729
+ }).from(tasks2).where(sql`${tasks2.cwd} IS NOT NULL AND trim(${tasks2.cwd}) <> ''`).groupBy(tasks2.cwd).orderBy(desc(lastCreatedAt), desc(lastTaskId)).limit(limit);
27730
+ return rows.flatMap((row) => row.cwd === null ? [] : [{
27731
+ cwd: row.cwd,
27732
+ total: Number(row.total),
27733
+ pending: Number(row.pending),
27734
+ running: Number(row.running),
27735
+ failed: Number(row.failed),
27736
+ done: Number(row.done),
27737
+ lastCreatedAt: row.lastCreatedAt === null ? null : Number(row.lastCreatedAt) * 1e3
27738
+ }]);
27739
+ }
27609
27740
  static async delete(id, scope = {}) {
27610
27741
  const conditions = [
27611
27742
  eq(tasks2.id, id),
@@ -27758,24 +27889,29 @@ function isValidCronExpr(expr) {
27758
27889
  var { taskTemplates: taskTemplates2 } = schema_exports;
27759
27890
  var TaskTemplateService = class {
27760
27891
  static async create(data) {
27761
- this.validate(data);
27892
+ const normalizedData = {
27893
+ ...data,
27894
+ batchId: normalizeTaskBatchId(data.batchId)
27895
+ };
27896
+ this.validate(normalizedData);
27762
27897
  const now = Date.now();
27763
- const nextRunAt = data.nextRunAt ?? this.calculateNextRunAt(
27764
- data.scheduleType,
27898
+ const nextRunAt = normalizedData.nextRunAt ?? this.calculateNextRunAt(
27899
+ normalizedData.scheduleType,
27765
27900
  {
27766
- cronExpr: data.cronExpr ?? null,
27767
- intervalMs: data.intervalMs ?? null,
27768
- runAt: data.runAt ?? null
27901
+ cronExpr: normalizedData.cronExpr ?? null,
27902
+ intervalMs: normalizedData.intervalMs ?? null,
27903
+ runAt: normalizedData.runAt ?? null
27769
27904
  },
27770
27905
  now
27771
27906
  );
27772
- const result = await db.insert(taskTemplates2).values({ ...data, nextRunAt, createdAt: now, updatedAt: now }).returning();
27907
+ const result = await db.insert(taskTemplates2).values({ ...normalizedData, nextRunAt, createdAt: now, updatedAt: now }).returning();
27773
27908
  return result[0];
27774
27909
  }
27775
27910
  static validate(data) {
27776
27911
  if (!data.name.trim()) throw new Error("name \u4E0D\u80FD\u4E3A\u7A7A");
27777
27912
  if (!data.agent.trim()) throw new Error("agent \u4E0D\u80FD\u4E3A\u7A7A");
27778
27913
  if (!data.prompt.trim()) throw new Error("prompt \u4E0D\u80FD\u4E3A\u7A7A");
27914
+ validateTaskWorkingDirectory(data.cwd);
27779
27915
  const scheduleType = data.scheduleType;
27780
27916
  if (!["cron", "delayed", "recurring"].includes(scheduleType)) {
27781
27917
  throw new Error("scheduleType \u5FC5\u987B\u662F cron\u3001delayed \u6216 recurring");
@@ -27802,13 +27938,40 @@ var TaskTemplateService = class {
27802
27938
  throw new Error(`${name} \u5FC5\u987B\u662F ${min} \u5230 ${max} \u4E4B\u95F4\u7684\u6574\u6570`);
27803
27939
  }
27804
27940
  }
27805
- static async list(limit = 50) {
27806
- return await db.select().from(taskTemplates2).orderBy(desc(taskTemplates2.createdAt), desc(taskTemplates2.id)).limit(limit);
27941
+ static async list(limit = 50, offset = 0) {
27942
+ return await db.select().from(taskTemplates2).orderBy(desc(taskTemplates2.createdAt), desc(taskTemplates2.id)).limit(limit).offset(offset);
27943
+ }
27944
+ static async stats() {
27945
+ const result = await db.select({
27946
+ total: sql`count(*)`,
27947
+ enabled: sql`sum(case when ${taskTemplates2.enabled} = 1 then 1 else 0 end)`
27948
+ }).from(taskTemplates2);
27949
+ const total = Number(result[0]?.total ?? 0);
27950
+ const enabled = Number(result[0]?.enabled ?? 0);
27951
+ return { total, enabled, disabled: total - enabled };
27807
27952
  }
27808
27953
  static async getById(id) {
27809
27954
  const result = await db.select().from(taskTemplates2).where(eq(taskTemplates2.id, id));
27810
27955
  return result[0] || null;
27811
27956
  }
27957
+ static async update(id, data) {
27958
+ const normalizedData = {
27959
+ ...data,
27960
+ batchId: normalizeTaskBatchId(data.batchId) ?? null
27961
+ };
27962
+ this.validate(normalizedData);
27963
+ const now = Date.now();
27964
+ const nextRunAt = this.calculateNextRunAt(
27965
+ normalizedData.scheduleType,
27966
+ normalizedData,
27967
+ now
27968
+ );
27969
+ return db.transaction((tx) => {
27970
+ const existing = tx.select({ id: taskTemplates2.id }).from(taskTemplates2).where(eq(taskTemplates2.id, id)).limit(1).get();
27971
+ if (!existing) return null;
27972
+ return tx.update(taskTemplates2).set({ ...normalizedData, nextRunAt, updatedAt: now }).where(eq(taskTemplates2.id, id)).returning().get() ?? null;
27973
+ }, { behavior: "immediate" });
27974
+ }
27812
27975
  static async enable(id) {
27813
27976
  return db.transaction((tx) => {
27814
27977
  const template = tx.select().from(taskTemplates2).where(eq(taskTemplates2.id, id)).limit(1).get();
@@ -27981,11 +28144,11 @@ import {
27981
28144
  mkdirSync as mkdirSync3,
27982
28145
  readFileSync as readFileSync3,
27983
28146
  rmSync,
27984
- statSync,
28147
+ statSync as statSync2,
27985
28148
  writeFileSync
27986
28149
  } from "fs";
27987
28150
  import { homedir as homedir3, userInfo } from "os";
27988
- import { delimiter, dirname as dirname4, isAbsolute, join as join4, resolve } from "path";
28151
+ import { delimiter, dirname as dirname4, isAbsolute as isAbsolute2, join as join4, resolve } from "path";
27989
28152
  import { fileURLToPath as fileURLToPath3 } from "url";
27990
28153
  import { Database as Database4 } from "bun:sqlite";
27991
28154
 
@@ -28206,7 +28369,7 @@ function getRunningVersion(env = process.env, cwd = process.cwd()) {
28206
28369
  }
28207
28370
  }
28208
28371
  function resolveRuntimeExecutable(command, env, cwd) {
28209
- if (isAbsolute(command) || command.includes("/")) return runtimePath(command, cwd);
28372
+ if (isAbsolute2(command) || command.includes("/")) return runtimePath(command, cwd);
28210
28373
  for (const entry of (env.PATH ?? "").split(delimiter)) {
28211
28374
  if (!entry) continue;
28212
28375
  const candidate = resolve(cwd, entry, command);
@@ -28251,7 +28414,7 @@ function pm2CommandTimeoutMs(env = process.env) {
28251
28414
  }
28252
28415
  function resolvePm2Bin() {
28253
28416
  const configured = pm2Bin();
28254
- if (isAbsolute(configured)) return configured;
28417
+ if (isAbsolute2(configured)) return configured;
28255
28418
  const result = spawnSync("which", [configured], { encoding: "utf8" });
28256
28419
  const resolved = result.status === 0 ? result.stdout.trim().split("\n")[0] : "";
28257
28420
  if (!resolved) throw new Error(`[supertask] \u65E0\u6CD5\u89E3\u6790 pm2 \u53EF\u6267\u884C\u6587\u4EF6: ${configured}`);
@@ -28358,7 +28521,7 @@ function gatewayRuntimeFromProcess(processInfo) {
28358
28521
  if (typeof savedBunPath !== "string" || typeof savedCwd !== "string") return null;
28359
28522
  try {
28360
28523
  accessSync(savedBunPath, constants.X_OK);
28361
- if (!statSync(savedCwd).isDirectory()) return null;
28524
+ if (!statSync2(savedCwd).isDirectory()) return null;
28362
28525
  if (spawnSync(savedBunPath, ["--version"], {
28363
28526
  stdio: "ignore",
28364
28527
  env: savedEnv,
@@ -28495,7 +28658,7 @@ function pm2StartGateway(runtime = currentGatewayRuntime()) {
28495
28658
  try {
28496
28659
  accessSync(runtime.bunPath, constants.X_OK);
28497
28660
  accessSync(runtime.gatewayEntry, constants.R_OK);
28498
- if (!statSync(runtime.cwd).isDirectory()) throw new Error("cwd is not a directory");
28661
+ if (!statSync2(runtime.cwd).isDirectory()) throw new Error("cwd is not a directory");
28499
28662
  } catch (error45) {
28500
28663
  throw new Error(`[supertask] Gateway \u76EE\u6807\u8FD0\u884C\u65F6\u4E0D\u53EF\u7528: ${error45 instanceof Error ? error45.message : String(error45)}`);
28501
28664
  }
@@ -28819,7 +28982,7 @@ function latestVersion() {
28819
28982
  }
28820
28983
  return uniqueVersions[0];
28821
28984
  }
28822
- function resolveInstalledVersion(expectedVersion) {
28985
+ function resolveInstalledPluginVersion(expectedVersion) {
28823
28986
  const override = process.env.SUPERTASK_PLUGIN_PACKAGE_DIR;
28824
28987
  const installed = override ? [pluginAt(override)].filter((plugin) => plugin !== null) : installedPlugins();
28825
28988
  const matched = installed.find((plugin) => plugin.version === expectedVersion);
@@ -28848,7 +29011,7 @@ function installPluginVersion(version3) {
28848
29011
  if (result.status !== 0) {
28849
29012
  throw new Error(`[supertask] OpenCode \u63D2\u4EF6\u66F4\u65B0\u5931\u8D25: ${output || `\u9000\u51FA\u7801 ${result.status}`}`);
28850
29013
  }
28851
- return resolveInstalledVersion(version3);
29014
+ return resolveInstalledPluginVersion(version3);
28852
29015
  }
28853
29016
  function installLatestPlugin() {
28854
29017
  return installPluginVersion(latestVersion());
@@ -28937,12 +29100,21 @@ var SYSTEM_INSTRUCTION = `
28937
29100
  - \u5F53\u7528\u6237\u95EE"\u4EFB\u52A1\u8FDB\u5C55\u5982\u4F55"\u65F6\uFF0C\u7528 \`supertask_status\` \u548C \`supertask_list\`
28938
29101
  - \u5F53\u7528\u6237\u8BF4"\u91CD\u8BD5\u5931\u8D25\u7684\u4EFB\u52A1"\u65F6\uFF0C\u7528 \`supertask_retry\`
28939
29102
 
29103
+ ### \u6279\u6B21\u3001\u5E76\u53D1\u4E0E\u4F9D\u8D56
29104
+
29105
+ - \`batchId\` \u662F\u5168\u5C40\u4E32\u884C\u6267\u884C\u952E\uFF1A\u5373\u4F7F\u4EFB\u52A1\u5C5E\u4E8E\u4E0D\u540C\u9879\u76EE\u76EE\u5F55\uFF0C\u6240\u6709\u5177\u6709\u76F8\u540C\u975E\u7A7A \`batchId\` \u7684\u4EFB\u52A1\u4E5F\u4E0D\u4F1A\u540C\u65F6\u6267\u884C\uFF0CGateway \u91CD\u542F\u540E\u4ECD\u7136\u6210\u7ACB
29106
+ - \u4E0D\u540C \`batchId\` \u6216\u672A\u8BBE\u7F6E \`batchId\` \u7684\u4EFB\u52A1\u53EF\u4EE5\u5E76\u884C\uFF0C\u4F46\u4ECD\u53D7 Gateway \u5168\u5C40\u5E76\u53D1\u4E0A\u9650\u548C\u4EFB\u52A1\u4F9D\u8D56\u7EA6\u675F
29107
+ - \u628A\u9700\u8981\u4E92\u65A5\u6267\u884C\u7684\u4EFB\u52A1\u8BBE\u7F6E\u4E3A\u540C\u4E00 \`batchId\`\uFF1B\u5EFA\u8BAE\u4F7F\u7528\u201C\u9879\u76EE:\u7528\u9014\u201D\u7B49\u5168\u5C40\u552F\u4E00\u540D\u79F0\uFF0C\u4E0D\u8981\u7ED9\u5F7C\u6B64\u72EC\u7ACB\u3001\u5E0C\u671B\u5E76\u884C\u7684\u4EFB\u52A1\u590D\u7528\u540C\u4E00 \`batchId\`
29108
+ - \`batchId\` \u53EA\u4FDD\u8BC1\u4E0D\u5E76\u53D1\uFF0C\u4E0D\u4FDD\u8BC1\u4E0D\u540C\u4F18\u5148\u7EA7\u4EFB\u52A1\u6309\u521B\u5EFA\u987A\u5E8F\u8FD0\u884C\uFF1B\u82E5\u4EFB\u52A1 B \u5FC5\u987B\u7B49\u5F85\u4EFB\u52A1 A \u5B8C\u6210\uFF0C\u5E94\u628A B \u7684 \`dependsOn\` \u8BBE\u7F6E\u4E3A A \u7684\u4EFB\u52A1 ID
29109
+ - \u5411\u5DF2\u6709\u6279\u6B21\u8FFD\u52A0\u4EFB\u52A1\u524D\uFF0C\u53EF\u7528 \`supertask_status\` \u7684 \`batchId\` \u67E5\u770B\u5F53\u524D\u9879\u76EE\u7EDF\u8BA1\uFF0C\u5E76\u4EE5\u8FD4\u56DE\u503C \`globalBatch.activeRunning\` \u5224\u65AD\u8BE5\u6279\u6B21\u662F\u5426\u5DF2\u88AB\u4EFB\u4E00\u9879\u76EE\u5360\u7528\uFF1B\`blockedByOtherProject\` \u8868\u793A\u5360\u7528\u6765\u81EA\u5176\u4ED6\u9879\u76EE
29110
+
28940
29111
  ### \u8C03\u5EA6\u6A21\u677F
28941
29112
 
28942
29113
  \u7528 \`supertask_schedule\` \u53EF\u521B\u5EFA\u4E09\u79CD\u5B9A\u65F6\u4EFB\u52A1\uFF1A
28943
29114
  - \`cron\`: cron \u8868\u8FBE\u5F0F\uFF08\u5982 "0 9 * * 1-5" = \u5DE5\u4F5C\u65E5 9 \u70B9\uFF09
28944
29115
  - \`recurring\`: \u56FA\u5B9A\u95F4\u9694\u5FAA\u73AF\uFF08\u5982\u6BCF 6 \u5C0F\u65F6\uFF09
28945
29116
  - \`delayed\`: \u4E00\u6B21\u6027\u5B9A\u65F6\u6267\u884C
29117
+ - \`max_instances\` \u53EA\u9650\u5236\u81EA\u52A8\u8C03\u5EA6\u4EA7\u751F\u7684\u6D3B\u8DC3\u5B9E\u4F8B\uFF08\u6392\u961F\u3001\u8FD0\u884C\u4E2D\u3001\u7B49\u5F85\u91CD\u8BD5\uFF09\uFF1B\u624B\u52A8\u201C\u7ACB\u5373\u8FD0\u884C\u4E00\u6B21\u201D\u59CB\u7EC8\u521B\u5EFA\u4EFB\u52A1\u5E76\u52A0\u5165\u961F\u5217
28946
29118
  `;
28947
29119
  var SuperTaskPlugin = async () => {
28948
29120
  return {
@@ -28955,7 +29127,7 @@ var SuperTaskPlugin = async () => {
28955
29127
  tool: {
28956
29128
  // 创建任务
28957
29129
  supertask_add: tool({
28958
- 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",
29130
+ description: "\u521B\u5EFA\u65B0\u4EFB\u52A1\u5230\u6301\u4E45\u961F\u5217\u3002\u8DE8\u9879\u76EE\u7684\u76F8\u540C\u975E\u7A7A batchId \u4EFB\u52A1\u5168\u5C40\u4E25\u683C\u4E32\u884C\uFF0C\u4E0D\u540C\u6279\u6B21\u53EF\u5728\u5168\u5C40\u5E76\u53D1\u4E0A\u9650\u5185\u5E76\u884C\uFF1BdependsOn \u7528\u4E8E\u8868\u8FBE\u5FC5\u987B\u5B8C\u6210\u7684\u5148\u540E\u5173\u7CFB\u3002\u8FD4\u56DE\u4EFB\u52A1 ID\u3002\u4EFB\u52A1\u6309 urgency\u3001importance\u3001createdAt\u3001id \u7684\u987A\u5E8F\u8C03\u5EA6\u3002",
28959
29131
  args: {
28960
29132
  name: tool.schema.string().trim().min(1).describe("\u4EFB\u52A1\u540D\u79F0\uFF08\u4EBA\u7C7B\u53EF\u8BFB\uFF09"),
28961
29133
  agent: tool.schema.string().trim().min(1).describe("\u6267\u884C\u7684 Agent \u540D\u79F0\uFF0C\u5982 localize-gen, course-gen"),
@@ -28964,8 +29136,8 @@ var SuperTaskPlugin = async () => {
28964
29136
  category: tool.schema.enum(["translate", "generate", "review", "test", "general"]).optional().describe("\u4EFB\u52A1\u5206\u7C7B"),
28965
29137
  importance: tool.schema.number().int().min(1).max(5).optional().describe("\u91CD\u8981\u7A0B\u5EA6 1-5\uFF085 \u6700\u91CD\u8981\uFF09"),
28966
29138
  urgency: tool.schema.number().int().min(1).max(5).optional().describe("\u7D27\u6025\u7A0B\u5EA6 1-5\uFF085 \u6700\u7D27\u6025\uFF09"),
28967
- batchId: tool.schema.string().optional().describe("\u6279\u6B21 ID\uFF0C\u7528\u4E8E\u5206\u7EC4\u7BA1\u7406"),
28968
- dependsOn: tool.schema.number().int().positive().optional().describe("\u4F9D\u8D56\u7684\u4EFB\u52A1 ID\uFF0C\u8BE5\u4EFB\u52A1\u5B8C\u6210\u540E\u624D\u4F1A\u6267\u884C"),
29139
+ batchId: tool.schema.string().trim().min(1).optional().describe("\u5168\u5C40\u4E32\u884C\u6279\u6B21 ID\uFF1A\u5373\u4F7F\u5C5E\u4E8E\u4E0D\u540C\u9879\u76EE\uFF0C\u6240\u6709\u76F8\u540C\u975E\u7A7A batchId \u7684\u4EFB\u52A1\u4E5F\u4E0D\u4F1A\u540C\u65F6\u6267\u884C\uFF1B\u72EC\u7ACB\u4EFB\u52A1\u8BF7\u7701\u7565\uFF0C\u4E0D\u8981\u4F20\u7A7A\u5B57\u7B26\u4E32\u6216\u590D\u7528\u540C\u4E00\u503C"),
29140
+ dependsOn: tool.schema.number().int().positive().optional().describe("\u4F9D\u8D56\u7684\u4EFB\u52A1 ID\uFF1B\u9700\u8981\u4E25\u683C\u5148\u540E\u987A\u5E8F\u65F6\u8BBE\u7F6E\uFF0C\u524D\u7F6E\u4EFB\u52A1\u5B8C\u6210\u540E\u624D\u4F1A\u6267\u884C"),
28969
29141
  max_retries: tool.schema.number().int().min(0).max(1e3).optional().describe("\u9996\u6B21\u6267\u884C\u4E4B\u5916\u5141\u8BB8\u7684\u91CD\u8BD5\u6B21\u6570\uFF0C\u9ED8\u8BA4 3"),
28970
29142
  retry_backoff_ms: tool.schema.number().int().min(0).max(864e5).optional().describe("\u91CD\u8BD5\u9000\u907F\u57FA\u7840\u95F4\u9694 ms\uFF0C\u9ED8\u8BA4 30000"),
28971
29143
  timeout_ms: tool.schema.number().int().min(1e3).max(6048e5).optional().describe("\u4EFB\u52A1\u786C\u8D85\u65F6 ms\uFF1B\u672A\u4F20\u5219\u4F7F\u7528 Gateway \u9ED8\u8BA4\u503C"),
@@ -29032,15 +29204,32 @@ var SuperTaskPlugin = async () => {
29032
29204
  }),
29033
29205
  // 查看统计
29034
29206
  supertask_status: tool({
29035
- description: "\u67E5\u770B\u4EFB\u52A1\u961F\u5217\u7EDF\u8BA1\u3002\u53EF\u6309\u6279\u6B21\u7B5B\u9009\u3002",
29207
+ description: "\u67E5\u770B\u5F53\u524D\u9879\u76EE\u7684\u4EFB\u52A1\u961F\u5217\u7EDF\u8BA1\u3002\u4F20\u5165 batchId \u65F6\u8FD8\u8FD4\u56DE\u8DE8\u9879\u76EE\u7684 globalBatch \u7EDF\u8BA1\u548C\u662F\u5426\u88AB\u5176\u4ED6\u9879\u76EE\u5360\u7528\u3002",
29036
29208
  args: {
29037
- batchId: tool.schema.string().optional().describe("\u6309\u6279\u6B21\u7B5B\u9009"),
29209
+ batchId: tool.schema.string().trim().min(1).optional().describe("\u6309\u6279\u6B21\u7B5B\u9009\uFF1B\u540C\u540D\u6279\u6B21\u5728\u6240\u6709\u9879\u76EE\u95F4\u5168\u5C40\u4E32\u884C"),
29038
29210
  cwd: tool.schema.string().optional().describe("\u9879\u76EE\u9694\u79BB\uFF1A\u4F20\u5165\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\uFF0C\u53EA\u7EDF\u8BA1\u8BE5\u9879\u76EE\u7684\u4EFB\u52A1")
29039
29211
  },
29040
29212
  async execute(args, context) {
29041
29213
  try {
29042
- const stats = await TaskService.stats({ batchId: args.batchId, cwd: context.directory });
29043
- return JSON.stringify(stats);
29214
+ const project = await TaskService.stats({
29215
+ batchId: args.batchId,
29216
+ cwd: context.directory
29217
+ });
29218
+ if (args.batchId === void 0) return JSON.stringify(project);
29219
+ const [globalBatch, activeRunning, globalActiveRunning] = await Promise.all([
29220
+ TaskService.stats({ batchId: args.batchId }),
29221
+ TaskService.countRunning({ batchId: args.batchId, cwd: context.directory }),
29222
+ TaskService.countRunning({ batchId: args.batchId })
29223
+ ]);
29224
+ return JSON.stringify({
29225
+ ...project,
29226
+ activeRunning,
29227
+ globalBatch: {
29228
+ ...globalBatch,
29229
+ activeRunning: globalActiveRunning
29230
+ },
29231
+ blockedByOtherProject: globalActiveRunning > activeRunning
29232
+ });
29044
29233
  } catch (error45) {
29045
29234
  return JSON.stringify({
29046
29235
  error: error45 instanceof Error ? error45.message : String(error45)
@@ -29144,14 +29333,14 @@ var SuperTaskPlugin = async () => {
29144
29333
  category: tool.schema.enum(["translate", "generate", "review", "test", "general"]).optional().describe("\u4EFB\u52A1\u5206\u7C7B"),
29145
29334
  importance: tool.schema.number().int().min(1).max(5).optional().describe("\u91CD\u8981\u7A0B\u5EA6 1-5"),
29146
29335
  urgency: tool.schema.number().int().min(1).max(5).optional().describe("\u7D27\u6025\u7A0B\u5EA6 1-5"),
29147
- batchId: tool.schema.string().optional().describe("\u6A21\u677F\u751F\u6210\u7684\u4EFB\u52A1\u5F52\u5C5E\u7684\u6279\u6B21 ID"),
29336
+ batchId: tool.schema.string().trim().min(1).optional().describe("\u6A21\u677F\u751F\u6210\u4EFB\u52A1\u7684\u5168\u5C40\u4E32\u884C\u6279\u6B21 ID\uFF1B\u8DE8\u9879\u76EE\u7684\u76F8\u540C\u975E\u7A7A batchId \u5B9E\u4F8B\u4E0D\u4F1A\u540C\u65F6\u6267\u884C\uFF1B\u65E0\u6279\u6B21\u65F6\u8BF7\u7701\u7565"),
29148
29337
  schedule: tool.schema.object({
29149
29338
  type: tool.schema.enum(["cron", "delayed", "recurring"]).describe("\u8C03\u5EA6\u7C7B\u578B"),
29150
29339
  cron_expr: tool.schema.string().optional().describe("cron \u8868\u8FBE\u5F0F\uFF08cron \u7C7B\u578B\u5FC5\u586B\uFF0C\u5982 '0 9 * * 1-5'\uFF09"),
29151
29340
  delay: tool.schema.string().optional().describe("\u5EF6\u8FDF\u65F6\u95F4\uFF08delayed \u7C7B\u578B\u5FC5\u586B\uFF09\uFF0C\u53CB\u597D\u683C\u5F0F\u5982 '30s' '5min' '1h' '2d'\uFF0C\u4E5F\u652F\u6301 ISO 8601 duration \u5982 'PT30M'"),
29152
29341
  interval: tool.schema.string().optional().describe("\u5FAA\u73AF\u95F4\u9694\uFF08recurring \u7C7B\u578B\u5FC5\u586B\uFF09\uFF0C\u53CB\u597D\u683C\u5F0F\u5982 '1h' '30min' '5s'\uFF0C\u4E5F\u652F\u6301 ISO 8601 duration \u5982 'PT1H'")
29153
29342
  }).describe("\u8C03\u5EA6\u914D\u7F6E"),
29154
- max_instances: tool.schema.number().int().min(1).max(1e3).optional().describe("\u6700\u5927\u5E76\u53D1\u5B9E\u4F8B\u6570\uFF0C\u9ED8\u8BA4 1"),
29343
+ max_instances: tool.schema.number().int().min(1).max(1e3).optional().describe("\u81EA\u52A8\u8C03\u5EA6\u7684\u6D3B\u8DC3\u5B9E\u4F8B\u4E0A\u9650\uFF0C\u9ED8\u8BA4 1\uFF1B\u6D3B\u8DC3\u5B9E\u4F8B\u5305\u542B\u6392\u961F\u3001\u8FD0\u884C\u4E2D\u548C\u7B49\u5F85\u91CD\u8BD5\uFF0C\u624B\u52A8\u7ACB\u5373\u8FD0\u884C\u4E0D\u53D7\u6B64\u9650\u5236"),
29155
29344
  max_retries: tool.schema.number().int().min(0).max(1e3).optional().describe("\u514B\u9686\u7ED9 task \u7684\u6700\u5927\u91CD\u8BD5\u6B21\u6570\uFF0C\u9ED8\u8BA4 3"),
29156
29345
  retry_backoff_ms: tool.schema.number().int().min(0).max(864e5).optional().describe("\u514B\u9686\u7ED9 task \u7684\u9000\u907F\u57FA\u7840\u95F4\u9694 ms\uFF0C\u9ED8\u8BA4 30000"),
29157
29346
  timeout_ms: tool.schema.number().int().min(1e3).max(6048e5).optional().describe("\u514B\u9686\u7ED9 task \u7684\u786C\u8D85\u65F6 ms\uFF1B\u672A\u4F20\u5219\u4F7F\u7528 Gateway \u9ED8\u8BA4\u503C")
@@ -29262,7 +29451,7 @@ var SuperTaskPlugin = async () => {
29262
29451
  before: result.before,
29263
29452
  after: result.after,
29264
29453
  restarted: result.restarted,
29265
- message: `SuperTask \u5DF2\u4ECE ${result.before ?? "unknown"} \u5347\u7EA7\u5230 ${result.after}\uFF0CGateway \u5DF2\u91CD\u542F\u3002\u8BF7\u91CD\u542F opencode \u4EE5\u52A0\u8F7D\u65B0\u7248\u63D2\u4EF6\u3002`
29454
+ message: `SuperTask \u5DF2\u4ECE ${result.before ?? "unknown"} \u5347\u7EA7\u5230 ${result.after}\uFF0CGateway \u5DF2\u91CD\u542F\u3002\u8BF7\u91CD\u542F opencode \u4EE5\u52A0\u8F7D\u65B0\u7248\u63D2\u4EF6\uFF1B\u82E5\u4F7F\u7528\u5168\u5C40 CLI\uFF0C\u8BF7\u7528\u539F\u5305\u7BA1\u7406\u5668\u5C06\u5B83\u66F4\u65B0\u5230 opencode-supertask@${result.after}\uFF08npm install -g \u6216 bun add -g\uFF09\u3002`
29266
29455
  });
29267
29456
  } catch (error45) {
29268
29457
  return JSON.stringify({