opencode-supertask 0.1.26 → 0.1.27
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 +41 -17
- package/dist/cli/index.js +3464 -1089
- package/dist/cli/index.js.map +1 -1
- package/dist/daemon/pm2-supervisor.d.ts +3 -0
- package/dist/daemon/pm2-supervisor.js +145 -0
- package/dist/daemon/pm2-supervisor.js.map +1 -0
- package/dist/gateway/index.d.ts +8 -1
- package/dist/gateway/index.js +2131 -681
- package/dist/gateway/index.js.map +1 -1
- package/dist/plugin/supertask.d.ts +2 -1
- package/dist/plugin/supertask.js +1352 -345
- package/dist/plugin/supertask.js.map +1 -1
- package/dist/web/index.d.ts +1 -0
- package/dist/web/index.js +1042 -264
- package/dist/web/index.js.map +1 -1
- package/dist/worker/index.d.ts +17 -3
- package/dist/worker/index.js +1204 -247
- package/dist/worker/index.js.map +1 -1
- package/dist/worker/launcher.d.ts +9 -0
- package/dist/worker/launcher.js +164 -0
- package/dist/worker/launcher.js.map +1 -0
- package/drizzle/0005_natural_ma_gnuci.sql +1 -0
- package/drizzle/0006_worried_agent_zero.sql +2 -0
- package/drizzle/0007_lean_titanium_man.sql +1 -0
- package/drizzle/meta/0005_snapshot.json +559 -0
- package/drizzle/meta/0006_snapshot.json +575 -0
- package/drizzle/meta/0007_snapshot.json +585 -0
- package/drizzle/meta/_journal.json +21 -0
- package/package.json +1 -1
package/dist/plugin/supertask.js
CHANGED
|
@@ -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: readFileSync5 } = __require("fs");
|
|
9104
|
+
const data = readFileSync5(filePath, "utf8");
|
|
9105
9105
|
return _CronFileParser.#parseContent(data);
|
|
9106
9106
|
}
|
|
9107
9107
|
/**
|
|
@@ -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 join6(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 = join6;
|
|
22667
22667
|
function identifier(value) {
|
|
22668
22668
|
return new Name(value);
|
|
22669
22669
|
}
|
|
@@ -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((join6) => join6.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((join6) => join6.alias === tableName)) {
|
|
25776
25776
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
25777
25777
|
}
|
|
25778
25778
|
if (typeof on === "function") {
|
|
@@ -26802,7 +26802,9 @@ var tasks = sqliteTable("tasks", {
|
|
|
26802
26802
|
}, (table) => [
|
|
26803
26803
|
index("tasks_queue_idx").on(table.status, table.retryAfter, table.urgency, table.importance, table.createdAt, table.id),
|
|
26804
26804
|
index("tasks_batch_status_idx").on(table.batchId, table.status),
|
|
26805
|
-
index("tasks_template_status_idx").on(table.templateId, table.status)
|
|
26805
|
+
index("tasks_template_status_idx").on(table.templateId, table.status),
|
|
26806
|
+
index("tasks_depends_on_status_idx").on(table.dependsOn, table.status),
|
|
26807
|
+
index("tasks_cleanup_idx").on(table.finishedAt, table.id, table.status)
|
|
26806
26808
|
]);
|
|
26807
26809
|
var TASK_CATEGORIES = [
|
|
26808
26810
|
"translate",
|
|
@@ -26825,7 +26827,8 @@ var taskRuns = sqliteTable("task_runs", {
|
|
|
26825
26827
|
lockedBy: text("locked_by"),
|
|
26826
26828
|
heartbeatAt: integer2("heartbeat_at"),
|
|
26827
26829
|
workerPid: integer2("worker_pid"),
|
|
26828
|
-
childPid: integer2("child_pid")
|
|
26830
|
+
childPid: integer2("child_pid"),
|
|
26831
|
+
launchProtocol: text("launch_protocol")
|
|
26829
26832
|
}, (table) => [
|
|
26830
26833
|
index("task_runs_task_started_idx").on(table.taskId, table.startedAt, table.id),
|
|
26831
26834
|
index("task_runs_status_heartbeat_idx").on(table.status, table.heartbeatAt)
|
|
@@ -26855,7 +26858,13 @@ var taskTemplates = sqliteTable("task_templates", {
|
|
|
26855
26858
|
createdAt: integer2("created_at").default(0),
|
|
26856
26859
|
updatedAt: integer2("updated_at").default(0)
|
|
26857
26860
|
}, (table) => [
|
|
26858
|
-
index("task_templates_due_idx").on(table.enabled, table.nextRunAt, table.id)
|
|
26861
|
+
index("task_templates_due_idx").on(table.enabled, table.nextRunAt, table.id),
|
|
26862
|
+
index("task_templates_retention_idx").on(
|
|
26863
|
+
table.scheduleType,
|
|
26864
|
+
table.enabled,
|
|
26865
|
+
table.lastRunAt,
|
|
26866
|
+
table.id
|
|
26867
|
+
)
|
|
26859
26868
|
]);
|
|
26860
26869
|
|
|
26861
26870
|
// src/core/db/index.ts
|
|
@@ -26880,36 +26889,47 @@ function getMigrationsFolder() {
|
|
|
26880
26889
|
}
|
|
26881
26890
|
return join(__dirname2, "../../drizzle");
|
|
26882
26891
|
}
|
|
26883
|
-
function
|
|
26884
|
-
|
|
26885
|
-
if (!existsSync(dataDir)) {
|
|
26886
|
-
mkdirSync(dataDir, { recursive: true });
|
|
26887
|
-
}
|
|
26888
|
-
_sqlite = new Database2(DB_FILE_PATH);
|
|
26889
|
-
_sqlite.exec("PRAGMA journal_mode = WAL;");
|
|
26890
|
-
_sqlite.exec("PRAGMA busy_timeout = 5000;");
|
|
26891
|
-
_sqlite.exec(`
|
|
26892
|
+
function ensureGatewayLock(sqliteDb) {
|
|
26893
|
+
sqliteDb.exec(`
|
|
26892
26894
|
CREATE TABLE IF NOT EXISTS gateway_lock (
|
|
26893
26895
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
26894
26896
|
pid INTEGER NOT NULL,
|
|
26895
26897
|
acquired_at INTEGER NOT NULL,
|
|
26896
26898
|
heartbeat_at INTEGER NOT NULL,
|
|
26897
|
-
ready_at INTEGER
|
|
26899
|
+
ready_at INTEGER,
|
|
26900
|
+
version TEXT
|
|
26898
26901
|
);
|
|
26899
26902
|
`);
|
|
26900
|
-
const
|
|
26901
|
-
if (!
|
|
26902
|
-
|
|
26903
|
+
const columns = sqliteDb.query("PRAGMA table_info(gateway_lock)").all();
|
|
26904
|
+
if (!columns.some((column) => column.name === "ready_at")) {
|
|
26905
|
+
sqliteDb.exec("ALTER TABLE gateway_lock ADD COLUMN ready_at INTEGER;");
|
|
26906
|
+
}
|
|
26907
|
+
if (!columns.some((column) => column.name === "version")) {
|
|
26908
|
+
sqliteDb.exec("ALTER TABLE gateway_lock ADD COLUMN version TEXT;");
|
|
26903
26909
|
}
|
|
26904
|
-
|
|
26910
|
+
}
|
|
26911
|
+
function migrateSqliteDatabase(sqliteDb) {
|
|
26912
|
+
ensureGatewayLock(sqliteDb);
|
|
26913
|
+
const drizzleDb = drizzle(sqliteDb, { schema: schema_exports });
|
|
26914
|
+
migrate(drizzleDb, { migrationsFolder: getMigrationsFolder() });
|
|
26915
|
+
sqliteDb.exec("PRAGMA foreign_keys = ON;");
|
|
26916
|
+
const violations = sqliteDb.query("PRAGMA foreign_key_check;").all();
|
|
26917
|
+
if (violations.length > 0) {
|
|
26918
|
+
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`);
|
|
26919
|
+
}
|
|
26920
|
+
return drizzleDb;
|
|
26921
|
+
}
|
|
26922
|
+
function initDb() {
|
|
26923
|
+
const dataDir = dirname(DB_FILE_PATH);
|
|
26924
|
+
if (!existsSync(dataDir)) {
|
|
26925
|
+
mkdirSync(dataDir, { recursive: true });
|
|
26926
|
+
}
|
|
26927
|
+
_sqlite = new Database2(DB_FILE_PATH);
|
|
26928
|
+
_sqlite.exec("PRAGMA journal_mode = WAL;");
|
|
26929
|
+
_sqlite.exec("PRAGMA busy_timeout = 5000;");
|
|
26905
26930
|
if (!_migrationRan) {
|
|
26906
26931
|
try {
|
|
26907
|
-
|
|
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
|
-
}
|
|
26932
|
+
_db = migrateSqliteDatabase(_sqlite);
|
|
26913
26933
|
_migrationRan = true;
|
|
26914
26934
|
} catch (err) {
|
|
26915
26935
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -26920,6 +26940,8 @@ function initDb() {
|
|
|
26920
26940
|
console.error(`[supertask] migration failed: ${msg}`);
|
|
26921
26941
|
throw new Error(`[supertask] DB migration failed: ${msg}`);
|
|
26922
26942
|
}
|
|
26943
|
+
} else {
|
|
26944
|
+
_db = drizzle(_sqlite, { schema: schema_exports });
|
|
26923
26945
|
}
|
|
26924
26946
|
return _db;
|
|
26925
26947
|
}
|
|
@@ -26956,6 +26978,58 @@ function computeBackoff(retryCount, baseMs = 3e4, maxMs = MAX_BACKOFF_MS) {
|
|
|
26956
26978
|
|
|
26957
26979
|
// src/core/services/task.service.ts
|
|
26958
26980
|
var { tasks: tasks2, taskRuns: taskRuns2 } = schema_exports;
|
|
26981
|
+
var cleanupInvocation = 0;
|
|
26982
|
+
var TaskDeletionConflictError = class extends Error {
|
|
26983
|
+
constructor(message) {
|
|
26984
|
+
super(message);
|
|
26985
|
+
this.name = "TaskDeletionConflictError";
|
|
26986
|
+
}
|
|
26987
|
+
};
|
|
26988
|
+
function hasNoExecutableDependents() {
|
|
26989
|
+
return sql`NOT EXISTS (
|
|
26990
|
+
SELECT 1 FROM tasks AS dependent_task
|
|
26991
|
+
WHERE dependent_task.depends_on = ${tasks2.id}
|
|
26992
|
+
AND dependent_task.status IN ('pending', 'running', 'failed', 'dead_letter')
|
|
26993
|
+
)`;
|
|
26994
|
+
}
|
|
26995
|
+
function hasViableDependency() {
|
|
26996
|
+
return or(
|
|
26997
|
+
isNull(tasks2.dependsOn),
|
|
26998
|
+
sql`EXISTS (
|
|
26999
|
+
SELECT 1 FROM tasks AS dependency_task
|
|
27000
|
+
WHERE dependency_task.id = ${tasks2.dependsOn}
|
|
27001
|
+
AND dependency_task.cwd IS ${tasks2.cwd}
|
|
27002
|
+
AND (
|
|
27003
|
+
dependency_task.status IN ('pending', 'running', 'done')
|
|
27004
|
+
OR (
|
|
27005
|
+
dependency_task.status = 'failed'
|
|
27006
|
+
AND dependency_task.retry_count <= dependency_task.max_retries
|
|
27007
|
+
)
|
|
27008
|
+
)
|
|
27009
|
+
)`
|
|
27010
|
+
);
|
|
27011
|
+
}
|
|
27012
|
+
function blockedDependentsOf(prerequisiteId) {
|
|
27013
|
+
return sql`${tasks2.id} IN (
|
|
27014
|
+
WITH RECURSIVE blocked_task(id) AS (
|
|
27015
|
+
SELECT direct_dependent.id
|
|
27016
|
+
FROM tasks AS direct_dependent
|
|
27017
|
+
WHERE direct_dependent.depends_on = ${prerequisiteId}
|
|
27018
|
+
AND direct_dependent.status IN ('pending', 'failed')
|
|
27019
|
+
AND EXISTS (
|
|
27020
|
+
SELECT 1 FROM tasks AS prerequisite
|
|
27021
|
+
WHERE prerequisite.id = ${prerequisiteId}
|
|
27022
|
+
AND prerequisite.status IN ('dead_letter', 'cancelled')
|
|
27023
|
+
)
|
|
27024
|
+
UNION
|
|
27025
|
+
SELECT descendant.id
|
|
27026
|
+
FROM tasks AS descendant
|
|
27027
|
+
INNER JOIN blocked_task ON descendant.depends_on = blocked_task.id
|
|
27028
|
+
WHERE descendant.status IN ('pending', 'failed')
|
|
27029
|
+
)
|
|
27030
|
+
SELECT id FROM blocked_task
|
|
27031
|
+
)`;
|
|
27032
|
+
}
|
|
26959
27033
|
var TaskService = class {
|
|
26960
27034
|
static buildScopeWhere(scope) {
|
|
26961
27035
|
const conditions = [];
|
|
@@ -26966,8 +27040,28 @@ var TaskService = class {
|
|
|
26966
27040
|
}
|
|
26967
27041
|
static async add(data) {
|
|
26968
27042
|
this.validateNewTask(data);
|
|
26969
|
-
|
|
26970
|
-
|
|
27043
|
+
return db.transaction((tx) => {
|
|
27044
|
+
if (data.dependsOn != null) {
|
|
27045
|
+
const dependency = tx.select({
|
|
27046
|
+
id: tasks2.id,
|
|
27047
|
+
cwd: tasks2.cwd,
|
|
27048
|
+
status: tasks2.status,
|
|
27049
|
+
retryCount: tasks2.retryCount,
|
|
27050
|
+
maxRetries: tasks2.maxRetries
|
|
27051
|
+
}).from(tasks2).where(eq(tasks2.id, data.dependsOn)).get();
|
|
27052
|
+
if (!dependency) {
|
|
27053
|
+
throw new Error(`dependsOn \u6307\u5411\u7684\u4EFB\u52A1 #${data.dependsOn} \u4E0D\u5B58\u5728`);
|
|
27054
|
+
}
|
|
27055
|
+
if ((dependency.cwd ?? null) !== (data.cwd ?? null)) {
|
|
27056
|
+
throw new Error("dependsOn \u5FC5\u987B\u6307\u5411\u540C\u4E00 cwd \u7684\u4EFB\u52A1");
|
|
27057
|
+
}
|
|
27058
|
+
const dependencyIsRecoverable = dependency.status === "pending" || dependency.status === "running" || dependency.status === "done" || dependency.status === "failed" && (dependency.retryCount ?? 0) <= (dependency.maxRetries ?? 3);
|
|
27059
|
+
if (!dependencyIsRecoverable) {
|
|
27060
|
+
throw new Error(`dependsOn \u6307\u5411\u7684\u4EFB\u52A1 #${data.dependsOn} \u5DF2\u8FDB\u5165\u4E0D\u53EF\u6062\u590D\u7EC8\u6001`);
|
|
27061
|
+
}
|
|
27062
|
+
}
|
|
27063
|
+
return tx.insert(tasks2).values(data).returning().get();
|
|
27064
|
+
}, { behavior: "immediate" });
|
|
26971
27065
|
}
|
|
26972
27066
|
static validateNewTask(data) {
|
|
26973
27067
|
if (!data.name.trim()) throw new Error("name \u4E0D\u80FD\u4E3A\u7A7A");
|
|
@@ -27019,23 +27113,54 @@ var TaskService = class {
|
|
|
27019
27113
|
if (batchFilter) {
|
|
27020
27114
|
conditions.push(batchFilter);
|
|
27021
27115
|
}
|
|
27022
|
-
const
|
|
27116
|
+
const result = await db.select().from(tasks2).where(and(
|
|
27117
|
+
...conditions,
|
|
27118
|
+
or(
|
|
27119
|
+
isNull(tasks2.dependsOn),
|
|
27120
|
+
sql`EXISTS (
|
|
27121
|
+
SELECT 1 FROM tasks AS dependency_task
|
|
27122
|
+
WHERE dependency_task.id = ${tasks2.dependsOn}
|
|
27123
|
+
AND dependency_task.status = 'done'
|
|
27124
|
+
AND dependency_task.cwd IS ${tasks2.cwd}
|
|
27125
|
+
)`
|
|
27126
|
+
),
|
|
27127
|
+
or(
|
|
27128
|
+
isNull(tasks2.batchId),
|
|
27129
|
+
sql`NOT EXISTS (
|
|
27130
|
+
SELECT 1 FROM tasks AS running_batch_task
|
|
27131
|
+
WHERE running_batch_task.batch_id = ${tasks2.batchId}
|
|
27132
|
+
AND (
|
|
27133
|
+
running_batch_task.status = 'running'
|
|
27134
|
+
OR EXISTS (
|
|
27135
|
+
SELECT 1 FROM task_runs AS running_batch_run
|
|
27136
|
+
WHERE running_batch_run.task_id = running_batch_task.id
|
|
27137
|
+
AND running_batch_run.status = 'running'
|
|
27138
|
+
)
|
|
27139
|
+
)
|
|
27140
|
+
)`
|
|
27141
|
+
),
|
|
27142
|
+
sql`NOT EXISTS (
|
|
27143
|
+
SELECT 1 FROM task_runs AS candidate_active_run
|
|
27144
|
+
WHERE candidate_active_run.task_id = ${tasks2.id}
|
|
27145
|
+
AND candidate_active_run.status = 'running'
|
|
27146
|
+
)`
|
|
27147
|
+
)).orderBy(
|
|
27023
27148
|
desc(tasks2.urgency),
|
|
27024
27149
|
desc(tasks2.importance),
|
|
27025
27150
|
asc(tasks2.createdAt),
|
|
27026
27151
|
asc(tasks2.id)
|
|
27027
|
-
);
|
|
27028
|
-
|
|
27029
|
-
|
|
27030
|
-
|
|
27031
|
-
|
|
27032
|
-
|
|
27033
|
-
|
|
27034
|
-
|
|
27035
|
-
|
|
27036
|
-
|
|
27037
|
-
|
|
27038
|
-
|
|
27152
|
+
).limit(1);
|
|
27153
|
+
return result[0] ?? null;
|
|
27154
|
+
}
|
|
27155
|
+
static async countRunning() {
|
|
27156
|
+
return db.transaction((tx) => {
|
|
27157
|
+
const runningTasks = tx.select({ count: sql`count(*)` }).from(tasks2).where(eq(tasks2.status, "running")).get();
|
|
27158
|
+
const runsWithoutRunningTask = tx.select({ count: sql`count(DISTINCT ${taskRuns2.taskId})` }).from(taskRuns2).innerJoin(tasks2, eq(tasks2.id, taskRuns2.taskId)).where(and(
|
|
27159
|
+
eq(taskRuns2.status, "running"),
|
|
27160
|
+
sql`${tasks2.status} <> 'running'`
|
|
27161
|
+
)).get();
|
|
27162
|
+
return Number(runningTasks?.count ?? 0) + Number(runsWithoutRunningTask?.count ?? 0);
|
|
27163
|
+
}, { behavior: "deferred" });
|
|
27039
27164
|
}
|
|
27040
27165
|
static async start(id, scope = {}) {
|
|
27041
27166
|
const conditions = [
|
|
@@ -27071,27 +27196,202 @@ var TaskService = class {
|
|
|
27071
27196
|
return result[0] || null;
|
|
27072
27197
|
}
|
|
27073
27198
|
static async fail(id, log, scope = {}, options) {
|
|
27074
|
-
|
|
27075
|
-
|
|
27076
|
-
|
|
27077
|
-
|
|
27078
|
-
|
|
27079
|
-
|
|
27080
|
-
|
|
27081
|
-
|
|
27082
|
-
|
|
27083
|
-
|
|
27199
|
+
return db.transaction((tx) => {
|
|
27200
|
+
const current = tx.select().from(tasks2).where(and(
|
|
27201
|
+
eq(tasks2.id, id),
|
|
27202
|
+
eq(tasks2.status, "running"),
|
|
27203
|
+
...this.buildScopeWhere(scope)
|
|
27204
|
+
)).get();
|
|
27205
|
+
if (!current) return null;
|
|
27206
|
+
const newRetryCount = (current.retryCount ?? 0) + 1;
|
|
27207
|
+
const maxRetries = current.maxRetries ?? 3;
|
|
27208
|
+
const isDeadLetter = options?.setDeadLetter ?? newRetryCount > maxRetries;
|
|
27209
|
+
const failed = tx.update(tasks2).set({
|
|
27210
|
+
status: isDeadLetter ? "dead_letter" : "failed",
|
|
27211
|
+
finishedAt: /* @__PURE__ */ new Date(),
|
|
27212
|
+
resultLog: log,
|
|
27213
|
+
retryCount: newRetryCount,
|
|
27214
|
+
retryAfter: isDeadLetter ? null : options?.retryAfterMs ?? Date.now() + computeBackoff(
|
|
27215
|
+
newRetryCount,
|
|
27216
|
+
current.retryBackoffMs ?? 3e4
|
|
27217
|
+
)
|
|
27218
|
+
}).where(and(eq(tasks2.id, id), eq(tasks2.status, "running"))).returning().get();
|
|
27219
|
+
if (failed?.status === "dead_letter") {
|
|
27220
|
+
tx.update(tasks2).set({
|
|
27221
|
+
status: "dead_letter",
|
|
27222
|
+
finishedAt: /* @__PURE__ */ new Date(),
|
|
27223
|
+
retryAfter: null,
|
|
27224
|
+
resultLog: `\u4F9D\u8D56\u4EFB\u52A1 #${id} \u5DF2\u8FDB\u5165\u4E0D\u53EF\u6062\u590D\u7EC8\u6001`
|
|
27225
|
+
}).where(blockedDependentsOf(id)).run();
|
|
27226
|
+
}
|
|
27227
|
+
return failed ?? null;
|
|
27228
|
+
}, { behavior: "immediate" });
|
|
27229
|
+
}
|
|
27230
|
+
static async completeRun(taskId, runId, log) {
|
|
27231
|
+
return db.transaction((tx) => {
|
|
27232
|
+
const currentTask = tx.select().from(tasks2).where(and(eq(tasks2.id, taskId), eq(tasks2.status, "running"))).get();
|
|
27233
|
+
const currentRun = tx.select({ id: taskRuns2.id }).from(taskRuns2).where(and(
|
|
27234
|
+
eq(taskRuns2.id, runId),
|
|
27235
|
+
eq(taskRuns2.taskId, taskId),
|
|
27236
|
+
eq(taskRuns2.status, "running")
|
|
27237
|
+
)).get();
|
|
27238
|
+
if (!currentTask || !currentRun) return null;
|
|
27239
|
+
const finishedAt = /* @__PURE__ */ new Date();
|
|
27240
|
+
const completed = tx.update(tasks2).set({
|
|
27241
|
+
status: "done",
|
|
27242
|
+
finishedAt,
|
|
27243
|
+
resultLog: log,
|
|
27244
|
+
retryAfter: null
|
|
27245
|
+
}).where(and(eq(tasks2.id, taskId), eq(tasks2.status, "running"))).returning().get();
|
|
27246
|
+
if (!completed) return null;
|
|
27247
|
+
tx.update(taskRuns2).set({ status: "done", finishedAt, log }).where(and(eq(taskRuns2.id, runId), eq(taskRuns2.status, "running"))).run();
|
|
27248
|
+
return completed;
|
|
27249
|
+
}, { behavior: "immediate" });
|
|
27250
|
+
}
|
|
27251
|
+
static async failRun(taskId, runId, log, options) {
|
|
27252
|
+
const failed = db.transaction((tx) => {
|
|
27253
|
+
const currentTask = tx.select().from(tasks2).where(and(eq(tasks2.id, taskId), eq(tasks2.status, "running"))).get();
|
|
27254
|
+
const currentRun = tx.select({ id: taskRuns2.id }).from(taskRuns2).where(and(
|
|
27255
|
+
eq(taskRuns2.id, runId),
|
|
27256
|
+
eq(taskRuns2.taskId, taskId),
|
|
27257
|
+
eq(taskRuns2.status, "running")
|
|
27258
|
+
)).get();
|
|
27259
|
+
if (!currentTask || !currentRun) return null;
|
|
27260
|
+
const newRetryCount = (currentTask.retryCount ?? 0) + 1;
|
|
27261
|
+
const maxRetries = currentTask.maxRetries ?? 3;
|
|
27262
|
+
const isDeadLetter = options?.setDeadLetter ?? newRetryCount > maxRetries;
|
|
27263
|
+
const finishedAt = /* @__PURE__ */ new Date();
|
|
27264
|
+
const retryAfter = isDeadLetter ? null : options?.retryAfterMs ?? Date.now() + computeBackoff(
|
|
27265
|
+
newRetryCount,
|
|
27266
|
+
currentTask.retryBackoffMs ?? 3e4
|
|
27267
|
+
);
|
|
27268
|
+
const failed2 = tx.update(tasks2).set({
|
|
27269
|
+
status: isDeadLetter ? "dead_letter" : "failed",
|
|
27270
|
+
finishedAt,
|
|
27271
|
+
resultLog: log,
|
|
27272
|
+
retryCount: newRetryCount,
|
|
27273
|
+
retryAfter
|
|
27274
|
+
}).where(and(eq(tasks2.id, taskId), eq(tasks2.status, "running"))).returning().get();
|
|
27275
|
+
if (!failed2) return null;
|
|
27276
|
+
tx.update(taskRuns2).set({ status: "failed", finishedAt, log }).where(and(eq(taskRuns2.id, runId), eq(taskRuns2.status, "running"))).run();
|
|
27277
|
+
if (failed2.status === "dead_letter") {
|
|
27278
|
+
tx.update(tasks2).set({
|
|
27279
|
+
status: "dead_letter",
|
|
27280
|
+
finishedAt,
|
|
27281
|
+
retryAfter: null,
|
|
27282
|
+
resultLog: `\u4F9D\u8D56\u4EFB\u52A1 #${taskId} \u5DF2\u8FDB\u5165\u4E0D\u53EF\u6062\u590D\u7EC8\u6001`
|
|
27283
|
+
}).where(blockedDependentsOf(taskId)).run();
|
|
27284
|
+
}
|
|
27285
|
+
return failed2;
|
|
27286
|
+
}, { behavior: "immediate" });
|
|
27287
|
+
return failed;
|
|
27288
|
+
}
|
|
27289
|
+
static async recoverRun(taskId, runId, log) {
|
|
27290
|
+
const recovery = db.transaction((tx) => {
|
|
27291
|
+
const currentTask = tx.select().from(tasks2).where(and(eq(tasks2.id, taskId), eq(tasks2.status, "running"))).get();
|
|
27292
|
+
const currentRun = tx.select({ id: taskRuns2.id }).from(taskRuns2).where(and(
|
|
27293
|
+
eq(taskRuns2.id, runId),
|
|
27294
|
+
eq(taskRuns2.taskId, taskId),
|
|
27295
|
+
eq(taskRuns2.status, "running")
|
|
27296
|
+
)).get();
|
|
27297
|
+
if (!currentRun) return null;
|
|
27298
|
+
const finishedAt = /* @__PURE__ */ new Date();
|
|
27299
|
+
tx.update(taskRuns2).set({ status: "failed", finishedAt, log }).where(and(eq(taskRuns2.id, runId), eq(taskRuns2.status, "running"))).run();
|
|
27300
|
+
if (!currentTask) return null;
|
|
27301
|
+
const retryCount = (currentTask.retryCount ?? 0) + 1;
|
|
27302
|
+
const maxRetries = currentTask.maxRetries ?? 3;
|
|
27303
|
+
const isDeadLetter = retryCount > maxRetries;
|
|
27304
|
+
const recoveryStatus = isDeadLetter ? "dead_letter" : "pending";
|
|
27305
|
+
const retryAfterMs = isDeadLetter ? null : Date.now() + computeBackoff(
|
|
27306
|
+
retryCount,
|
|
27307
|
+
currentTask.retryBackoffMs ?? 3e4
|
|
27308
|
+
);
|
|
27309
|
+
tx.update(tasks2).set({
|
|
27310
|
+
status: recoveryStatus,
|
|
27311
|
+
startedAt: null,
|
|
27312
|
+
finishedAt: isDeadLetter ? finishedAt : null,
|
|
27313
|
+
retryCount,
|
|
27314
|
+
retryAfter: retryAfterMs,
|
|
27315
|
+
resultLog: log
|
|
27316
|
+
}).where(and(eq(tasks2.id, taskId), eq(tasks2.status, "running"))).run();
|
|
27317
|
+
if (recoveryStatus === "dead_letter") {
|
|
27318
|
+
tx.update(tasks2).set({
|
|
27319
|
+
status: "dead_letter",
|
|
27320
|
+
finishedAt,
|
|
27321
|
+
retryAfter: null,
|
|
27322
|
+
resultLog: `\u4F9D\u8D56\u4EFB\u52A1 #${taskId} \u5DF2\u8FDB\u5165\u4E0D\u53EF\u6062\u590D\u7EC8\u6001`
|
|
27323
|
+
}).where(blockedDependentsOf(taskId)).run();
|
|
27324
|
+
}
|
|
27325
|
+
return {
|
|
27326
|
+
status: recoveryStatus,
|
|
27327
|
+
retryCount,
|
|
27328
|
+
retryAfterMs
|
|
27329
|
+
};
|
|
27330
|
+
}, { behavior: "immediate" });
|
|
27331
|
+
return recovery;
|
|
27332
|
+
}
|
|
27333
|
+
static async interruptRun(taskId, runId, log) {
|
|
27334
|
+
return db.transaction((tx) => {
|
|
27335
|
+
const currentRun = tx.select({ id: taskRuns2.id }).from(taskRuns2).where(and(
|
|
27336
|
+
eq(taskRuns2.id, runId),
|
|
27337
|
+
eq(taskRuns2.taskId, taskId),
|
|
27338
|
+
eq(taskRuns2.status, "running")
|
|
27339
|
+
)).get();
|
|
27340
|
+
if (!currentRun) return false;
|
|
27341
|
+
const updatedRun = tx.update(taskRuns2).set({ status: "failed", finishedAt: /* @__PURE__ */ new Date(), log }).where(and(eq(taskRuns2.id, runId), eq(taskRuns2.status, "running"))).returning({ id: taskRuns2.id }).get();
|
|
27342
|
+
tx.update(tasks2).set({
|
|
27343
|
+
status: "pending",
|
|
27344
|
+
startedAt: null,
|
|
27345
|
+
finishedAt: null,
|
|
27346
|
+
retryAfter: null,
|
|
27347
|
+
resultLog: log
|
|
27348
|
+
}).where(and(eq(tasks2.id, taskId), eq(tasks2.status, "running"))).run();
|
|
27349
|
+
return updatedRun != null;
|
|
27350
|
+
}, { behavior: "immediate" });
|
|
27351
|
+
}
|
|
27352
|
+
static async resolveBlockedDependencies() {
|
|
27353
|
+
const finishedAt = /* @__PURE__ */ new Date();
|
|
27084
27354
|
const result = await db.update(tasks2).set({
|
|
27085
|
-
status:
|
|
27355
|
+
status: "dead_letter",
|
|
27356
|
+
finishedAt,
|
|
27357
|
+
retryAfter: null,
|
|
27358
|
+
resultLog: "\u4F9D\u8D56\u4EFB\u52A1\u4E0D\u5B58\u5728\u3001\u8DE8\u9879\u76EE\u6216\u5DF2\u8FDB\u5165\u4E0D\u53EF\u6062\u590D\u7EC8\u6001"
|
|
27359
|
+
}).where(sql`${tasks2.id} IN (
|
|
27360
|
+
WITH RECURSIVE blocked_task(id) AS (
|
|
27361
|
+
SELECT candidate.id
|
|
27362
|
+
FROM tasks AS candidate
|
|
27363
|
+
WHERE candidate.status IN ('pending', 'failed')
|
|
27364
|
+
AND candidate.depends_on IS NOT NULL
|
|
27365
|
+
AND NOT EXISTS (
|
|
27366
|
+
SELECT 1 FROM tasks AS viable_dependency
|
|
27367
|
+
WHERE viable_dependency.id = candidate.depends_on
|
|
27368
|
+
AND viable_dependency.cwd IS candidate.cwd
|
|
27369
|
+
AND (
|
|
27370
|
+
viable_dependency.status IN ('pending', 'running', 'done')
|
|
27371
|
+
OR (
|
|
27372
|
+
viable_dependency.status = 'failed'
|
|
27373
|
+
AND viable_dependency.retry_count <= viable_dependency.max_retries
|
|
27374
|
+
)
|
|
27375
|
+
)
|
|
27376
|
+
)
|
|
27377
|
+
UNION
|
|
27378
|
+
SELECT descendant.id
|
|
27379
|
+
FROM tasks AS descendant
|
|
27380
|
+
INNER JOIN blocked_task ON descendant.depends_on = blocked_task.id
|
|
27381
|
+
WHERE descendant.status IN ('pending', 'failed')
|
|
27382
|
+
)
|
|
27383
|
+
SELECT id FROM blocked_task
|
|
27384
|
+
)`).returning();
|
|
27385
|
+
return result.length;
|
|
27386
|
+
}
|
|
27387
|
+
static async rejectBlockedDependents(prerequisiteId) {
|
|
27388
|
+
const result = await db.update(tasks2).set({
|
|
27389
|
+
status: "dead_letter",
|
|
27086
27390
|
finishedAt: /* @__PURE__ */ new Date(),
|
|
27087
|
-
|
|
27088
|
-
|
|
27089
|
-
|
|
27090
|
-
|
|
27091
|
-
current.retryBackoffMs ?? 3e4
|
|
27092
|
-
)
|
|
27093
|
-
}).where(and(...conditions)).returning();
|
|
27094
|
-
return result[0] || null;
|
|
27391
|
+
retryAfter: null,
|
|
27392
|
+
resultLog: `\u4F9D\u8D56\u4EFB\u52A1 #${prerequisiteId} \u5DF2\u8FDB\u5165\u4E0D\u53EF\u6062\u590D\u7EC8\u6001`
|
|
27393
|
+
}).where(blockedDependentsOf(prerequisiteId)).returning({ id: tasks2.id });
|
|
27394
|
+
return result.length;
|
|
27095
27395
|
}
|
|
27096
27396
|
static async markPendingForRetry(id, retryAfterMs, retryCount) {
|
|
27097
27397
|
const result = await db.update(tasks2).set({
|
|
@@ -27104,8 +27404,18 @@ var TaskService = class {
|
|
|
27104
27404
|
return result[0] || null;
|
|
27105
27405
|
}
|
|
27106
27406
|
static async markDeadLetter(id, retryCount) {
|
|
27107
|
-
|
|
27108
|
-
|
|
27407
|
+
return db.transaction((tx) => {
|
|
27408
|
+
const finishedAt = /* @__PURE__ */ new Date();
|
|
27409
|
+
const task = tx.update(tasks2).set({ status: "dead_letter", finishedAt, retryCount }).where(eq(tasks2.id, id)).returning().get();
|
|
27410
|
+
if (!task) return null;
|
|
27411
|
+
tx.update(tasks2).set({
|
|
27412
|
+
status: "dead_letter",
|
|
27413
|
+
finishedAt,
|
|
27414
|
+
retryAfter: null,
|
|
27415
|
+
resultLog: `\u4F9D\u8D56\u4EFB\u52A1 #${id} \u5DF2\u8FDB\u5165\u4E0D\u53EF\u6062\u590D\u7EC8\u6001`
|
|
27416
|
+
}).where(blockedDependentsOf(id)).run();
|
|
27417
|
+
return task;
|
|
27418
|
+
}, { behavior: "immediate" });
|
|
27109
27419
|
}
|
|
27110
27420
|
static async resetRunningToPending(ids) {
|
|
27111
27421
|
if (ids.length === 0) return 0;
|
|
@@ -27148,41 +27458,91 @@ var TaskService = class {
|
|
|
27148
27458
|
),
|
|
27149
27459
|
...this.buildScopeWhere(scope)
|
|
27150
27460
|
];
|
|
27151
|
-
|
|
27152
|
-
|
|
27153
|
-
|
|
27154
|
-
|
|
27155
|
-
|
|
27156
|
-
|
|
27461
|
+
return db.transaction((tx) => {
|
|
27462
|
+
const finishedAt = /* @__PURE__ */ new Date();
|
|
27463
|
+
const task = tx.update(tasks2).set({
|
|
27464
|
+
status: "cancelled",
|
|
27465
|
+
finishedAt,
|
|
27466
|
+
retryAfter: null
|
|
27467
|
+
}).where(and(...conditions)).returning().get();
|
|
27468
|
+
if (!task) return null;
|
|
27469
|
+
tx.update(tasks2).set({
|
|
27470
|
+
status: "dead_letter",
|
|
27471
|
+
finishedAt,
|
|
27472
|
+
retryAfter: null,
|
|
27473
|
+
resultLog: `\u4F9D\u8D56\u4EFB\u52A1 #${id} \u5DF2\u8FDB\u5165\u4E0D\u53EF\u6062\u590D\u7EC8\u6001`
|
|
27474
|
+
}).where(blockedDependentsOf(id)).run();
|
|
27475
|
+
return task;
|
|
27476
|
+
}, { behavior: "immediate" });
|
|
27157
27477
|
}
|
|
27158
27478
|
static async retry(id, scope = {}) {
|
|
27159
|
-
const current = await this.getById(id, scope);
|
|
27160
|
-
if (!current) return null;
|
|
27161
|
-
if (current.status !== "failed" && current.status !== "dead_letter") return null;
|
|
27162
|
-
const conditions = [eq(tasks2.id, id), ...this.buildScopeWhere(scope)];
|
|
27163
|
-
const result = await db.update(tasks2).set({
|
|
27164
|
-
status: "pending",
|
|
27165
|
-
startedAt: null,
|
|
27166
|
-
finishedAt: null,
|
|
27167
|
-
retryAfter: null,
|
|
27168
|
-
retryCount: 0
|
|
27169
|
-
}).where(and(...conditions)).returning();
|
|
27170
|
-
return result[0] || null;
|
|
27171
|
-
}
|
|
27172
|
-
static async retryBatch(batchId, scope = {}) {
|
|
27173
27479
|
const conditions = [
|
|
27174
|
-
eq(tasks2.
|
|
27480
|
+
eq(tasks2.id, id),
|
|
27175
27481
|
or(eq(tasks2.status, "failed"), eq(tasks2.status, "dead_letter")),
|
|
27176
27482
|
...this.buildScopeWhere(scope)
|
|
27177
27483
|
];
|
|
27178
|
-
|
|
27484
|
+
return db.transaction((tx) => tx.update(tasks2).set({
|
|
27179
27485
|
status: "pending",
|
|
27180
27486
|
startedAt: null,
|
|
27181
27487
|
finishedAt: null,
|
|
27182
27488
|
retryAfter: null,
|
|
27183
27489
|
retryCount: 0
|
|
27184
|
-
}).where(and(...conditions)).returning();
|
|
27185
|
-
|
|
27490
|
+
}).where(and(...conditions, hasViableDependency())).returning().get() ?? null, { behavior: "immediate" });
|
|
27491
|
+
}
|
|
27492
|
+
static async retryBatch(batchId, scope = {}) {
|
|
27493
|
+
const sqlite2 = getSqlite();
|
|
27494
|
+
const scopeFilter = scope.cwd === void 0 ? "" : "AND candidate.cwd = ?";
|
|
27495
|
+
const parameters = scope.cwd === void 0 ? [batchId] : [batchId, scope.cwd];
|
|
27496
|
+
return db.transaction(() => sqlite2.query(`
|
|
27497
|
+
WITH RECURSIVE
|
|
27498
|
+
candidate(id, cwd, depends_on) AS MATERIALIZED (
|
|
27499
|
+
SELECT candidate.id, candidate.cwd, candidate.depends_on
|
|
27500
|
+
FROM tasks AS candidate
|
|
27501
|
+
WHERE candidate.batch_id = ?
|
|
27502
|
+
AND candidate.status IN ('failed', 'dead_letter')
|
|
27503
|
+
${scopeFilter}
|
|
27504
|
+
),
|
|
27505
|
+
retryable(id, cwd, depends_on) AS (
|
|
27506
|
+
SELECT candidate.id,
|
|
27507
|
+
candidate.cwd,
|
|
27508
|
+
candidate.depends_on
|
|
27509
|
+
FROM candidate
|
|
27510
|
+
WHERE candidate.depends_on IS NULL
|
|
27511
|
+
OR (
|
|
27512
|
+
NOT EXISTS (
|
|
27513
|
+
SELECT 1 FROM candidate AS internal_parent
|
|
27514
|
+
WHERE internal_parent.id = candidate.depends_on
|
|
27515
|
+
)
|
|
27516
|
+
AND EXISTS (
|
|
27517
|
+
SELECT 1 FROM tasks AS external_parent
|
|
27518
|
+
WHERE external_parent.id = candidate.depends_on
|
|
27519
|
+
AND external_parent.cwd IS candidate.cwd
|
|
27520
|
+
AND (
|
|
27521
|
+
external_parent.status IN ('pending', 'running', 'done')
|
|
27522
|
+
OR (
|
|
27523
|
+
external_parent.status = 'failed'
|
|
27524
|
+
AND external_parent.retry_count <= external_parent.max_retries
|
|
27525
|
+
)
|
|
27526
|
+
)
|
|
27527
|
+
)
|
|
27528
|
+
)
|
|
27529
|
+
UNION
|
|
27530
|
+
SELECT child.id,
|
|
27531
|
+
child.cwd,
|
|
27532
|
+
child.depends_on
|
|
27533
|
+
FROM candidate AS child
|
|
27534
|
+
INNER JOIN retryable AS parent
|
|
27535
|
+
ON child.depends_on = parent.id
|
|
27536
|
+
AND child.cwd IS parent.cwd
|
|
27537
|
+
)
|
|
27538
|
+
UPDATE tasks
|
|
27539
|
+
SET status = 'pending',
|
|
27540
|
+
started_at = NULL,
|
|
27541
|
+
finished_at = NULL,
|
|
27542
|
+
retry_after = NULL,
|
|
27543
|
+
retry_count = 0
|
|
27544
|
+
WHERE id IN (SELECT id FROM retryable)
|
|
27545
|
+
`).run(...parameters).changes, { behavior: "immediate" });
|
|
27186
27546
|
}
|
|
27187
27547
|
static async getById(id, scope = {}) {
|
|
27188
27548
|
const conditions = [eq(tasks2.id, id), ...this.buildScopeWhere(scope)];
|
|
@@ -27247,23 +27607,129 @@ var TaskService = class {
|
|
|
27247
27607
|
return stats;
|
|
27248
27608
|
}
|
|
27249
27609
|
static async delete(id, scope = {}) {
|
|
27250
|
-
const conditions = [
|
|
27251
|
-
|
|
27252
|
-
|
|
27253
|
-
|
|
27610
|
+
const conditions = [
|
|
27611
|
+
eq(tasks2.id, id),
|
|
27612
|
+
sql`${tasks2.status} <> 'running'`,
|
|
27613
|
+
sql`NOT EXISTS (
|
|
27614
|
+
SELECT 1 FROM ${taskRuns2}
|
|
27615
|
+
WHERE ${taskRuns2.taskId} = ${tasks2.id}
|
|
27616
|
+
AND ${taskRuns2.status} = 'running'
|
|
27617
|
+
)`,
|
|
27618
|
+
hasNoExecutableDependents(),
|
|
27619
|
+
...this.buildScopeWhere(scope)
|
|
27620
|
+
];
|
|
27254
27621
|
const result = await db.delete(tasks2).where(and(...conditions)).returning();
|
|
27255
|
-
|
|
27622
|
+
if (result.length > 0) {
|
|
27623
|
+
await db.delete(taskRuns2).where(eq(taskRuns2.taskId, id));
|
|
27624
|
+
return true;
|
|
27625
|
+
}
|
|
27626
|
+
if (!await this.getById(id, scope)) return false;
|
|
27627
|
+
const dependent = await db.select({ id: tasks2.id }).from(tasks2).where(and(
|
|
27628
|
+
eq(tasks2.dependsOn, id),
|
|
27629
|
+
sql`${tasks2.status} IN ('pending', 'running', 'failed', 'dead_letter')`
|
|
27630
|
+
)).orderBy(asc(tasks2.id)).limit(1);
|
|
27631
|
+
if (dependent[0]) {
|
|
27632
|
+
throw new TaskDeletionConflictError(
|
|
27633
|
+
`\u4EFB\u52A1 #${id} \u4ECD\u88AB\u53EF\u6267\u884C\u4EFB\u52A1 #${dependent[0].id} \u4F9D\u8D56\uFF0C\u8BF7\u5148\u5904\u7406\u4F9D\u8D56\u4EFB\u52A1`
|
|
27634
|
+
);
|
|
27635
|
+
}
|
|
27636
|
+
throw new TaskDeletionConflictError(
|
|
27637
|
+
`\u4EFB\u52A1 #${id} \u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148\u53D6\u6D88\u4EFB\u52A1\u5E76\u7B49\u5F85\u6267\u884C\u8FDB\u7A0B\u9000\u51FA`
|
|
27638
|
+
);
|
|
27256
27639
|
}
|
|
27257
|
-
static async deleteOlderThan(retentionDays) {
|
|
27640
|
+
static async deleteOlderThan(retentionDays, shouldStop = () => false) {
|
|
27258
27641
|
const cutoffSec = Math.floor(Date.now() / 1e3) - retentionDays * 86400;
|
|
27259
|
-
const
|
|
27260
|
-
|
|
27261
|
-
|
|
27262
|
-
|
|
27263
|
-
|
|
27264
|
-
|
|
27265
|
-
|
|
27266
|
-
|
|
27642
|
+
const batchSize = 500;
|
|
27643
|
+
const sqlite2 = getSqlite();
|
|
27644
|
+
cleanupInvocation += 1;
|
|
27645
|
+
const candidateTable = `cleanup_candidates_${process.pid}_${cleanupInvocation}`;
|
|
27646
|
+
let deletedTotal = 0;
|
|
27647
|
+
sqlite2.exec(`
|
|
27648
|
+
CREATE TEMP TABLE ${candidateTable} (
|
|
27649
|
+
id INTEGER NOT NULL PRIMARY KEY
|
|
27650
|
+
) WITHOUT ROWID;
|
|
27651
|
+
`);
|
|
27652
|
+
try {
|
|
27653
|
+
let ceilingId = null;
|
|
27654
|
+
while (true) {
|
|
27655
|
+
if (shouldStop()) return deletedTotal;
|
|
27656
|
+
const batch = db.transaction(() => {
|
|
27657
|
+
sqlite2.query(`DELETE FROM ${candidateTable}`).run();
|
|
27658
|
+
const ceilingPredicate = ceilingId == null ? "" : "AND candidate.id < ?";
|
|
27659
|
+
const rawCandidateStatement = sqlite2.query(`
|
|
27660
|
+
INSERT INTO ${candidateTable}(id)
|
|
27661
|
+
SELECT candidate.id
|
|
27662
|
+
FROM tasks AS candidate NOT INDEXED
|
|
27663
|
+
WHERE candidate.status IN ('done', 'dead_letter', 'cancelled')
|
|
27664
|
+
AND candidate.finished_at IS NOT NULL
|
|
27665
|
+
AND candidate.finished_at < ?
|
|
27666
|
+
${ceilingPredicate}
|
|
27667
|
+
AND NOT EXISTS (
|
|
27668
|
+
SELECT 1 FROM task_runs AS active_run
|
|
27669
|
+
WHERE active_run.task_id = candidate.id
|
|
27670
|
+
AND active_run.status = 'running'
|
|
27671
|
+
)
|
|
27672
|
+
ORDER BY candidate.id DESC
|
|
27673
|
+
LIMIT ?
|
|
27674
|
+
`);
|
|
27675
|
+
const rawCount = ceilingId == null ? rawCandidateStatement.run(cutoffSec, batchSize).changes : rawCandidateStatement.run(cutoffSec, ceilingId, batchSize).changes;
|
|
27676
|
+
if (rawCount === 0) return { deleted: 0, nextCeilingId: null };
|
|
27677
|
+
const rawPage = sqlite2.query(`
|
|
27678
|
+
SELECT min(id) AS nextCeilingId FROM ${candidateTable}
|
|
27679
|
+
`).get();
|
|
27680
|
+
sqlite2.query(`
|
|
27681
|
+
DELETE FROM ${candidateTable}
|
|
27682
|
+
WHERE EXISTS (
|
|
27683
|
+
SELECT 1 FROM tasks AS anomalous
|
|
27684
|
+
WHERE anomalous.id = ${candidateTable}.id
|
|
27685
|
+
AND anomalous.depends_on IS NOT NULL
|
|
27686
|
+
AND anomalous.depends_on >= anomalous.id
|
|
27687
|
+
)
|
|
27688
|
+
`).run();
|
|
27689
|
+
while (true) {
|
|
27690
|
+
const pruned = sqlite2.query(`
|
|
27691
|
+
DELETE FROM ${candidateTable}
|
|
27692
|
+
WHERE EXISTS (
|
|
27693
|
+
SELECT 1 FROM tasks AS dependent_task
|
|
27694
|
+
WHERE dependent_task.depends_on = ${candidateTable}.id
|
|
27695
|
+
AND NOT EXISTS (
|
|
27696
|
+
SELECT 1 FROM ${candidateTable} AS selected_dependent
|
|
27697
|
+
WHERE selected_dependent.id = dependent_task.id
|
|
27698
|
+
)
|
|
27699
|
+
)
|
|
27700
|
+
`).run().changes;
|
|
27701
|
+
if (pruned === 0) break;
|
|
27702
|
+
}
|
|
27703
|
+
const selected = sqlite2.query(`
|
|
27704
|
+
SELECT count(*) AS count FROM ${candidateTable}
|
|
27705
|
+
`).get();
|
|
27706
|
+
if (selected.count === 0) {
|
|
27707
|
+
return { deleted: 0, nextCeilingId: rawPage.nextCeilingId };
|
|
27708
|
+
}
|
|
27709
|
+
sqlite2.query(`
|
|
27710
|
+
DELETE FROM tasks
|
|
27711
|
+
WHERE id IN (SELECT id FROM ${candidateTable})
|
|
27712
|
+
`).run();
|
|
27713
|
+
const remaining = sqlite2.query(`
|
|
27714
|
+
SELECT count(*) AS count
|
|
27715
|
+
FROM tasks
|
|
27716
|
+
WHERE id IN (SELECT id FROM ${candidateTable})
|
|
27717
|
+
`).get();
|
|
27718
|
+
if (remaining.count !== 0) {
|
|
27719
|
+
throw new Error("\u5386\u53F2\u6E05\u7406\u5019\u9009\u5728\u540C\u4E00\u5199\u4E8B\u52A1\u5185\u53D1\u751F\u6F02\u79FB\uFF0C\u5DF2\u56DE\u6EDA\u672C\u6279\u5220\u9664");
|
|
27720
|
+
}
|
|
27721
|
+
return { deleted: selected.count, nextCeilingId: rawPage.nextCeilingId };
|
|
27722
|
+
}, { behavior: "immediate" });
|
|
27723
|
+
if (batch.nextCeilingId == null) break;
|
|
27724
|
+
ceilingId = batch.nextCeilingId;
|
|
27725
|
+
deletedTotal += batch.deleted;
|
|
27726
|
+
await Bun.sleep(0);
|
|
27727
|
+
if (shouldStop()) return deletedTotal;
|
|
27728
|
+
}
|
|
27729
|
+
return deletedTotal;
|
|
27730
|
+
} finally {
|
|
27731
|
+
sqlite2.exec(`DROP TABLE IF EXISTS ${candidateTable}`);
|
|
27732
|
+
}
|
|
27267
27733
|
}
|
|
27268
27734
|
};
|
|
27269
27735
|
|
|
@@ -27294,19 +27760,17 @@ var TaskTemplateService = class {
|
|
|
27294
27760
|
static async create(data) {
|
|
27295
27761
|
this.validate(data);
|
|
27296
27762
|
const now = Date.now();
|
|
27297
|
-
const
|
|
27298
|
-
|
|
27299
|
-
|
|
27300
|
-
|
|
27301
|
-
|
|
27302
|
-
|
|
27303
|
-
|
|
27304
|
-
|
|
27305
|
-
|
|
27306
|
-
|
|
27307
|
-
|
|
27308
|
-
}
|
|
27309
|
-
return tmpl;
|
|
27763
|
+
const nextRunAt = data.nextRunAt ?? this.calculateNextRunAt(
|
|
27764
|
+
data.scheduleType,
|
|
27765
|
+
{
|
|
27766
|
+
cronExpr: data.cronExpr ?? null,
|
|
27767
|
+
intervalMs: data.intervalMs ?? null,
|
|
27768
|
+
runAt: data.runAt ?? null
|
|
27769
|
+
},
|
|
27770
|
+
now
|
|
27771
|
+
);
|
|
27772
|
+
const result = await db.insert(taskTemplates2).values({ ...data, nextRunAt, createdAt: now, updatedAt: now }).returning();
|
|
27773
|
+
return result[0];
|
|
27310
27774
|
}
|
|
27311
27775
|
static validate(data) {
|
|
27312
27776
|
if (!data.name.trim()) throw new Error("name \u4E0D\u80FD\u4E3A\u7A7A");
|
|
@@ -27346,8 +27810,18 @@ var TaskTemplateService = class {
|
|
|
27346
27810
|
return result[0] || null;
|
|
27347
27811
|
}
|
|
27348
27812
|
static async enable(id) {
|
|
27349
|
-
|
|
27350
|
-
|
|
27813
|
+
return db.transaction((tx) => {
|
|
27814
|
+
const template = tx.select().from(taskTemplates2).where(eq(taskTemplates2.id, id)).limit(1).get();
|
|
27815
|
+
if (!template) return null;
|
|
27816
|
+
const nextRunAt = template.nextRunAt ?? this.calculateNextRunAt(
|
|
27817
|
+
template.scheduleType,
|
|
27818
|
+
template
|
|
27819
|
+
);
|
|
27820
|
+
if (nextRunAt == null) {
|
|
27821
|
+
throw new Error(`\u6A21\u677F #${id} \u65E0\u6CD5\u8BA1\u7B97\u4E0B\u4E00\u6B21\u6267\u884C\u65F6\u95F4\uFF0C\u5DF2\u4FDD\u6301\u7981\u7528`);
|
|
27822
|
+
}
|
|
27823
|
+
return tx.update(taskTemplates2).set({ enabled: true, nextRunAt, updatedAt: Date.now() }).where(eq(taskTemplates2.id, id)).returning().get() ?? null;
|
|
27824
|
+
}, { behavior: "immediate" });
|
|
27351
27825
|
}
|
|
27352
27826
|
static async disable(id) {
|
|
27353
27827
|
const result = await db.update(taskTemplates2).set({ enabled: false, updatedAt: Date.now() }).where(eq(taskTemplates2.id, id)).returning();
|
|
@@ -27357,6 +27831,47 @@ var TaskTemplateService = class {
|
|
|
27357
27831
|
const result = await db.delete(taskTemplates2).where(eq(taskTemplates2.id, id)).returning();
|
|
27358
27832
|
return result.length > 0;
|
|
27359
27833
|
}
|
|
27834
|
+
static async deleteExpiredDelayed(retentionDays, shouldStop = () => false) {
|
|
27835
|
+
const cutoffMs = Date.now() - retentionDays * 864e5;
|
|
27836
|
+
const batchSize = 500;
|
|
27837
|
+
let deletedTotal = 0;
|
|
27838
|
+
while (!shouldStop()) {
|
|
27839
|
+
const deleted = db.transaction((tx) => tx.delete(taskTemplates2).where(and(
|
|
27840
|
+
sql`${taskTemplates2.id} IN (
|
|
27841
|
+
SELECT candidate.id
|
|
27842
|
+
FROM task_templates AS candidate
|
|
27843
|
+
WHERE candidate.schedule_type = 'delayed'
|
|
27844
|
+
AND candidate.enabled = 0
|
|
27845
|
+
AND candidate.last_run_at IS NOT NULL
|
|
27846
|
+
AND candidate.last_run_at < ${cutoffMs}
|
|
27847
|
+
AND NOT EXISTS (
|
|
27848
|
+
SELECT 1 FROM tasks AS active_task
|
|
27849
|
+
WHERE active_task.template_id = candidate.id
|
|
27850
|
+
AND (
|
|
27851
|
+
active_task.status IN ('pending', 'running')
|
|
27852
|
+
OR (
|
|
27853
|
+
active_task.status = 'failed'
|
|
27854
|
+
AND active_task.retry_count <= active_task.max_retries
|
|
27855
|
+
)
|
|
27856
|
+
)
|
|
27857
|
+
)
|
|
27858
|
+
AND NOT EXISTS (
|
|
27859
|
+
SELECT 1
|
|
27860
|
+
FROM task_runs AS active_run
|
|
27861
|
+
INNER JOIN tasks AS run_task ON run_task.id = active_run.task_id
|
|
27862
|
+
WHERE run_task.template_id = candidate.id
|
|
27863
|
+
AND active_run.status = 'running'
|
|
27864
|
+
)
|
|
27865
|
+
ORDER BY candidate.last_run_at, candidate.id
|
|
27866
|
+
LIMIT ${batchSize}
|
|
27867
|
+
)`
|
|
27868
|
+
)).returning({ id: taskTemplates2.id }).all().length, { behavior: "immediate" });
|
|
27869
|
+
if (deleted === 0) break;
|
|
27870
|
+
deletedTotal += deleted;
|
|
27871
|
+
await Bun.sleep(0);
|
|
27872
|
+
}
|
|
27873
|
+
return deletedTotal;
|
|
27874
|
+
}
|
|
27360
27875
|
static calculateNextRunAt(scheduleType, template, afterMs) {
|
|
27361
27876
|
const base = afterMs ?? Date.now();
|
|
27362
27877
|
switch (scheduleType) {
|
|
@@ -27406,13 +27921,73 @@ function parseDuration(input) {
|
|
|
27406
27921
|
return null;
|
|
27407
27922
|
}
|
|
27408
27923
|
|
|
27924
|
+
// src/core/semver.ts
|
|
27925
|
+
function parseSemanticVersion(version3) {
|
|
27926
|
+
const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.exec(version3);
|
|
27927
|
+
if (!match) return null;
|
|
27928
|
+
const prerelease = match[4]?.split(".") ?? [];
|
|
27929
|
+
if (prerelease.some((identifier) => /^\d+$/.test(identifier) && identifier.length > 1 && identifier.startsWith("0"))) {
|
|
27930
|
+
return null;
|
|
27931
|
+
}
|
|
27932
|
+
return {
|
|
27933
|
+
major: Number(match[1]),
|
|
27934
|
+
minor: Number(match[2]),
|
|
27935
|
+
patch: Number(match[3]),
|
|
27936
|
+
prerelease
|
|
27937
|
+
};
|
|
27938
|
+
}
|
|
27939
|
+
function compareSemanticVersions(left, right) {
|
|
27940
|
+
const a = parseSemanticVersion(left);
|
|
27941
|
+
const b = parseSemanticVersion(right);
|
|
27942
|
+
if (!a || !b) return null;
|
|
27943
|
+
for (const key of ["major", "minor", "patch"]) {
|
|
27944
|
+
if (a[key] !== b[key]) return a[key] - b[key];
|
|
27945
|
+
}
|
|
27946
|
+
if (a.prerelease.length === 0 || b.prerelease.length === 0) {
|
|
27947
|
+
if (a.prerelease.length === b.prerelease.length) return 0;
|
|
27948
|
+
return a.prerelease.length === 0 ? 1 : -1;
|
|
27949
|
+
}
|
|
27950
|
+
const identifiers = Math.max(a.prerelease.length, b.prerelease.length);
|
|
27951
|
+
for (let index2 = 0; index2 < identifiers; index2 += 1) {
|
|
27952
|
+
const leftIdentifier = a.prerelease[index2];
|
|
27953
|
+
const rightIdentifier = b.prerelease[index2];
|
|
27954
|
+
if (leftIdentifier === void 0 || rightIdentifier === void 0) {
|
|
27955
|
+
return leftIdentifier === rightIdentifier ? 0 : leftIdentifier === void 0 ? -1 : 1;
|
|
27956
|
+
}
|
|
27957
|
+
if (leftIdentifier === rightIdentifier) continue;
|
|
27958
|
+
const leftNumeric = /^\d+$/.test(leftIdentifier);
|
|
27959
|
+
const rightNumeric = /^\d+$/.test(rightIdentifier);
|
|
27960
|
+
if (leftNumeric && rightNumeric) return Number(leftIdentifier) - Number(rightIdentifier);
|
|
27961
|
+
if (leftNumeric !== rightNumeric) return leftNumeric ? -1 : 1;
|
|
27962
|
+
return leftIdentifier < rightIdentifier ? -1 : 1;
|
|
27963
|
+
}
|
|
27964
|
+
return 0;
|
|
27965
|
+
}
|
|
27966
|
+
function isSemanticVersion(version3) {
|
|
27967
|
+
return parseSemanticVersion(version3) !== null;
|
|
27968
|
+
}
|
|
27969
|
+
|
|
27970
|
+
// src/core/launch-protocol.ts
|
|
27971
|
+
var MANAGED_RUN_ENV = "SUPERTASK_MANAGED_RUN";
|
|
27972
|
+
var MANAGED_RUN_ENV_VALUE = "1";
|
|
27973
|
+
|
|
27409
27974
|
// src/daemon/pm2.ts
|
|
27410
27975
|
import { execSync, spawnSync } from "child_process";
|
|
27411
|
-
import {
|
|
27412
|
-
|
|
27413
|
-
|
|
27414
|
-
|
|
27415
|
-
|
|
27976
|
+
import {
|
|
27977
|
+
accessSync,
|
|
27978
|
+
chmodSync,
|
|
27979
|
+
constants,
|
|
27980
|
+
existsSync as existsSync4,
|
|
27981
|
+
mkdirSync as mkdirSync3,
|
|
27982
|
+
readFileSync as readFileSync3,
|
|
27983
|
+
rmSync,
|
|
27984
|
+
statSync,
|
|
27985
|
+
writeFileSync
|
|
27986
|
+
} from "fs";
|
|
27987
|
+
import { homedir as homedir3, userInfo } from "os";
|
|
27988
|
+
import { delimiter, dirname as dirname4, isAbsolute, join as join4, resolve } from "path";
|
|
27989
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
27990
|
+
import { Database as Database4 } from "bun:sqlite";
|
|
27416
27991
|
|
|
27417
27992
|
// src/gateway/config.ts
|
|
27418
27993
|
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
@@ -27529,80 +28104,223 @@ function loadConfig(path = getConfigPath()) {
|
|
|
27529
28104
|
}
|
|
27530
28105
|
}
|
|
27531
28106
|
|
|
27532
|
-
// src/
|
|
27533
|
-
|
|
27534
|
-
|
|
27535
|
-
|
|
28107
|
+
// src/core/package-version.ts
|
|
28108
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
28109
|
+
import { dirname as dirname2, join as join3 } from "path";
|
|
28110
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
27536
28111
|
function getPackageVersion() {
|
|
27537
|
-
|
|
27538
|
-
|
|
28112
|
+
let directory = dirname2(fileURLToPath2(import.meta.url));
|
|
28113
|
+
for (let depth = 0; depth < 5; depth += 1) {
|
|
28114
|
+
const packagePath = join3(directory, "package.json");
|
|
28115
|
+
if (existsSync3(packagePath)) {
|
|
28116
|
+
try {
|
|
28117
|
+
const pkg = JSON.parse(readFileSync2(packagePath, "utf8"));
|
|
28118
|
+
if (typeof pkg.version === "string") return pkg.version;
|
|
28119
|
+
} catch {
|
|
28120
|
+
return "0.0.0";
|
|
28121
|
+
}
|
|
28122
|
+
}
|
|
28123
|
+
directory = dirname2(directory);
|
|
28124
|
+
}
|
|
28125
|
+
return "0.0.0";
|
|
28126
|
+
}
|
|
28127
|
+
|
|
28128
|
+
// src/daemon/management-lock.ts
|
|
28129
|
+
import { Database as Database3 } from "bun:sqlite";
|
|
28130
|
+
import { mkdirSync as mkdirSync2 } from "fs";
|
|
28131
|
+
import { dirname as dirname3 } from "path";
|
|
28132
|
+
var ManagementLockBusyError = class extends Error {
|
|
28133
|
+
constructor() {
|
|
28134
|
+
super("Gateway management lock is busy");
|
|
28135
|
+
this.name = "ManagementLockBusyError";
|
|
28136
|
+
}
|
|
28137
|
+
};
|
|
28138
|
+
function isBusyError(error45) {
|
|
28139
|
+
if (!(error45 instanceof Error)) return false;
|
|
28140
|
+
const code = error45.code;
|
|
28141
|
+
return code === "SQLITE_BUSY" || /database is locked/i.test(error45.message);
|
|
28142
|
+
}
|
|
28143
|
+
function withExclusiveManagementLock(path, timeoutMs, action) {
|
|
28144
|
+
mkdirSync2(dirname3(path), { recursive: true });
|
|
28145
|
+
const database = new Database3(path, { create: true });
|
|
28146
|
+
const normalizedTimeout = Number.isFinite(timeoutMs) && timeoutMs >= 0 ? Math.floor(timeoutMs) : 0;
|
|
28147
|
+
database.exec(`PRAGMA busy_timeout = ${normalizedTimeout}`);
|
|
27539
28148
|
try {
|
|
27540
|
-
|
|
27541
|
-
|
|
27542
|
-
|
|
27543
|
-
|
|
27544
|
-
|
|
28149
|
+
try {
|
|
28150
|
+
database.exec("BEGIN IMMEDIATE");
|
|
28151
|
+
} catch (error45) {
|
|
28152
|
+
if (isBusyError(error45)) throw new ManagementLockBusyError();
|
|
28153
|
+
throw error45;
|
|
28154
|
+
}
|
|
28155
|
+
try {
|
|
28156
|
+
const result = action();
|
|
28157
|
+
database.exec("COMMIT");
|
|
28158
|
+
return result;
|
|
28159
|
+
} catch (error45) {
|
|
28160
|
+
try {
|
|
28161
|
+
database.exec("ROLLBACK");
|
|
28162
|
+
} catch {
|
|
28163
|
+
}
|
|
28164
|
+
throw error45;
|
|
28165
|
+
}
|
|
28166
|
+
} finally {
|
|
28167
|
+
database.close();
|
|
27545
28168
|
}
|
|
27546
28169
|
}
|
|
28170
|
+
|
|
28171
|
+
// src/daemon/pm2.ts
|
|
28172
|
+
var __dirname = dirname4(fileURLToPath3(import.meta.url));
|
|
28173
|
+
var PROCESS_NAME = "supertask-gateway";
|
|
28174
|
+
var MAC_LAUNCH_AGENT_LABEL = "com.supertask.pm2-resurrect";
|
|
28175
|
+
var GATEWAY_LOCK_STALE_MS = 3e4;
|
|
27547
28176
|
function resolveGatewayEntry() {
|
|
27548
28177
|
const override = process.env.SUPERTASK_GATEWAY_ENTRY;
|
|
27549
28178
|
if (override) {
|
|
27550
|
-
if (!
|
|
28179
|
+
if (!existsSync4(override)) throw new Error(`[supertask] Gateway entry not found: ${override}`);
|
|
27551
28180
|
return override;
|
|
27552
28181
|
}
|
|
27553
28182
|
const candidates = [
|
|
27554
|
-
|
|
27555
|
-
|
|
28183
|
+
join4(__dirname, "../gateway/index.js"),
|
|
28184
|
+
join4(__dirname, "../gateway/index.ts")
|
|
27556
28185
|
];
|
|
27557
|
-
const entry = candidates.find((candidate) =>
|
|
28186
|
+
const entry = candidates.find((candidate) => existsSync4(candidate));
|
|
27558
28187
|
if (!entry) throw new Error(`[supertask] Gateway entry not found. Checked: ${candidates.join(", ")}`);
|
|
27559
28188
|
return entry;
|
|
27560
28189
|
}
|
|
27561
|
-
function
|
|
27562
|
-
return
|
|
28190
|
+
function runtimeHome(env) {
|
|
28191
|
+
return resolve(env.HOME || homedir3());
|
|
28192
|
+
}
|
|
28193
|
+
function runtimePath(value, cwd) {
|
|
28194
|
+
return resolve(cwd, value);
|
|
27563
28195
|
}
|
|
27564
|
-
function
|
|
28196
|
+
function versionFile(env = process.env, cwd = process.cwd()) {
|
|
28197
|
+
return env.SUPERTASK_VERSION_FILE ? runtimePath(env.SUPERTASK_VERSION_FILE, cwd) : join4(runtimeHome(env), ".local/share/opencode/supertask-gateway-version");
|
|
28198
|
+
}
|
|
28199
|
+
function getRunningVersion(env = process.env, cwd = process.cwd()) {
|
|
27565
28200
|
try {
|
|
27566
|
-
const path = versionFile();
|
|
27567
|
-
if (!
|
|
27568
|
-
return
|
|
28201
|
+
const path = versionFile(env, cwd);
|
|
28202
|
+
if (!existsSync4(path)) return null;
|
|
28203
|
+
return readFileSync3(path, "utf-8").trim() || null;
|
|
27569
28204
|
} catch {
|
|
27570
28205
|
return null;
|
|
27571
28206
|
}
|
|
27572
28207
|
}
|
|
27573
|
-
function
|
|
27574
|
-
|
|
27575
|
-
|
|
28208
|
+
function resolveRuntimeExecutable(command, env, cwd) {
|
|
28209
|
+
if (isAbsolute(command) || command.includes("/")) return runtimePath(command, cwd);
|
|
28210
|
+
for (const entry of (env.PATH ?? "").split(delimiter)) {
|
|
28211
|
+
if (!entry) continue;
|
|
28212
|
+
const candidate = resolve(cwd, entry, command);
|
|
28213
|
+
try {
|
|
28214
|
+
accessSync(candidate, constants.X_OK);
|
|
28215
|
+
return candidate;
|
|
28216
|
+
} catch {
|
|
28217
|
+
}
|
|
28218
|
+
}
|
|
28219
|
+
return command;
|
|
28220
|
+
}
|
|
28221
|
+
function runtimeScope(runtime) {
|
|
28222
|
+
const { cwd, env } = runtime;
|
|
28223
|
+
const home = runtimeHome(env);
|
|
28224
|
+
return {
|
|
28225
|
+
cwd: resolve(cwd),
|
|
28226
|
+
databasePath: env.SUPERTASK_DB_PATH ? runtimePath(env.SUPERTASK_DB_PATH, cwd) : join4(home, ".local/share/opencode/tasks.db"),
|
|
28227
|
+
configPath: env.SUPERTASK_CONFIG_PATH ? runtimePath(env.SUPERTASK_CONFIG_PATH, cwd) : join4(home, ".config/opencode/supertask.json"),
|
|
28228
|
+
opencodePath: resolveRuntimeExecutable(env.SUPERTASK_OPENCODE_BIN ?? "opencode", env, cwd),
|
|
28229
|
+
home,
|
|
28230
|
+
pm2Home: env.PM2_HOME ? runtimePath(env.PM2_HOME, cwd) : join4(home, ".pm2"),
|
|
28231
|
+
managementLockPath: canonicalManagementLockPath(env, cwd)
|
|
28232
|
+
};
|
|
28233
|
+
}
|
|
28234
|
+
function scopesMatch(left, right) {
|
|
28235
|
+
return left.databasePath === right.databasePath && left.configPath === right.configPath && left.opencodePath === right.opencodePath && left.home === right.home && left.pm2Home === right.pm2Home && left.managementLockPath === right.managementLockPath;
|
|
28236
|
+
}
|
|
28237
|
+
function currentScope() {
|
|
28238
|
+
return runtimeScope({ cwd: process.cwd(), env: process.env });
|
|
28239
|
+
}
|
|
28240
|
+
function writeRunningVersion(version3, env = process.env, cwd = process.cwd()) {
|
|
28241
|
+
const path = versionFile(env, cwd);
|
|
28242
|
+
mkdirSync3(dirname4(path), { recursive: true });
|
|
27576
28243
|
writeFileSync(path, version3, "utf-8");
|
|
27577
28244
|
}
|
|
27578
|
-
function pm2Bin() {
|
|
27579
|
-
return
|
|
28245
|
+
function pm2Bin(env = process.env) {
|
|
28246
|
+
return env.SUPERTASK_PM2_BIN ?? (process.platform === "win32" ? "pm2.cmd" : "pm2");
|
|
27580
28247
|
}
|
|
27581
|
-
function
|
|
27582
|
-
const
|
|
28248
|
+
function pm2CommandTimeoutMs(env = process.env) {
|
|
28249
|
+
const configured = Number(env.SUPERTASK_PM2_COMMAND_TIMEOUT_MS ?? 15e3);
|
|
28250
|
+
return Number.isFinite(configured) && configured > 0 ? configured : 15e3;
|
|
28251
|
+
}
|
|
28252
|
+
function resolvePm2Bin() {
|
|
28253
|
+
const configured = pm2Bin();
|
|
28254
|
+
if (isAbsolute(configured)) return configured;
|
|
28255
|
+
const result = spawnSync("which", [configured], { encoding: "utf8" });
|
|
28256
|
+
const resolved = result.status === 0 ? result.stdout.trim().split("\n")[0] : "";
|
|
28257
|
+
if (!resolved) throw new Error(`[supertask] \u65E0\u6CD5\u89E3\u6790 pm2 \u53EF\u6267\u884C\u6587\u4EF6: ${configured}`);
|
|
28258
|
+
return resolved;
|
|
28259
|
+
}
|
|
28260
|
+
function launchAgentPath() {
|
|
28261
|
+
return process.env.SUPERTASK_LAUNCH_AGENT_PATH ?? join4(runtimeHome(process.env), "Library/LaunchAgents", `${MAC_LAUNCH_AGENT_LABEL}.plist`);
|
|
28262
|
+
}
|
|
28263
|
+
function xmlUnescape(value) {
|
|
28264
|
+
return value.replaceAll("'", "'").replaceAll(""", '"').replaceAll(">", ">").replaceAll("<", "<").replaceAll("&", "&");
|
|
28265
|
+
}
|
|
28266
|
+
function plistValue(contents, key) {
|
|
28267
|
+
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
28268
|
+
const match = contents.match(new RegExp(`<key>\\s*${escapedKey}\\s*</key>\\s*<string>([^<]*)</string>`));
|
|
28269
|
+
return match?.[1] ? xmlUnescape(match[1]) : null;
|
|
28270
|
+
}
|
|
28271
|
+
function isPm2Installed(env = process.env) {
|
|
28272
|
+
const result = spawnSync(pm2Bin(env), ["--version"], {
|
|
27583
28273
|
stdio: "ignore",
|
|
27584
|
-
|
|
28274
|
+
env,
|
|
28275
|
+
shell: process.platform === "win32",
|
|
28276
|
+
timeout: pm2CommandTimeoutMs(env),
|
|
28277
|
+
killSignal: "SIGKILL"
|
|
27585
28278
|
});
|
|
27586
28279
|
return result.status === 0;
|
|
27587
28280
|
}
|
|
27588
|
-
function
|
|
27589
|
-
const
|
|
28281
|
+
function resolveCommand(command) {
|
|
28282
|
+
const lookup = process.platform === "win32" ? "where" : "which";
|
|
28283
|
+
const result = spawnSync(lookup, [command], { encoding: "utf8" });
|
|
28284
|
+
return result.status === 0 ? result.stdout.trim().split("\n")[0] || null : null;
|
|
28285
|
+
}
|
|
28286
|
+
function npmPreferredEnvironment() {
|
|
28287
|
+
if (process.platform === "win32") return null;
|
|
28288
|
+
const npm = resolveCommand("npm");
|
|
28289
|
+
const node = resolveCommand("node");
|
|
28290
|
+
if (!npm || !node) return null;
|
|
28291
|
+
const command = resolvePm2Bin();
|
|
28292
|
+
const path = [.../* @__PURE__ */ new Set([
|
|
28293
|
+
dirname4(command),
|
|
28294
|
+
dirname4(npm),
|
|
28295
|
+
dirname4(node),
|
|
28296
|
+
"/usr/local/bin",
|
|
28297
|
+
"/usr/bin",
|
|
28298
|
+
"/bin"
|
|
28299
|
+
])].join(delimiter);
|
|
28300
|
+
return { command, env: { ...process.env, PATH: path } };
|
|
28301
|
+
}
|
|
28302
|
+
function pm2Exec(args, options = {}) {
|
|
28303
|
+
const npmEnvironment = options.preferNpm ? npmPreferredEnvironment() : null;
|
|
28304
|
+
const effectiveEnv = options.env ?? npmEnvironment?.env ?? process.env;
|
|
28305
|
+
const result = spawnSync(npmEnvironment?.command ?? pm2Bin(effectiveEnv), args, {
|
|
27590
28306
|
stdio: ["pipe", "pipe", "pipe"],
|
|
27591
28307
|
encoding: "utf-8",
|
|
27592
|
-
env:
|
|
27593
|
-
shell: process.platform === "win32"
|
|
28308
|
+
env: effectiveEnv,
|
|
28309
|
+
shell: process.platform === "win32",
|
|
28310
|
+
timeout: options.timeoutMs ?? pm2CommandTimeoutMs(effectiveEnv),
|
|
28311
|
+
killSignal: "SIGKILL"
|
|
27594
28312
|
});
|
|
27595
28313
|
const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
|
|
27596
28314
|
if (result.error) return { ok: false, output: result.error.message };
|
|
27597
28315
|
return { ok: result.status === 0, output };
|
|
27598
28316
|
}
|
|
27599
|
-
function requirePm2(args, action) {
|
|
27600
|
-
const result = pm2Exec(args);
|
|
28317
|
+
function requirePm2(args, action, options = {}) {
|
|
28318
|
+
const result = pm2Exec(args, options);
|
|
27601
28319
|
if (!result.ok) throw new Error(`[supertask] ${action} failed: ${result.output || "unknown pm2 error"}`);
|
|
27602
28320
|
return result.output;
|
|
27603
28321
|
}
|
|
27604
|
-
function pm2JsonList() {
|
|
27605
|
-
const output = requirePm2(["jlist"], "pm2 jlist");
|
|
28322
|
+
function pm2JsonList(env = process.env) {
|
|
28323
|
+
const output = requirePm2(["jlist"], "pm2 jlist", { env });
|
|
27606
28324
|
try {
|
|
27607
28325
|
const parsed = JSON.parse(output);
|
|
27608
28326
|
if (!Array.isArray(parsed)) throw new Error("result is not an array");
|
|
@@ -27612,15 +28330,69 @@ function pm2JsonList() {
|
|
|
27612
28330
|
throw new Error(`[supertask] Invalid pm2 jlist output: ${message}`);
|
|
27613
28331
|
}
|
|
27614
28332
|
}
|
|
27615
|
-
function
|
|
27616
|
-
|
|
28333
|
+
function gatewayEntryFromProcess(processInfo) {
|
|
28334
|
+
const args = processInfo?.pm2_env?.args ?? processInfo?.args;
|
|
28335
|
+
const candidates = Array.isArray(args) ? [...args].reverse() : typeof args === "string" ? [args] : [];
|
|
28336
|
+
const savedCwd = processInfo?.pm2_env?.pm_cwd;
|
|
28337
|
+
for (const candidate of candidates) {
|
|
28338
|
+
const path = typeof savedCwd === "string" ? runtimePath(candidate, savedCwd) : candidate;
|
|
28339
|
+
if (existsSync4(path)) return resolve(path);
|
|
28340
|
+
}
|
|
28341
|
+
return null;
|
|
27617
28342
|
}
|
|
27618
|
-
function
|
|
27619
|
-
const
|
|
27620
|
-
if (!
|
|
28343
|
+
function gatewayEnvironmentFromProcess(processInfo) {
|
|
28344
|
+
const saved = processInfo?.pm2_env?.env ?? processInfo?.env;
|
|
28345
|
+
if (!saved) return { ...process.env };
|
|
28346
|
+
const env = {};
|
|
28347
|
+
for (const [key, value] of Object.entries(saved)) {
|
|
28348
|
+
if (typeof value === "string") env[key] = value;
|
|
28349
|
+
}
|
|
28350
|
+
return env;
|
|
28351
|
+
}
|
|
28352
|
+
function gatewayRuntimeFromProcess(processInfo) {
|
|
28353
|
+
const gatewayEntry = gatewayEntryFromProcess(processInfo);
|
|
28354
|
+
if (!gatewayEntry) return null;
|
|
28355
|
+
const savedBunPath = processInfo?.pm2_env?.pm_exec_path ?? processInfo?.pm_exec_path;
|
|
28356
|
+
const savedCwd = processInfo?.pm2_env?.pm_cwd;
|
|
28357
|
+
const savedEnv = gatewayEnvironmentFromProcess(processInfo);
|
|
28358
|
+
if (typeof savedBunPath !== "string" || typeof savedCwd !== "string") return null;
|
|
28359
|
+
try {
|
|
28360
|
+
accessSync(savedBunPath, constants.X_OK);
|
|
28361
|
+
if (!statSync(savedCwd).isDirectory()) return null;
|
|
28362
|
+
if (spawnSync(savedBunPath, ["--version"], {
|
|
28363
|
+
stdio: "ignore",
|
|
28364
|
+
env: savedEnv,
|
|
28365
|
+
timeout: pm2CommandTimeoutMs(savedEnv),
|
|
28366
|
+
killSignal: "SIGKILL"
|
|
28367
|
+
}).status !== 0) return null;
|
|
28368
|
+
} catch {
|
|
28369
|
+
return null;
|
|
28370
|
+
}
|
|
28371
|
+
const killTimeout = processInfo?.pm2_env?.kill_timeout ?? processInfo?.kill_timeout;
|
|
28372
|
+
return {
|
|
28373
|
+
gatewayEntry,
|
|
28374
|
+
bunPath: savedBunPath,
|
|
28375
|
+
cwd: resolve(savedCwd),
|
|
28376
|
+
env: savedEnv,
|
|
28377
|
+
killTimeoutMs: Number.isInteger(killTimeout) && killTimeout >= 5e3 ? killTimeout : void 0
|
|
28378
|
+
};
|
|
28379
|
+
}
|
|
28380
|
+
function currentGatewayRuntime(gatewayEntry = resolveGatewayEntry()) {
|
|
28381
|
+
return {
|
|
28382
|
+
gatewayEntry: resolve(gatewayEntry),
|
|
28383
|
+
bunPath: resolve(findBunPath()),
|
|
28384
|
+
cwd: process.cwd(),
|
|
28385
|
+
env: { ...process.env }
|
|
28386
|
+
};
|
|
28387
|
+
}
|
|
28388
|
+
function databasePath(env = process.env, cwd = process.cwd()) {
|
|
28389
|
+
return env.SUPERTASK_DB_PATH ? runtimePath(env.SUPERTASK_DB_PATH, cwd) : join4(runtimeHome(env), ".local/share/opencode/tasks.db");
|
|
28390
|
+
}
|
|
28391
|
+
function isGatewayReady(expectedPid, path = databasePath()) {
|
|
28392
|
+
if (!existsSync4(path)) return false;
|
|
27621
28393
|
let database = null;
|
|
27622
28394
|
try {
|
|
27623
|
-
database = new
|
|
28395
|
+
database = new Database4(path, { readonly: true });
|
|
27624
28396
|
const row = database.query(
|
|
27625
28397
|
"SELECT pid, heartbeat_at, ready_at FROM gateway_lock WHERE id = 1"
|
|
27626
28398
|
).get();
|
|
@@ -27634,18 +28406,34 @@ function isGatewayReady(expectedPid) {
|
|
|
27634
28406
|
database?.close();
|
|
27635
28407
|
}
|
|
27636
28408
|
}
|
|
27637
|
-
function
|
|
27638
|
-
|
|
28409
|
+
function gatewayVersionFromLock(expectedPid, path) {
|
|
28410
|
+
if (!existsSync4(path)) return void 0;
|
|
28411
|
+
let database = null;
|
|
28412
|
+
try {
|
|
28413
|
+
database = new Database4(path, { readonly: true });
|
|
28414
|
+
const row = database.query(
|
|
28415
|
+
"SELECT pid, version FROM gateway_lock WHERE id = 1"
|
|
28416
|
+
).get();
|
|
28417
|
+
if (!row || row.pid !== expectedPid) return void 0;
|
|
28418
|
+
return row.version;
|
|
28419
|
+
} catch {
|
|
28420
|
+
return void 0;
|
|
28421
|
+
} finally {
|
|
28422
|
+
database?.close();
|
|
28423
|
+
}
|
|
28424
|
+
}
|
|
28425
|
+
function readyTimeoutMs(env = process.env) {
|
|
28426
|
+
const value = Number(env.SUPERTASK_GATEWAY_READY_TIMEOUT_MS ?? 3e4);
|
|
27639
28427
|
return Number.isFinite(value) && value > 0 ? value : 3e4;
|
|
27640
28428
|
}
|
|
27641
|
-
function waitForGatewayReady(pid) {
|
|
27642
|
-
const deadline = Date.now() + readyTimeoutMs();
|
|
28429
|
+
function waitForGatewayReady(pid, path = databasePath(), env = process.env) {
|
|
28430
|
+
const deadline = Date.now() + readyTimeoutMs(env);
|
|
27643
28431
|
const sleeper = new Int32Array(new SharedArrayBuffer(4));
|
|
27644
28432
|
while (Date.now() < deadline) {
|
|
27645
|
-
if (isGatewayReady(pid)) return true;
|
|
28433
|
+
if (isGatewayReady(pid, path)) return true;
|
|
27646
28434
|
Atomics.wait(sleeper, 0, 0, 100);
|
|
27647
28435
|
}
|
|
27648
|
-
return isGatewayReady(pid);
|
|
28436
|
+
return isGatewayReady(pid, path);
|
|
27649
28437
|
}
|
|
27650
28438
|
function findBunPath() {
|
|
27651
28439
|
const override = process.env.SUPERTASK_BUN_BIN;
|
|
@@ -27657,12 +28445,64 @@ function findBunPath() {
|
|
|
27657
28445
|
return process.execPath;
|
|
27658
28446
|
}
|
|
27659
28447
|
}
|
|
27660
|
-
function
|
|
27661
|
-
const
|
|
27662
|
-
|
|
28448
|
+
function pm2MaxMemoryRestart(env = process.env) {
|
|
28449
|
+
const value = env.SUPERTASK_PM2_MAX_MEMORY ?? "512M";
|
|
28450
|
+
if (!/^\d+(?:K|M|G)$/i.test(value)) {
|
|
28451
|
+
throw new Error("[supertask] SUPERTASK_PM2_MAX_MEMORY \u5FC5\u987B\u4F7F\u7528 512M / 1G \u8FD9\u7C7B\u683C\u5F0F");
|
|
28452
|
+
}
|
|
28453
|
+
return value;
|
|
28454
|
+
}
|
|
28455
|
+
function gatewayKillTimeoutMs(runtime) {
|
|
28456
|
+
const shutdownGracePeriodMs = loadConfig(runtimeScope(runtime).configPath).worker.shutdownGracePeriodMs;
|
|
28457
|
+
const minimumKillTimeoutMs = shutdownGracePeriodMs + 15e3;
|
|
28458
|
+
const configuredValue = runtime.env.SUPERTASK_PM2_KILL_TIMEOUT_MS;
|
|
28459
|
+
if (configuredValue !== void 0) {
|
|
28460
|
+
const configuredKillTimeout = Number(configuredValue);
|
|
28461
|
+
if (!Number.isInteger(configuredKillTimeout) || configuredKillTimeout < minimumKillTimeoutMs) {
|
|
28462
|
+
throw new Error(
|
|
28463
|
+
`[supertask] SUPERTASK_PM2_KILL_TIMEOUT_MS \u5FC5\u987B\u662F\u81F3\u5C11 ${minimumKillTimeoutMs} \u7684\u6574\u6570\uFF08worker shutdown grace ${shutdownGracePeriodMs}ms + 15000ms \u6536\u5C3E\u4F59\u91CF\uFF09`
|
|
28464
|
+
);
|
|
28465
|
+
}
|
|
28466
|
+
return configuredKillTimeout;
|
|
28467
|
+
}
|
|
28468
|
+
return Math.max(runtime.killTimeoutMs ?? 0, minimumKillTimeoutMs);
|
|
28469
|
+
}
|
|
28470
|
+
function gatewayTerminationCommandTimeoutMs(runtime) {
|
|
28471
|
+
const effectiveKillTimeoutMs = Math.max(
|
|
28472
|
+
runtime.killTimeoutMs ?? 0,
|
|
28473
|
+
gatewayKillTimeoutMs(runtime)
|
|
28474
|
+
);
|
|
28475
|
+
const minimumCommandTimeoutMs = effectiveKillTimeoutMs + 5e3;
|
|
28476
|
+
const configuredValue = runtime.env.SUPERTASK_PM2_COMMAND_TIMEOUT_MS;
|
|
28477
|
+
if (configuredValue !== void 0) {
|
|
28478
|
+
const configuredTimeoutMs = Number(configuredValue);
|
|
28479
|
+
if (!Number.isInteger(configuredTimeoutMs) || configuredTimeoutMs < minimumCommandTimeoutMs) {
|
|
28480
|
+
throw new Error(
|
|
28481
|
+
`[supertask] SUPERTASK_PM2_COMMAND_TIMEOUT_MS \u5FC5\u987B\u662F\u81F3\u5C11 ${minimumCommandTimeoutMs} \u7684\u6574\u6570\uFF08Gateway kill timeout ${effectiveKillTimeoutMs}ms + 5000ms PM2 \u6536\u5C3E\u4F59\u91CF\uFF09`
|
|
28482
|
+
);
|
|
28483
|
+
}
|
|
28484
|
+
return configuredTimeoutMs;
|
|
28485
|
+
}
|
|
28486
|
+
return Math.max(pm2CommandTimeoutMs(runtime.env), minimumCommandTimeoutMs);
|
|
28487
|
+
}
|
|
28488
|
+
function requirePm2Termination(command, action, runtime) {
|
|
28489
|
+
return requirePm2([command, PROCESS_NAME], action, {
|
|
28490
|
+
env: runtime.env,
|
|
28491
|
+
timeoutMs: gatewayTerminationCommandTimeoutMs(runtime)
|
|
28492
|
+
});
|
|
28493
|
+
}
|
|
28494
|
+
function pm2StartGateway(runtime = currentGatewayRuntime()) {
|
|
28495
|
+
try {
|
|
28496
|
+
accessSync(runtime.bunPath, constants.X_OK);
|
|
28497
|
+
accessSync(runtime.gatewayEntry, constants.R_OK);
|
|
28498
|
+
if (!statSync(runtime.cwd).isDirectory()) throw new Error("cwd is not a directory");
|
|
28499
|
+
} catch (error45) {
|
|
28500
|
+
throw new Error(`[supertask] Gateway \u76EE\u6807\u8FD0\u884C\u65F6\u4E0D\u53EF\u7528: ${error45 instanceof Error ? error45.message : String(error45)}`);
|
|
28501
|
+
}
|
|
28502
|
+
const killTimeoutMs = gatewayKillTimeoutMs(runtime);
|
|
27663
28503
|
requirePm2([
|
|
27664
28504
|
"start",
|
|
27665
|
-
|
|
28505
|
+
runtime.bunPath,
|
|
27666
28506
|
"--name",
|
|
27667
28507
|
PROCESS_NAME,
|
|
27668
28508
|
"--interpreter",
|
|
@@ -27671,43 +28511,186 @@ function pm2StartGateway(gatewayEntry = resolveGatewayEntry()) {
|
|
|
27671
28511
|
"5000",
|
|
27672
28512
|
"--max-restarts",
|
|
27673
28513
|
"30",
|
|
28514
|
+
"--max-memory-restart",
|
|
28515
|
+
pm2MaxMemoryRestart(runtime.env),
|
|
27674
28516
|
"--kill-timeout",
|
|
27675
28517
|
String(killTimeoutMs),
|
|
28518
|
+
"--cwd",
|
|
28519
|
+
runtime.cwd,
|
|
27676
28520
|
"--",
|
|
27677
|
-
gatewayEntry
|
|
27678
|
-
], "pm2 start");
|
|
27679
|
-
const started = pm2JsonList().find((item) => item.name === PROCESS_NAME);
|
|
28521
|
+
runtime.gatewayEntry
|
|
28522
|
+
], "pm2 start", { env: runtime.env });
|
|
28523
|
+
const started = pm2JsonList(runtime.env).find((item) => item.name === PROCESS_NAME);
|
|
27680
28524
|
if (started?.pm2_env?.status !== "online") {
|
|
27681
28525
|
throw new Error(`[supertask] Gateway did not become online (status: ${started?.pm2_env?.status ?? "missing"})`);
|
|
27682
28526
|
}
|
|
27683
|
-
|
|
28527
|
+
const observedRuntime = gatewayRuntimeFromProcess(started);
|
|
28528
|
+
if (!observedRuntime || observedRuntime.gatewayEntry !== resolve(runtime.gatewayEntry) || observedRuntime.bunPath !== runtime.bunPath || observedRuntime.cwd !== resolve(runtime.cwd) || !scopesMatch(runtimeScope(observedRuntime), runtimeScope(runtime))) {
|
|
28529
|
+
throw new Error("[supertask] PM2 online \u8BB0\u5F55\u4E0E\u672C\u6B21\u542F\u52A8\u76EE\u6807\u4E0D\u4E00\u81F4\uFF0C\u5DF2\u62D2\u7EDD\u63A5\u7BA1");
|
|
28530
|
+
}
|
|
28531
|
+
if (typeof started.pid !== "number" || !waitForGatewayReady(started.pid, runtimeScope(runtime).databasePath, runtime.env)) {
|
|
27684
28532
|
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
28533
|
}
|
|
27686
28534
|
}
|
|
27687
|
-
function
|
|
27688
|
-
|
|
28535
|
+
function assertRuntimeCanControlPm2(runtime, existing) {
|
|
28536
|
+
if (!existing) return;
|
|
28537
|
+
if (!runtime) {
|
|
28538
|
+
throw new Error(
|
|
28539
|
+
"[supertask] \u65E0\u6CD5\u5B8C\u6574\u9A8C\u8BC1\u5DF2\u6709 Gateway \u7684\u5165\u53E3\u3001Bun \u548C cwd\uFF0C\u5DF2\u62D2\u7EDD\u5220\u9664\u73B0\u6709\u8FDB\u7A0B"
|
|
28540
|
+
);
|
|
28541
|
+
}
|
|
28542
|
+
const probe = pm2Exec(["--version"], { env: runtime.env });
|
|
28543
|
+
if (!probe.ok) {
|
|
28544
|
+
throw new Error(
|
|
28545
|
+
`[supertask] \u4FDD\u5B58\u7684 Gateway \u8FD0\u884C\u73AF\u5883\u65E0\u6CD5\u6267\u884C pm2\uFF0C\u5DF2\u62D2\u7EDD\u5220\u9664\u73B0\u6709\u8FDB\u7A0B\uFF1B\u8BF7\u5148\u4FEE\u590D PATH \u6216 SUPERTASK_PM2_BIN: ${probe.output || "pm2 unavailable"}`
|
|
28546
|
+
);
|
|
28547
|
+
}
|
|
28548
|
+
const observed = pm2JsonList(runtime.env).find((item) => item.name === PROCESS_NAME);
|
|
28549
|
+
const observedRuntime = gatewayRuntimeFromProcess(observed);
|
|
28550
|
+
if (!observed || observed.pid !== existing.pid || !observedRuntime || observedRuntime.gatewayEntry !== runtime.gatewayEntry || observedRuntime.bunPath !== runtime.bunPath || observedRuntime.cwd !== runtime.cwd) {
|
|
28551
|
+
throw new Error("[supertask] \u4FDD\u5B58\u7684 PM2 \u73AF\u5883\u65E0\u6CD5\u91CD\u73B0\u540C\u4E00 Gateway \u8BB0\u5F55\uFF0C\u5DF2\u62D2\u7EDD\u5220\u9664\u73B0\u6709\u8FDB\u7A0B");
|
|
28552
|
+
}
|
|
28553
|
+
}
|
|
28554
|
+
function savePm2State(env = process.env) {
|
|
28555
|
+
requirePm2(["save"], "pm2 save", { env });
|
|
28556
|
+
}
|
|
28557
|
+
function canonicalManagementLockPath(env = process.env, cwd = process.cwd()) {
|
|
28558
|
+
const home = runtimeHome(env);
|
|
28559
|
+
const pm2Home = env.PM2_HOME ? runtimePath(env.PM2_HOME, cwd) : join4(home, ".pm2");
|
|
28560
|
+
return join4(pm2Home, "supertask-gateway.manage.sqlite");
|
|
28561
|
+
}
|
|
28562
|
+
function addLegacyManagementLock(paths, env, cwd) {
|
|
28563
|
+
const value = env?.SUPERTASK_PM2_MANAGEMENT_LOCK;
|
|
28564
|
+
if (typeof value === "string" && value.length > 0) paths.add(runtimePath(value, cwd));
|
|
28565
|
+
}
|
|
28566
|
+
function savedManagementLockPaths() {
|
|
28567
|
+
const canonical = canonicalManagementLockPath();
|
|
28568
|
+
const legacy = /* @__PURE__ */ new Set();
|
|
28569
|
+
addLegacyManagementLock(legacy, process.env, process.cwd());
|
|
28570
|
+
const pm2Home = currentScope().pm2Home;
|
|
28571
|
+
try {
|
|
28572
|
+
const dump = JSON.parse(readFileSync3(join4(pm2Home, "dump.pm2"), "utf8"));
|
|
28573
|
+
if (Array.isArray(dump)) {
|
|
28574
|
+
const gateway = dump.find((item) => item.name === PROCESS_NAME);
|
|
28575
|
+
const cwd = gateway?.pm2_env?.pm_cwd;
|
|
28576
|
+
if (typeof cwd === "string") {
|
|
28577
|
+
addLegacyManagementLock(
|
|
28578
|
+
legacy,
|
|
28579
|
+
gateway?.pm2_env?.env ?? gateway?.env,
|
|
28580
|
+
cwd
|
|
28581
|
+
);
|
|
28582
|
+
}
|
|
28583
|
+
}
|
|
28584
|
+
} catch {
|
|
28585
|
+
}
|
|
28586
|
+
if (process.platform === "darwin") {
|
|
28587
|
+
try {
|
|
28588
|
+
const configured = plistValue(
|
|
28589
|
+
readFileSync3(launchAgentPath(), "utf8"),
|
|
28590
|
+
"SUPERTASK_PM2_MANAGEMENT_LOCK"
|
|
28591
|
+
);
|
|
28592
|
+
if (configured) legacy.add(resolve(configured));
|
|
28593
|
+
} catch {
|
|
28594
|
+
}
|
|
28595
|
+
}
|
|
28596
|
+
if (isPm2Installed()) {
|
|
28597
|
+
const gateway = pm2JsonList().find((item) => item.name === PROCESS_NAME);
|
|
28598
|
+
const cwd = gateway?.pm2_env?.pm_cwd;
|
|
28599
|
+
if (typeof cwd === "string") {
|
|
28600
|
+
addLegacyManagementLock(
|
|
28601
|
+
legacy,
|
|
28602
|
+
gateway?.pm2_env?.env ?? gateway?.env,
|
|
28603
|
+
cwd
|
|
28604
|
+
);
|
|
28605
|
+
}
|
|
28606
|
+
}
|
|
28607
|
+
return [
|
|
28608
|
+
canonical,
|
|
28609
|
+
...[...legacy].filter((path) => path !== canonical).sort()
|
|
28610
|
+
];
|
|
28611
|
+
}
|
|
28612
|
+
function withManagementLocks(paths, timeoutMs, action, index2 = 0) {
|
|
28613
|
+
const path = paths[index2];
|
|
28614
|
+
if (!path) return action();
|
|
28615
|
+
return withExclusiveManagementLock(
|
|
28616
|
+
path,
|
|
28617
|
+
timeoutMs,
|
|
28618
|
+
() => withManagementLocks(paths, timeoutMs, action, index2 + 1)
|
|
28619
|
+
);
|
|
28620
|
+
}
|
|
28621
|
+
function assertMacLaunchAgentPm2HomeMatchesCurrent() {
|
|
28622
|
+
if (process.platform !== "darwin") return;
|
|
28623
|
+
try {
|
|
28624
|
+
const configuredPm2Home = plistValue(
|
|
28625
|
+
readFileSync3(launchAgentPath(), "utf8"),
|
|
28626
|
+
"PM2_HOME"
|
|
28627
|
+
);
|
|
28628
|
+
if (!configuredPm2Home) return;
|
|
28629
|
+
const installedPm2Home = resolve(configuredPm2Home);
|
|
28630
|
+
const activePm2Home = currentScope().pm2Home;
|
|
28631
|
+
if (installedPm2Home !== activePm2Home) {
|
|
28632
|
+
throw new Error(
|
|
28633
|
+
`[supertask] \u5DF2\u5B89\u88C5\u7684 macOS LaunchAgent \u4F7F\u7528 PM2_HOME=${installedPm2Home}\uFF0C\u5F53\u524D CLI \u4F7F\u7528 ${activePm2Home}\uFF1B\u4E3A\u907F\u514D\u4E24\u4E2A PM2 daemon \u540C\u65F6\u7BA1\u7406 Gateway\uFF0C\u5DF2\u62D2\u7EDD\u4FEE\u6539\u3002\u8BF7\u4F7F\u7528 PM2_HOME=${installedPm2Home} \u91CD\u65B0\u6267\u884C\u547D\u4EE4`
|
|
28634
|
+
);
|
|
28635
|
+
}
|
|
28636
|
+
} catch (error45) {
|
|
28637
|
+
if (error45 instanceof Error && error45.message.startsWith("[supertask] \u5DF2\u5B89\u88C5\u7684 macOS LaunchAgent")) {
|
|
28638
|
+
throw error45;
|
|
28639
|
+
}
|
|
28640
|
+
if (error45.code !== "ENOENT") throw error45;
|
|
28641
|
+
}
|
|
28642
|
+
}
|
|
28643
|
+
function withManagementLock(action) {
|
|
28644
|
+
const paths = savedManagementLockPaths();
|
|
28645
|
+
const configuredTimeout = Number(process.env.SUPERTASK_PM2_MANAGEMENT_LOCK_TIMEOUT_MS ?? 15e3);
|
|
28646
|
+
const timeoutMs = Number.isFinite(configuredTimeout) && configuredTimeout > 0 ? configuredTimeout : 15e3;
|
|
28647
|
+
try {
|
|
28648
|
+
return withManagementLocks(paths, timeoutMs, () => {
|
|
28649
|
+
assertMacLaunchAgentPm2HomeMatchesCurrent();
|
|
28650
|
+
return action();
|
|
28651
|
+
});
|
|
28652
|
+
} catch (error45) {
|
|
28653
|
+
if (error45 instanceof ManagementLockBusyError) {
|
|
28654
|
+
throw new Error("[supertask] \u53E6\u4E00\u4E2A Gateway \u7BA1\u7406\u64CD\u4F5C\u4ECD\u5728\u8FDB\u884C\uFF0C\u5DF2\u62D2\u7EDD\u5E76\u53D1\u4FEE\u6539 PM2 \u72B6\u6001");
|
|
28655
|
+
}
|
|
28656
|
+
throw error45;
|
|
28657
|
+
}
|
|
27689
28658
|
}
|
|
27690
28659
|
function upgrade(target) {
|
|
28660
|
+
return withManagementLock(() => upgradeUnlocked(target));
|
|
28661
|
+
}
|
|
28662
|
+
function upgradeUnlocked(target) {
|
|
27691
28663
|
if (!isPm2Installed()) {
|
|
27692
28664
|
throw new Error("[supertask] pm2 is not installed. Run `supertask install` first.");
|
|
27693
28665
|
}
|
|
27694
|
-
const before = getRunningVersion();
|
|
27695
|
-
const oldGatewayEntry = resolveGatewayEntry();
|
|
27696
|
-
const currentVersion = target?.version ?? getPackageVersion();
|
|
27697
28666
|
const existing = pm2JsonList().find((item) => item.name === PROCESS_NAME);
|
|
27698
|
-
|
|
28667
|
+
const oldRuntime = gatewayRuntimeFromProcess(existing) ?? (process.env.SUPERTASK_GATEWAY_ENTRY ? currentGatewayRuntime(resolveGatewayEntry()) : null);
|
|
28668
|
+
const before = getRunningVersion(oldRuntime?.env ?? process.env, oldRuntime?.cwd);
|
|
28669
|
+
const currentVersion = target?.version ?? getPackageVersion();
|
|
28670
|
+
const targetRuntime = oldRuntime ? { ...oldRuntime, gatewayEntry: resolve(target?.gatewayEntry ?? oldRuntime.gatewayEntry) } : currentGatewayRuntime(target?.gatewayEntry ?? resolveGatewayEntry());
|
|
28671
|
+
assertRuntimeCanControlPm2(oldRuntime, existing);
|
|
28672
|
+
gatewayKillTimeoutMs(targetRuntime);
|
|
28673
|
+
gatewayTerminationCommandTimeoutMs(targetRuntime);
|
|
28674
|
+
if (existing) requirePm2Termination("delete", "pm2 delete old Gateway", oldRuntime);
|
|
27699
28675
|
try {
|
|
27700
|
-
pm2StartGateway(
|
|
27701
|
-
|
|
27702
|
-
|
|
28676
|
+
pm2StartGateway(targetRuntime);
|
|
28677
|
+
savePm2State(targetRuntime.env);
|
|
28678
|
+
writeRunningVersion(currentVersion, targetRuntime.env, targetRuntime.cwd);
|
|
27703
28679
|
return { before, after: currentVersion, restarted: true };
|
|
27704
28680
|
} catch (error45) {
|
|
27705
|
-
const failed = pm2JsonList().find((item) => item.name === PROCESS_NAME);
|
|
27706
|
-
if (failed)
|
|
28681
|
+
const failed = pm2JsonList(targetRuntime.env).find((item) => item.name === PROCESS_NAME);
|
|
28682
|
+
if (failed) {
|
|
28683
|
+
requirePm2Termination(
|
|
28684
|
+
"delete",
|
|
28685
|
+
"pm2 delete failed Gateway",
|
|
28686
|
+
gatewayRuntimeFromProcess(failed) ?? targetRuntime
|
|
28687
|
+
);
|
|
28688
|
+
}
|
|
28689
|
+
if (!oldRuntime) throw error45;
|
|
27707
28690
|
try {
|
|
27708
|
-
pm2StartGateway(
|
|
27709
|
-
|
|
27710
|
-
|
|
28691
|
+
pm2StartGateway(oldRuntime);
|
|
28692
|
+
savePm2State(oldRuntime.env);
|
|
28693
|
+
if (before) writeRunningVersion(before, oldRuntime.env, oldRuntime.cwd);
|
|
27711
28694
|
} catch (rollbackError) {
|
|
27712
28695
|
const original = error45 instanceof Error ? error45.message : String(error45);
|
|
27713
28696
|
const rollback = rollbackError instanceof Error ? rollbackError.message : String(rollbackError);
|
|
@@ -27718,50 +28701,84 @@ function upgrade(target) {
|
|
|
27718
28701
|
}
|
|
27719
28702
|
}
|
|
27720
28703
|
function ensureGateway() {
|
|
28704
|
+
return withManagementLock(ensureGatewayUnlocked);
|
|
28705
|
+
}
|
|
28706
|
+
function ensureGatewayUnlocked() {
|
|
27721
28707
|
if (!isPm2Installed()) {
|
|
27722
28708
|
return { ok: false, reason: "pm2-not-installed" };
|
|
27723
28709
|
}
|
|
27724
28710
|
const currentVersion = getPackageVersion();
|
|
27725
28711
|
const processList = pm2JsonList();
|
|
27726
28712
|
const existing = processList.find((item) => item.name === PROCESS_NAME);
|
|
27727
|
-
|
|
28713
|
+
const oldRuntime = gatewayRuntimeFromProcess(existing);
|
|
28714
|
+
if (oldRuntime && !scopesMatch(currentScope(), runtimeScope(oldRuntime))) {
|
|
28715
|
+
throw new Error("[supertask] \u5F53\u524D OpenCode/CLI \u4E0E PM2 Gateway \u7684\u6570\u636E\u5E93\u3001\u914D\u7F6E\u6216 OpenCode \u53EF\u6267\u884C\u6587\u4EF6\u4F5C\u7528\u57DF\u4E0D\u4E00\u81F4\uFF0C\u5DF2\u62D2\u7EDD\u7EE7\u7EED\u5199\u5165");
|
|
28716
|
+
}
|
|
28717
|
+
assertRuntimeCanControlPm2(oldRuntime, existing);
|
|
28718
|
+
const lockedVersion = existing && typeof existing.pid === "number" && oldRuntime ? gatewayVersionFromLock(existing.pid, runtimeScope(oldRuntime).databasePath) : void 0;
|
|
28719
|
+
if (existing?.pm2_env?.status === "online" && typeof existing.pid === "number" && oldRuntime !== null && isGatewayReady(existing.pid, runtimeScope(oldRuntime).databasePath) && (lockedVersion === currentVersion || lockedVersion === void 0 && getRunningVersion(oldRuntime.env, oldRuntime.cwd) === currentVersion)) {
|
|
27728
28720
|
return { ok: true, action: "already-running" };
|
|
27729
28721
|
}
|
|
27730
|
-
|
|
27731
|
-
|
|
27732
|
-
|
|
27733
|
-
|
|
28722
|
+
const targetRuntime = oldRuntime ? { ...oldRuntime, gatewayEntry: resolve(resolveGatewayEntry()) } : currentGatewayRuntime();
|
|
28723
|
+
const before = getRunningVersion(oldRuntime?.env ?? process.env, oldRuntime?.cwd);
|
|
28724
|
+
gatewayKillTimeoutMs(targetRuntime);
|
|
28725
|
+
gatewayTerminationCommandTimeoutMs(targetRuntime);
|
|
28726
|
+
if (existing) requirePm2Termination("delete", "pm2 delete stale Gateway", oldRuntime);
|
|
28727
|
+
try {
|
|
28728
|
+
pm2StartGateway(targetRuntime);
|
|
28729
|
+
savePm2State(targetRuntime.env);
|
|
28730
|
+
writeRunningVersion(currentVersion, targetRuntime.env, targetRuntime.cwd);
|
|
28731
|
+
} catch (error45) {
|
|
28732
|
+
const failed = pm2JsonList(targetRuntime.env).find((item) => item.name === PROCESS_NAME);
|
|
28733
|
+
if (failed) {
|
|
28734
|
+
requirePm2Termination(
|
|
28735
|
+
"delete",
|
|
28736
|
+
"pm2 delete failed Gateway",
|
|
28737
|
+
gatewayRuntimeFromProcess(failed) ?? targetRuntime
|
|
28738
|
+
);
|
|
28739
|
+
}
|
|
28740
|
+
if (oldRuntime) {
|
|
28741
|
+
try {
|
|
28742
|
+
pm2StartGateway(oldRuntime);
|
|
28743
|
+
savePm2State(oldRuntime.env);
|
|
28744
|
+
if (before) writeRunningVersion(before, oldRuntime.env, oldRuntime.cwd);
|
|
28745
|
+
} catch (rollbackError) {
|
|
28746
|
+
const original = error45 instanceof Error ? error45.message : String(error45);
|
|
28747
|
+
const rollback = rollbackError instanceof Error ? rollbackError.message : String(rollbackError);
|
|
28748
|
+
throw new Error(`${original}; \u65E7 Gateway \u56DE\u6EDA\u4E5F\u5931\u8D25: ${rollback}`);
|
|
28749
|
+
}
|
|
28750
|
+
const message = error45 instanceof Error ? error45.message : String(error45);
|
|
28751
|
+
throw new Error(`${message}; \u5DF2\u56DE\u6EDA\u5230\u65E7 Gateway`);
|
|
28752
|
+
}
|
|
28753
|
+
throw error45;
|
|
28754
|
+
}
|
|
27734
28755
|
return { ok: true, action: existing ? "restarted" : "started" };
|
|
27735
28756
|
}
|
|
27736
28757
|
|
|
27737
28758
|
// src/daemon/update.ts
|
|
27738
28759
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
27739
|
-
import { existsSync as
|
|
28760
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync } from "fs";
|
|
27740
28761
|
import { homedir as homedir4 } from "os";
|
|
27741
|
-
import { join as
|
|
28762
|
+
import { join as join5 } from "path";
|
|
27742
28763
|
var PACKAGE_NAME = "opencode-supertask";
|
|
27743
28764
|
function pluginAt(packageDir) {
|
|
27744
|
-
const packageJson =
|
|
27745
|
-
const gatewayEntry =
|
|
27746
|
-
if (!
|
|
28765
|
+
const packageJson = join5(packageDir, "package.json");
|
|
28766
|
+
const gatewayEntry = join5(packageDir, "dist/gateway/index.js");
|
|
28767
|
+
if (!existsSync5(packageJson) || !existsSync5(gatewayEntry)) return null;
|
|
27747
28768
|
try {
|
|
27748
|
-
const pkg = JSON.parse(
|
|
28769
|
+
const pkg = JSON.parse(readFileSync4(packageJson, "utf8"));
|
|
27749
28770
|
if (pkg.name !== PACKAGE_NAME || typeof pkg.version !== "string") return null;
|
|
27750
28771
|
return { packageDir, gatewayEntry, version: pkg.version };
|
|
27751
28772
|
} catch {
|
|
27752
28773
|
return null;
|
|
27753
28774
|
}
|
|
27754
28775
|
}
|
|
27755
|
-
function versionParts(version3) {
|
|
27756
|
-
const match = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(version3);
|
|
27757
|
-
return match ? match.slice(1).map(Number) : null;
|
|
27758
|
-
}
|
|
27759
28776
|
function cacheRoot() {
|
|
27760
|
-
return process.env.SUPERTASK_OPENCODE_CACHE_DIR ??
|
|
28777
|
+
return process.env.SUPERTASK_OPENCODE_CACHE_DIR ?? join5(homedir4(), ".cache/opencode/packages");
|
|
27761
28778
|
}
|
|
27762
28779
|
function installedPlugins() {
|
|
27763
28780
|
const root = cacheRoot();
|
|
27764
|
-
const packageDirs =
|
|
28781
|
+
const packageDirs = existsSync5(root) ? readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && (entry.name === PACKAGE_NAME || entry.name.startsWith(`${PACKAGE_NAME}@`))).map((entry) => join5(root, entry.name, "node_modules", PACKAGE_NAME)) : [];
|
|
27765
28782
|
return packageDirs.map(pluginAt).filter((plugin) => plugin !== null);
|
|
27766
28783
|
}
|
|
27767
28784
|
function opencodeBin() {
|
|
@@ -27795,7 +28812,7 @@ function latestVersion() {
|
|
|
27795
28812
|
} catch {
|
|
27796
28813
|
throw new Error(`[supertask] npm latest \u8FD4\u56DE\u65E0\u6CD5\u89E3\u6790: ${output || "(empty)"}`);
|
|
27797
28814
|
}
|
|
27798
|
-
if (typeof version3 !== "string" ||
|
|
28815
|
+
if (typeof version3 !== "string" || !isSemanticVersion(version3)) {
|
|
27799
28816
|
throw new Error(`[supertask] npm latest \u7248\u672C\u65E0\u6548: ${String(version3)}`);
|
|
27800
28817
|
}
|
|
27801
28818
|
return version3;
|
|
@@ -27808,8 +28825,10 @@ function resolveInstalledVersion(expectedVersion) {
|
|
|
27808
28825
|
const actual = installed.length > 0 ? installed.map((plugin) => plugin.version).join(", ") : "\u672A\u627E\u5230\u53EF\u8FD0\u884C\u7F13\u5B58";
|
|
27809
28826
|
throw new Error(`[supertask] OpenCode \u63D2\u4EF6\u7F13\u5B58\u7248\u672C\u4E0D\u5339\u914D\uFF1A\u671F\u671B ${expectedVersion}\uFF0C\u5B9E\u9645 ${actual}`);
|
|
27810
28827
|
}
|
|
27811
|
-
function
|
|
27812
|
-
|
|
28828
|
+
function installPluginVersion(version3) {
|
|
28829
|
+
if (!isSemanticVersion(version3)) {
|
|
28830
|
+
throw new Error(`[supertask] OpenCode \u63D2\u4EF6\u7248\u672C\u65E0\u6548: ${version3}`);
|
|
28831
|
+
}
|
|
27813
28832
|
const result = spawnSync2(opencodeBin(), [
|
|
27814
28833
|
"plugin",
|
|
27815
28834
|
`${PACKAGE_NAME}@${version3}`,
|
|
@@ -27829,9 +28848,27 @@ function installLatestPlugin() {
|
|
|
27829
28848
|
}
|
|
27830
28849
|
return resolveInstalledVersion(version3);
|
|
27831
28850
|
}
|
|
28851
|
+
function installLatestPlugin() {
|
|
28852
|
+
return installPluginVersion(latestVersion());
|
|
28853
|
+
}
|
|
27832
28854
|
|
|
27833
28855
|
// plugin/supertask.ts
|
|
27834
28856
|
var _initialized = false;
|
|
28857
|
+
var _writeBlockedReason = null;
|
|
28858
|
+
function readyGatewayVersion() {
|
|
28859
|
+
const lockRow = sqlite.prepare(
|
|
28860
|
+
"SELECT heartbeat_at, ready_at, version FROM gateway_lock WHERE id = 1"
|
|
28861
|
+
).get();
|
|
28862
|
+
return {
|
|
28863
|
+
fresh: lockRow?.ready_at != null && Date.now() - lockRow.heartbeat_at < 3e4,
|
|
28864
|
+
version: lockRow?.version ?? null
|
|
28865
|
+
};
|
|
28866
|
+
}
|
|
28867
|
+
function shouldAttemptGatewayReplacement(pluginVersion, gatewayVersion) {
|
|
28868
|
+
if (gatewayVersion === null) return true;
|
|
28869
|
+
const direction = compareSemanticVersions(pluginVersion, gatewayVersion);
|
|
28870
|
+
return direction !== null && direction >= 0;
|
|
28871
|
+
}
|
|
27835
28872
|
function ensureInit() {
|
|
27836
28873
|
if (_initialized) return;
|
|
27837
28874
|
try {
|
|
@@ -27840,9 +28877,16 @@ function ensureInit() {
|
|
|
27840
28877
|
console.error("[supertask] DB init failed:", err instanceof Error ? err.message : String(err));
|
|
27841
28878
|
return;
|
|
27842
28879
|
}
|
|
28880
|
+
const expectedVersion = getPackageVersion();
|
|
28881
|
+
let initialReady = { fresh: false, version: null };
|
|
27843
28882
|
try {
|
|
27844
|
-
|
|
27845
|
-
if (
|
|
28883
|
+
initialReady = readyGatewayVersion();
|
|
28884
|
+
if (initialReady.fresh && initialReady.version === expectedVersion) {
|
|
28885
|
+
_initialized = true;
|
|
28886
|
+
return;
|
|
28887
|
+
}
|
|
28888
|
+
if (initialReady.fresh && !shouldAttemptGatewayReplacement(expectedVersion, initialReady.version)) {
|
|
28889
|
+
_writeBlockedReason = `Gateway \u7248\u672C ${initialReady.version ?? "unknown"} \u4E0E\u5F53\u524D\u63D2\u4EF6 ${expectedVersion} \u4E0D\u4E00\u81F4\uFF1B\u8BF7\u91CD\u542F OpenCode \u6216\u663E\u5F0F\u6267\u884C supertask upgrade`;
|
|
27846
28890
|
_initialized = true;
|
|
27847
28891
|
return;
|
|
27848
28892
|
}
|
|
@@ -27851,13 +28895,29 @@ function ensureInit() {
|
|
|
27851
28895
|
try {
|
|
27852
28896
|
const gateway = ensureGateway();
|
|
27853
28897
|
if (!gateway.ok) {
|
|
27854
|
-
|
|
28898
|
+
if (initialReady.fresh) {
|
|
28899
|
+
_writeBlockedReason = `Gateway \u7248\u672C ${initialReady.version ?? "unknown"} \u4E0E\u63D2\u4EF6 ${expectedVersion} \u4E0D\u4E00\u81F4\uFF0C\u4E14\u672A\u5B89\u88C5 pm2 \u65E0\u6CD5\u5B89\u5168\u66FF\u6362`;
|
|
28900
|
+
} else {
|
|
28901
|
+
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");
|
|
28902
|
+
}
|
|
28903
|
+
} else {
|
|
28904
|
+
const current = readyGatewayVersion();
|
|
28905
|
+
if (!current.fresh || current.version !== expectedVersion) {
|
|
28906
|
+
_writeBlockedReason = `Gateway \u5C31\u7EEA\u7248\u672C ${current.version ?? "unknown"} \u4E0E\u63D2\u4EF6 ${expectedVersion} \u4E0D\u4E00\u81F4`;
|
|
28907
|
+
}
|
|
27855
28908
|
}
|
|
27856
28909
|
} catch (error45) {
|
|
27857
|
-
|
|
28910
|
+
_writeBlockedReason = error45 instanceof Error ? error45.message : String(error45);
|
|
28911
|
+
console.error("[supertask] Gateway init failed:", _writeBlockedReason);
|
|
27858
28912
|
}
|
|
27859
28913
|
_initialized = true;
|
|
27860
28914
|
}
|
|
28915
|
+
function assertRuntimeWritable() {
|
|
28916
|
+
ensureInit();
|
|
28917
|
+
if (_writeBlockedReason) {
|
|
28918
|
+
throw new Error(`[supertask] \u5DF2\u963B\u6B62\u961F\u5217\u5199\u5165: ${_writeBlockedReason}`);
|
|
28919
|
+
}
|
|
28920
|
+
}
|
|
27861
28921
|
var SYSTEM_INSTRUCTION = `
|
|
27862
28922
|
## SuperTask \u4EFB\u52A1\u961F\u5217\u7CFB\u7EDF
|
|
27863
28923
|
|
|
@@ -27895,25 +28955,25 @@ var SuperTaskPlugin = async () => {
|
|
|
27895
28955
|
supertask_add: tool({
|
|
27896
28956
|
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",
|
|
27897
28957
|
args: {
|
|
27898
|
-
name: tool.schema.string().describe("\u4EFB\u52A1\u540D\u79F0\uFF08\u4EBA\u7C7B\u53EF\u8BFB\uFF09"),
|
|
27899
|
-
agent: tool.schema.string().describe("\u6267\u884C\u7684 Agent \u540D\u79F0\uFF0C\u5982 localize-gen, course-gen"),
|
|
27900
|
-
prompt: tool.schema.string().describe("\u53D1\u9001\u7ED9 Agent \u7684\u5B8C\u6574\u63D0\u793A\u8BCD"),
|
|
28958
|
+
name: tool.schema.string().trim().min(1).describe("\u4EFB\u52A1\u540D\u79F0\uFF08\u4EBA\u7C7B\u53EF\u8BFB\uFF09"),
|
|
28959
|
+
agent: tool.schema.string().trim().min(1).describe("\u6267\u884C\u7684 Agent \u540D\u79F0\uFF0C\u5982 localize-gen, course-gen"),
|
|
28960
|
+
prompt: tool.schema.string().trim().min(1).describe("\u53D1\u9001\u7ED9 Agent \u7684\u5B8C\u6574\u63D0\u793A\u8BCD"),
|
|
27901
28961
|
model: tool.schema.string().optional().describe("\u4F7F\u7528\u7684\u6A21\u578B\uFF0C\u5982 gemini-2.5-pro"),
|
|
27902
|
-
category: tool.schema.
|
|
27903
|
-
importance: tool.schema.number().optional().describe("\u91CD\u8981\u7A0B\u5EA6 1-5\uFF085 \u6700\u91CD\u8981\uFF09"),
|
|
27904
|
-
urgency: tool.schema.number().optional().describe("\u7D27\u6025\u7A0B\u5EA6 1-5\uFF085 \u6700\u7D27\u6025\uFF09"),
|
|
28962
|
+
category: tool.schema.enum(["translate", "generate", "review", "test", "general"]).optional().describe("\u4EFB\u52A1\u5206\u7C7B"),
|
|
28963
|
+
importance: tool.schema.number().int().min(1).max(5).optional().describe("\u91CD\u8981\u7A0B\u5EA6 1-5\uFF085 \u6700\u91CD\u8981\uFF09"),
|
|
28964
|
+
urgency: tool.schema.number().int().min(1).max(5).optional().describe("\u7D27\u6025\u7A0B\u5EA6 1-5\uFF085 \u6700\u7D27\u6025\uFF09"),
|
|
27905
28965
|
batchId: tool.schema.string().optional().describe("\u6279\u6B21 ID\uFF0C\u7528\u4E8E\u5206\u7EC4\u7BA1\u7406"),
|
|
27906
|
-
dependsOn: tool.schema.number().optional().describe("\u4F9D\u8D56\u7684\u4EFB\u52A1 ID\uFF0C\u8BE5\u4EFB\u52A1\u5B8C\u6210\u540E\u624D\u4F1A\u6267\u884C"),
|
|
27907
|
-
max_retries: tool.schema.number().optional().describe("\u9996\u6B21\u6267\u884C\u4E4B\u5916\u5141\u8BB8\u7684\u91CD\u8BD5\u6B21\u6570\uFF0C\u9ED8\u8BA4 3"),
|
|
27908
|
-
retry_backoff_ms: tool.schema.number().optional().describe("\u91CD\u8BD5\u9000\u907F\u57FA\u7840\u95F4\u9694 ms\uFF0C\u9ED8\u8BA4 30000"),
|
|
27909
|
-
timeout_ms: tool.schema.number().optional().describe("\u4EFB\u52A1\u786C\u8D85\u65F6 ms\uFF1B\u672A\u4F20\u5219\u4F7F\u7528 Gateway \u9ED8\u8BA4\u503C"),
|
|
28966
|
+
dependsOn: tool.schema.number().int().positive().optional().describe("\u4F9D\u8D56\u7684\u4EFB\u52A1 ID\uFF0C\u8BE5\u4EFB\u52A1\u5B8C\u6210\u540E\u624D\u4F1A\u6267\u884C"),
|
|
28967
|
+
max_retries: tool.schema.number().int().min(0).max(1e3).optional().describe("\u9996\u6B21\u6267\u884C\u4E4B\u5916\u5141\u8BB8\u7684\u91CD\u8BD5\u6B21\u6570\uFF0C\u9ED8\u8BA4 3"),
|
|
28968
|
+
retry_backoff_ms: tool.schema.number().int().min(0).max(864e5).optional().describe("\u91CD\u8BD5\u9000\u907F\u57FA\u7840\u95F4\u9694 ms\uFF0C\u9ED8\u8BA4 30000"),
|
|
28969
|
+
timeout_ms: tool.schema.number().int().min(1e3).max(6048e5).optional().describe("\u4EFB\u52A1\u786C\u8D85\u65F6 ms\uFF1B\u672A\u4F20\u5219\u4F7F\u7528 Gateway \u9ED8\u8BA4\u503C"),
|
|
27910
28970
|
cwd: tool.schema.string().optional().describe(
|
|
27911
28971
|
"(\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"
|
|
27912
28972
|
)
|
|
27913
28973
|
},
|
|
27914
|
-
async execute(args) {
|
|
28974
|
+
async execute(args, context) {
|
|
27915
28975
|
try {
|
|
27916
|
-
|
|
28976
|
+
assertRuntimeWritable();
|
|
27917
28977
|
const task = await TaskService.add({
|
|
27918
28978
|
name: args.name,
|
|
27919
28979
|
agent: args.agent,
|
|
@@ -27924,7 +28984,7 @@ var SuperTaskPlugin = async () => {
|
|
|
27924
28984
|
urgency: args.urgency ?? 3,
|
|
27925
28985
|
batchId: args.batchId,
|
|
27926
28986
|
dependsOn: args.dependsOn,
|
|
27927
|
-
cwd:
|
|
28987
|
+
cwd: context.directory,
|
|
27928
28988
|
maxRetries: args.max_retries,
|
|
27929
28989
|
retryBackoffMs: args.retry_backoff_ms,
|
|
27930
28990
|
timeoutMs: args.timeout_ms
|
|
@@ -27943,10 +29003,9 @@ var SuperTaskPlugin = async () => {
|
|
|
27943
29003
|
args: {
|
|
27944
29004
|
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")
|
|
27945
29005
|
},
|
|
27946
|
-
async execute(args) {
|
|
29006
|
+
async execute(args, context) {
|
|
27947
29007
|
try {
|
|
27948
|
-
const
|
|
27949
|
-
const task = await TaskService.next({ cwd: scopeCwd });
|
|
29008
|
+
const task = await TaskService.next({ cwd: context.directory });
|
|
27950
29009
|
if (task) {
|
|
27951
29010
|
return JSON.stringify({
|
|
27952
29011
|
id: task.id,
|
|
@@ -27969,85 +29028,6 @@ var SuperTaskPlugin = async () => {
|
|
|
27969
29028
|
}
|
|
27970
29029
|
}
|
|
27971
29030
|
}),
|
|
27972
|
-
// 开始执行任务
|
|
27973
|
-
supertask_start: tool({
|
|
27974
|
-
description: "\u6807\u8BB0\u4EFB\u52A1\u4E3A\u6B63\u5728\u6267\u884C\uFF08running\uFF09\u3002\u8BB0\u5F55\u5F00\u59CB\u65F6\u95F4\u3002",
|
|
27975
|
-
args: {
|
|
27976
|
-
id: tool.schema.number().describe("\u4EFB\u52A1 ID"),
|
|
27977
|
-
cwd: tool.schema.string().optional().describe("\u9879\u76EE\u9694\u79BB\uFF1A\u4F20\u5165\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\uFF0C\u53EA\u64CD\u4F5C\u8BE5\u9879\u76EE\u7684\u4EFB\u52A1")
|
|
27978
|
-
},
|
|
27979
|
-
async execute(args) {
|
|
27980
|
-
try {
|
|
27981
|
-
const scopeCwd = args.cwd ?? process.cwd();
|
|
27982
|
-
const task = await TaskService.start(args.id, { cwd: scopeCwd });
|
|
27983
|
-
if (task) return JSON.stringify({ id: task.id, status: task.status });
|
|
27984
|
-
const existing = await TaskService.getById(args.id, { cwd: scopeCwd });
|
|
27985
|
-
if (!existing) return JSON.stringify({ error: "Task not found" });
|
|
27986
|
-
return JSON.stringify({
|
|
27987
|
-
error: "Task status does not allow start",
|
|
27988
|
-
id: existing.id,
|
|
27989
|
-
status: existing.status,
|
|
27990
|
-
message: `Only pending or retryable failed tasks can be started; current status is '${existing.status}'.`
|
|
27991
|
-
});
|
|
27992
|
-
} catch (error45) {
|
|
27993
|
-
return JSON.stringify({
|
|
27994
|
-
error: error45 instanceof Error ? error45.message : String(error45)
|
|
27995
|
-
});
|
|
27996
|
-
}
|
|
27997
|
-
}
|
|
27998
|
-
}),
|
|
27999
|
-
// 完成任务
|
|
28000
|
-
supertask_done: tool({
|
|
28001
|
-
description: "\u6807\u8BB0\u4EFB\u52A1\u5B8C\u6210\uFF08done\uFF09\u3002\u8BB0\u5F55\u7ED3\u675F\u65F6\u95F4\u548C\u7ED3\u679C\u65E5\u5FD7\u3002",
|
|
28002
|
-
args: {
|
|
28003
|
-
id: tool.schema.number().describe("\u4EFB\u52A1 ID"),
|
|
28004
|
-
log: tool.schema.string().optional().describe("\u6267\u884C\u7ED3\u679C\u65E5\u5FD7"),
|
|
28005
|
-
cwd: tool.schema.string().optional().describe("\u9879\u76EE\u9694\u79BB\uFF1A\u4F20\u5165\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\uFF0C\u53EA\u64CD\u4F5C\u8BE5\u9879\u76EE\u7684\u4EFB\u52A1")
|
|
28006
|
-
},
|
|
28007
|
-
async execute(args) {
|
|
28008
|
-
try {
|
|
28009
|
-
const scopeCwd = args.cwd ?? process.cwd();
|
|
28010
|
-
const task = await TaskService.done(args.id, args.log, { cwd: scopeCwd });
|
|
28011
|
-
if (task) {
|
|
28012
|
-
return JSON.stringify({ id: task.id, status: task.status });
|
|
28013
|
-
} else {
|
|
28014
|
-
return JSON.stringify({ error: "Task not found" });
|
|
28015
|
-
}
|
|
28016
|
-
} catch (error45) {
|
|
28017
|
-
return JSON.stringify({
|
|
28018
|
-
error: error45 instanceof Error ? error45.message : String(error45)
|
|
28019
|
-
});
|
|
28020
|
-
}
|
|
28021
|
-
}
|
|
28022
|
-
}),
|
|
28023
|
-
// 任务失败
|
|
28024
|
-
supertask_fail: tool({
|
|
28025
|
-
description: "\u6807\u8BB0\u4EFB\u52A1\u5931\u8D25\uFF08failed\uFF09\u3002\u8BB0\u5F55\u9519\u8BEF\u65E5\u5FD7\uFF0C\u81EA\u52A8\u589E\u52A0\u91CD\u8BD5\u8BA1\u6570\u3002",
|
|
28026
|
-
args: {
|
|
28027
|
-
id: tool.schema.number().describe("\u4EFB\u52A1 ID"),
|
|
28028
|
-
log: tool.schema.string().optional().describe("\u9519\u8BEF\u65E5\u5FD7"),
|
|
28029
|
-
cwd: tool.schema.string().optional().describe("\u9879\u76EE\u9694\u79BB\uFF1A\u4F20\u5165\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\uFF0C\u53EA\u64CD\u4F5C\u8BE5\u9879\u76EE\u7684\u4EFB\u52A1")
|
|
28030
|
-
},
|
|
28031
|
-
async execute(args) {
|
|
28032
|
-
try {
|
|
28033
|
-
const scopeCwd = args.cwd ?? process.cwd();
|
|
28034
|
-
const task = await TaskService.fail(args.id, args.log, { cwd: scopeCwd });
|
|
28035
|
-
if (task) {
|
|
28036
|
-
return JSON.stringify({
|
|
28037
|
-
id: task.id,
|
|
28038
|
-
status: task.status,
|
|
28039
|
-
retryCount: task.retryCount
|
|
28040
|
-
});
|
|
28041
|
-
} else {
|
|
28042
|
-
return JSON.stringify({ error: "Task not found" });
|
|
28043
|
-
}
|
|
28044
|
-
} catch (error45) {
|
|
28045
|
-
return JSON.stringify({
|
|
28046
|
-
error: error45 instanceof Error ? error45.message : String(error45)
|
|
28047
|
-
});
|
|
28048
|
-
}
|
|
28049
|
-
}
|
|
28050
|
-
}),
|
|
28051
29031
|
// 查看统计
|
|
28052
29032
|
supertask_status: tool({
|
|
28053
29033
|
description: "\u67E5\u770B\u4EFB\u52A1\u961F\u5217\u7EDF\u8BA1\u3002\u53EF\u6309\u6279\u6B21\u7B5B\u9009\u3002",
|
|
@@ -28055,10 +29035,9 @@ var SuperTaskPlugin = async () => {
|
|
|
28055
29035
|
batchId: tool.schema.string().optional().describe("\u6309\u6279\u6B21\u7B5B\u9009"),
|
|
28056
29036
|
cwd: tool.schema.string().optional().describe("\u9879\u76EE\u9694\u79BB\uFF1A\u4F20\u5165\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\uFF0C\u53EA\u7EDF\u8BA1\u8BE5\u9879\u76EE\u7684\u4EFB\u52A1")
|
|
28057
29037
|
},
|
|
28058
|
-
async execute(args) {
|
|
29038
|
+
async execute(args, context) {
|
|
28059
29039
|
try {
|
|
28060
|
-
const
|
|
28061
|
-
const stats = await TaskService.stats({ batchId: args.batchId, cwd: scopeCwd });
|
|
29040
|
+
const stats = await TaskService.stats({ batchId: args.batchId, cwd: context.directory });
|
|
28062
29041
|
return JSON.stringify(stats);
|
|
28063
29042
|
} catch (error45) {
|
|
28064
29043
|
return JSON.stringify({
|
|
@@ -28071,22 +29050,22 @@ var SuperTaskPlugin = async () => {
|
|
|
28071
29050
|
supertask_retry: tool({
|
|
28072
29051
|
description: "\u91CD\u8BD5\u5931\u8D25\u7684\u4EFB\u52A1\u3002\u5C06 failed \u72B6\u6001\u91CD\u7F6E\u4E3A pending\uFF0C\u81EA\u52A8\u6E05\u9664\u5F00\u59CB/\u7ED3\u675F\u65F6\u95F4\u3002",
|
|
28073
29052
|
args: {
|
|
28074
|
-
id: tool.schema.number().optional().describe("\u4EFB\u52A1 ID"),
|
|
29053
|
+
id: tool.schema.number().int().positive().optional().describe("\u4EFB\u52A1 ID"),
|
|
28075
29054
|
batchId: tool.schema.string().optional().describe("\u6279\u6B21 ID\uFF08\u6279\u91CF\u91CD\u8BD5\uFF09"),
|
|
28076
29055
|
cwd: tool.schema.string().optional().describe("\u9879\u76EE\u9694\u79BB\uFF1A\u4F20\u5165\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\uFF0C\u53EA\u64CD\u4F5C\u8BE5\u9879\u76EE\u7684\u4EFB\u52A1")
|
|
28077
29056
|
},
|
|
28078
|
-
async execute(args) {
|
|
29057
|
+
async execute(args, context) {
|
|
28079
29058
|
try {
|
|
28080
|
-
|
|
29059
|
+
assertRuntimeWritable();
|
|
28081
29060
|
if (args.id !== void 0) {
|
|
28082
|
-
const task = await TaskService.retry(args.id, { cwd:
|
|
29061
|
+
const task = await TaskService.retry(args.id, { cwd: context.directory });
|
|
28083
29062
|
if (task) {
|
|
28084
29063
|
return JSON.stringify({ id: task.id, status: task.status });
|
|
28085
29064
|
} else {
|
|
28086
29065
|
return JSON.stringify({ error: "Task not found or not failed" });
|
|
28087
29066
|
}
|
|
28088
29067
|
} else if (args.batchId !== void 0) {
|
|
28089
|
-
const count = await TaskService.retryBatch(args.batchId, { cwd:
|
|
29068
|
+
const count = await TaskService.retryBatch(args.batchId, { cwd: context.directory });
|
|
28090
29069
|
return JSON.stringify({ retried: count, batchId: args.batchId });
|
|
28091
29070
|
} else {
|
|
28092
29071
|
return JSON.stringify({ error: "Please specify id or batchId" });
|
|
@@ -28102,16 +29081,15 @@ var SuperTaskPlugin = async () => {
|
|
|
28102
29081
|
supertask_list: tool({
|
|
28103
29082
|
description: "\u5217\u51FA\u6700\u8FD1\u7684\u4EFB\u52A1\u3002\u652F\u6301\u6309\u72B6\u6001\u7B5B\u9009\uFF0C\u6309\u521B\u5EFA\u65F6\u95F4\u5012\u5E8F\u3002",
|
|
28104
29083
|
args: {
|
|
28105
|
-
status: tool.schema.
|
|
28106
|
-
limit: tool.schema.number().optional().describe("\u8FD4\u56DE\u6570\u91CF\uFF0C\u9ED8\u8BA4 20"),
|
|
29084
|
+
status: tool.schema.enum(["pending", "running", "done", "failed", "dead_letter", "cancelled"]).optional().describe("\u6309\u72B6\u6001\u7B5B\u9009"),
|
|
29085
|
+
limit: tool.schema.number().int().min(1).max(1e3).optional().describe("\u8FD4\u56DE\u6570\u91CF\uFF0C\u9ED8\u8BA4 20"),
|
|
28107
29086
|
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")
|
|
28108
29087
|
},
|
|
28109
|
-
async execute(args) {
|
|
29088
|
+
async execute(args, context) {
|
|
28110
29089
|
try {
|
|
28111
|
-
const scopeCwd = args.cwd ?? process.cwd();
|
|
28112
29090
|
const tasks3 = await TaskService.list({
|
|
28113
29091
|
status: args.status,
|
|
28114
|
-
cwd:
|
|
29092
|
+
cwd: context.directory,
|
|
28115
29093
|
limit: args.limit ?? 20
|
|
28116
29094
|
});
|
|
28117
29095
|
return JSON.stringify(tasks3);
|
|
@@ -28126,13 +29104,12 @@ var SuperTaskPlugin = async () => {
|
|
|
28126
29104
|
supertask_get: tool({
|
|
28127
29105
|
description: "\u83B7\u53D6\u6307\u5B9A ID \u7684\u4EFB\u52A1\u8BE6\u60C5\u3002",
|
|
28128
29106
|
args: {
|
|
28129
|
-
id: tool.schema.number().describe("\u4EFB\u52A1 ID"),
|
|
29107
|
+
id: tool.schema.number().int().positive().describe("\u4EFB\u52A1 ID"),
|
|
28130
29108
|
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")
|
|
28131
29109
|
},
|
|
28132
|
-
async execute(args) {
|
|
29110
|
+
async execute(args, context) {
|
|
28133
29111
|
try {
|
|
28134
|
-
const
|
|
28135
|
-
const task = await TaskService.getById(args.id, { cwd: scopeCwd });
|
|
29112
|
+
const task = await TaskService.getById(args.id, { cwd: context.directory });
|
|
28136
29113
|
if (task) {
|
|
28137
29114
|
return JSON.stringify({
|
|
28138
29115
|
id: task.id,
|
|
@@ -28158,13 +29135,13 @@ var SuperTaskPlugin = async () => {
|
|
|
28158
29135
|
supertask_schedule: tool({
|
|
28159
29136
|
description: "\u521B\u5EFA\u8C03\u5EA6\u6A21\u677F\uFF0C\u7528\u4E8E\u5B9A\u65F6/\u5EF6\u8FDF/\u5FAA\u73AF\u6267\u884C\u4EFB\u52A1\u3002\u652F\u6301 cron \u8868\u8FBE\u5F0F\u3001\u4E00\u6B21\u6027\u5EF6\u8FDF\u548C\u56FA\u5B9A\u95F4\u9694\u5FAA\u73AF\u3002Gateway \u4F1A\u6309\u6A21\u677F\u81EA\u52A8\u751F\u6210\u4EFB\u52A1\u5230\u961F\u5217\u3002",
|
|
28160
29137
|
args: {
|
|
28161
|
-
name: tool.schema.string().describe("\u6A21\u677F\u540D\u79F0"),
|
|
28162
|
-
agent: tool.schema.string().describe("\u6267\u884C\u7684 Agent \u540D\u79F0"),
|
|
28163
|
-
prompt: tool.schema.string().describe("\u53D1\u9001\u7ED9 Agent \u7684\u5B8C\u6574\u63D0\u793A\u8BCD"),
|
|
29138
|
+
name: tool.schema.string().trim().min(1).describe("\u6A21\u677F\u540D\u79F0"),
|
|
29139
|
+
agent: tool.schema.string().trim().min(1).describe("\u6267\u884C\u7684 Agent \u540D\u79F0"),
|
|
29140
|
+
prompt: tool.schema.string().trim().min(1).describe("\u53D1\u9001\u7ED9 Agent \u7684\u5B8C\u6574\u63D0\u793A\u8BCD"),
|
|
28164
29141
|
model: tool.schema.string().optional().describe("\u4F7F\u7528\u7684\u6A21\u578B"),
|
|
28165
|
-
category: tool.schema.
|
|
28166
|
-
importance: tool.schema.number().optional().describe("\u91CD\u8981\u7A0B\u5EA6 1-5"),
|
|
28167
|
-
urgency: tool.schema.number().optional().describe("\u7D27\u6025\u7A0B\u5EA6 1-5"),
|
|
29142
|
+
category: tool.schema.enum(["translate", "generate", "review", "test", "general"]).optional().describe("\u4EFB\u52A1\u5206\u7C7B"),
|
|
29143
|
+
importance: tool.schema.number().int().min(1).max(5).optional().describe("\u91CD\u8981\u7A0B\u5EA6 1-5"),
|
|
29144
|
+
urgency: tool.schema.number().int().min(1).max(5).optional().describe("\u7D27\u6025\u7A0B\u5EA6 1-5"),
|
|
28168
29145
|
batchId: tool.schema.string().optional().describe("\u6A21\u677F\u751F\u6210\u7684\u4EFB\u52A1\u5F52\u5C5E\u7684\u6279\u6B21 ID"),
|
|
28169
29146
|
schedule: tool.schema.object({
|
|
28170
29147
|
type: tool.schema.enum(["cron", "delayed", "recurring"]).describe("\u8C03\u5EA6\u7C7B\u578B"),
|
|
@@ -28172,13 +29149,14 @@ var SuperTaskPlugin = async () => {
|
|
|
28172
29149
|
delay: tool.schema.string().optional().describe("\u5EF6\u8FDF\u65F6\u95F4\uFF08delayed \u7C7B\u578B\u5FC5\u586B\uFF09\uFF0C\u53CB\u597D\u683C\u5F0F\u5982 '30s' '5min' '1h' '2d'\uFF0C\u4E5F\u652F\u6301 ISO 8601 duration \u5982 'PT30M'"),
|
|
28173
29150
|
interval: tool.schema.string().optional().describe("\u5FAA\u73AF\u95F4\u9694\uFF08recurring \u7C7B\u578B\u5FC5\u586B\uFF09\uFF0C\u53CB\u597D\u683C\u5F0F\u5982 '1h' '30min' '5s'\uFF0C\u4E5F\u652F\u6301 ISO 8601 duration \u5982 'PT1H'")
|
|
28174
29151
|
}).describe("\u8C03\u5EA6\u914D\u7F6E"),
|
|
28175
|
-
max_instances: tool.schema.number().optional().describe("\u6700\u5927\u5E76\u53D1\u5B9E\u4F8B\u6570\uFF0C\u9ED8\u8BA4 1"),
|
|
28176
|
-
max_retries: tool.schema.number().optional().describe("\u514B\u9686\u7ED9 task \u7684\u6700\u5927\u91CD\u8BD5\u6B21\u6570\uFF0C\u9ED8\u8BA4 3"),
|
|
28177
|
-
retry_backoff_ms: tool.schema.number().optional().describe("\u514B\u9686\u7ED9 task \u7684\u9000\u907F\u57FA\u7840\u95F4\u9694 ms\uFF0C\u9ED8\u8BA4 30000"),
|
|
28178
|
-
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")
|
|
29152
|
+
max_instances: tool.schema.number().int().min(1).max(1e3).optional().describe("\u6700\u5927\u5E76\u53D1\u5B9E\u4F8B\u6570\uFF0C\u9ED8\u8BA4 1"),
|
|
29153
|
+
max_retries: tool.schema.number().int().min(0).max(1e3).optional().describe("\u514B\u9686\u7ED9 task \u7684\u6700\u5927\u91CD\u8BD5\u6B21\u6570\uFF0C\u9ED8\u8BA4 3"),
|
|
29154
|
+
retry_backoff_ms: tool.schema.number().int().min(0).max(864e5).optional().describe("\u514B\u9686\u7ED9 task \u7684\u9000\u907F\u57FA\u7840\u95F4\u9694 ms\uFF0C\u9ED8\u8BA4 30000"),
|
|
29155
|
+
timeout_ms: tool.schema.number().int().min(1e3).max(6048e5).optional().describe("\u514B\u9686\u7ED9 task \u7684\u786C\u8D85\u65F6 ms\uFF1B\u672A\u4F20\u5219\u4F7F\u7528 Gateway \u9ED8\u8BA4\u503C")
|
|
28179
29156
|
},
|
|
28180
|
-
async execute(args) {
|
|
29157
|
+
async execute(args, context) {
|
|
28181
29158
|
try {
|
|
29159
|
+
assertRuntimeWritable();
|
|
28182
29160
|
if (!args.schedule) {
|
|
28183
29161
|
return JSON.stringify({ error: "schedule is required" });
|
|
28184
29162
|
}
|
|
@@ -28207,7 +29185,7 @@ var SuperTaskPlugin = async () => {
|
|
|
28207
29185
|
category: args.category ?? "general",
|
|
28208
29186
|
importance: args.importance ?? 3,
|
|
28209
29187
|
urgency: args.urgency ?? 3,
|
|
28210
|
-
cwd:
|
|
29188
|
+
cwd: context.directory,
|
|
28211
29189
|
batchId: args.batchId,
|
|
28212
29190
|
scheduleType,
|
|
28213
29191
|
cronExpr,
|
|
@@ -28236,19 +29214,47 @@ var SuperTaskPlugin = async () => {
|
|
|
28236
29214
|
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",
|
|
28237
29215
|
args: {},
|
|
28238
29216
|
async execute() {
|
|
29217
|
+
if (process.env[MANAGED_RUN_ENV] === MANAGED_RUN_ENV_VALUE) {
|
|
29218
|
+
return JSON.stringify({
|
|
29219
|
+
success: false,
|
|
29220
|
+
error: "Gateway \u7BA1\u7406\u7684\u961F\u5217\u4EFB\u52A1\u4E0D\u80FD\u5347\u7EA7 SuperTask\uFF0C\u5426\u5219\u4F1A\u7EC8\u6B62\u627F\u8F7D\u5F53\u524D\u4EFB\u52A1\u7684 Gateway\u3002",
|
|
29221
|
+
hint: "\u8BF7\u4ECE\u5916\u90E8\u7EC8\u7AEF\u6267\u884C `supertask upgrade`\uFF0C\u6216\u5728\u975E\u961F\u5217 OpenCode \u4F1A\u8BDD\u4E2D\u8C03\u7528\u5347\u7EA7\u5DE5\u5177\u3002"
|
|
29222
|
+
});
|
|
29223
|
+
}
|
|
28239
29224
|
try {
|
|
28240
29225
|
console.log("[supertask] Updating OpenCode plugin cache...");
|
|
29226
|
+
const previousVersion = getPackageVersion();
|
|
28241
29227
|
let installed;
|
|
28242
29228
|
try {
|
|
28243
29229
|
installed = installLatestPlugin();
|
|
28244
29230
|
} catch (updateError) {
|
|
29231
|
+
let detail = updateError instanceof Error ? updateError.message : String(updateError);
|
|
29232
|
+
try {
|
|
29233
|
+
installPluginVersion(previousVersion);
|
|
29234
|
+
} catch (rollbackError) {
|
|
29235
|
+
detail += `; OpenCode \u63D2\u4EF6\u56DE\u6EDA\u5931\u8D25: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`;
|
|
29236
|
+
}
|
|
28245
29237
|
return JSON.stringify({
|
|
28246
29238
|
success: false,
|
|
28247
|
-
error:
|
|
29239
|
+
error: detail,
|
|
28248
29240
|
hint: "Query npm dist-tags.latest, then install that exact version with opencode plugin."
|
|
28249
29241
|
});
|
|
28250
29242
|
}
|
|
28251
|
-
|
|
29243
|
+
let result;
|
|
29244
|
+
try {
|
|
29245
|
+
result = upgrade(installed);
|
|
29246
|
+
} catch (upgradeError) {
|
|
29247
|
+
try {
|
|
29248
|
+
if (previousVersion !== installed.version) {
|
|
29249
|
+
installPluginVersion(previousVersion);
|
|
29250
|
+
}
|
|
29251
|
+
} catch (rollbackError) {
|
|
29252
|
+
const original = upgradeError instanceof Error ? upgradeError.message : String(upgradeError);
|
|
29253
|
+
const rollback = rollbackError instanceof Error ? rollbackError.message : String(rollbackError);
|
|
29254
|
+
throw new Error(`${original}; Gateway \u5DF2\u56DE\u6EDA\uFF0C\u4F46 OpenCode \u63D2\u4EF6\u56DE\u6EDA\u5931\u8D25: ${rollback}`);
|
|
29255
|
+
}
|
|
29256
|
+
throw upgradeError;
|
|
29257
|
+
}
|
|
28252
29258
|
return JSON.stringify({
|
|
28253
29259
|
success: true,
|
|
28254
29260
|
before: result.before,
|
|
@@ -28268,6 +29274,7 @@ var SuperTaskPlugin = async () => {
|
|
|
28268
29274
|
};
|
|
28269
29275
|
};
|
|
28270
29276
|
export {
|
|
28271
|
-
SuperTaskPlugin
|
|
29277
|
+
SuperTaskPlugin,
|
|
29278
|
+
shouldAttemptGatewayReplacement
|
|
28272
29279
|
};
|
|
28273
29280
|
//# sourceMappingURL=supertask.js.map
|