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.
- package/README.md +51 -25
- package/dist/cli/index.js +1374 -581
- package/dist/cli/index.js.map +1 -1
- package/dist/gateway/index.d.ts +3 -1
- package/dist/gateway/index.js +878 -296
- package/dist/gateway/index.js.map +1 -1
- package/dist/plugin/supertask.js +593 -232
- package/dist/plugin/supertask.js.map +1 -1
- package/dist/web/index.js +554 -212
- package/dist/web/index.js.map +1 -1
- package/dist/worker/index.d.ts +19 -7
- package/dist/worker/index.js +442 -151
- package/dist/worker/index.js.map +1 -1
- package/drizzle/0004_reliability_fields.sql +31 -0
- package/drizzle/meta/0004_snapshot.json +85 -13
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +1 -1
package/dist/plugin/supertask.js
CHANGED
|
@@ -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,
|
|
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 = (_,
|
|
8082
|
+
const mapFn = (_, index2) => {
|
|
8083
8083
|
if (typeof range.start !== "number") {
|
|
8084
8084
|
throw new Error("Unexpected range start");
|
|
8085
8085
|
}
|
|
8086
|
-
return
|
|
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
|
|
8979
|
-
if (stack.indexOf(
|
|
8980
|
-
stack.push(
|
|
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:
|
|
9104
|
-
const data =
|
|
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,
|
|
11971
|
+
function handleArrayResult(result, final, index2) {
|
|
11972
11972
|
if (result.issues.length) {
|
|
11973
|
-
final.issues.push(...prefixIssues(
|
|
11973
|
+
final.issues.push(...prefixIssues(index2, result.issues));
|
|
11974
11974
|
}
|
|
11975
|
-
final.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
|
|
12371
|
-
const itemA = a[
|
|
12372
|
-
const itemB = b[
|
|
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: [
|
|
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,
|
|
12469
|
+
function handleTupleResult(result, final, index2) {
|
|
12470
12470
|
if (result.issues.length) {
|
|
12471
|
-
final.issues.push(...prefixIssues(
|
|
12471
|
+
final.issues.push(...prefixIssues(index2, result.issues));
|
|
12472
12472
|
}
|
|
12473
|
-
final.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
|
|
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 =
|
|
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 [
|
|
22826
|
-
if (key !== rightKeys[
|
|
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 [
|
|
24393
|
-
if (
|
|
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 (
|
|
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 [
|
|
24423
|
+
for (const [index2, orderByValue] of orderBy.entries()) {
|
|
24424
24424
|
orderByList.push(orderByValue);
|
|
24425
|
-
if (
|
|
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 [
|
|
24474
|
+
for (const [index2, groupByValue] of groupBy.entries()) {
|
|
24475
24475
|
groupByList.push(groupByValue);
|
|
24476
|
-
if (
|
|
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((
|
|
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((
|
|
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}
|
|
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}
|
|
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 = [
|
|
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
|
|
26984
|
-
const conditions = [
|
|
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(
|
|
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 = [
|
|
27024
|
-
|
|
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 {
|
|
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 {
|
|
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
|
|
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 =
|
|
27255
|
-
const pkg = JSON.parse(
|
|
27256
|
-
return pkg.version
|
|
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
|
-
|
|
27264
|
-
|
|
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
|
-
|
|
27271
|
-
|
|
27272
|
-
|
|
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
|
-
|
|
27280
|
-
|
|
27281
|
-
|
|
27282
|
-
|
|
27283
|
-
|
|
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
|
|
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
|
|
27291
|
-
|
|
27292
|
-
|
|
27293
|
-
|
|
27294
|
-
|
|
27295
|
-
|
|
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
|
|
27302
|
-
|
|
27303
|
-
|
|
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
|
-
|
|
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
|
|
27313
|
-
return execSync(
|
|
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(
|
|
27319
|
-
const
|
|
27320
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
27338
|
-
const
|
|
27339
|
-
const
|
|
27340
|
-
if (
|
|
27341
|
-
|
|
27342
|
-
|
|
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
|
-
|
|
27349
|
-
|
|
27350
|
-
}
|
|
27351
|
-
|
|
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
|
|
27359
|
-
|
|
27360
|
-
|
|
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
|
-
|
|
27375
|
-
|
|
27376
|
-
|
|
27377
|
-
|
|
27378
|
-
|
|
27379
|
-
|
|
27380
|
-
|
|
27381
|
-
|
|
27382
|
-
|
|
27383
|
-
|
|
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
|
-
|
|
27388
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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\
|
|
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\
|
|
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
|
|
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\
|
|
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
|
-
|
|
27852
|
-
|
|
27853
|
-
console.log("[supertask] Updating npm package...");
|
|
28217
|
+
console.log("[supertask] Updating OpenCode plugin cache...");
|
|
28218
|
+
let installed;
|
|
27854
28219
|
try {
|
|
27855
|
-
|
|
27856
|
-
|
|
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:
|
|
27864
|
-
hint: "Try manually:
|
|
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,
|