opencode-supertask 0.1.4 → 0.1.6

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
@@ -19106,6 +19106,10 @@ var init_config = __esm({
19106
19106
  cleanupIntervalMs: 6e4,
19107
19107
  retentionDays: 30
19108
19108
  },
19109
+ dashboard: {
19110
+ enabled: true,
19111
+ port: 4680
19112
+ },
19109
19113
  logging: {
19110
19114
  level: "info",
19111
19115
  format: "json"
@@ -19723,137 +19727,6 @@ var init_scheduler = __esm({
19723
19727
  }
19724
19728
  });
19725
19729
 
19726
- // src/gateway/index.ts
19727
- var gateway_exports = {};
19728
- __export(gateway_exports, {
19729
- main: () => main
19730
- });
19731
- function acquireLock() {
19732
- const now = Date.now();
19733
- const pid = process.pid;
19734
- try {
19735
- sqlite.exec("BEGIN IMMEDIATE");
19736
- const existing = sqlite.prepare("SELECT id, pid, heartbeat_at FROM gateway_lock WHERE id = 1").get();
19737
- if (existing) {
19738
- if (now - existing.heartbeat_at < STALE_THRESHOLD_MS) {
19739
- sqlite.exec("ROLLBACK");
19740
- console.error(JSON.stringify({
19741
- ts: (/* @__PURE__ */ new Date()).toISOString(),
19742
- level: "fatal",
19743
- msg: "another Gateway instance is already running",
19744
- existingPid: existing.pid
19745
- }));
19746
- return false;
19747
- }
19748
- sqlite.exec("DELETE FROM gateway_lock WHERE id = 1");
19749
- }
19750
- sqlite.exec(
19751
- "INSERT INTO gateway_lock (id, pid, acquired_at, heartbeat_at) VALUES (1, ?, ?, ?)",
19752
- [pid, now, now]
19753
- );
19754
- sqlite.exec("COMMIT");
19755
- return true;
19756
- } catch (err) {
19757
- try {
19758
- sqlite.exec("ROLLBACK");
19759
- } catch {
19760
- }
19761
- console.error(JSON.stringify({
19762
- ts: (/* @__PURE__ */ new Date()).toISOString(),
19763
- level: "fatal",
19764
- msg: "failed to acquire lock",
19765
- error: err instanceof Error ? err.message : String(err)
19766
- }));
19767
- return false;
19768
- }
19769
- }
19770
- function releaseLock() {
19771
- try {
19772
- sqlite.exec("DELETE FROM gateway_lock WHERE pid = ?", [process.pid]);
19773
- } catch {
19774
- }
19775
- }
19776
- function updateLockHeartbeat() {
19777
- try {
19778
- sqlite.exec(
19779
- "UPDATE gateway_lock SET heartbeat_at = ? WHERE pid = ?",
19780
- [Date.now(), process.pid]
19781
- );
19782
- } catch {
19783
- }
19784
- }
19785
- async function main() {
19786
- console.log(JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), level: "info", msg: "SuperTask Gateway starting", pid: process.pid }));
19787
- if (!acquireLock()) {
19788
- process.exit(1);
19789
- }
19790
- const heartbeatTimer = setInterval(updateLockHeartbeat, 1e4);
19791
- heartbeatTimer.unref();
19792
- const cfg = loadConfig();
19793
- const worker = new WorkerEngine(cfg);
19794
- const watchdog = new Watchdog(cfg);
19795
- const scheduler = new Scheduler(cfg);
19796
- worker.start();
19797
- watchdog.start();
19798
- await scheduler.start();
19799
- console.log(JSON.stringify({
19800
- ts: (/* @__PURE__ */ new Date()).toISOString(),
19801
- level: "info",
19802
- msg: "Gateway started",
19803
- maxConcurrency: cfg.worker.maxConcurrency,
19804
- schedulerEnabled: cfg.scheduler.enabled
19805
- }));
19806
- let shuttingDown = false;
19807
- const shutdown = async (signal) => {
19808
- if (shuttingDown) return;
19809
- shuttingDown = true;
19810
- console.log(JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), level: "info", msg: `received ${signal}, shutting down...` }));
19811
- clearInterval(heartbeatTimer);
19812
- scheduler.stop();
19813
- watchdog.stop();
19814
- const runningIds = worker.getRunningTaskIds();
19815
- await worker.stop();
19816
- if (runningIds.length > 0) {
19817
- const resetCount = await TaskService.resetRunningToPending(runningIds);
19818
- console.log(JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), level: "info", msg: "reset running tasks to pending", count: resetCount }));
19819
- }
19820
- const allRunningRuns = await TaskRunService.getAllRunningRuns();
19821
- for (const run of allRunningRuns) {
19822
- await TaskRunService.fail(run.id, "Gateway shutdown");
19823
- }
19824
- releaseLock();
19825
- closeDb();
19826
- console.log(JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), level: "info", msg: "Gateway stopped" }));
19827
- process.exit(0);
19828
- };
19829
- process.on("SIGTERM", () => shutdown("SIGTERM"));
19830
- process.on("SIGINT", () => shutdown("SIGINT"));
19831
- process.on("uncaughtException", (err) => {
19832
- console.error(JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), level: "fatal", msg: "uncaughtException", error: err.message, stack: err.stack }));
19833
- });
19834
- process.on("unhandledRejection", (reason) => {
19835
- console.error(JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), level: "fatal", msg: "unhandledRejection", reason: String(reason) }));
19836
- });
19837
- }
19838
- var STALE_THRESHOLD_MS;
19839
- var init_gateway = __esm({
19840
- "src/gateway/index.ts"() {
19841
- "use strict";
19842
- init_db2();
19843
- init_config();
19844
- init_worker();
19845
- init_watchdog();
19846
- init_scheduler();
19847
- init_db2();
19848
- init_task_service();
19849
- init_task_run_service();
19850
- STALE_THRESHOLD_MS = 3e4;
19851
- if (import.meta.main) {
19852
- main();
19853
- }
19854
- }
19855
- });
19856
-
19857
19730
  // node_modules/hono/dist/compose.js
