0sec-cli 0.22.1 → 0.22.4

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.
Files changed (107) hide show
  1. package/0.js +56 -45
  2. package/README.md +12 -0
  3. package/chunks/{adapt-loop-J6QCZZTB.js → adapt-loop-3CCOWE43.js} +4 -4
  4. package/chunks/{artifact-scraper-MFKT6OI4.js → artifact-scraper-Z4UUIOH3.js} +2 -2
  5. package/chunks/{assumption-mining-646LYSJY.js → assumption-mining-2UAKQB6V.js} +18 -14
  6. package/chunks/{chunk-OHHCGDJ2.js → chunk-2WWAPKAW.js} +126 -1089
  7. package/chunks/{chunk-XDKB2P5X.js → chunk-3BPX75OR.js} +1 -1
  8. package/chunks/chunk-4ZYYX5GY.js +74 -0
  9. package/chunks/{chunk-3E2KALZ5.js → chunk-5JNTMWPK.js} +2 -2
  10. package/chunks/{chunk-CPWA6RWE.js → chunk-5WAJGLCW.js} +3 -3
  11. package/chunks/{chunk-KAU6NLGP.js → chunk-6KFB6AYN.js} +515 -20
  12. package/chunks/{chunk-XA2O4WXA.js → chunk-6WQDXWQC.js} +169 -15
  13. package/chunks/chunk-6YZE552K.js +5603 -0
  14. package/chunks/chunk-BGX53365.js +309 -0
  15. package/chunks/{chunk-X7LAEEUW.js → chunk-C6JPA32H.js} +1 -1
  16. package/chunks/chunk-CF3MNS3O.js +990 -0
  17. package/chunks/{chunk-4VGLO2YS.js → chunk-CPCHLL6A.js} +5 -2
  18. package/chunks/{chunk-ZKQ2I3S2.js → chunk-DPUXXMJW.js} +1 -1
  19. package/chunks/{chunk-XCISCI4T.js → chunk-E3OOBVOK.js} +30 -12
  20. package/chunks/{chunk-4G7OVYJX.js → chunk-E74LXQFY.js} +1 -1
  21. package/chunks/{chunk-OSBPGWTE.js → chunk-ELI3BVIK.js} +3 -3
  22. package/chunks/chunk-FFHRANYC.js +547 -0
  23. package/chunks/{chunk-WSDDGFA6.js → chunk-G2BE2FC7.js} +1 -1
  24. package/chunks/chunk-GQ3DGHWE.js +1659 -0
  25. package/chunks/{chunk-V36HVTUL.js → chunk-GVY3S5E5.js} +21 -18
  26. package/chunks/{chunk-5BTJMINS.js → chunk-H4DH7COV.js} +192 -40
  27. package/chunks/{chunk-HHPWRY2M.js → chunk-H6Q2QAIY.js} +23475 -22747
  28. package/chunks/{chunk-7XCKYNLO.js → chunk-IHPTOTLL.js} +1 -1
  29. package/chunks/chunk-IXAHQICR.js +31 -0
  30. package/chunks/{chunk-TH2LW437.js → chunk-KCA35IL3.js} +2 -190
  31. package/chunks/{chunk-VWITLWXB.js → chunk-L2CCO4KE.js} +282 -34
  32. package/chunks/{chunk-6YNKRUMC.js → chunk-NJLKFC66.js} +321 -5
  33. package/chunks/{chunk-W6UWZFKL.js → chunk-O6KPVYOI.js} +100 -5
  34. package/chunks/{chunk-LGCASF2C.js → chunk-OAS5SAVZ.js} +6767 -2100
  35. package/chunks/chunk-PS5E7LUQ.js +277 -0
  36. package/chunks/{chunk-AV2LG2L7.js → chunk-QVIRWJ45.js} +257 -145
  37. package/chunks/{chunk-TV27OWIW.js → chunk-QWK4R3DB.js} +9 -9
  38. package/chunks/{chunk-WZRVS7SB.js → chunk-RCRTSKQN.js} +1 -1
  39. package/chunks/chunk-RS4MAN3Q.js +73 -0
  40. package/chunks/{chunk-V6GHX34G.js → chunk-RTBZ36BD.js} +8 -274
  41. package/chunks/{chunk-YL6YAGLL.js → chunk-SCWWLABR.js} +1 -1
  42. package/chunks/chunk-VDOWARMJ.js +63 -0
  43. package/chunks/{chunk-IFME676H.js → chunk-WBRGXCU2.js} +79 -18
  44. package/chunks/{chunk-IVQV574Y.js → chunk-WHUAUFSX.js} +1 -1
  45. package/chunks/{chunk-GOEA3NW6.js → chunk-XODDLNBI.js} +1534 -1305
  46. package/chunks/chunk-YAZARMJY.js +355 -0
  47. package/chunks/{chunk-NLH4LMWH.js → chunk-YED25PA4.js} +1 -1
  48. package/chunks/{codex-models-NHQW45SO.js → codex-models-Z43V3427.js} +9 -3
  49. package/chunks/{commands-N6GN5SBF.js → commands-EUGF4FUZ.js} +5842 -4093
  50. package/chunks/{db-5IWEJUYV.js → db-NVAW4WWR.js} +3 -3
  51. package/chunks/{dist-L3XJWWZ6.js → dist-27BNG6WD.js} +193 -112
  52. package/chunks/{dist-FSS7MBDF.js → dist-7RUOKPRJ.js} +12 -2
  53. package/chunks/{dist-KEULE5IQ.js → dist-LNXGATR2.js} +2 -2
  54. package/chunks/{dist-XZB53PS5.js → dist-M2X367VW.js} +2 -2
  55. package/chunks/{dist-BS4CY4RG.js → dist-NG2HT5XU.js} +27 -1
  56. package/chunks/{eval-runner-EMEGF24P.js → eval-runner-6Q3H52SP.js} +13 -9
  57. package/chunks/{exploit-agent-KW4EB7BE.js → exploit-agent-FCCR2HYX.js} +1 -1
  58. package/chunks/{exploit-autoclimb-CXJ2ERRO.js → exploit-autoclimb-X5ZS5FAO.js} +1 -1
  59. package/chunks/{exploit-climb-TJWLY4XC.js → exploit-climb-3JHGEO36.js} +2 -2
  60. package/chunks/{fix-UUKPXCFB.js → fix-NSNPU33B.js} +12 -6
  61. package/chunks/{harness-C5HOKIEV.js → harness-KXQOVIDQ.js} +3 -3
  62. package/chunks/{hunt-scan-KZZ444V2.js → hunt-scan-ELTKGKC4.js} +16 -12
  63. package/chunks/{kernel-primitive-Z77IR7YS.js → kernel-primitive-MT7YZSIG.js} +2 -2
  64. package/chunks/{kernel-vm-runner-JW77P276.js → kernel-vm-runner-YFRHYC6G.js} +2 -2
  65. package/chunks/{native-loop-P6P5GYVJ.js → native-loop-OFJDRHPI.js} +18 -8
  66. package/chunks/orchestrate-DRZ2SVCP.js +62 -0
  67. package/chunks/{prepare-KYHGFWCJ.js → prepare-72GWTRVI.js} +6 -2
  68. package/chunks/{process-N245QIQG.js → process-MJYJ2K5E.js} +2 -1
  69. package/chunks/{run-GAFKBCTN.js → run-WTJCZZHV.js} +1901 -4816
  70. package/chunks/{runtime-H7YBR5IZ.js → runtime-FCKOUB7V.js} +10 -4
  71. package/chunks/{runtime-XFWJTBUO.js → runtime-LCJ4XSKE.js} +5 -1
  72. package/chunks/{scan-stream-BMOCPKQX.js → scan-stream-GT6GKQ3U.js} +7 -1
  73. package/chunks/{scope-QWY4VQZ7.js → scope-E5QZUOOH.js} +2 -2
  74. package/chunks/service-plugins-MSK2EQP6.js +62 -0
  75. package/chunks/{session-store-XIDFGG7K.js → session-store-H3Q7KCYG.js} +6 -4
  76. package/chunks/{variant-candidates-7D4F5E4H.js → variant-candidates-SZKDCOLK.js} +9 -3
  77. package/chunks/{web-recon-prepass-VXXBPDAV.js → web-recon-prepass-IO3GXQMW.js} +11 -6
  78. package/dashboard/apple-touch-icon.png +0 -0
  79. package/dashboard/assets/operations-BvB5Qpmk.css +1 -0
  80. package/dashboard/assets/operations-D8Ii01SV.js +114 -0
  81. package/dashboard/assets/zero-peek-Dzm3MT3X.png +0 -0
  82. package/dashboard/favicon.ico +0 -0
  83. package/dashboard/favicon.svg +10 -0
  84. package/dashboard/index.html +5 -3
  85. package/package.json +1 -1
  86. package/chunks/chunk-6GHSR47F.js +0 -147
  87. package/chunks/chunk-BCSRRQG5.js +0 -3514
  88. package/chunks/chunk-BOUGKEKA.js +0 -193
  89. package/chunks/chunk-GHWODZMR.js +0 -16
  90. package/chunks/orchestrate-DWWUT5RC.js +0 -58
  91. package/dashboard/assets/bot-Bg1Tp9_d.js +0 -1
  92. package/dashboard/assets/dist-B_dDCPbu.js +0 -1
  93. package/dashboard/assets/findings-page-mayGhNx6.js +0 -5
  94. package/dashboard/assets/format--susmHX7.js +0 -1
  95. package/dashboard/assets/input-TuVPR3Zl.js +0 -1
  96. package/dashboard/assets/jsx-runtime-MGseXjj5.js +0 -3
  97. package/dashboard/assets/live-page-HWWHX99U.js +0 -1
  98. package/dashboard/assets/meta-tile-ckHo-w7k.js +0 -1
  99. package/dashboard/assets/operations-BxZ0OoXO.js +0 -10
  100. package/dashboard/assets/operations-CMFVrXEe.css +0 -2
  101. package/dashboard/assets/operations-app-D3d35rgo.js +0 -46
  102. package/dashboard/assets/overview-page-W_4IteEr.js +0 -1
  103. package/dashboard/assets/page-header-Bw9AJgBV.js +0 -1
  104. package/dashboard/assets/scans-page-CgzVXHnn.js +0 -1
  105. package/dashboard/assets/siren-KQj8QAnJ.js +0 -1
  106. package/dashboard/assets/table-8EDklulN.js +0 -1
  107. package/dashboard/assets/tabs-CFHPOwg8.js +0 -1
