opencode-supertask 0.1.20 → 0.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -25
- package/dist/cli/index.js +1245 -579
- package/dist/cli/index.js.map +1 -1
- package/dist/gateway/index.js +838 -294
- package/dist/gateway/index.js.map +1 -1
- package/dist/plugin/supertask.js +576 -232
- package/dist/plugin/supertask.js.map +1 -1
- package/dist/web/index.js +537 -212
- package/dist/web/index.js.map +1 -1
- package/dist/worker/index.d.ts +19 -7
- package/dist/worker/index.js +425 -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
|
}
|
|
@@ -27020,8 +27122,20 @@ var TaskService = class {
|
|
|
27020
27122
|
return result.length;
|
|
27021
27123
|
}
|
|
27022
27124
|
static async cancel(id, scope = {}) {
|
|
27023
|
-
const conditions = [
|
|
27024
|
-
|
|
27125
|
+
const conditions = [
|
|
27126
|
+
eq(tasks2.id, id),
|
|
27127
|
+
or(
|
|
27128
|
+
eq(tasks2.status, "pending"),
|
|
27129
|
+
eq(tasks2.status, "running"),
|
|
27130
|
+
eq(tasks2.status, "failed")
|
|
27131
|
+
),
|
|
27132
|
+
...this.buildScopeWhere(scope)
|
|
27133
|
+
];
|
|
27134
|
+
const result = await db.update(tasks2).set({
|
|
27135
|
+
status: "cancelled",
|
|
27136
|
+
finishedAt: /* @__PURE__ */ new Date(),
|
|
27137
|
+
retryAfter: null
|
|
27138
|
+
}).where(and(...conditions)).returning();
|
|
27025
27139
|
return result[0] || null;
|
|
27026
27140
|
}
|
|
27027
27141
|
static async retry(id, scope = {}) {
|
|
@@ -27033,7 +27147,8 @@ var TaskService = class {
|
|
|
27033
27147
|
status: "pending",
|
|
27034
27148
|
startedAt: null,
|
|
27035
27149
|
finishedAt: null,
|
|
27036
|
-
retryAfter: null
|
|
27150
|
+
retryAfter: null,
|
|
27151
|
+
retryCount: 0
|
|
27037
27152
|
}).where(and(...conditions)).returning();
|
|
27038
27153
|
return result[0] || null;
|
|
27039
27154
|
}
|
|
@@ -27047,7 +27162,8 @@ var TaskService = class {
|
|
|
27047
27162
|
status: "pending",
|
|
27048
27163
|
startedAt: null,
|
|
27049
27164
|
finishedAt: null,
|
|
27050
|
-
retryAfter: null
|
|
27165
|
+
retryAfter: null,
|
|
27166
|
+
retryCount: 0
|
|
27051
27167
|
}).where(and(...conditions)).returning();
|
|
27052
27168
|
return result.length;
|
|
27053
27169
|
}
|
|
@@ -27115,6 +27231,9 @@ var TaskService = class {
|
|
|
27115
27231
|
}
|
|
27116
27232
|
static async delete(id, scope = {}) {
|
|
27117
27233
|
const conditions = [eq(tasks2.id, id), ...this.buildScopeWhere(scope)];
|
|
27234
|
+
const existing = await db.select({ id: tasks2.id }).from(tasks2).where(and(...conditions)).limit(1);
|
|
27235
|
+
if (!existing[0]) return false;
|
|
27236
|
+
await db.delete(taskRuns2).where(eq(taskRuns2.taskId, id));
|
|
27118
27237
|
const result = await db.delete(tasks2).where(and(...conditions)).returning();
|
|
27119
27238
|
return result.length > 0;
|
|
27120
27239
|
}
|
|
@@ -27156,6 +27275,7 @@ function isValidCronExpr(expr) {
|
|
|
27156
27275
|
var { taskTemplates: taskTemplates2 } = schema_exports;
|
|
27157
27276
|
var TaskTemplateService = class {
|
|
27158
27277
|
static async create(data) {
|
|
27278
|
+
this.validate(data);
|
|
27159
27279
|
const now = Date.now();
|
|
27160
27280
|
const result = await db.insert(taskTemplates2).values({ ...data, createdAt: now, updatedAt: now }).returning();
|
|
27161
27281
|
const tmpl = result[0];
|
|
@@ -27171,8 +27291,38 @@ var TaskTemplateService = class {
|
|
|
27171
27291
|
}
|
|
27172
27292
|
return tmpl;
|
|
27173
27293
|
}
|
|
27294
|
+
static validate(data) {
|
|
27295
|
+
if (!data.name.trim()) throw new Error("name \u4E0D\u80FD\u4E3A\u7A7A");
|
|
27296
|
+
if (!data.agent.trim()) throw new Error("agent \u4E0D\u80FD\u4E3A\u7A7A");
|
|
27297
|
+
if (!data.prompt.trim()) throw new Error("prompt \u4E0D\u80FD\u4E3A\u7A7A");
|
|
27298
|
+
const scheduleType = data.scheduleType;
|
|
27299
|
+
if (!["cron", "delayed", "recurring"].includes(scheduleType)) {
|
|
27300
|
+
throw new Error("scheduleType \u5FC5\u987B\u662F cron\u3001delayed \u6216 recurring");
|
|
27301
|
+
}
|
|
27302
|
+
if (scheduleType === "cron" && (!data.cronExpr || !isValidCronExpr(data.cronExpr))) {
|
|
27303
|
+
throw new Error("cronExpr \u7F3A\u5931\u6216\u683C\u5F0F\u65E0\u6548");
|
|
27304
|
+
}
|
|
27305
|
+
if (scheduleType === "recurring" && (!Number.isInteger(data.intervalMs) || (data.intervalMs ?? 0) <= 0)) {
|
|
27306
|
+
throw new Error("intervalMs \u5FC5\u987B\u662F\u6B63\u6574\u6570");
|
|
27307
|
+
}
|
|
27308
|
+
if (scheduleType === "delayed" && (!Number.isInteger(data.runAt) || (data.runAt ?? 0) <= 0)) {
|
|
27309
|
+
throw new Error("runAt \u5FC5\u987B\u662F\u6B63\u6574\u6570\u65F6\u95F4\u6233");
|
|
27310
|
+
}
|
|
27311
|
+
this.validateInteger("importance", data.importance, 1, 5);
|
|
27312
|
+
this.validateInteger("urgency", data.urgency, 1, 5);
|
|
27313
|
+
this.validateInteger("maxInstances", data.maxInstances, 1, 1e3);
|
|
27314
|
+
this.validateInteger("maxRetries", data.maxRetries, 0, 1e3);
|
|
27315
|
+
this.validateInteger("retryBackoffMs", data.retryBackoffMs, 0, 864e5);
|
|
27316
|
+
this.validateInteger("timeoutMs", data.timeoutMs, 1e3, 6048e5);
|
|
27317
|
+
}
|
|
27318
|
+
static validateInteger(name, value, min, max) {
|
|
27319
|
+
if (value === void 0 || value === null) return;
|
|
27320
|
+
if (!Number.isInteger(value) || value < min || value > max) {
|
|
27321
|
+
throw new Error(`${name} \u5FC5\u987B\u662F ${min} \u5230 ${max} \u4E4B\u95F4\u7684\u6574\u6570`);
|
|
27322
|
+
}
|
|
27323
|
+
}
|
|
27174
27324
|
static async list(limit = 50) {
|
|
27175
|
-
return await db.select().from(taskTemplates2).orderBy(desc(taskTemplates2.createdAt)).limit(limit);
|
|
27325
|
+
return await db.select().from(taskTemplates2).orderBy(desc(taskTemplates2.createdAt), desc(taskTemplates2.id)).limit(limit);
|
|
27176
27326
|
}
|
|
27177
27327
|
static async getById(id) {
|
|
27178
27328
|
const result = await db.select().from(taskTemplates2).where(eq(taskTemplates2.id, id));
|
|
@@ -27241,85 +27391,261 @@ function parseDuration(input) {
|
|
|
27241
27391
|
|
|
27242
27392
|
// src/daemon/pm2.ts
|
|
27243
27393
|
import { execSync, spawnSync } from "child_process";
|
|
27244
|
-
import {
|
|
27394
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
27395
|
+
import { homedir as homedir3 } from "os";
|
|
27396
|
+
import { dirname as dirname2, join as join3 } from "path";
|
|
27245
27397
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
27246
|
-
import {
|
|
27398
|
+
import { Database as Database3 } from "bun:sqlite";
|
|
27399
|
+
|
|
27400
|
+
// src/gateway/config.ts
|
|
27401
|
+
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
27247
27402
|
import { homedir as homedir2 } from "os";
|
|
27403
|
+
import { join as join2 } from "path";
|
|
27404
|
+
var DEFAULT_CONFIG = {
|
|
27405
|
+
configVersion: 2,
|
|
27406
|
+
worker: {
|
|
27407
|
+
maxConcurrency: 2,
|
|
27408
|
+
pollIntervalMs: 1e3,
|
|
27409
|
+
heartbeatIntervalMs: 3e4,
|
|
27410
|
+
taskTimeoutMs: 18e5,
|
|
27411
|
+
shutdownGracePeriodMs: 3e4
|
|
27412
|
+
},
|
|
27413
|
+
scheduler: {
|
|
27414
|
+
enabled: true,
|
|
27415
|
+
checkIntervalMs: 1e3
|
|
27416
|
+
},
|
|
27417
|
+
watchdog: {
|
|
27418
|
+
heartbeatTimeoutMs: 6e5,
|
|
27419
|
+
checkIntervalMs: 6e4,
|
|
27420
|
+
cleanupIntervalMs: 864e5,
|
|
27421
|
+
retentionDays: 30
|
|
27422
|
+
},
|
|
27423
|
+
dashboard: {
|
|
27424
|
+
enabled: true,
|
|
27425
|
+
port: 4680
|
|
27426
|
+
}
|
|
27427
|
+
};
|
|
27428
|
+
var DEFAULT_CONFIG_PATH = join2(homedir2(), ".config/opencode/supertask.json");
|
|
27429
|
+
function getConfigPath() {
|
|
27430
|
+
return process.env.SUPERTASK_CONFIG_PATH ?? DEFAULT_CONFIG_PATH;
|
|
27431
|
+
}
|
|
27432
|
+
function objectAt(value, path) {
|
|
27433
|
+
if (value === void 0) return {};
|
|
27434
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
27435
|
+
throw new Error(`${path} \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
27436
|
+
}
|
|
27437
|
+
return value;
|
|
27438
|
+
}
|
|
27439
|
+
function integerAt(source, key, fallback, min, max, path) {
|
|
27440
|
+
const value = source[key];
|
|
27441
|
+
if (value === void 0) return fallback;
|
|
27442
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < min || value > max) {
|
|
27443
|
+
throw new Error(`${path}.${key} \u5FC5\u987B\u662F ${min} \u5230 ${max} \u4E4B\u95F4\u7684\u6574\u6570`);
|
|
27444
|
+
}
|
|
27445
|
+
return value;
|
|
27446
|
+
}
|
|
27447
|
+
function booleanAt(source, key, fallback, path) {
|
|
27448
|
+
const value = source[key];
|
|
27449
|
+
if (value === void 0) return fallback;
|
|
27450
|
+
if (typeof value !== "boolean") throw new Error(`${path}.${key} \u5FC5\u987B\u662F\u5E03\u5C14\u503C`);
|
|
27451
|
+
return value;
|
|
27452
|
+
}
|
|
27453
|
+
function validateConfig(input) {
|
|
27454
|
+
const root = objectAt(input, "config");
|
|
27455
|
+
const version3 = root.configVersion ?? 1;
|
|
27456
|
+
if (version3 !== 1 && version3 !== 2) {
|
|
27457
|
+
throw new Error("config.configVersion \u53EA\u652F\u6301 1 \u6216 2");
|
|
27458
|
+
}
|
|
27459
|
+
const worker = objectAt(root.worker, "worker");
|
|
27460
|
+
const scheduler = objectAt(root.scheduler, "scheduler");
|
|
27461
|
+
const watchdog = objectAt(root.watchdog, "watchdog");
|
|
27462
|
+
const dashboard = objectAt(root.dashboard, "dashboard");
|
|
27463
|
+
const heartbeatIntervalMs = integerAt(worker, "heartbeatIntervalMs", DEFAULT_CONFIG.worker.heartbeatIntervalMs, 1e3, 36e5, "worker");
|
|
27464
|
+
const heartbeatTimeoutMs = integerAt(watchdog, "heartbeatTimeoutMs", DEFAULT_CONFIG.watchdog.heartbeatTimeoutMs, 1e3, 864e5, "watchdog");
|
|
27465
|
+
let checkIntervalMs;
|
|
27466
|
+
let cleanupIntervalMs;
|
|
27467
|
+
if (version3 === 1 && watchdog.checkIntervalMs === void 0 && watchdog.cleanupIntervalMs !== void 0) {
|
|
27468
|
+
checkIntervalMs = integerAt(watchdog, "cleanupIntervalMs", DEFAULT_CONFIG.watchdog.checkIntervalMs, 1e3, 36e5, "watchdog");
|
|
27469
|
+
cleanupIntervalMs = DEFAULT_CONFIG.watchdog.cleanupIntervalMs;
|
|
27470
|
+
} else {
|
|
27471
|
+
checkIntervalMs = integerAt(watchdog, "checkIntervalMs", DEFAULT_CONFIG.watchdog.checkIntervalMs, 1e3, 36e5, "watchdog");
|
|
27472
|
+
cleanupIntervalMs = integerAt(watchdog, "cleanupIntervalMs", DEFAULT_CONFIG.watchdog.cleanupIntervalMs, 6e4, 6048e5, "watchdog");
|
|
27473
|
+
}
|
|
27474
|
+
if (heartbeatIntervalMs >= heartbeatTimeoutMs) {
|
|
27475
|
+
throw new Error("worker.heartbeatIntervalMs \u5FC5\u987B\u5C0F\u4E8E watchdog.heartbeatTimeoutMs");
|
|
27476
|
+
}
|
|
27477
|
+
if (checkIntervalMs > heartbeatTimeoutMs) {
|
|
27478
|
+
throw new Error("watchdog.checkIntervalMs \u4E0D\u80FD\u5927\u4E8E watchdog.heartbeatTimeoutMs");
|
|
27479
|
+
}
|
|
27480
|
+
return {
|
|
27481
|
+
configVersion: 2,
|
|
27482
|
+
worker: {
|
|
27483
|
+
maxConcurrency: integerAt(worker, "maxConcurrency", DEFAULT_CONFIG.worker.maxConcurrency, 1, 64, "worker"),
|
|
27484
|
+
pollIntervalMs: integerAt(worker, "pollIntervalMs", DEFAULT_CONFIG.worker.pollIntervalMs, 50, 6e4, "worker"),
|
|
27485
|
+
heartbeatIntervalMs,
|
|
27486
|
+
taskTimeoutMs: integerAt(worker, "taskTimeoutMs", DEFAULT_CONFIG.worker.taskTimeoutMs, 1e3, 6048e5, "worker"),
|
|
27487
|
+
shutdownGracePeriodMs: integerAt(worker, "shutdownGracePeriodMs", DEFAULT_CONFIG.worker.shutdownGracePeriodMs, 0, 36e5, "worker")
|
|
27488
|
+
},
|
|
27489
|
+
scheduler: {
|
|
27490
|
+
enabled: booleanAt(scheduler, "enabled", DEFAULT_CONFIG.scheduler.enabled, "scheduler"),
|
|
27491
|
+
checkIntervalMs: integerAt(scheduler, "checkIntervalMs", DEFAULT_CONFIG.scheduler.checkIntervalMs, 100, 6e4, "scheduler")
|
|
27492
|
+
},
|
|
27493
|
+
watchdog: {
|
|
27494
|
+
heartbeatTimeoutMs,
|
|
27495
|
+
checkIntervalMs,
|
|
27496
|
+
cleanupIntervalMs,
|
|
27497
|
+
retentionDays: integerAt(watchdog, "retentionDays", DEFAULT_CONFIG.watchdog.retentionDays, 1, 3650, "watchdog")
|
|
27498
|
+
},
|
|
27499
|
+
dashboard: {
|
|
27500
|
+
enabled: booleanAt(dashboard, "enabled", DEFAULT_CONFIG.dashboard.enabled, "dashboard"),
|
|
27501
|
+
port: integerAt(dashboard, "port", DEFAULT_CONFIG.dashboard.port, 1, 65535, "dashboard")
|
|
27502
|
+
}
|
|
27503
|
+
};
|
|
27504
|
+
}
|
|
27505
|
+
function loadConfig(path = getConfigPath()) {
|
|
27506
|
+
if (!existsSync2(path)) return validateConfig({ configVersion: 2 });
|
|
27507
|
+
try {
|
|
27508
|
+
return validateConfig(JSON.parse(readFileSync(path, "utf-8")));
|
|
27509
|
+
} catch (error45) {
|
|
27510
|
+
const message = error45 instanceof Error ? error45.message : String(error45);
|
|
27511
|
+
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u914D\u7F6E ${path}: ${message}`);
|
|
27512
|
+
}
|
|
27513
|
+
}
|
|
27514
|
+
|
|
27515
|
+
// src/daemon/pm2.ts
|
|
27248
27516
|
var __dirname = dirname2(fileURLToPath2(import.meta.url));
|
|
27249
|
-
var GATEWAY_ENTRY = join2(__dirname, "../gateway/index.js");
|
|
27250
27517
|
var PROCESS_NAME = "supertask-gateway";
|
|
27251
|
-
var
|
|
27518
|
+
var GATEWAY_LOCK_STALE_MS = 3e4;
|
|
27252
27519
|
function getPackageVersion() {
|
|
27520
|
+
const envVersion = process.env.npm_package_version;
|
|
27521
|
+
if (envVersion) return envVersion;
|
|
27253
27522
|
try {
|
|
27254
|
-
const pkgPath =
|
|
27255
|
-
const pkg = JSON.parse(
|
|
27256
|
-
return pkg.version
|
|
27523
|
+
const pkgPath = join3(__dirname, "../../package.json");
|
|
27524
|
+
const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
27525
|
+
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
27257
27526
|
} catch {
|
|
27258
27527
|
return "0.0.0";
|
|
27259
27528
|
}
|
|
27260
27529
|
}
|
|
27530
|
+
function resolveGatewayEntry() {
|
|
27531
|
+
const override = process.env.SUPERTASK_GATEWAY_ENTRY;
|
|
27532
|
+
if (override) {
|
|
27533
|
+
if (!existsSync3(override)) throw new Error(`[supertask] Gateway entry not found: ${override}`);
|
|
27534
|
+
return override;
|
|
27535
|
+
}
|
|
27536
|
+
const candidates = [
|
|
27537
|
+
join3(__dirname, "../gateway/index.js"),
|
|
27538
|
+
join3(__dirname, "../gateway/index.ts")
|
|
27539
|
+
];
|
|
27540
|
+
const entry = candidates.find((candidate) => existsSync3(candidate));
|
|
27541
|
+
if (!entry) throw new Error(`[supertask] Gateway entry not found. Checked: ${candidates.join(", ")}`);
|
|
27542
|
+
return entry;
|
|
27543
|
+
}
|
|
27544
|
+
function versionFile() {
|
|
27545
|
+
return process.env.SUPERTASK_VERSION_FILE ?? join3(homedir3(), ".local/share/opencode/supertask-gateway-version");
|
|
27546
|
+
}
|
|
27261
27547
|
function getRunningVersion() {
|
|
27262
27548
|
try {
|
|
27263
|
-
|
|
27264
|
-
|
|
27549
|
+
const path = versionFile();
|
|
27550
|
+
if (!existsSync3(path)) return null;
|
|
27551
|
+
return readFileSync2(path, "utf-8").trim() || null;
|
|
27265
27552
|
} catch {
|
|
27266
27553
|
return null;
|
|
27267
27554
|
}
|
|
27268
27555
|
}
|
|
27269
27556
|
function writeRunningVersion(version3) {
|
|
27270
|
-
|
|
27271
|
-
|
|
27272
|
-
|
|
27273
|
-
}
|
|
27557
|
+
const path = versionFile();
|
|
27558
|
+
mkdirSync2(dirname2(path), { recursive: true });
|
|
27559
|
+
writeFileSync(path, version3, "utf-8");
|
|
27274
27560
|
}
|
|
27275
27561
|
function pm2Bin() {
|
|
27276
|
-
return process.platform === "win32" ? "pm2.cmd" : "pm2";
|
|
27562
|
+
return process.env.SUPERTASK_PM2_BIN ?? (process.platform === "win32" ? "pm2.cmd" : "pm2");
|
|
27277
27563
|
}
|
|
27278
27564
|
function isPm2Installed() {
|
|
27279
|
-
|
|
27280
|
-
|
|
27281
|
-
|
|
27282
|
-
|
|
27283
|
-
|
|
27284
|
-
return false;
|
|
27285
|
-
}
|
|
27565
|
+
const result = spawnSync(pm2Bin(), ["--version"], {
|
|
27566
|
+
stdio: "ignore",
|
|
27567
|
+
shell: process.platform === "win32"
|
|
27568
|
+
});
|
|
27569
|
+
return result.status === 0;
|
|
27286
27570
|
}
|
|
27287
27571
|
function pm2Exec(args) {
|
|
27288
|
-
const
|
|
27572
|
+
const result = spawnSync(pm2Bin(), args, {
|
|
27573
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
27574
|
+
encoding: "utf-8",
|
|
27575
|
+
env: process.env,
|
|
27576
|
+
shell: process.platform === "win32"
|
|
27577
|
+
});
|
|
27578
|
+
const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
|
|
27579
|
+
if (result.error) return { ok: false, output: result.error.message };
|
|
27580
|
+
return { ok: result.status === 0, output };
|
|
27581
|
+
}
|
|
27582
|
+
function requirePm2(args, action) {
|
|
27583
|
+
const result = pm2Exec(args);
|
|
27584
|
+
if (!result.ok) throw new Error(`[supertask] ${action} failed: ${result.output || "unknown pm2 error"}`);
|
|
27585
|
+
return result.output;
|
|
27586
|
+
}
|
|
27587
|
+
function pm2JsonList() {
|
|
27588
|
+
const output = requirePm2(["jlist"], "pm2 jlist");
|
|
27289
27589
|
try {
|
|
27290
|
-
const
|
|
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) };
|
|
27590
|
+
const parsed = JSON.parse(output);
|
|
27591
|
+
if (!Array.isArray(parsed)) throw new Error("result is not an array");
|
|
27592
|
+
return parsed;
|
|
27593
|
+
} catch (error45) {
|
|
27594
|
+
const message = error45 instanceof Error ? error45.message : String(error45);
|
|
27595
|
+
throw new Error(`[supertask] Invalid pm2 jlist output: ${message}`);
|
|
27299
27596
|
}
|
|
27300
27597
|
}
|
|
27301
|
-
function
|
|
27302
|
-
|
|
27303
|
-
|
|
27598
|
+
function databasePath() {
|
|
27599
|
+
return process.env.SUPERTASK_DB_PATH ?? join3(homedir3(), ".local/share/opencode/tasks.db");
|
|
27600
|
+
}
|
|
27601
|
+
function isGatewayReady(expectedPid) {
|
|
27602
|
+
const path = databasePath();
|
|
27603
|
+
if (!existsSync3(path)) return false;
|
|
27604
|
+
let database = null;
|
|
27304
27605
|
try {
|
|
27305
|
-
|
|
27606
|
+
database = new Database3(path, { readonly: true });
|
|
27607
|
+
const row = database.query(
|
|
27608
|
+
"SELECT pid, heartbeat_at, ready_at FROM gateway_lock WHERE id = 1"
|
|
27609
|
+
).get();
|
|
27610
|
+
if (!row || row.ready_at == null) return false;
|
|
27611
|
+
if (expectedPid !== void 0 && row.pid !== expectedPid) return false;
|
|
27612
|
+
const ageMs = Date.now() - row.heartbeat_at;
|
|
27613
|
+
return ageMs >= -5e3 && ageMs < GATEWAY_LOCK_STALE_MS;
|
|
27306
27614
|
} catch {
|
|
27307
|
-
return
|
|
27615
|
+
return false;
|
|
27616
|
+
} finally {
|
|
27617
|
+
database?.close();
|
|
27308
27618
|
}
|
|
27309
27619
|
}
|
|
27620
|
+
function readyTimeoutMs() {
|
|
27621
|
+
const value = Number(process.env.SUPERTASK_GATEWAY_READY_TIMEOUT_MS ?? 3e4);
|
|
27622
|
+
return Number.isFinite(value) && value > 0 ? value : 3e4;
|
|
27623
|
+
}
|
|
27624
|
+
function waitForGatewayReady(pid) {
|
|
27625
|
+
const deadline = Date.now() + readyTimeoutMs();
|
|
27626
|
+
const sleeper = new Int32Array(new SharedArrayBuffer(4));
|
|
27627
|
+
while (Date.now() < deadline) {
|
|
27628
|
+
if (isGatewayReady(pid)) return true;
|
|
27629
|
+
Atomics.wait(sleeper, 0, 0, 100);
|
|
27630
|
+
}
|
|
27631
|
+
return isGatewayReady(pid);
|
|
27632
|
+
}
|
|
27310
27633
|
function findBunPath() {
|
|
27634
|
+
const override = process.env.SUPERTASK_BUN_BIN;
|
|
27635
|
+
if (override) return override;
|
|
27311
27636
|
try {
|
|
27312
|
-
const
|
|
27313
|
-
return execSync(
|
|
27637
|
+
const command = process.platform === "win32" ? "where bun" : "which bun";
|
|
27638
|
+
return execSync(command, { stdio: "pipe" }).toString().trim().split("\n")[0];
|
|
27314
27639
|
} catch {
|
|
27315
27640
|
return process.execPath;
|
|
27316
27641
|
}
|
|
27317
27642
|
}
|
|
27318
|
-
function pm2StartGateway(
|
|
27319
|
-
const
|
|
27320
|
-
|
|
27643
|
+
function pm2StartGateway(gatewayEntry = resolveGatewayEntry()) {
|
|
27644
|
+
const configuredKillTimeout = Number(process.env.SUPERTASK_PM2_KILL_TIMEOUT_MS);
|
|
27645
|
+
const killTimeoutMs = Number.isInteger(configuredKillTimeout) && configuredKillTimeout >= 5e3 ? configuredKillTimeout : loadConfig().worker.shutdownGracePeriodMs + 5e3;
|
|
27646
|
+
requirePm2([
|
|
27321
27647
|
"start",
|
|
27322
|
-
|
|
27648
|
+
findBunPath(),
|
|
27323
27649
|
"--name",
|
|
27324
27650
|
PROCESS_NAME,
|
|
27325
27651
|
"--interpreter",
|
|
@@ -27328,80 +27654,146 @@ function pm2StartGateway(version3) {
|
|
|
27328
27654
|
"5000",
|
|
27329
27655
|
"--max-restarts",
|
|
27330
27656
|
"30",
|
|
27657
|
+
"--kill-timeout",
|
|
27658
|
+
String(killTimeoutMs),
|
|
27331
27659
|
"--",
|
|
27332
|
-
|
|
27333
|
-
]);
|
|
27660
|
+
gatewayEntry
|
|
27661
|
+
], "pm2 start");
|
|
27662
|
+
const started = pm2JsonList().find((item) => item.name === PROCESS_NAME);
|
|
27663
|
+
if (started?.pm2_env?.status !== "online") {
|
|
27664
|
+
throw new Error(`[supertask] Gateway did not become online (status: ${started?.pm2_env?.status ?? "missing"})`);
|
|
27665
|
+
}
|
|
27666
|
+
if (typeof started.pid !== "number" || !waitForGatewayReady(started.pid)) {
|
|
27667
|
+
throw new Error("[supertask] Gateway \u8FDB\u7A0B online\uFF0C\u4F46\u672A\u5728\u9650\u5B9A\u65F6\u95F4\u5185\u5C31\u7EEA\uFF1B\u8BF7\u67E5\u770B pm2 logs supertask-gateway");
|
|
27668
|
+
}
|
|
27669
|
+
}
|
|
27670
|
+
function savePm2State() {
|
|
27671
|
+
requirePm2(["save"], "pm2 save");
|
|
27334
27672
|
}
|
|
27335
|
-
function upgrade() {
|
|
27673
|
+
function upgrade(target) {
|
|
27674
|
+
if (!isPm2Installed()) {
|
|
27675
|
+
throw new Error("[supertask] pm2 is not installed. Run `supertask install` first.");
|
|
27676
|
+
}
|
|
27336
27677
|
const before = getRunningVersion();
|
|
27337
|
-
const
|
|
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) {
|
|
27678
|
+
const oldGatewayEntry = resolveGatewayEntry();
|
|
27679
|
+
const currentVersion = target?.version ?? getPackageVersion();
|
|
27680
|
+
const existing = pm2JsonList().find((item) => item.name === PROCESS_NAME);
|
|
27681
|
+
if (existing) requirePm2(["delete", PROCESS_NAME], "pm2 delete old Gateway");
|
|
27682
|
+
try {
|
|
27683
|
+
pm2StartGateway(target?.gatewayEntry ?? oldGatewayEntry);
|
|
27347
27684
|
writeRunningVersion(currentVersion);
|
|
27348
|
-
|
|
27349
|
-
|
|
27350
|
-
}
|
|
27351
|
-
|
|
27685
|
+
savePm2State();
|
|
27686
|
+
return { before, after: currentVersion, restarted: true };
|
|
27687
|
+
} catch (error45) {
|
|
27688
|
+
const failed = pm2JsonList().find((item) => item.name === PROCESS_NAME);
|
|
27689
|
+
if (failed) requirePm2(["delete", PROCESS_NAME], "pm2 delete failed Gateway");
|
|
27690
|
+
try {
|
|
27691
|
+
pm2StartGateway(oldGatewayEntry);
|
|
27692
|
+
if (before) writeRunningVersion(before);
|
|
27693
|
+
savePm2State();
|
|
27694
|
+
} catch (rollbackError) {
|
|
27695
|
+
const original = error45 instanceof Error ? error45.message : String(error45);
|
|
27696
|
+
const rollback = rollbackError instanceof Error ? rollbackError.message : String(rollbackError);
|
|
27697
|
+
throw new Error(`${original}; \u65E7 Gateway \u56DE\u6EDA\u4E5F\u5931\u8D25: ${rollback}`);
|
|
27698
|
+
}
|
|
27699
|
+
const message = error45 instanceof Error ? error45.message : String(error45);
|
|
27700
|
+
throw new Error(`${message}; \u5DF2\u56DE\u6EDA\u5230\u65E7 Gateway`);
|
|
27352
27701
|
}
|
|
27353
|
-
return { before, after: currentVersion, restarted: true };
|
|
27354
27702
|
}
|
|
27355
27703
|
function ensureGateway() {
|
|
27704
|
+
if (!isPm2Installed()) {
|
|
27705
|
+
return { ok: false, reason: "pm2-not-installed" };
|
|
27706
|
+
}
|
|
27356
27707
|
const currentVersion = getPackageVersion();
|
|
27708
|
+
const processList = pm2JsonList();
|
|
27709
|
+
const existing = processList.find((item) => item.name === PROCESS_NAME);
|
|
27710
|
+
if (existing?.pm2_env?.status === "online" && typeof existing.pid === "number" && isGatewayReady(existing.pid) && getRunningVersion() === currentVersion) {
|
|
27711
|
+
return { ok: true, action: "already-running" };
|
|
27712
|
+
}
|
|
27713
|
+
if (existing) requirePm2(["delete", PROCESS_NAME], "pm2 delete stale Gateway");
|
|
27714
|
+
pm2StartGateway();
|
|
27715
|
+
writeRunningVersion(currentVersion);
|
|
27716
|
+
savePm2State();
|
|
27717
|
+
return { ok: true, action: existing ? "restarted" : "started" };
|
|
27718
|
+
}
|
|
27719
|
+
|
|
27720
|
+
// src/daemon/update.ts
|
|
27721
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
27722
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync } from "fs";
|
|
27723
|
+
import { homedir as homedir4 } from "os";
|
|
27724
|
+
import { join as join4 } from "path";
|
|
27725
|
+
var PACKAGE_NAME = "opencode-supertask";
|
|
27726
|
+
function pluginAt(packageDir) {
|
|
27727
|
+
const packageJson = join4(packageDir, "package.json");
|
|
27728
|
+
const gatewayEntry = join4(packageDir, "dist/gateway/index.js");
|
|
27729
|
+
if (!existsSync4(packageJson) || !existsSync4(gatewayEntry)) return null;
|
|
27357
27730
|
try {
|
|
27358
|
-
const
|
|
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
|
-
}
|
|
27731
|
+
const pkg = JSON.parse(readFileSync3(packageJson, "utf8"));
|
|
27732
|
+
if (pkg.name !== PACKAGE_NAME || typeof pkg.version !== "string") return null;
|
|
27733
|
+
return { packageDir, gatewayEntry, version: pkg.version };
|
|
27372
27734
|
} catch {
|
|
27735
|
+
return null;
|
|
27373
27736
|
}
|
|
27374
|
-
|
|
27375
|
-
|
|
27376
|
-
|
|
27377
|
-
|
|
27378
|
-
|
|
27379
|
-
|
|
27380
|
-
|
|
27381
|
-
|
|
27382
|
-
|
|
27383
|
-
|
|
27384
|
-
|
|
27385
|
-
|
|
27737
|
+
}
|
|
27738
|
+
function versionParts(version3) {
|
|
27739
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(version3);
|
|
27740
|
+
return match ? match.slice(1).map(Number) : null;
|
|
27741
|
+
}
|
|
27742
|
+
function compareVersions(left, right) {
|
|
27743
|
+
const a = versionParts(left);
|
|
27744
|
+
const b = versionParts(right);
|
|
27745
|
+
if (!a || !b) return left.localeCompare(right);
|
|
27746
|
+
for (let index2 = 0; index2 < 3; index2 += 1) {
|
|
27747
|
+
if (a[index2] !== b[index2]) return a[index2] - b[index2];
|
|
27748
|
+
}
|
|
27749
|
+
return 0;
|
|
27750
|
+
}
|
|
27751
|
+
function cacheRoot() {
|
|
27752
|
+
return process.env.SUPERTASK_OPENCODE_CACHE_DIR ?? join4(homedir4(), ".cache/opencode/packages");
|
|
27753
|
+
}
|
|
27754
|
+
function resolveInstalledPlugin() {
|
|
27755
|
+
const override = process.env.SUPERTASK_PLUGIN_PACKAGE_DIR;
|
|
27756
|
+
if (override) {
|
|
27757
|
+
const plugin = pluginAt(override);
|
|
27758
|
+
if (!plugin) throw new Error(`[supertask] \u5B89\u88C5\u5305\u65E0\u6548\u6216\u7F3A\u5C11 Gateway \u6784\u5EFA\u4EA7\u7269: ${override}`);
|
|
27759
|
+
return plugin;
|
|
27760
|
+
}
|
|
27761
|
+
const root = cacheRoot();
|
|
27762
|
+
const packageDirs = existsSync4(root) ? readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && (entry.name === PACKAGE_NAME || entry.name.startsWith(`${PACKAGE_NAME}@`))).map((entry) => join4(root, entry.name, "node_modules", PACKAGE_NAME)) : [];
|
|
27763
|
+
const installed = packageDirs.map(pluginAt).filter((plugin) => plugin !== null).sort((left, right) => compareVersions(right.version, left.version));
|
|
27764
|
+
if (!installed[0]) {
|
|
27765
|
+
throw new Error(`[supertask] OpenCode \u63D2\u4EF6\u7F13\u5B58\u4E2D\u627E\u4E0D\u5230\u53EF\u8FD0\u884C\u7684 ${PACKAGE_NAME}`);
|
|
27766
|
+
}
|
|
27767
|
+
return installed[0];
|
|
27768
|
+
}
|
|
27769
|
+
function opencodeBin() {
|
|
27770
|
+
return process.env.SUPERTASK_OPENCODE_BIN ?? "opencode";
|
|
27771
|
+
}
|
|
27772
|
+
function installLatestPlugin() {
|
|
27773
|
+
const result = spawnSync2(opencodeBin(), [
|
|
27774
|
+
"plugin",
|
|
27775
|
+
`${PACKAGE_NAME}@latest`,
|
|
27776
|
+
"--global",
|
|
27777
|
+
"--force"
|
|
27778
|
+
], {
|
|
27779
|
+
encoding: "utf8",
|
|
27780
|
+
env: process.env,
|
|
27781
|
+
timeout: 12e4
|
|
27782
|
+
});
|
|
27783
|
+
const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
|
|
27784
|
+
if (result.error) {
|
|
27785
|
+
throw new Error(`[supertask] OpenCode \u63D2\u4EF6\u66F4\u65B0\u5931\u8D25: ${result.error.message}`);
|
|
27386
27786
|
}
|
|
27387
|
-
|
|
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);
|
|
27787
|
+
if (result.status !== 0) {
|
|
27788
|
+
throw new Error(`[supertask] OpenCode \u63D2\u4EF6\u66F4\u65B0\u5931\u8D25: ${output || `\u9000\u51FA\u7801 ${result.status}`}`);
|
|
27395
27789
|
}
|
|
27396
|
-
|
|
27790
|
+
return resolveInstalledPlugin();
|
|
27397
27791
|
}
|
|
27398
27792
|
|
|
27399
27793
|
// plugin/supertask.ts
|
|
27400
|
-
import { homedir as homedir3 } from "os";
|
|
27401
27794
|
var _initialized = false;
|
|
27402
27795
|
function ensureInit() {
|
|
27403
27796
|
if (_initialized) return;
|
|
27404
|
-
_initialized = true;
|
|
27405
27797
|
try {
|
|
27406
27798
|
getDb();
|
|
27407
27799
|
} catch (err) {
|
|
@@ -27409,65 +27801,23 @@ function ensureInit() {
|
|
|
27409
27801
|
return;
|
|
27410
27802
|
}
|
|
27411
27803
|
try {
|
|
27412
|
-
const lockRow = sqlite.prepare("SELECT pid, heartbeat_at FROM gateway_lock WHERE id = 1").get();
|
|
27413
|
-
if (lockRow && Date.now() - lockRow.heartbeat_at < 3e4) {
|
|
27804
|
+
const lockRow = sqlite.prepare("SELECT pid, heartbeat_at, ready_at FROM gateway_lock WHERE id = 1").get();
|
|
27805
|
+
if (lockRow?.ready_at != null && Date.now() - lockRow.heartbeat_at < 3e4) {
|
|
27806
|
+
_initialized = true;
|
|
27414
27807
|
return;
|
|
27415
27808
|
}
|
|
27416
27809
|
} catch {
|
|
27417
27810
|
}
|
|
27418
27811
|
try {
|
|
27419
|
-
ensureGateway();
|
|
27420
|
-
|
|
27812
|
+
const gateway = ensureGateway();
|
|
27813
|
+
if (!gateway.ok) {
|
|
27814
|
+
console.warn("[supertask] Gateway \u672A\u81EA\u52A8\u542F\u52A8\uFF1A\u672A\u5B89\u88C5 pm2\u3002\u8FD0\u884C `supertask install` \u542F\u7528\u5E38\u9A7B\u6267\u884C\uFF0C\u6216\u8FD0\u884C `supertask gateway` \u524D\u53F0\u542F\u52A8\u3002");
|
|
27815
|
+
}
|
|
27816
|
+
} catch (error45) {
|
|
27817
|
+
console.error("[supertask] Gateway init failed:", error45 instanceof Error ? error45.message : String(error45));
|
|
27421
27818
|
}
|
|
27819
|
+
_initialized = true;
|
|
27422
27820
|
}
|
|
27423
|
-
var RUNNER_PROMPT = `\u4F60\u662F **SuperTask \u4EFB\u52A1\u6267\u884C\u5668**\u3002
|
|
27424
|
-
|
|
27425
|
-
## \u5DE5\u4F5C\u6D41\u7A0B
|
|
27426
|
-
|
|
27427
|
-
### 1. \u83B7\u53D6\u4EFB\u52A1
|
|
27428
|
-
|
|
27429
|
-
- \u5982\u679C\u7528\u6237\u8F93\u5165\u5305\u542B \`\u6267\u884C\u4EFB\u52A1 ID: <\u6570\u5B57>\`\uFF0C\u7528 \`supertask_get(id)\` \u83B7\u53D6\u8BE5\u4EFB\u52A1
|
|
27430
|
-
- \u5426\u5219\u7528 \`supertask_next\` \u83B7\u53D6\u4E0B\u4E00\u4E2A\u5F85\u6267\u884C\u4EFB\u52A1
|
|
27431
|
-
- \u5982\u679C\u6CA1\u6709\u4EFB\u52A1\uFF0C\u62A5\u544A"\u961F\u5217\u4E3A\u7A7A"\u5E76\u7ED3\u675F
|
|
27432
|
-
|
|
27433
|
-
### 2. \u6807\u8BB0\u5F00\u59CB
|
|
27434
|
-
|
|
27435
|
-
- \u5982\u679C\u4EFB\u52A1\u72B6\u6001\u662F \`pending\`\uFF0C\u8C03\u7528 \`supertask_start(id)\` \u6807\u8BB0\u4E3A running
|
|
27436
|
-
- \u5982\u679C\u5DF2\u7ECF\u662F \`running\`\uFF08Worker \u5DF2\u6807\u8BB0\uFF09\uFF0C\u8DF3\u8FC7\u6B64\u6B65
|
|
27437
|
-
|
|
27438
|
-
### 3. \u6267\u884C\u4EFB\u52A1
|
|
27439
|
-
|
|
27440
|
-
\u7528 Bash \u5DE5\u5177\u6267\u884C\u5B50 Agent\uFF0C**\u5FC5\u987B\u4F20\u5165 timeout \u53C2\u6570**\uFF1A
|
|
27441
|
-
|
|
27442
|
-
**\u5DE5\u5177\u8C03\u7528\u683C\u5F0F**\uFF1A
|
|
27443
|
-
\`\`\`
|
|
27444
|
-
Bash(
|
|
27445
|
-
command: "opencode run --agent \\"<task.agent>\\" -m \\"<model>\\" --format json \\"<task.prompt>\\"",
|
|
27446
|
-
workdir: "<task.cwd>",
|
|
27447
|
-
timeout: 3600000
|
|
27448
|
-
)
|
|
27449
|
-
\`\`\`
|
|
27450
|
-
|
|
27451
|
-
- **command**\uFF1A\u6267\u884C\u5B50 Agent \u7684\u547D\u4EE4
|
|
27452
|
-
- **workdir**\uFF1A\u4F7F\u7528 \`task.cwd\`\uFF08\u82E5\u4E3A\u7A7A\u5219\u7528\u5F53\u524D\u76EE\u5F55\uFF09
|
|
27453
|
-
- **model**\uFF1A\u4ECE\u7528\u6237\u8F93\u5165\u89E3\u6790 \`OVERRIDE_MODEL=xxx\`\uFF0C\u7528\u5B83\u4F5C\u4E3A \`-m\` \u53C2\u6570\uFF1B\u89E3\u6790\u4E0D\u5230\u5C31\u4E0D\u4F20 \`-m\`
|
|
27454
|
-
- **timeout**\uFF1A**\u5FC5\u987B\u8BBE\u7F6E\u4E3A 3600000\uFF0860 \u5206\u949F\uFF09**
|
|
27455
|
-
- **\u5B89\u5168\u68C0\u67E5**\uFF1A\u5982\u679C \`task.agent\` \u662F \`supertask-runner\`\uFF0C\u76F4\u63A5 fail \u5E76\u7ED3\u675F\uFF08\u9632\u6B62\u9012\u5F52\uFF09
|
|
27456
|
-
|
|
27457
|
-
### 4. \u5224\u65AD\u7ED3\u679C\u5E76\u66F4\u65B0\u72B6\u6001
|
|
27458
|
-
|
|
27459
|
-
\u770B\u5B50 Agent \u7684\u8F93\u51FA\u5185\u5BB9\uFF0C\u5224\u65AD\u4EFB\u52A1\u662F\u5426\u6210\u529F\u5B8C\u6210\uFF1A
|
|
27460
|
-
|
|
27461
|
-
- **\u6210\u529F**\uFF1A\u5B50 Agent \u5B8C\u6210\u4E86\u4EFB\u52A1\u8981\u6C42\u7684\u5DE5\u4F5C \u2192 \u8C03\u7528 \`supertask_done(id, "\u7B80\u8981\u63CF\u8FF0\u5B8C\u6210\u60C5\u51B5")\`
|
|
27462
|
-
- **\u5931\u8D25**\uFF1A\u5B50 Agent \u62A5\u9519\u3001\u62D2\u7EDD\u6267\u884C\u3001\u660E\u786E\u8BF4\u65E0\u6CD5\u5B8C\u6210\u3001\u6216\u660E\u663E\u6CA1\u505A\u5B8C \u2192 \u8C03\u7528 \`supertask_fail(id, "\u5931\u8D25\u539F\u56E0")\`
|
|
27463
|
-
|
|
27464
|
-
\u7528\u4F60\u7684\u5224\u65AD\u529B\uFF0C\u4E0D\u9700\u8981\u6B7B\u677F\u7684\u89C4\u5219\u3002
|
|
27465
|
-
|
|
27466
|
-
## \u6CE8\u610F\u4E8B\u9879
|
|
27467
|
-
|
|
27468
|
-
1. \u4F60\u662F\u8C03\u5EA6\u5668\uFF0C\u4E0D\u8981\u81EA\u5DF1\u6267\u884C\u4EFB\u52A1\u5185\u5BB9\uFF0C\u5FC5\u987B\u7528 Bash \u8C03\u7528 \`opencode run\`
|
|
27469
|
-
2. \u5B8C\u6574\u4F20\u9012 \`task.prompt\`\uFF0C\u4E0D\u8981\u64C5\u81EA\u4FEE\u6539
|
|
27470
|
-
3. \u4E00\u6B21\u53EA\u5904\u7406\u4E00\u4E2A\u4EFB\u52A1\uFF0C\u5904\u7406\u5B8C\u5C31\u7ED3\u675F`;
|
|
27471
27821
|
var SYSTEM_INSTRUCTION = `
|
|
27472
27822
|
## SuperTask \u4EFB\u52A1\u961F\u5217\u7CFB\u7EDF
|
|
27473
27823
|
|
|
@@ -27494,18 +27844,7 @@ var SYSTEM_INSTRUCTION = `
|
|
|
27494
27844
|
`;
|
|
27495
27845
|
var SuperTaskPlugin = async () => {
|
|
27496
27846
|
return {
|
|
27497
|
-
async config(
|
|
27498
|
-
cfg.agent = cfg.agent ?? {};
|
|
27499
|
-
cfg.agent["supertask-runner"] = {
|
|
27500
|
-
description: "SuperTask \u4EFB\u52A1\u6267\u884C\u5668 - \u4ECE\u4EFB\u52A1\u961F\u5217\u83B7\u53D6\u4EFB\u52A1\u5E76\u6D3E\u53D1\u7ED9\u5B50 Agent \u6267\u884C",
|
|
27501
|
-
mode: "all",
|
|
27502
|
-
hidden: true,
|
|
27503
|
-
prompt: RUNNER_PROMPT,
|
|
27504
|
-
temperature: 0.3,
|
|
27505
|
-
permission: {
|
|
27506
|
-
bash: "allow"
|
|
27507
|
-
}
|
|
27508
|
-
};
|
|
27847
|
+
async config() {
|
|
27509
27848
|
ensureInit();
|
|
27510
27849
|
},
|
|
27511
27850
|
async "experimental.chat.system.transform"(input, output) {
|
|
@@ -27514,7 +27853,7 @@ var SuperTaskPlugin = async () => {
|
|
|
27514
27853
|
tool: {
|
|
27515
27854
|
// 创建任务
|
|
27516
27855
|
supertask_add: tool({
|
|
27517
|
-
description: "\u521B\u5EFA\u65B0\u4EFB\u52A1\u5230\u961F\u5217\u3002\u8FD4\u56DE\u4EFB\u52A1 ID\u3002\u4EFB\u52A1\
|
|
27856
|
+
description: "\u521B\u5EFA\u65B0\u4EFB\u52A1\u5230\u961F\u5217\u3002\u8FD4\u56DE\u4EFB\u52A1 ID\u3002\u4EFB\u52A1\u6309 urgency\u3001importance\u3001createdAt\u3001id \u7684\u987A\u5E8F\u8C03\u5EA6\u3002",
|
|
27518
27857
|
args: {
|
|
27519
27858
|
name: tool.schema.string().describe("\u4EFB\u52A1\u540D\u79F0\uFF08\u4EBA\u7C7B\u53EF\u8BFB\uFF09"),
|
|
27520
27859
|
agent: tool.schema.string().describe("\u6267\u884C\u7684 Agent \u540D\u79F0\uFF0C\u5982 localize-gen, course-gen"),
|
|
@@ -27525,6 +27864,9 @@ var SuperTaskPlugin = async () => {
|
|
|
27525
27864
|
urgency: tool.schema.number().optional().describe("\u7D27\u6025\u7A0B\u5EA6 1-5\uFF085 \u6700\u7D27\u6025\uFF09"),
|
|
27526
27865
|
batchId: tool.schema.string().optional().describe("\u6279\u6B21 ID\uFF0C\u7528\u4E8E\u5206\u7EC4\u7BA1\u7406"),
|
|
27527
27866
|
dependsOn: tool.schema.number().optional().describe("\u4F9D\u8D56\u7684\u4EFB\u52A1 ID\uFF0C\u8BE5\u4EFB\u52A1\u5B8C\u6210\u540E\u624D\u4F1A\u6267\u884C"),
|
|
27867
|
+
max_retries: tool.schema.number().optional().describe("\u9996\u6B21\u6267\u884C\u4E4B\u5916\u5141\u8BB8\u7684\u91CD\u8BD5\u6B21\u6570\uFF0C\u9ED8\u8BA4 3"),
|
|
27868
|
+
retry_backoff_ms: tool.schema.number().optional().describe("\u91CD\u8BD5\u9000\u907F\u57FA\u7840\u95F4\u9694 ms\uFF0C\u9ED8\u8BA4 30000"),
|
|
27869
|
+
timeout_ms: tool.schema.number().optional().describe("\u4EFB\u52A1\u786C\u8D85\u65F6 ms\uFF1B\u672A\u4F20\u5219\u4F7F\u7528 Gateway \u9ED8\u8BA4\u503C"),
|
|
27528
27870
|
cwd: tool.schema.string().optional().describe(
|
|
27529
27871
|
"(\u5DF2\u5E9F\u5F03) \u5DE5\u4F5C\u76EE\u5F55\u3002\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u8BB0\u5F55\u63D0\u4EA4\u4EFB\u52A1\u65F6\u7684 opencode run \u542F\u52A8\u76EE\u5F55\u3002"
|
|
27530
27872
|
)
|
|
@@ -27542,7 +27884,10 @@ var SuperTaskPlugin = async () => {
|
|
|
27542
27884
|
urgency: args.urgency ?? 3,
|
|
27543
27885
|
batchId: args.batchId,
|
|
27544
27886
|
dependsOn: args.dependsOn,
|
|
27545
|
-
cwd: submitCwd
|
|
27887
|
+
cwd: submitCwd,
|
|
27888
|
+
maxRetries: args.max_retries,
|
|
27889
|
+
retryBackoffMs: args.retry_backoff_ms,
|
|
27890
|
+
timeoutMs: args.timeout_ms
|
|
27546
27891
|
});
|
|
27547
27892
|
return JSON.stringify({ id: task.id, status: "created" });
|
|
27548
27893
|
} catch (error45) {
|
|
@@ -27554,7 +27899,7 @@ var SuperTaskPlugin = async () => {
|
|
|
27554
27899
|
}),
|
|
27555
27900
|
// 获取下一条任务
|
|
27556
27901
|
supertask_next: tool({
|
|
27557
|
-
description: "\u83B7\u53D6\u4E0B\u4E00\u4E2A\
|
|
27902
|
+
description: "\u83B7\u53D6\u4E0B\u4E00\u4E2A\u53EF\u6267\u884C\u4EFB\u52A1\u3002\u5019\u9009\u5305\u62EC pending \u548C\u9000\u907F\u5230\u671F\u4E14 retryCount <= maxRetries \u7684 failed \u4EFB\u52A1\uFF0C\u6309 urgency\u3001importance\u3001createdAt\u3001id \u6392\u5E8F\uFF0C\u5E76\u8DF3\u8FC7\u4F9D\u8D56\u672A\u5B8C\u6210\u7684\u4EFB\u52A1\u3002",
|
|
27558
27903
|
args: {
|
|
27559
27904
|
cwd: tool.schema.string().optional().describe("\u9879\u76EE\u9694\u79BB\uFF1A\u4F20\u5165\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\uFF0C\u53EA\u8FD4\u56DE\u8BE5\u9879\u76EE\u7684\u4EFB\u52A1")
|
|
27560
27905
|
},
|
|
@@ -27575,7 +27920,7 @@ var SuperTaskPlugin = async () => {
|
|
|
27575
27920
|
urgency: task.urgency
|
|
27576
27921
|
});
|
|
27577
27922
|
} else {
|
|
27578
|
-
return JSON.stringify({ id: null, message: "No
|
|
27923
|
+
return JSON.stringify({ id: null, message: "No executable tasks" });
|
|
27579
27924
|
}
|
|
27580
27925
|
} catch (error45) {
|
|
27581
27926
|
return JSON.stringify({
|
|
@@ -27602,7 +27947,7 @@ var SuperTaskPlugin = async () => {
|
|
|
27602
27947
|
error: "Task status does not allow start",
|
|
27603
27948
|
id: existing.id,
|
|
27604
27949
|
status: existing.status,
|
|
27605
|
-
message: `Only pending tasks can be started; current status is '${existing.status}'.`
|
|
27950
|
+
message: `Only pending or retryable failed tasks can be started; current status is '${existing.status}'.`
|
|
27606
27951
|
});
|
|
27607
27952
|
} catch (error45) {
|
|
27608
27953
|
return JSON.stringify({
|
|
@@ -27789,7 +28134,8 @@ var SuperTaskPlugin = async () => {
|
|
|
27789
28134
|
}).describe("\u8C03\u5EA6\u914D\u7F6E"),
|
|
27790
28135
|
max_instances: tool.schema.number().optional().describe("\u6700\u5927\u5E76\u53D1\u5B9E\u4F8B\u6570\uFF0C\u9ED8\u8BA4 1"),
|
|
27791
28136
|
max_retries: tool.schema.number().optional().describe("\u514B\u9686\u7ED9 task \u7684\u6700\u5927\u91CD\u8BD5\u6B21\u6570\uFF0C\u9ED8\u8BA4 3"),
|
|
27792
|
-
retry_backoff_ms: tool.schema.number().optional().describe("\u514B\u9686\u7ED9 task \u7684\u9000\u907F\u57FA\u7840\u95F4\u9694 ms\uFF0C\u9ED8\u8BA4 30000")
|
|
28137
|
+
retry_backoff_ms: tool.schema.number().optional().describe("\u514B\u9686\u7ED9 task \u7684\u9000\u907F\u57FA\u7840\u95F4\u9694 ms\uFF0C\u9ED8\u8BA4 30000"),
|
|
28138
|
+
timeout_ms: tool.schema.number().optional().describe("\u514B\u9686\u7ED9 task \u7684\u786C\u8D85\u65F6 ms\uFF1B\u672A\u4F20\u5219\u4F7F\u7528 Gateway \u9ED8\u8BA4\u503C")
|
|
27793
28139
|
},
|
|
27794
28140
|
async execute(args) {
|
|
27795
28141
|
try {
|
|
@@ -27821,13 +28167,16 @@ var SuperTaskPlugin = async () => {
|
|
|
27821
28167
|
category: args.category ?? "general",
|
|
27822
28168
|
importance: args.importance ?? 3,
|
|
27823
28169
|
urgency: args.urgency ?? 3,
|
|
28170
|
+
cwd: process.cwd(),
|
|
28171
|
+
batchId: args.batchId,
|
|
27824
28172
|
scheduleType,
|
|
27825
28173
|
cronExpr,
|
|
27826
28174
|
intervalMs,
|
|
27827
28175
|
runAt,
|
|
27828
28176
|
maxInstances: args.max_instances,
|
|
27829
28177
|
maxRetries: args.max_retries,
|
|
27830
|
-
retryBackoffMs: args.retry_backoff_ms
|
|
28178
|
+
retryBackoffMs: args.retry_backoff_ms,
|
|
28179
|
+
timeoutMs: args.timeout_ms
|
|
27831
28180
|
});
|
|
27832
28181
|
return JSON.stringify({
|
|
27833
28182
|
id: tmpl.id,
|
|
@@ -27844,27 +28193,22 @@ var SuperTaskPlugin = async () => {
|
|
|
27844
28193
|
}
|
|
27845
28194
|
}),
|
|
27846
28195
|
supertask_upgrade: tool({
|
|
27847
|
-
description: "\u5347\u7EA7 SuperTask \u63D2\u4EF6\u3002\
|
|
28196
|
+
description: "\u5347\u7EA7 SuperTask \u63D2\u4EF6\u3002\u901A\u8FC7 OpenCode \u5237\u65B0\u63D2\u4EF6\u7F13\u5B58\uFF0C\u6821\u9A8C\u65B0\u7248\u6784\u5EFA\u4EA7\u7269\u540E\u91CD\u542F Gateway\u3002\u5F53\u7528\u6237\u8BF4'\u5347\u7EA7\u63D2\u4EF6'\u3001'\u66F4\u65B0 supertask'\u3001'upgrade'\u65F6\u4F7F\u7528\u3002",
|
|
27848
28197
|
args: {},
|
|
27849
28198
|
async execute() {
|
|
27850
28199
|
try {
|
|
27851
|
-
|
|
27852
|
-
|
|
27853
|
-
console.log("[supertask] Updating npm package...");
|
|
28200
|
+
console.log("[supertask] Updating OpenCode plugin cache...");
|
|
28201
|
+
let installed;
|
|
27854
28202
|
try {
|
|
27855
|
-
|
|
27856
|
-
|
|
27857
|
-
stdio: "pipe",
|
|
27858
|
-
timeout: 6e4
|
|
27859
|
-
});
|
|
27860
|
-
} catch (npmErr) {
|
|
28203
|
+
installed = installLatestPlugin();
|
|
28204
|
+
} catch (updateError) {
|
|
27861
28205
|
return JSON.stringify({
|
|
27862
28206
|
success: false,
|
|
27863
|
-
error:
|
|
27864
|
-
hint: "Try manually:
|
|
28207
|
+
error: updateError instanceof Error ? updateError.message : String(updateError),
|
|
28208
|
+
hint: "Try manually: opencode plugin opencode-supertask@latest --global --force"
|
|
27865
28209
|
});
|
|
27866
28210
|
}
|
|
27867
|
-
const result = upgrade();
|
|
28211
|
+
const result = upgrade(installed);
|
|
27868
28212
|
return JSON.stringify({
|
|
27869
28213
|
success: true,
|
|
27870
28214
|
before: result.before,
|