19858
19731
  var compose;
19859
19732
  var init_compose = __esm({
@@ -22191,6 +22064,7 @@ var init_html2 = __esm({
22191
22064
  // src/web/index.tsx
22192
22065
  var web_exports = {};
22193
22066
  __export(web_exports, {
22067
+ dashboardApp: () => dashboardApp,
22194
22068
  default: () => web_default
22195
22069
  });
22196
22070
  import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
@@ -22301,7 +22175,7 @@ async function saveConfig(){
22301
22175
  <dialog id="dd"><div class="dh"><h3 style="margin:0">\u8BE6\u60C5</h3><button class="cb" onclick="document.getElementById('dd').close()">&times;</button></div><div class="db"><pre id="dc"></pre></div></dialog>
22302
22176
  </body></html>`;
22303
22177
  }
22304
- var app, SHARED_STYLES, web_default;
22178
+ var app, SHARED_STYLES, dashboardApp, web_default;
22305
22179
  var init_web = __esm({
22306
22180
  "src/web/index.tsx"() {
22307
22181
  "use strict";
@@ -22725,13 +22599,155 @@ var init_web = __esm({
22725
22599
  return c.json({ success: false, error: err instanceof Error ? err.message : String(err) });
22726
22600
  }
22727
22601
  });
22602
+ dashboardApp = app;
22728
22603
  web_default = {
22729
- port: 3e3,
22604
+ port: 4680,
22730
22605
  fetch: app.fetch
22731
22606
  };
22732
22607
  }
22733
22608
  });
22734
22609
 
22610
+ // src/gateway/index.ts
22611
+ var gateway_exports = {};
22612
+ __export(gateway_exports, {
22613
+ main: () => main
22614
+ });
22615
+ function acquireLock() {
22616
+ const now = Date.now();
22617
+ const pid = process.pid;
22618
+ try {
22619
+ sqlite.exec("BEGIN IMMEDIATE");
22620
+ const existing = sqlite.prepare("SELECT id, pid, heartbeat_at FROM gateway_lock WHERE id = 1").get();
22621
+ if (existing) {
22622
+ if (now - existing.heartbeat_at < STALE_THRESHOLD_MS) {
22623
+ sqlite.exec("ROLLBACK");
22624
+ console.error(JSON.stringify({
22625
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
22626
+ level: "fatal",
22627
+ msg: "another Gateway instance is already running",
22628
+ existingPid: existing.pid
22629
+ }));
22630
+ return false;
22631
+ }
22632
+ sqlite.exec("DELETE FROM gateway_lock WHERE id = 1");
22633
+ }
22634
+ sqlite.exec(
22635
+ "INSERT INTO gateway_lock (id, pid, acquired_at, heartbeat_at) VALUES (1, ?, ?, ?)",
22636
+ [pid, now, now]
22637
+ );
22638
+ sqlite.exec("COMMIT");
22639
+ return true;
22640
+ } catch (err) {
22641
+ try {
22642
+ sqlite.exec("ROLLBACK");
22643
+ } catch {
22644
+ }
22645
+ console.error(JSON.stringify({
22646
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
22647
+ level: "fatal",
22648
+ msg: "failed to acquire lock",
22649
+ error: err instanceof Error ? err.message : String(err)
22650
+ }));
22651
+ return false;
22652
+ }
22653
+ }
22654
+ function releaseLock() {
22655
+ try {
22656
+ sqlite.exec("DELETE FROM gateway_lock WHERE pid = ?", [process.pid]);
22657
+ } catch {
22658
+ }
22659
+ }
22660
+ function updateLockHeartbeat() {
22661
+ try {
22662
+ sqlite.exec(
22663
+ "UPDATE gateway_lock SET heartbeat_at = ? WHERE pid = ?",
22664
+ [Date.now(), process.pid]
22665
+ );
22666
+ } catch {
22667
+ }
22668
+ }
22669
+ async function main() {
22670
+ console.log(JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), level: "info", msg: "SuperTask Gateway starting", pid: process.pid }));
22671
+ if (!acquireLock()) {
22672
+ process.exit(1);
22673
+ }
22674
+ const heartbeatTimer = setInterval(updateLockHeartbeat, 1e4);
22675
+ heartbeatTimer.unref();
22676
+ const cfg = loadConfig();
22677
+ const worker = new WorkerEngine(cfg);
22678
+ const watchdog = new Watchdog(cfg);
22679
+ const scheduler = new Scheduler(cfg);
22680
+ worker.start();
22681
+ watchdog.start();
22682
+ await scheduler.start();
22683
+ if (cfg.dashboard.enabled) {
22684
+ const { dashboardApp: dashboardApp2 } = await Promise.resolve().then(() => (init_web(), web_exports));
22685
+ Bun.serve({ port: cfg.dashboard.port, fetch: dashboardApp2.fetch });
22686
+ console.log(JSON.stringify({
22687
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
22688
+ level: "info",
22689
+ msg: "Dashboard started",
22690
+ url: `http://localhost:${cfg.dashboard.port}`
22691
+ }));
22692
+ }
22693
+ console.log(JSON.stringify({
22694
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
22695
+ level: "info",
22696
+ msg: "Gateway started",
22697
+ maxConcurrency: cfg.worker.maxConcurrency,
22698
+ schedulerEnabled: cfg.scheduler.enabled
22699
+ }));
22700
+ let shuttingDown = false;
22701
+ const shutdown = async (signal) => {
22702
+ if (shuttingDown) return;
22703
+ shuttingDown = true;
22704
+ console.log(JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), level: "info", msg: `received ${signal}, shutting down...` }));
22705
+ clearInterval(heartbeatTimer);
22706
+ scheduler.stop();
22707
+ watchdog.stop();
22708
+ const runningIds = worker.getRunningTaskIds();
22709
+ await worker.stop();
22710
+ if (runningIds.length > 0) {
22711
+ const resetCount = await TaskService.resetRunningToPending(runningIds);
22712
+ console.log(JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), level: "info", msg: "reset running tasks to pending", count: resetCount }));
22713
+ }
22714
+ const allRunningRuns = await TaskRunService.getAllRunningRuns();
22715
+ for (const run of allRunningRuns) {
22716
+ await TaskRunService.fail(run.id, "Gateway shutdown");
22717
+ }
22718
+ releaseLock();
22719
+ closeDb();
22720
+ console.log(JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), level: "info", msg: "Gateway stopped" }));
22721
+ process.exit(0);
22722
+ };
22723
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
22724
+ process.on("SIGINT", () => shutdown("SIGINT"));
22725
+ process.on("uncaughtException", (err) => {
22726
+ console.error(JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), level: "fatal", msg: "uncaughtException", error: err.message, stack: err.stack }));
22727
+ });
22728
+ process.on("unhandledRejection", (reason) => {
22729
+ console.error(JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), level: "fatal", msg: "unhandledRejection", reason: String(reason) }));
22730
+ });
22731
+ }
22732
+ var STALE_THRESHOLD_MS;
22733
+ var init_gateway = __esm({
22734
+ "src/gateway/index.ts"() {
22735
+ "use strict";
22736
+ init_db2();
22737
+ init_config();
22738
+ init_worker();
22739
+ init_watchdog();
22740
+ init_scheduler();
22741
+ init_db2();
22742
+ init_task_service();
22743
+ init_task_run_service();
22744
+ STALE_THRESHOLD_MS = 3e4;
22745
+ if (import.meta.main) {
22746
+ main();
22747
+ }
22748
+ }
22749
+ });
22750
+
22735
22751
  // src/daemon/pm2.ts