@@ -3,8 +3,9 @@ import { createRequire as __0CreateRequire } from "node:module";
3
3
  const require = __0CreateRequire(import.meta.url);
4
4
  import {
5
5
  homeStateDir,
6
+ parseSecurityWorkflowInput,
6
7
  runStateDir
7
- } from "./chunk-KAU6NLGP.js";
8
+ } from "./chunk-6KFB6AYN.js";
8
9
  import {
9
10
  __export
10
11
  } from "./chunk-VCSFCHAJ.js";
@@ -229,11 +230,22 @@ function createDrizzleFromShim(client, config = {}) {
229
230
  return db;
230
231
  }
231
232
 
233
+ // packages/db/dist/db-directory.js
234
+ import { existsSync, mkdirSync } from "node:fs";
235
+ import { dirname, resolve } from "node:path";
236
+ function ensureDatabaseDirectory(path) {
237
+ if (path === ":memory:")
238
+ return;
239
+ const directory = dirname(resolve(path));
240
+ if (!existsSync(directory))
241
+ mkdirSync(directory, { recursive: true, mode: 448 });
242
+ }
243
+
232
244
  // packages/db/dist/database.js
233
245
  import { asc, eq, desc, and, gt, inArray, or } from "drizzle-orm";
234
246
  import { createHash, randomUUID } from "node:crypto";
