opencode-supertask 0.1.23 → 0.1.25

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 CHANGED
@@ -13,7 +13,13 @@ Documentation: [current architecture](docs/architecture.md) · [operations and t
13
13
 
14
14
  ### Quick Install
15
15
 
16
- Add the plugin to `~/.config/opencode/opencode.json`:
16
+ Install the CLI globally:
17
+
18
+ ```bash
19
+ npm install -g opencode-supertask
20
+ ```
21
+
22
+ Then add the plugin to `~/.config/opencode/opencode.json`:
17
23
 
18
24
  ```json
19
25
  {
@@ -36,13 +42,14 @@ The plugin never installs global dependencies by itself. Without a running Gatew
36
42
  2. Remove `"opencode-supertask"` from `~/.config/opencode/opencode.json`
37
43
  3. Restart OpenCode
38
44
 
39
- To clear all task data safely, stop the Gateway and use the backup-first database command:
45
+ To clear all task data safely, use the backup-first database command:
40
46
 
41
47
  ```bash
42
- pm2 stop supertask-gateway
43
48
  supertask db clear --confirm CLEAR
44
49
  ```
45
50
 
51
+ If the matching Gateway is managed by PM2, the CLI stops it first and restarts it after maintenance.
52
+
46
53
  ### CLI Install / Uninstall
47
54
 
48
55
  ```bash
@@ -114,11 +121,11 @@ supertask template enable --id 1
114
121
  # Database maintenance
115
122
  supertask db check
116
123
  supertask db backup [--output /path/to/tasks-backup.db]
117
- supertask db clear --confirm CLEAR
118
- supertask db restore --from /path/to/tasks-backup.db --confirm RESTORE
124
+ supertask db clear --confirm CLEAR [--keep-stopped]
125
+ supertask db restore --from /path/to/tasks-backup.db --confirm RESTORE [--keep-stopped]
119
126
  ```
120
127
 
121
- `db backup` creates and validates a standalone SQLite snapshot. `db clear` and `db restore` refuse to run while another Gateway or task is active; both create a safety backup before changing data. Stop the PM2 Gateway before using the destructive CLI commands.
128
+ `db backup` creates and validates a standalone SQLite snapshot. For `db clear` and `db restore`, the CLI automatically stops a PM2 Gateway whose PID matches the current database's fresh ready lock, then restores its previous running state. Both commands create a safety backup before changing data and still refuse active tasks or an unverified/foreground Gateway. Use `--keep-stopped` to leave a previously running PM2 Gateway stopped.
122
129
 
123
130
  ### Duration Format
124
131
 
@@ -214,7 +221,13 @@ SuperTask 是一个基于 SQLite 的 AI Agent 任务调度系统,专为 [OpenC
214
221
 
215
222
  ### 安装
216
223
 
217
- `~/.config/opencode/opencode.json` 中添加:
224
+ 先安装全局 CLI:
225
+
226
+ ```bash
227
+ npm install -g opencode-supertask
228
+ ```
229
+
230
+ 然后在 `~/.config/opencode/opencode.json` 中添加:
218
231
 
219
232
  ```json
220
233
  {
@@ -240,10 +253,11 @@ supertask gateway # 前台运行:不需要 pm2
240
253
  安全清理所有任务数据:
241
254
 
242
255
  ```bash
243
- pm2 stop supertask-gateway
244
256
  supertask db clear --confirm CLEAR
245
257
  ```
246
258
 
259
+ 若当前数据库对应的 Gateway 由 PM2 管理,CLI 会自动停止并在维护结束后恢复运行。
260
+
247
261
  ### 定时任务
248
262
 
249
263
  支持友好的时间格式:
@@ -274,11 +288,11 @@ supertask template add --type cron --cron "0 9 * * *" ...
274
288
  ```bash
275
289
  supertask db check
276
290
  supertask db backup [--output /path/to/tasks-backup.db]
277
- supertask db clear --confirm CLEAR
278
- supertask db restore --from /path/to/tasks-backup.db --confirm RESTORE
291
+ supertask db clear --confirm CLEAR [--keep-stopped]
292
+ supertask db restore --from /path/to/tasks-backup.db --confirm RESTORE [--keep-stopped]
279
293
  ```
280
294
 
281
- `db backup` 会生成并校验可独立恢复的 SQLite 快照。CLI 清空和恢复会拒绝活跃 Gateway 或运行中任务,并在修改数据前自动保留安全备份;执行这两个危险命令前应先停止 PM2 Gateway
295
+ `db backup` 会生成并校验可独立恢复的 SQLite 快照。CLI 清空和恢复会先确认 PM2 PID 与当前数据库的新鲜 ready 锁一致,再自动停止并按原状态重启 Gateway;操作失败时也会尝试恢复 Gateway。它们仍会拒绝运行中任务、前台 Gateway 或无法确认归属的进程,并在修改数据前自动保留安全备份。传入 `--keep-stopped` 可让原本运行的 PM2 Gateway 保持停止。
282
296
 
283
297
  ### 数据位置
284
298
 
package/dist/cli/index.js CHANGED
@@ -19562,7 +19562,7 @@ var init_database_maintenance_service = __esm({
19562
19562
  if (!lock || !isProcessAlive(lock.pid)) return;
19563
19563
  if (allowCurrentGateway && lock.pid === process.pid) return;
19564
19564
  throw new DatabaseMaintenanceConflictError(
19565
- `Gateway PID ${lock.pid} \u4ECD\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148\u6267\u884C pm2 stop supertask-gateway`
19565
+ `Gateway PID ${lock.pid} \u4ECD\u5728\u8FD0\u884C\uFF0C\u4F46\u672A\u786E\u8BA4\u7531\u5F53\u524D\u6570\u636E\u5E93\u5BF9\u5E94\u7684 PM2 \u8FDB\u7A0B\u7BA1\u7406\uFF1B\u8BF7\u5148\u505C\u6B62\u8BE5 Gateway\uFF08PM2 \u53EF\u6267\u884C pm2 stop supertask-gateway\uFF09`
19566
19566
  );
19567
19567
  }
19568
19568
  static assertNoRunningWork(sqlite2) {
@@ -19718,6 +19718,8 @@ __export(pm2_exports, {
19718
19718
  isGatewayRunning: () => isGatewayRunning,
19719
19719
  isPm2Installed: () => isPm2Installed,
19720
19720
  resolveGatewayEntry: () => resolveGatewayEntry,
19721
+ restartGatewayAfterMaintenance: () => restartGatewayAfterMaintenance,
19722
+ stopGatewayForMaintenance: () => stopGatewayForMaintenance,
19721
19723
  uninstall: () => uninstall,
19722
19724
  upgrade: () => upgrade
19723
19725
  });
@@ -19937,6 +19939,28 @@ function waitForGatewayReady(pid) {
19937
19939
  }
19938
19940
  return isGatewayReady(pid);
19939
19941
  }
19942
+ function stopGatewayForMaintenance() {
19943
+ if (!isPm2Installed()) return { wasRunning: false };
19944
+ const proc = pm2JsonList().find((item) => item.name === PROCESS_NAME);
19945
+ const managedCurrentDatabase = proc?.pm2_env?.status === "online" && typeof proc.pid === "number" && isGatewayReady(proc.pid);
19946
+ if (!managedCurrentDatabase) return { wasRunning: false };
19947
+ requirePm2(["stop", PROCESS_NAME], "pm2 stop Gateway for database maintenance");
19948
+ return { wasRunning: true };
19949
+ }
19950
+ function restartGatewayAfterMaintenance(state2) {
19951
+ if (!state2.wasRunning) return false;
19952
+ requirePm2(["start", PROCESS_NAME], "pm2 restart Gateway after database maintenance");
19953
+ const started = pm2JsonList().find((item) => item.name === PROCESS_NAME);
19954
+ if (started?.pm2_env?.status !== "online") {
19955
+ throw new Error(
19956
+ `[supertask] Gateway \u6570\u636E\u5E93\u7EF4\u62A4\u540E\u672A\u6062\u590D online\uFF08\u72B6\u6001\uFF1A${started?.pm2_env?.status ?? "missing"}\uFF09`
19957
+ );
19958
+ }
19959
+ if (typeof started.pid !== "number" || !waitForGatewayReady(started.pid)) {
19960
+ throw new Error("[supertask] Gateway \u6570\u636E\u5E93\u7EF4\u62A4\u540E\u5DF2 online\uFF0C\u4F46\u672A\u5728\u9650\u5B9A\u65F6\u95F4\u5185\u5C31\u7EEA\uFF1B\u8BF7\u67E5\u770B pm2 logs supertask-gateway");
19961
+ }
19962
+ return true;
19963
+ }
19940
19964
  function findBunPath() {
19941
19965
  const override = process.env.SUPERTASK_BUN_BIN;
19942
19966
  if (override) return override;
@@ -24128,6 +24152,11 @@ function compareVersions(left, right) {
24128
24152
  function cacheRoot() {
24129
24153
  return process.env.SUPERTASK_OPENCODE_CACHE_DIR ?? join4(homedir4(), ".cache/opencode/packages");
24130
24154
  }
24155
+ function installedPlugins() {
24156
+ const root = cacheRoot();
24157
+ const packageDirs = existsSync6(root) ? readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && (entry.name === PACKAGE_NAME || entry.name.startsWith(`${PACKAGE_NAME}@`))).map((entry) => join4(root, entry.name, "node_modules", PACKAGE_NAME)) : [];
24158
+ return packageDirs.map(pluginAt).filter((plugin) => plugin !== null);
24159
+ }
24131
24160
  function resolveInstalledPlugin() {
24132
24161
  const override = process.env.SUPERTASK_PLUGIN_PACKAGE_DIR;
24133
24162
  if (override) {
@@ -24135,9 +24164,7 @@ function resolveInstalledPlugin() {
24135
24164
  if (!plugin) throw new Error(`[supertask] \u5B89\u88C5\u5305\u65E0\u6548\u6216\u7F3A\u5C11 Gateway \u6784\u5EFA\u4EA7\u7269: ${override}`);
24136
24165
  return plugin;
24137
24166
  }
24138
- const root = cacheRoot();
24139
- const packageDirs = existsSync6(root) ? readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && (entry.name === PACKAGE_NAME || entry.name.startsWith(`${PACKAGE_NAME}@`))).map((entry) => join4(root, entry.name, "node_modules", PACKAGE_NAME)) : [];
24140
- const installed = packageDirs.map(pluginAt).filter((plugin) => plugin !== null).sort((left, right) => compareVersions(right.version, left.version));
24167
+ const installed = installedPlugins().sort((left, right) => compareVersions(right.version, left.version));
24141
24168
  if (!installed[0]) {
24142
24169
  throw new Error(`[supertask] OpenCode \u63D2\u4EF6\u7F13\u5B58\u4E2D\u627E\u4E0D\u5230\u53EF\u8FD0\u884C\u7684 ${PACKAGE_NAME}`);
24143
24170
  }
@@ -24146,10 +24173,52 @@ function resolveInstalledPlugin() {
24146
24173
  function opencodeBin() {
24147
24174
  return process.env.SUPERTASK_OPENCODE_BIN ?? "opencode";
24148
24175
  }
24176
+ function npmBin() {
24177
+ return process.env.SUPERTASK_NPM_BIN ?? "npm";
24178
+ }
24179
+ function latestVersion() {
24180
+ const result = spawnSync3(npmBin(), [
24181
+ "view",
24182
+ PACKAGE_NAME,
24183
+ "dist-tags.latest",
24184
+ "--json"
24185
+ ], {
24186
+ encoding: "utf8",
24187
+ env: process.env,
24188
+ timeout: 3e4
24189
+ });
24190
+ const output = `${result.stdout ?? ""}`.trim();
24191
+ if (result.error) {
24192
+ throw new Error(`[supertask] \u67E5\u8BE2 npm latest \u5931\u8D25: ${result.error.message}`);
24193
+ }
24194
+ if (result.status !== 0) {
24195
+ const detail = `${result.stderr ?? ""}`.trim();
24196
+ throw new Error(`[supertask] \u67E5\u8BE2 npm latest \u5931\u8D25: ${detail || `\u9000\u51FA\u7801 ${result.status}`}`);
24197
+ }
24198
+ let version2;
24199
+ try {
24200
+ version2 = JSON.parse(output);
24201
+ } catch {
24202
+ throw new Error(`[supertask] npm latest \u8FD4\u56DE\u65E0\u6CD5\u89E3\u6790: ${output || "(empty)"}`);
24203
+ }
24204
+ if (typeof version2 !== "string" || versionParts(version2) === null) {
24205
+ throw new Error(`[supertask] npm latest \u7248\u672C\u65E0\u6548: ${String(version2)}`);
24206
+ }
24207
+ return version2;
24208
+ }
24209
+ function resolveInstalledVersion(expectedVersion) {
24210
+ const override = process.env.SUPERTASK_PLUGIN_PACKAGE_DIR;
24211
+ const installed = override ? [pluginAt(override)].filter((plugin) => plugin !== null) : installedPlugins();
24212
+ const matched = installed.find((plugin) => plugin.version === expectedVersion);
24213
+ if (matched) return matched;
24214
+ const actual = installed.length > 0 ? installed.map((plugin) => plugin.version).join(", ") : "\u672A\u627E\u5230\u53EF\u8FD0\u884C\u7F13\u5B58";
24215
+ throw new Error(`[supertask] OpenCode \u63D2\u4EF6\u7F13\u5B58\u7248\u672C\u4E0D\u5339\u914D\uFF1A\u671F\u671B ${expectedVersion}\uFF0C\u5B9E\u9645 ${actual}`);
24216
+ }
24149
24217
  function installLatestPlugin() {
24218
+ const version2 = latestVersion();
24150
24219
  const result = spawnSync3(opencodeBin(), [
24151
24220
  "plugin",
24152
- `${PACKAGE_NAME}@latest`,
24221
+ `${PACKAGE_NAME}@${version2}`,
24153
24222
  "--global",
24154
24223
  "--force"
24155
24224
  ], {
@@ -24164,7 +24233,7 @@ function installLatestPlugin() {
24164
24233
  if (result.status !== 0) {
24165
24234
  throw new Error(`[supertask] OpenCode \u63D2\u4EF6\u66F4\u65B0\u5931\u8D25: ${output || `\u9000\u51FA\u7801 ${result.status}`}`);
24166
24235
  }
24167
- return resolveInstalledPlugin();
24236
+ return resolveInstalledVersion(version2);
24168
24237
  }
24169
24238
  var PACKAGE_NAME;
24170
24239
  var init_update2 = __esm({
@@ -24239,6 +24308,43 @@ async function withDb(fn) {
24239
24308
  closeDb();
24240
24309
  }
24241
24310
  }
24311
+ function messageOf(error) {
24312
+ return error instanceof Error ? error.message : String(error);
24313
+ }
24314
+ function runDestructiveDatabaseMaintenance(keepStopped, operation) {
24315
+ const gatewayState = stopGatewayForMaintenance();
24316
+ let result;
24317
+ try {
24318
+ result = operation();
24319
+ } catch (error) {
24320
+ if (gatewayState.wasRunning && !keepStopped) {
24321
+ try {
24322
+ restartGatewayAfterMaintenance(gatewayState);
24323
+ } catch (restartError) {
24324
+ throw new Error(
24325
+ `${messageOf(error)}\uFF1BGateway \u81EA\u52A8\u6062\u590D\u4E5F\u5931\u8D25\uFF1A${messageOf(restartError)}`
24326
+ );
24327
+ }
24328
+ }
24329
+ throw error;
24330
+ }
24331
+ let restarted = false;
24332
+ if (gatewayState.wasRunning && !keepStopped) {
24333
+ try {
24334
+ restarted = restartGatewayAfterMaintenance(gatewayState);
24335
+ } catch (error) {
24336
+ throw new Error(`\u6570\u636E\u5E93\u7EF4\u62A4\u5DF2\u5B8C\u6210\uFF0C\u4F46 Gateway \u81EA\u52A8\u91CD\u542F\u5931\u8D25\uFF1A${messageOf(error)}`);
24337
+ }
24338
+ }
24339
+ return {
24340
+ ...result,
24341
+ gateway: {
24342
+ wasRunning: gatewayState.wasRunning,
24343
+ restarted,
24344
+ keptStopped: gatewayState.wasRunning && keepStopped
24345
+ }
24346
+ };
24347
+ }
24242
24348
  var program2 = new Command();
24243
24349
  program2.name("supertask").description("\u901A\u7528\u4EFB\u52A1\u7BA1\u7406\u7CFB\u7EDF - AI Agent \u4EFB\u52A1\u8C03\u5EA6\u5668").version(getPackageVersion());
24244
24350
  program2.command("add").description("\u521B\u5EFA\u65B0\u4EFB\u52A1").requiredOption("-n, --name <name>", "\u4EFB\u52A1\u540D\u79F0").requiredOption("-a, --agent <agent>", "\u4E3B Agent \u540D\u79F0").requiredOption("-p, --prompt <prompt>", "\u63D0\u793A\u8BCD").option("-m, --model <model>", "\u6A21\u578B").option("-c, --category <category>", "\u5206\u7C7B", "general").option("-i, --importance <number>", "\u91CD\u8981\u7A0B\u5EA6 (1-5)", "3").option("-u, --urgency <number>", "\u7D27\u6025\u7A0B\u5EA6 (1-5)", "3").option("-b, --batch <batchId>", "\u6279\u6B21 ID").option("-d, --depends <taskId>", "\u4F9D\u8D56\u7684\u4EFB\u52A1 ID").option("--max-retries <number>", "\u9996\u6B21\u6267\u884C\u4E4B\u5916\u5141\u8BB8\u7684\u91CD\u8BD5\u6B21\u6570", "3").option("--retry-backoff <duration>", "\u91CD\u8BD5\u9000\u907F\u57FA\u7840\u95F4\u9694\uFF0C\u5982 30s / 5min", "30s").option("--timeout <duration>", "\u4EFB\u52A1\u786C\u8D85\u65F6\uFF0C\u5982 30min / 2h").option("-w, --cwd <path>", "(\u5DF2\u5E9F\u5F03) \u5DE5\u4F5C\u76EE\u5F55\u3002\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u8BB0\u5F55\u63D0\u4EA4\u4EFB\u52A1\u65F6\u7684\u5F53\u524D\u76EE\u5F55").action(async (options) => withDb(async () => {
@@ -24450,17 +24556,25 @@ databaseCommand.command("check").description("\u68C0\u67E5\u6570\u636E\u5E93\u5B
24450
24556
  databaseCommand.command("backup").description("\u521B\u5EFA\u7ECF\u8FC7\u5B8C\u6574\u6027\u6821\u9A8C\u7684\u4E00\u81F4\u6027\u5907\u4EFD").option("-o, --output <path>", "\u5907\u4EFD\u6587\u4EF6\u8DEF\u5F84\uFF08\u9ED8\u8BA4\u5199\u5165\u6570\u636E\u5E93\u76EE\u5F55\uFF09").action(async (options) => withDb(async () => {
24451
24557
  console.log(JSON.stringify(DatabaseMaintenanceService.backup(options.output), null, 2));
24452
24558
  }));
24453
- databaseCommand.command("clear").description("\u5907\u4EFD\u540E\u4E8B\u52A1\u6027\u6E05\u7A7A\u4EFB\u52A1\u3001\u6267\u884C\u8BB0\u5F55\u548C\u8C03\u5EA6\u6A21\u677F").option("--confirm <word>", "\u5371\u9669\u64CD\u4F5C\u786E\u8BA4\uFF0C\u5FC5\u987B\u586B\u5199 CLEAR").action(async (options) => withDb(async () => {
24559
+ databaseCommand.command("clear").description("\u5907\u4EFD\u540E\u4E8B\u52A1\u6027\u6E05\u7A7A\u4EFB\u52A1\u3001\u6267\u884C\u8BB0\u5F55\u548C\u8C03\u5EA6\u6A21\u677F").option("--confirm <word>", "\u5371\u9669\u64CD\u4F5C\u786E\u8BA4\uFF0C\u5FC5\u987B\u586B\u5199 CLEAR").option("--keep-stopped", "\u7EF4\u62A4\u7ED3\u675F\u540E\u4E0D\u91CD\u542F\u539F\u672C\u7531 PM2 \u7BA1\u7406\u7684 Gateway").action(async (options) => withDb(async () => {
24454
24560
  if (options.confirm !== "CLEAR") {
24455
24561
  throw new Error("\u6E05\u7A7A\u6570\u636E\u5E93\u5FC5\u987B\u663E\u5F0F\u4F20\u5165 --confirm CLEAR");
24456
24562
  }
24457
- console.log(JSON.stringify(DatabaseMaintenanceService.clear(), null, 2));
24563
+ const result = runDestructiveDatabaseMaintenance(
24564
+ options.keepStopped ?? false,
24565
+ () => DatabaseMaintenanceService.clear()
24566
+ );
24567
+ console.log(JSON.stringify(result, null, 2));
24458
24568
  }));
24459
- databaseCommand.command("restore").description("\u81EA\u52A8\u5907\u4EFD\u5F53\u524D\u5E93\u540E\uFF0C\u4ECE\u6307\u5B9A\u5907\u4EFD\u6062\u590D\u6570\u636E\u5E93").requiredOption("--from <path>", "\u8981\u6062\u590D\u7684 SQLite \u5907\u4EFD\u6587\u4EF6").option("--confirm <word>", "\u5371\u9669\u64CD\u4F5C\u786E\u8BA4\uFF0C\u5FC5\u987B\u586B\u5199 RESTORE").action(async (options) => withDb(async () => {
24569
+ databaseCommand.command("restore").description("\u81EA\u52A8\u5907\u4EFD\u5F53\u524D\u5E93\u540E\uFF0C\u4ECE\u6307\u5B9A\u5907\u4EFD\u6062\u590D\u6570\u636E\u5E93").requiredOption("--from <path>", "\u8981\u6062\u590D\u7684 SQLite \u5907\u4EFD\u6587\u4EF6").option("--confirm <word>", "\u5371\u9669\u64CD\u4F5C\u786E\u8BA4\uFF0C\u5FC5\u987B\u586B\u5199 RESTORE").option("--keep-stopped", "\u7EF4\u62A4\u7ED3\u675F\u540E\u4E0D\u91CD\u542F\u539F\u672C\u7531 PM2 \u7BA1\u7406\u7684 Gateway").action(async (options) => withDb(async () => {
24460
24570
  if (options.confirm !== "RESTORE") {
24461
24571
  throw new Error("\u6062\u590D\u6570\u636E\u5E93\u5FC5\u987B\u663E\u5F0F\u4F20\u5165 --confirm RESTORE");
24462
24572
  }
24463
- console.log(JSON.stringify(DatabaseMaintenanceService.restore(options.from), null, 2));
24573
+ const result = runDestructiveDatabaseMaintenance(
24574
+ options.keepStopped ?? false,
24575
+ () => DatabaseMaintenanceService.restore(options.from)
24576
+ );
24577
+ console.log(JSON.stringify(result, null, 2));
24464
24578
  }));
24465
24579
  program2.addCommand(databaseCommand);
24466
24580
  program2.command("init").description("Initialize SuperTask (create config + run migrations)").action(async () => withDb(async () => {
@@ -24536,7 +24650,7 @@ program2.command("upgrade").description("Update OpenCode plugin cache and restar
24536
24650
  installed = installLatestPlugin2();
24537
24651
  } catch (err) {
24538
24652
  console.error(err instanceof Error ? err.message : String(err));
24539
- console.error("Try manually: opencode plugin opencode-supertask@latest --global --force");
24653
+ console.error("Try manually: query npm dist-tags.latest, then install that exact version with opencode plugin.");
24540
24654
  process.exit(1);
24541
24655
  }
24542
24656
  try {