opencode-supertask 0.1.21 → 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -9734,6 +9734,23 @@ var init_task_service = __esm({
9734
9734
  ).returning();
9735
9735
  return result.length;
9736
9736
  }
9737
+ static async resetOrphanRunningToPending() {
9738
+ const result = await db.update(tasks2).set({
9739
+ status: "pending",
9740
+ startedAt: null,
9741
+ finishedAt: null
9742
+ }).where(
9743
+ and(
9744
+ eq(tasks2.status, "running"),
9745
+ sql`NOT EXISTS (
9746
+ SELECT 1 FROM ${taskRuns2}
9747
+ WHERE ${taskRuns2.taskId} = ${tasks2.id}
9748
+ AND ${taskRuns2.status} = 'running'
9749
+ )`
9750
+ )
9751
+ ).returning();
9752
+ return result.length;
9753
+ }
9737
9754
  static async cancel(id, scope = {}) {
9738
9755
  const conditions = [
9739
9756
  eq(tasks2.id, id),
@@ -19290,6 +19307,7 @@ __export(pm2_exports, {
19290
19307
  ensureGateway: () => ensureGateway,
19291
19308
  getPackageVersion: () => getPackageVersion,
19292
19309
  install: () => install,
19310
+ installMacLaunchAgent: () => installMacLaunchAgent,
19293
19311
  isGatewayReady: () => isGatewayReady,
19294
19312
  isGatewayRunning: () => isGatewayRunning,
19295
19313
  isPm2Installed: () => isPm2Installed,
@@ -19298,9 +19316,9 @@ __export(pm2_exports, {
19298
19316
  upgrade: () => upgrade
19299
19317
  });
19300
19318
  import { execSync, spawnSync } from "child_process";
19301
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
19319
+ import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
19302
19320
  import { homedir as homedir3 } from "os";
19303
- import { dirname as dirname2, join as join3 } from "path";
19321
+ import { dirname as dirname2, isAbsolute, join as join3 } from "path";
19304
19322
  import { fileURLToPath as fileURLToPath2 } from "url";
19305
19323
  import { Database as Database3 } from "bun:sqlite";
19306
19324
  function getPackageVersion() {
@@ -19348,6 +19366,83 @@ function writeRunningVersion(version2) {
19348
19366
  function pm2Bin() {
19349
19367
  return process.env.SUPERTASK_PM2_BIN ?? (process.platform === "win32" ? "pm2.cmd" : "pm2");
19350
19368
  }
19369
+ function xmlEscape(value) {
19370
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
19371
+ }
19372
+ function resolvePm2Bin() {
19373
+ const configured = pm2Bin();
19374
+ if (isAbsolute(configured)) return configured;
19375
+ const result = spawnSync("which", [configured], { encoding: "utf8" });
19376
+ const resolved = result.status === 0 ? result.stdout.trim().split("\n")[0] : "";
19377
+ if (!resolved) throw new Error(`[supertask] \u65E0\u6CD5\u89E3\u6790 pm2 \u53EF\u6267\u884C\u6587\u4EF6: ${configured}`);
19378
+ return resolved;
19379
+ }
19380
+ function launchAgentPath() {
19381
+ return process.env.SUPERTASK_LAUNCH_AGENT_PATH ?? join3(homedir3(), "Library/LaunchAgents", `${MAC_LAUNCH_AGENT_LABEL}.plist`);
19382
+ }
19383
+ function launchctlBin() {
19384
+ return process.env.SUPERTASK_LAUNCHCTL_BIN ?? "launchctl";
19385
+ }
19386
+ function installMacLaunchAgent() {
19387
+ if (typeof process.getuid !== "function") {
19388
+ throw new Error("[supertask] \u5F53\u524D\u8FD0\u884C\u65F6\u65E0\u6CD5\u83B7\u53D6 macOS \u7528\u6237 ID");
19389
+ }
19390
+ const path = launchAgentPath();
19391
+ const home = homedir3();
19392
+ const pm2Home = process.env.PM2_HOME ?? join3(home, ".pm2");
19393
+ const environmentPath = process.env.PATH ?? "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
19394
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
19395
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
19396
+ <plist version="1.0">
19397
+ <dict>
19398
+ <key>Label</key>
19399
+ <string>${MAC_LAUNCH_AGENT_LABEL}</string>
19400
+ <key>ProgramArguments</key>
19401
+ <array>
19402
+ <string>${xmlEscape(resolvePm2Bin())}</string>
19403
+ <string>resurrect</string>
19404
+ </array>
19405
+ <key>RunAtLoad</key>
19406
+ <true/>
19407
+ <key>KeepAlive</key>
19408
+ <dict>
19409
+ <key>SuccessfulExit</key>
19410
+ <false/>
19411
+ </dict>
19412
+ <key>ThrottleInterval</key>
19413
+ <integer>10</integer>
19414
+ <key>EnvironmentVariables</key>
19415
+ <dict>
19416
+ <key>HOME</key>
19417
+ <string>${xmlEscape(home)}</string>
19418
+ <key>PATH</key>
19419
+ <string>${xmlEscape(environmentPath)}</string>
19420
+ <key>PM2_HOME</key>
19421
+ <string>${xmlEscape(pm2Home)}</string>
19422
+ </dict>
19423
+ <key>StandardErrorPath</key>
19424
+ <string>${xmlEscape(join3(pm2Home, "supertask-launchd-error.log"))}</string>
19425
+ <key>StandardOutPath</key>
19426
+ <string>${xmlEscape(join3(pm2Home, "supertask-launchd-output.log"))}</string>
19427
+ </dict>
19428
+ </plist>
19429
+ `;
19430
+ mkdirSync2(dirname2(path), { recursive: true });
19431
+ writeFileSync(path, plist, { mode: 384 });
19432
+ chmodSync(path, 384);
19433
+ const domain = `gui/${process.getuid()}`;
19434
+ spawnSync(launchctlBin(), ["bootout", `${domain}/${MAC_LAUNCH_AGENT_LABEL}`], {
19435
+ stdio: "ignore"
19436
+ });
19437
+ const loaded = spawnSync(launchctlBin(), ["bootstrap", domain, path], {
19438
+ encoding: "utf8"
19439
+ });
19440
+ if (loaded.status !== 0) {
19441
+ const output = `${loaded.stdout ?? ""}${loaded.stderr ?? ""}`.trim();
19442
+ throw new Error(`[supertask] macOS LaunchAgent \u52A0\u8F7D\u5931\u8D25: ${output || `\u9000\u51FA\u7801 ${loaded.status}`}`);
19443
+ }
19444
+ return path;
19445
+ }
19351
19446
  function isPm2Installed() {
19352
19447
  const result = spawnSync(pm2Bin(), ["--version"], {
19353
19448
  stdio: "ignore",
@@ -19486,10 +19581,19 @@ function install() {
19486
19581
  pm2StartGateway();
19487
19582
  writeRunningVersion(version2);
19488
19583
  savePm2State();
19489
- const startup = pm2Exec(["startup"]);
19490
- if (startup.output) console.log(startup.output);
19491
- if (!startup.ok) {
19492
- console.warn("[supertask] pm2 startup \u672A\u5B8C\u6210\uFF1B\u8BF7\u6309 pm2 \u8F93\u51FA\u6267\u884C\u9700\u8981\u7BA1\u7406\u5458\u6743\u9650\u7684\u547D\u4EE4\uFF0C\u7136\u540E\u8FD0\u884C `pm2 save`\u3002");
19584
+ if (process.platform === "darwin") {
19585
+ try {
19586
+ const path = installMacLaunchAgent();
19587
+ console.log(`[supertask] macOS LaunchAgent installed: ${path}`);
19588
+ } catch (error) {
19589
+ console.warn(error instanceof Error ? error.message : String(error));
19590
+ }
19591
+ } else {
19592
+ const startup = pm2Exec(["startup"]);
19593
+ if (startup.output) console.log(startup.output);
19594
+ if (!startup.ok) {
19595
+ console.warn("[supertask] pm2 startup \u672A\u5B8C\u6210\uFF1B\u8BF7\u6309 pm2 \u8F93\u51FA\u6267\u884C\u9700\u8981\u7BA1\u7406\u5458\u6743\u9650\u7684\u547D\u4EE4\uFF0C\u7136\u540E\u8FD0\u884C `pm2 save`\u3002");
19596
+ }
19493
19597
  }
19494
19598
  console.log("[supertask] Gateway installed and running.");
19495
19599
  console.log("[supertask] Manage with: pm2 status / pm2 logs supertask-gateway");
@@ -19547,13 +19651,14 @@ function ensureGateway() {
19547
19651
  savePm2State();
19548
19652
  return { ok: true, action: existing ? "restarted" : "started" };
19549
19653
  }
19550
- var __dirname, PROCESS_NAME, GATEWAY_LOCK_STALE_MS;
19654
+ var __dirname, PROCESS_NAME, MAC_LAUNCH_AGENT_LABEL, GATEWAY_LOCK_STALE_MS;
19551
19655
  var init_pm2 = __esm({
19552
19656
  "src/daemon/pm2.ts"() {
19553
19657
  "use strict";
19554
19658
  init_config();
19555
19659
  __dirname = dirname2(fileURLToPath2(import.meta.url));
19556
19660
  PROCESS_NAME = "supertask-gateway";
19661
+ MAC_LAUNCH_AGENT_LABEL = "com.supertask.pm2-resurrect";
19557
19662
  GATEWAY_LOCK_STALE_MS = 3e4;
19558
19663
  }
19559
19664
  });
@@ -19768,6 +19873,15 @@ import { basename } from "path";
19768
19873
  function isSafePid(pid) {
19769
19874
  return Number.isInteger(pid) && pid > 1 && pid !== process.pid;
19770
19875
  }
19876
+ function isProcessAlive(pid) {
19877
+ if (!Number.isInteger(pid) || pid <= 0) return false;
19878
+ try {
19879
+ process.kill(pid, 0);
19880
+ return true;
19881
+ } catch (error) {
19882
+ return error instanceof Error && "code" in error && error.code === "EPERM";
19883
+ }
19884
+ }
19771
19885
  function inspectUnixProcess(pid) {
19772
19886
  const result = spawnSync2("ps", ["-o", "pgid=", "-o", "command=", "-p", String(pid)], {
19773
19887
  encoding: "utf8",
@@ -23463,7 +23577,9 @@ var init_web = __esm({
23463
23577
  // src/gateway/index.ts
23464
23578
  var gateway_exports = {};
23465
23579
  __export(gateway_exports, {
23466
- main: () => main
23580
+ acquireLock: () => acquireLock,
23581
+ main: () => main,
23582
+ releaseLock: () => releaseLock
23467
23583
  });
23468
23584
  function acquireLock() {
23469
23585
  const now = Date.now();
@@ -23472,7 +23588,8 @@ function acquireLock() {
23472
23588
  sqlite.exec("BEGIN IMMEDIATE");
23473
23589
  const existing = sqlite.prepare("SELECT id, pid, heartbeat_at FROM gateway_lock WHERE id = 1").get();
23474
23590
  if (existing) {
23475
- if (now - existing.heartbeat_at < STALE_THRESHOLD_MS) {
23591
+ const lockHolderAlive = existing.pid !== pid && isProcessAlive(existing.pid);
23592
+ if (now - existing.heartbeat_at < STALE_THRESHOLD_MS && lockHolderAlive) {
23476
23593
  sqlite.exec("ROLLBACK");
23477
23594
  console.error(JSON.stringify({
23478
23595
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -23542,6 +23659,15 @@ async function main() {
23542
23659
  const worker = new WorkerEngine(cfg);
23543
23660
  const watchdog = new Watchdog(cfg);
23544
23661
  const scheduler = new Scheduler(cfg);
23662
+ const recoveredOrphans = await TaskService.resetOrphanRunningToPending();
23663
+ if (recoveredOrphans > 0) {
23664
+ console.log(JSON.stringify({
23665
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
23666
+ level: "warn",
23667
+ msg: "reset orphan running tasks to pending",
23668
+ count: recoveredOrphans
23669
+ }));
23670
+ }
23545
23671
  initializeGatewayHealth({
23546
23672
  workerPollIntervalMs: cfg.worker.pollIntervalMs,
23547
23673
  schedulerEnabled: cfg.scheduler.enabled,
@@ -23621,6 +23747,7 @@ var init_gateway = __esm({
23621
23747
  init_task_service();
23622
23748
  init_task_run_service();
23623
23749
  init_health();
23750
+ init_process_control();
23624
23751
  STALE_THRESHOLD_MS = 3e4;
23625
23752
  if (import.meta.main) {
23626
23753
  main();