22736
22752
  var pm2_exports = {};
22737
22753
  __export(pm2_exports, {
@@ -22943,6 +22959,37 @@ var {
22943
22959
  init_task_service();
22944
22960
  init_task_template_service();
22945
22961
  init_db2();
22962
+
22963
+ // src/core/duration.ts
22964
+ var DURATION_REGEX = /^(\d+(?:\.\d+)?)\s*(ms|s|sec|seconds?|min|minutes?|m|h|hours?|d|days?|w|weeks?)$/i;
22965
+ var ISO8601_REGEX = /^P(?:([.\d]+)D)?(?:T(?:([.\d]+)H)?(?:([.\d]+)M)?(?:([.\d]+)S)?)?$/i;
22966
+ function parseDuration(input) {
22967
+ const trimmed = input.trim();
22968
+ const simple = DURATION_REGEX.exec(trimmed);
22969
+ if (simple) {
22970
+ const value = parseFloat(simple[1]);
22971
+ const unit = simple[2].toLowerCase();
22972
+ if (unit === "ms") return value;
22973
+ if (unit === "s" || unit === "sec" || unit === "second" || unit === "seconds") return value * 1e3;
22974
+ if (unit === "min" || unit === "minute" || unit === "minutes" || unit === "m") return value * 6e4;
22975
+ if (unit === "h" || unit === "hour" || unit === "hours") return value * 36e5;
22976
+ if (unit === "d" || unit === "day" || unit === "days") return value * 864e5;
22977
+ if (unit === "w" || unit === "week" || unit === "weeks") return value * 6048e5;
22978
+ }
22979
+ const iso = ISO8601_REGEX.exec(trimmed);
22980
+ if (iso) {
22981
+ const days = parseFloat(iso[1] ?? "0");
22982
+ const hours = parseFloat(iso[2] ?? "0");
22983
+ const minutes = parseFloat(iso[3] ?? "0");
22984
+ const seconds = parseFloat(iso[4] ?? "0");
22985
+ return (days * 86400 + hours * 3600 + minutes * 60 + seconds) * 1e3;
22986
+ }
22987
+ const asNumber = Number(trimmed);
22988
+ if (!isNaN(asNumber) && asNumber > 0) return asNumber;
22989
+ return null;
22990
+ }
22991
+
22992
+ // src/cli/index.ts
22946
22993
  async function withDb(fn) {
22947
22994
  try {
22948
22995
  return await fn();
@@ -23075,7 +23122,24 @@ program2.command("delete").description("\u5220\u9664\u4EFB\u52A1").requiredOptio
23075
23122
  console.log(JSON.stringify({ deleted, id: parseInt(options.id) }));
23076
23123
  }));
23077
23124
  program2.command("template").description("\u7BA1\u7406\u4EFB\u52A1\u8C03\u5EA6\u6A21\u677F").addCommand(
23078
- new Command("add").description("\u521B\u5EFA\u8C03\u5EA6\u6A21\u677F").requiredOption("-n, --name <name>", "\u6A21\u677F\u540D\u79F0").requiredOption("-a, --agent <agent>", "Agent \u540D\u79F0").requiredOption("-p, --prompt <prompt>", "\u63D0\u793A\u8BCD").requiredOption("-t, --type <type>", "\u8C03\u5EA6\u7C7B\u578B\uFF1Acron/delayed/recurring").option("--cron <expr>", "cron \u8868\u8FBE\u5F0F\uFF08cron \u7C7B\u578B\u5FC5\u586B\uFF09").option("--interval <ms>", "\u95F4\u9694\u6BEB\u79D2\uFF08recurring \u7C7B\u578B\u5FC5\u586B\uFF09").option("--run-at <ms>", "\u6267\u884C\u65F6\u95F4\u6233 ms\uFF08delayed \u7C7B\u578B\u5FC5\u586B\uFF09").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("--max-instances <number>", "\u6700\u5927\u5E76\u53D1\u5B9E\u4F8B\u6570", "1").option("--max-retries <number>", "\u6700\u5927\u91CD\u8BD5\u6B21\u6570", "3").option("--retry-backoff <ms>", "\u9000\u907F\u57FA\u7840\u95F4\u9694 ms", "30000").action(async (options) => withDb(async () => {
23125
+ new Command("add").description("\u521B\u5EFA\u8C03\u5EA6\u6A21\u677F").requiredOption("-n, --name <name>", "\u6A21\u677F\u540D\u79F0").requiredOption("-a, --agent <agent>", "Agent \u540D\u79F0").requiredOption("-p, --prompt <prompt>", "\u63D0\u793A\u8BCD").requiredOption("-t, --type <type>", "\u8C03\u5EA6\u7C7B\u578B\uFF1Acron/delayed/recurring").option("--cron <expr>", "cron \u8868\u8FBE\u5F0F\uFF08cron \u7C7B\u578B\u5FC5\u586B\uFF09").option("--delay <duration>", "\u5EF6\u8FDF\u65F6\u95F4\uFF08delayed \u7C7B\u578B\u5FC5\u586B\uFF09\uFF0C\u5982 30s / 5min / 1h / 2d").option("--interval <duration>", "\u5FAA\u73AF\u95F4\u9694\uFF08recurring \u7C7B\u578B\u5FC5\u586B\uFF09\uFF0C\u5982 1h / 30min / 5s").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("--max-instances <number>", "\u6700\u5927\u5E76\u53D1\u5B9E\u4F8B\u6570", "1").option("--max-retries <number>", "\u6700\u5927\u91CD\u8BD5\u6B21\u6570", "3").option("--retry-backoff <ms>", "\u9000\u907F\u57FA\u7840\u95F4\u9694 ms", "30000").action(async (options) => withDb(async () => {
23126
+ let intervalMs = null;
23127
+ let runAt = null;
23128
+ if (options.interval) {
23129
+ intervalMs = parseDuration(options.interval);
23130
+ if (intervalMs === null) {
23131
+ console.error(JSON.stringify({ error: `Invalid interval: "${options.interval}". Use 30s / 5min / 1h / 2d` }));
23132
+ process.exit(1);
23133
+ }
23134
+ }
23135
+ if (options.delay) {
23136
+ const delayMs = parseDuration(options.delay);
23137
+ if (delayMs === null) {
23138
+ console.error(JSON.stringify({ error: `Invalid delay: "${options.delay}". Use 30s / 5min / 1h / 2d` }));
23139
+ process.exit(1);
23140
+ }
23141
+ runAt = Date.now() + delayMs;
23142
+ }
23079
23143
  const tmpl = await TaskTemplateService.create({
23080
23144
  name: options.name,
23081
23145
  agent: options.agent,
@@ -23086,8 +23150,8 @@ program2.command("template").description("\u7BA1\u7406\u4EFB\u52A1\u8C03\u5EA6\u
23086
23150
  urgency: parseInt(options.urgency),
23087
23151
  scheduleType: options.type,
23088
23152
  cronExpr: options.cron,
23089
- intervalMs: options.interval ? parseInt(options.interval) : null,
23090
- runAt: options.runAt ? parseInt(options.runAt) : null,
23153
+ intervalMs,
23154
+ runAt,
23091
23155
  maxInstances: parseInt(options.maxInstances),
23092
23156
  maxRetries: parseInt(options.maxRetries),
23093
23157
  retryBackoffMs: parseInt(options.retryBackoff)
@@ -23162,8 +23226,17 @@ program2.command("gateway").description("Start the Gateway process (foreground)"
23162
23226
  const { main: main2 } = await Promise.resolve().then(() => (init_gateway(), gateway_exports));
23163
23227
  await main2();
23164
23228
  });
23165
- program2.command("ui").description("Start the Web Dashboard").action(async () => {
23166
- await Promise.resolve().then(() => (init_web(), web_exports));
23229
+ program2.command("ui").description("Open Web Dashboard (embedded in Gateway)").action(async () => {
23230
+ const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
23231
+ const cfg = loadConfig2();
23232
+ const url = `http://localhost:${cfg.dashboard.port}`;
23233
+ console.log(`Dashboard: ${url}`);
23234
+ try {
23235
+ const { execSync: execSync2 } = await import("child_process");
23236
+ const cmd = process.platform === "win32" ? `start ${url}` : process.platform === "darwin" ? `open ${url}` : `xdg-open ${url}`;
23237
+ execSync2(cmd, { stdio: "ignore" });
23238
+ } catch {
23239
+ }
23167
23240
  });
23168
23241
  program2.command("config").description("Show current configuration").action(async () => {
23169
23242
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));