235
- import { dirname, join } from "node:path";
236
- import { closeSync, existsSync, mkdirSync, openSync, readSync, renameSync, rmSync, statSync, writeSync } from "node:fs";
247
+ import { join } from "node:path";
248
+ import { closeSync, existsSync as existsSync2, openSync, readSync, renameSync, rmSync, statSync, writeSync } from "node:fs";
237
249
 
238
250
  // packages/db/dist/schema.js
239
251
  var schema_exports = {};
@@ -622,12 +634,10 @@ function resolveOsecDbPath(dbPath) {
622
634
  }
623
635
  function resetOsecDatabase(dbPath) {
624
636
  const path = resolveOsecDbPath(dbPath);
625
- if (path !== ":memory:") {
626
- mkdirSync(dirname(path), { recursive: true, mode: 448 });
627
- }
637
+ ensureDatabaseDirectory(path);
628
638
  for (const suffix of ["", "-wal", "-shm"]) {
629
639
  const candidate = `${path}${suffix}`;
630
- if (existsSync(candidate)) {
640
+ if (existsSync2(candidate)) {
631
641
  rmSync(candidate, { force: true });
632
642
  }
633
643
  }
@@ -635,9 +645,7 @@ function resetOsecDatabase(dbPath) {
635
645
  }
636
646
  function repairOsecDatabase(dbPath) {
637
647
  const path = resolveOsecDbPath(dbPath);
638
- if (path !== ":memory:") {
639
- mkdirSync(dirname(path), { recursive: true, mode: 448 });
640
- }
648
+ ensureDatabaseDirectory(path);
641
649
  clearStaleLockIfAny(path);
642
650
  const backupPath = backupCorruptDatabase(path) ?? void 0;
643
651
  resetOsecDatabase(path);
@@ -653,13 +661,13 @@ function timestampTag() {
653
661
  return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
654
662
  }
655
663
  function backupCorruptDatabase(path) {
656
- if (!existsSync(path))
664
+ if (!existsSync2(path))
657
665
  return null;
658
666
  const backupPath = `${path}.corrupt-${timestampTag()}`;
659
667
  renameSync(path, backupPath);
660
668
  for (const suffix of ["-wal", "-shm", ".lock"]) {
661
669
  const candidate = `${path}${suffix}`;
662
- if (!existsSync(candidate))
670
+ if (!existsSync2(candidate))
663
671
  continue;
664
672
  try {
665
673
  renameSync(candidate, `${backupPath}${suffix}`);
@@ -679,7 +687,7 @@ function ensureDatabaseHealthy(sqlite) {
679
687
  }
680
688
  }
681
689
  function migrateWalHeaderIfNeeded(path) {
682
- if (!existsSync(path))
690
+ if (!existsSync2(path))
683
691
  return;
684
692
  let size;
685
693
  try {
@@ -708,7 +716,7 @@ function migrateWalHeaderIfNeeded(path) {
708
716
  const shmPath = `${path}-shm`;
709
717
  let hadWalData = false;
710
718
  try {
711
- if (existsSync(walPath) && statSync(walPath).size > 0)
719
+ if (existsSync2(walPath) && statSync(walPath).size > 0)
712
720
  hadWalData = true;
713
721
  } catch {
714
722
  }
@@ -723,7 +731,7 @@ function migrateWalHeaderIfNeeded(path) {
723
731
  return;
724
732
  }
725
733
  for (const sidecar of [walPath, shmPath]) {
726
- if (existsSync(sidecar)) {
734
+ if (existsSync2(sidecar)) {
727
735
  try {
728
736
  rmSync(sidecar, { force: true });
729
737
  } catch {
@@ -760,15 +768,13 @@ var osecDB = class {
760
768
  constructor(dbPath, options = {}) {
761
769
  const path = resolveOsecDbPath(dbPath);
762
770
  if (options.readOnly) {
763
- if (path !== ":memory:" && !existsSync(path)) {
771
+ if (path !== ":memory:" && !existsSync2(path)) {
764
772
  throw new Error(`Database does not exist: ${path}`);
765
773
  }
766
774
  this.initializeReadOnlyDatabase(path);
767
775
  return;
768
776
  }
769
- if (path !== ":memory:") {
770
- mkdirSync(dirname(path), { recursive: true, mode: 448 });
771
- }
777
+ ensureDatabaseDirectory(path);
772
778
  this.openWithRecovery(path);
773
779
  }
774
780
  openWithRecovery(path) {
@@ -1954,8 +1960,8 @@ var osecDB = class {
1954
1960
  });
1955
1961
  return result.changes;
1956
1962
  }
1957
- deleteWorkersByStatus(statuses) {
1958
- const values = Array.isArray(statuses) ? statuses : [statuses];
1963
+ deleteWorkersByStatus(statuses2) {
1964
+ const values = Array.isArray(statuses2) ? statuses2 : [statuses2];
1959
1965
  if (values.length === 0)
1960
1966
  return 0;
1961
1967
  const placeholders = values.map(() => "?").join(",");
@@ -2526,8 +2532,8 @@ function normalizeWorkflowStatus(value, fallback) {
2526
2532
 
2527
2533
  // packages/db/dist/run-storage.js
2528
2534
  import { randomUUID as randomUUID2 } from "node:crypto";
2529
- import { chmodSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, renameSync as renameSync2, rmSync as rmSync2, writeFileSync } from "node:fs";
2530
- import { dirname as dirname2, isAbsolute, join as join2, resolve } from "node:path";
2535
+ import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, renameSync as renameSync2, rmSync as rmSync2, writeFileSync } from "node:fs";
2536
+ import { dirname as dirname2, isAbsolute, join as join2, resolve as resolve2 } from "node:path";
2531
2537
  var STATE_DB_FILE = "state.db";
2532
2538
  var REPORT_FILE = "report.json";
2533
2539
  function normalizeRunId(value) {
@@ -2543,10 +2549,10 @@ function resolveOsecRunStorage(options = {}) {
2543
2549
  const stateDir = homeStateDir(options.homeDir);
2544
2550
  const configuredRunDir = env["ZERO_RUN_DIR"]?.trim() || void 0;
2545
2551
  const configuredDbPath = options.dbPath ?? (env["ZERO_DB_PATH"]?.trim() || void 0);
2546
- let runDir = configuredRunDir ? resolve(configuredRunDir) : runStateDir(runId, options.homeDir);
2547
- if (options.resume && !configuredRunDir && !configuredDbPath && !existsSync2(runDir)) {
2552
+ let runDir = configuredRunDir ? resolve2(configuredRunDir) : runStateDir(runId, options.homeDir);
2553
+ if (options.resume && !configuredRunDir && !configuredDbPath && !existsSync3(runDir)) {
2548
2554
  const runsDir = join2(stateDir, "runs");
2549
- if (existsSync2(runsDir)) {
2555
+ if (existsSync3(runsDir)) {
2550
2556
  const matches = readdirSync(runsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith(runId));
2551
2557
  if (matches.length > 1) {
2552
2558
  throw new Error(`0 run id prefix '${runId}' is ambiguous.`);
@@ -2561,12 +2567,12 @@ function resolveOsecRunStorage(options = {}) {
2561
2567
  if (!isAbsolute(runDir)) {
2562
2568
  throw new Error(`0 run directory must be absolute: ${JSON.stringify(runDir)}.`);
2563
2569
  }
2564
- let dbPath = configuredDbPath ? configuredDbPath === ":memory:" ? configuredDbPath : resolve(configuredDbPath) : join2(runDir, STATE_DB_FILE);
2570
+ let dbPath = configuredDbPath ? configuredDbPath === ":memory:" ? configuredDbPath : resolve2(configuredDbPath) : join2(runDir, STATE_DB_FILE);
2565
2571
  const configuredReportPath = env["ZERO_REPORT_PATH"]?.trim() || void 0;
2566
- const reportPath = configuredReportPath ? resolve(configuredReportPath) : !configuredDbPath || configuredRunDir ? join2(runDir, REPORT_FILE) : void 0;
2567
- if (options.resume && !configuredDbPath && !existsSync2(dbPath)) {
2572
+ const reportPath = configuredReportPath ? resolve2(configuredReportPath) : !configuredDbPath || configuredRunDir ? join2(runDir, REPORT_FILE) : void 0;
2573
+ if (options.resume && !configuredDbPath && !existsSync3(dbPath)) {
2568
2574
  const legacyDbPath = join2(stateDir, "0.db");
2569
- if (existsSync2(legacyDbPath))
2575
+ if (existsSync3(legacyDbPath))
2570
2576
  dbPath = legacyDbPath;
2571
2577
  }
2572
2578
  if (!configuredDbPath || configuredRunDir || reportPath) {
@@ -2587,9 +2593,9 @@ function resolveOsecRunStorage(options = {}) {
2587
2593
  }
2588
2594
  function listOsecRunDatabasePaths(homeDir) {
2589
2595
  const runsDir = join2(homeStateDir(homeDir), "runs");
2590
- if (!existsSync2(runsDir))
2596
+ if (!existsSync3(runsDir))
2591
2597
  return [];
2592
- return readdirSync(runsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join2(runsDir, entry.name, STATE_DB_FILE)).filter((path) => existsSync2(path));
2598
+ return readdirSync(runsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join2(runsDir, entry.name, STATE_DB_FILE)).filter((path) => existsSync3(path));
2593
2599
  }
2594
2600
  function writeOsecRunReport(storage, report) {
2595
2601
  const reportPath = storage.reportPath;
@@ -2603,12 +2609,249 @@ function writeOsecRunReport(storage, report) {
2603
2609
  renameSync2(temporaryPath, reportPath);
2604
2610
  chmodSync(reportPath, 384);
2605
2611
  } finally {
2606
- if (existsSync2(temporaryPath)) {
2612
+ if (existsSync3(temporaryPath)) {
2607
2613
  rmSync2(temporaryPath, { force: true });
2608
2614
  }
2609
2615
  }
2610
2616
  }
2611
2617
 
2618
+ // packages/db/dist/security-workflows.js
2619
+ import { randomUUID as randomUUID3 } from "node:crypto";
2620
+ var SecurityWorkflowStoreError = class extends Error {
2621
+ statusCode;
2622
+ constructor(message, statusCode) {
2623
+ super(message);
2624
+ this.statusCode = statusCode;
2625
+ this.name = "SecurityWorkflowStoreError";
2626
+ }
2627
+ };
2628
+ var activeStatuses = { queued: true, running: true };
2629
+ var statuses = { queued: true, running: true, completed: true, failed: true, cancelled: true, interrupted: true };
2630
+ var SecurityWorkflowStore = class {
2631
+ #db;
2632
+ #runnerInstanceId = randomUUID3();
2633
+ constructor(dbPath) {
2634
+ const path = resolveOsecDbPath(dbPath);
2635
+ ensureDatabaseDirectory(path);
2636
+ this.#db = createShimmedDatabase(path);
2637
+ this.#db.pragma("busy_timeout = 5000");
2638
+ this.#db.exec(`CREATE TABLE IF NOT EXISTS workflow_definitions (id TEXT PRIMARY KEY, revision INTEGER NOT NULL, definition_json TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL);
2639
+ CREATE TABLE IF NOT EXISTS workflow_executions (id TEXT PRIMARY KEY, workflow_id TEXT NOT NULL, workflow_revision INTEGER NOT NULL, session_id TEXT NOT NULL, status TEXT NOT NULL, execution_json TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL);
2640
+ CREATE INDEX IF NOT EXISTS workflow_executions_workflow ON workflow_executions(workflow_id, created_at);
2641
+ CREATE INDEX IF NOT EXISTS workflow_executions_session ON workflow_executions(session_id, created_at);`);
2642
+ }
2643
+ list() {
2644
+ return this.#db.prepare("SELECT definition_json FROM workflow_definitions ORDER BY updated_at DESC, id").all().map((row) => JSON.parse(String(row.definition_json)));
2645
+ }
2646
+ get(id) {
2647
+ const row = this.#db.prepare("SELECT definition_json FROM workflow_definitions WHERE id = ?").all(id)[0];
2648
+ return row ? JSON.parse(String(row.definition_json)) : null;
2649
+ }
2650
+ save(value) {
2651
+ let input;
2652
+ try {
2653
+ input = parseSecurityWorkflowInput(value);
2654
+ } catch (error) {
2655
+ throw new SecurityWorkflowStoreError(error instanceof Error ? error.message : "Invalid workflow.", 400);
2656
+ }
2657
+ return this.#db.transaction(() => {
2658
+ const existing = input.id ? this.get(input.id) : null;
2659
+ if (input.id && !existing && input.revision !== void 0)
2660
+ throw new SecurityWorkflowStoreError("Workflow not found.", 404);
2661
+ if (existing && input.revision !== existing.revision)
2662
+ throw new SecurityWorkflowStoreError("Workflow changed. Reload before saving.", 409);
2663
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2664
+ const workflow = { ...input, id: input.id ?? randomUUID3(), revision: (existing?.revision ?? 0) + 1, createdAt: existing?.createdAt ?? now, updatedAt: now };
2665
+ if (existing)
2666
+ this.#db.prepare("UPDATE workflow_definitions SET revision = ?, definition_json = ?, updated_at = ? WHERE id = ?").run(workflow.revision, JSON.stringify(workflow), now, workflow.id);
2667
+ else
2668
+ this.#db.prepare("INSERT INTO workflow_definitions (id, revision, definition_json, created_at, updated_at) VALUES (?, ?, ?, ?, ?)").run(workflow.id, workflow.revision, JSON.stringify(workflow), now, now);
2669
+ return workflow;
2670
+ })();
2671
+ }
2672
+ delete(id, revision) {
2673
+ return this.#db.transaction(() => {
2674
+ const workflow = this.get(id);
2675
+ if (!workflow)
2676
+ return false;
2677
+ if (revision !== void 0 && revision !== workflow.revision)
2678
+ throw new SecurityWorkflowStoreError("Workflow changed. Reload before deleting.", 409);
2679
+ this.#db.prepare("DELETE FROM workflow_definitions WHERE id = ?").run(id);
2680
+ return true;
2681
+ })();
2682
+ }
2683
+ createExecution(workflowId, sessionId, revision) {
2684
+ if (!sessionId || sessionId.length > 128)
2685
+ throw new SecurityWorkflowStoreError("Invalid execution session.", 400);
2686
+ return this.#db.transaction(() => {
2687
+ const workflow = this.get(workflowId);
2688
+ if (!workflow)
2689
+ throw new SecurityWorkflowStoreError("Workflow not found.", 404);
2690
+ if (revision !== void 0 && revision !== workflow.revision)
2691
+ throw new SecurityWorkflowStoreError("Workflow changed. Review it before running.", 409);
2692
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2693
+ const execution = { id: randomUUID3(), workflowId, workflowRevision: workflow.revision, workflow, sessionId, ownerPid: process.pid, runnerInstanceId: this.#runnerInstanceId, status: "queued", nodeResults: {}, createdAt: now, updatedAt: now };
2694
+ this.#db.prepare("INSERT INTO workflow_executions (id, workflow_id, workflow_revision, session_id, status, execution_json, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)").run(execution.id, workflowId, workflow.revision, sessionId, execution.status, JSON.stringify(execution), now, now);
2695
+ return execution;
2696
+ })();
2697
+ }
2698
+ getExecution(id) {
2699
+ const row = this.#db.prepare("SELECT execution_json FROM workflow_executions WHERE id = ?").all(id)[0];
2700
+ return row ? JSON.parse(String(row.execution_json)) : null;
2701
+ }
2702
+ listExecutions(workflowId, sessionId) {
2703
+ return this.#db.prepare("SELECT execution_json FROM workflow_executions WHERE (? IS NULL OR workflow_id = ?) AND (? IS NULL OR session_id = ?) ORDER BY created_at DESC, id LIMIT 200").all(workflowId ?? null, workflowId ?? null, sessionId ?? null, sessionId ?? null).map((row) => JSON.parse(String(row.execution_json)));
2704
+ }
2705
+ updateExecution(id, update) {
2706
+ return this.#db.transaction(() => {
2707
+ const current = this.getExecution(id);
2708
+ if (!current)
2709
+ throw new SecurityWorkflowStoreError("Execution not found.", 404);
2710
+ if (!activeStatuses[current.status])
2711
+ throw new SecurityWorkflowStoreError("Execution is already finished.", 409);
2712
+ if (update.status !== void 0 && (!Object.hasOwn(statuses, update.status) || current.status === "running" && update.status === "queued"))
2713
+ throw new SecurityWorkflowStoreError("Invalid execution status transition.", 400);
2714
+ if (update.jobId !== void 0 && (typeof update.jobId !== "string" || update.jobId.length > 128))
2715
+ throw new SecurityWorkflowStoreError("Invalid execution job ID.", 400);
2716
+ if (update.error !== void 0 && (typeof update.error !== "string" || update.error.length > 16e3))
2717
+ throw new SecurityWorkflowStoreError("Invalid execution error.", 400);
2718
+ if (update.nodeResults !== void 0) {
2719
+ const nodeIds = new Set(current.workflow.nodes.map((node) => node.id));
2720
+ if (!update.nodeResults || typeof update.nodeResults !== "object" || Array.isArray(update.nodeResults) || JSON.stringify(update.nodeResults).length > 1e5 || Object.entries(update.nodeResults).some(([key, result]) => {
2721
+ if (!nodeIds.has(key) || !result || typeof result !== "object" || Array.isArray(result) || !Object.hasOwn(result, "status") || typeof result.status !== "string" || !result.status.length || result.status.length > 64)
2722
+ return true;
2723
+ if (Object.keys(result).some((field) => !["status", "jobId", "scanId", "scanIds", "dbPaths", "error"].includes(field)))
2724
+ return true;
2725
+ if ([result.jobId, result.scanId].some((value) => value !== void 0 && (typeof value !== "string" || value.length > 128)))
2726
+ return true;
2727
+ if (result.error !== void 0 && (typeof result.error !== "string" || result.error.length > 16e3))
2728
+ return true;
2729
+ return [result.scanIds, result.dbPaths].some((values) => values !== void 0 && (!Array.isArray(values) || values.length > 64 || values.some((value) => typeof value !== "string" || value.length > 4096)));
2730
+ }))
2731
+ throw new SecurityWorkflowStoreError("Invalid execution node results.", 400);
2732
+ }
2733
+ const execution = { ...current, status: update.status ?? current.status, jobId: update.jobId ?? current.jobId, nodeResults: update.nodeResults ?? current.nodeResults, error: update.error ?? current.error, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
2734
+ this.#db.prepare("UPDATE workflow_executions SET status = ?, execution_json = ?, updated_at = ? WHERE id = ?").run(execution.status, JSON.stringify(execution), execution.updatedAt, id);
2735
+ return execution;
2736
+ })();
2737
+ }
2738
+ interruptActiveExecutions(isOwnerAlive = processAlive) {
2739
+ const rows = this.#db.prepare("SELECT execution_json FROM workflow_executions WHERE status IN ('queued', 'running')").all();
2740
+ let interrupted = 0;
2741
+ for (const row of rows) {
2742
+ const execution = JSON.parse(String(row.execution_json));
2743
+ if (!Number.isInteger(execution.ownerPid) || execution.ownerPid <= 0 || isOwnerAlive(execution.ownerPid))
2744
+ continue;
2745
+ try {
2746
+ this.updateExecution(execution.id, { status: "interrupted", error: "The local engine stopped. Run this workflow again to continue." });
2747
+ interrupted++;
2748
+ } catch (error) {
2749
+ if (!(error instanceof SecurityWorkflowStoreError && error.statusCode === 409))
2750
+ throw error;
2751
+ }
2752
+ }
2753
+ return interrupted;
2754
+ }
2755
+ close() {
2756
+ this.#db.close();
2757
+ }
2758
+ };
2759
+ function processAlive(pid) {
2760
+ try {
2761
+ process.kill(pid, 0);
2762
+ return true;
2763
+ } catch (error) {
2764
+ return !(error && typeof error === "object" && "code" in error && error.code === "ESRCH");
2765
+ }
2766
+ }
2767
+
2768
+ // packages/db/dist/workflow-triggers.js
2769
+ import { randomUUID as randomUUID4 } from "node:crypto";
2770
+ var TRIGGER_INTERVAL_MS = { hourly: 36e5, daily: 864e5, weekly: 6048e5 };
2771
+ function nextWorkflowTriggerFire(startAt, cadence, now) {
2772
+ const anchor = Date.parse(startAt);
2773
+ const interval = TRIGGER_INTERVAL_MS[cadence];
2774
+ if (!Number.isFinite(anchor) || !interval || !Number.isFinite(now))
2775
+ throw new Error("Invalid schedule.");
2776
+ return new Date(anchor > now ? anchor : anchor + (Math.floor((now - anchor) / interval) + 1) * interval).toISOString();
2777
+ }
2778
+ var WorkflowTriggerStore = class {
2779
+ #db;
2780
+ constructor(dbPath) {
2781
+ const path = resolveOsecDbPath(dbPath);
2782
+ ensureDatabaseDirectory(path);
2783
+ this.#db = createShimmedDatabase(path);
2784
+ this.#db.pragma("busy_timeout = 5000");
2785
+ this.#db.exec("CREATE TABLE IF NOT EXISTS workflow_triggers (id TEXT PRIMARY KEY, workflow_id TEXT NOT NULL, enabled INTEGER NOT NULL, next_fire_at TEXT NOT NULL, active_claim TEXT, trigger_json TEXT NOT NULL)");
2786
+ this.#db.exec("CREATE INDEX IF NOT EXISTS workflow_triggers_due ON workflow_triggers(enabled, next_fire_at)");
2787
+ }
2788
+ get(id) {
2789
+ const row = this.#db.prepare("SELECT trigger_json FROM workflow_triggers WHERE id = ?").all(id)[0];
2790
+ return row ? JSON.parse(String(row.trigger_json)) : null;
2791
+ }
2792
+ list(workflowId) {
2793
+ return this.#db.prepare("SELECT trigger_json FROM workflow_triggers WHERE (? IS NULL OR workflow_id = ?) ORDER BY next_fire_at, id LIMIT 200").all(workflowId ?? null, workflowId ?? null).map((row) => JSON.parse(String(row.trigger_json)));
2794
+ }
2795
+ create(input, now = Date.now()) {
2796
+ const stamp = new Date(now).toISOString();
2797
+ const trigger = { ...input, kind: "schedule", id: randomUUID4(), version: 1, nextFireAt: nextWorkflowTriggerFire(input.startAt, input.cadence, now), createdAt: stamp, updatedAt: stamp };
2798
+ this.#db.prepare("INSERT INTO workflow_triggers(id,workflow_id,enabled,next_fire_at,active_claim,trigger_json) VALUES(?,?,?,?,NULL,?)").run(trigger.id, trigger.workflowId, trigger.enabled ? 1 : 0, trigger.nextFireAt, JSON.stringify(trigger));
2799
+ return trigger;
2800
+ }
2801
+ update(id, update, expectedVersion) {
2802
+ return this.#db.transaction(() => {
2803
+ const current = this.get(id);
2804
+ if (!current)
2805
+ return null;
2806
+ if (expectedVersion !== void 0 && (current.version ?? 1) !== expectedVersion)
2807
+ throw new Error("Schedule changed. Review it again before enabling.");
2808
+ const next = { ...current, ...update, version: (current.version ?? 1) + 1, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
2809
+ if (!next.enabled && update.lastStatus === "running")
2810
+ next.lastStatus = "paused";
2811
+ this.#write(next);
2812
+ return next;
2813
+ })();
2814
+ }
2815
+ delete(id) {
2816
+ return this.#db.prepare("DELETE FROM workflow_triggers WHERE id = ? AND active_claim IS NULL").run(id).changes > 0;
2817
+ }
2818
+ /** CAS claims the occurrence across engines; advancing from now skips missed intervals. */
2819
+ claim(id, now = Date.now()) {
2820
+ const current = this.get(id);
2821
+ if (!current?.enabled || current.activeClaim || Date.parse(current.nextFireAt) > now)
2822
+ return null;
2823
+ const claim = randomUUID4();
2824
+ const next = { ...current, version: (current.version ?? 1) + 1, activeClaim: claim, ownerPid: process.pid, nextFireAt: nextWorkflowTriggerFire(current.startAt, current.cadence, now), lastStatus: "starting", lastExecutionId: void 0, lastError: void 0, updatedAt: new Date(now).toISOString() };
2825
+ this.#db.prepare("UPDATE workflow_triggers SET active_claim=?, next_fire_at=?, trigger_json=? WHERE id=? AND enabled=1 AND active_claim IS NULL AND next_fire_at=? AND trigger_json=?").run(claim, next.nextFireAt, JSON.stringify(next), id, current.nextFireAt, JSON.stringify(current));
2826
+ return this.get(id)?.activeClaim === claim ? next : null;
2827
+ }
2828
+ release(id, claim, update) {
2829
+ this.#db.exec("BEGIN IMMEDIATE");
2830
+ try {
2831
+ const current = this.get(id);
2832
+ if (!current || current.activeClaim !== claim) {
2833
+ this.#db.exec("COMMIT");
2834
+ return null;
2835
+ }
2836
+ const next = { ...current, ...update, version: (current.version ?? 1) + 1, enabled: current.enabled && update.enabled !== false, activeClaim: void 0, ownerPid: void 0, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
2837
+ if (!current.enabled && update.enabled !== false)
2838
+ next.lastStatus = "paused";
2839
+ this.#db.prepare("UPDATE workflow_triggers SET active_claim=NULL,enabled=?,trigger_json=? WHERE id=? AND active_claim=?").run(next.enabled ? 1 : 0, JSON.stringify(next), id, claim);
2840
+ this.#db.exec("COMMIT");
2841
+ return next;
2842
+ } catch (error) {
2843
+ this.#db.exec("ROLLBACK");
2844
+ throw error;
2845
+ }
2846
+ }
2847
+ #write(trigger) {
2848
+ this.#db.prepare("UPDATE workflow_triggers SET enabled=?,next_fire_at=?,trigger_json=? WHERE id=?").run(trigger.enabled ? 1 : 0, trigger.nextFireAt, JSON.stringify(trigger), trigger.id);
2849
+ }
2850
+ close() {
2851
+ this.#db.close();
2852
+ }
2853
+ };
2854
+
2612
2855
  export {
2613
2856
  findingStatuses,
2614
2857
  findingTriageStatuses,
@@ -2641,5 +2884,10 @@ export {
2641
2884
  restoreFindingReviewFields,
2642
2885
  resolveOsecRunStorage,
2643
2886
  listOsecRunDatabasePaths,
2644
- writeOsecRunReport
2887
+ writeOsecRunReport,
2888
+ SecurityWorkflowStoreError,
2889
+ SecurityWorkflowStore,
2890
+ TRIGGER_INTERVAL_MS,
2891
+ nextWorkflowTriggerFire,
2892
+ WorkflowTriggerStore
2645
2893
  };