0sec-cli 0.15.0 → 0.18.0

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 (209) hide show
  1. package/0sec.js +327 -0
  2. package/README.md +68 -48
  3. package/attacks/data-exfiltration/pii-leakage.yaml +27 -0
  4. package/attacks/encoding-bypass/base64-encoding.yaml +24 -0
  5. package/attacks/jailbreak/dan-roleplay.yaml +27 -0
  6. package/attacks/jailbreak/hypothetical-scenario.yaml +25 -0
  7. package/attacks/jailbreak/multilingual-bypass.yaml +22 -0
  8. package/attacks/output-manipulation/harmful-content.yaml +25 -0
  9. package/attacks/prompt-injection/context-manipulation.yaml +32 -0
  10. package/attacks/prompt-injection/direct-injection.yaml +28 -0
  11. package/attacks/prompt-injection/indirect-injection.yaml +33 -0
  12. package/attacks/system-prompt-extraction/direct-ask.yaml +30 -0
  13. package/attacks/system-prompt-extraction/markdown-exfil.yaml +26 -0
  14. package/attacks/tool-misuse/ssrf-via-tools.yaml +27 -0
  15. package/chunks/adapt-loop-SMH6V4PD.js +18 -0
  16. package/chunks/adgraph-JLGA6RYI.js +54 -0
  17. package/chunks/agent/skills/frameworks/entra-id.yaml +63 -0
  18. package/chunks/agent/skills/frameworks/graphql-introspection.yaml +116 -0
  19. package/chunks/agent/skills/frameworks/nextjs.yaml +82 -0
  20. package/chunks/agent/skills/frameworks/python-web.yaml +85 -0
  21. package/chunks/agent/skills/frameworks/supabase.yaml +83 -0
  22. package/chunks/agent/skills/frameworks/wordpress-deep.yaml +122 -0
  23. package/chunks/agent/skills/techniques/ad-attack-paths.yaml +57 -0
  24. package/chunks/agent/skills/techniques/advisory-disclosure.yaml +59 -0
  25. package/chunks/agent/skills/techniques/assumption-mining.yaml +53 -0
  26. package/chunks/agent/skills/techniques/blind-exploitation.yaml +143 -0
  27. package/chunks/agent/skills/techniques/crypto-misuse.yaml +88 -0
  28. package/chunks/agent/skills/techniques/cve-poc-adaptation.yaml +82 -0
  29. package/chunks/agent/skills/techniques/entra-attack-paths.yaml +55 -0
  30. package/chunks/agent/skills/techniques/http-conformance-diff.yaml +48 -0
  31. package/chunks/agent/skills/techniques/jwt-attacks.yaml +137 -0
  32. package/chunks/agent/skills/techniques/kernel-weaponization.yaml +53 -0
  33. package/chunks/agent/skills/techniques/llm-excessive-agency.yaml +55 -0
  34. package/chunks/agent/skills/techniques/llm-insecure-output-handling.yaml +56 -0
  35. package/chunks/agent/skills/techniques/llm-prompt-injection.yaml +54 -0
  36. package/chunks/agent/skills/techniques/llm-prompt-layer-write.yaml +68 -0
  37. package/chunks/agent/skills/techniques/llm-rag-poisoning.yaml +57 -0
  38. package/chunks/agent/skills/techniques/llm-safety-eval.yaml +51 -0
  39. package/chunks/agent/skills/techniques/npm-ecosystem.yaml +51 -0
  40. package/chunks/agent/skills/techniques/poc-verification.yaml +55 -0
  41. package/chunks/agent/skills/techniques/race-condition.yaml +122 -0
  42. package/chunks/agent/skills/techniques/scoped-fix.yaml +49 -0
  43. package/chunks/agent/skills/techniques/seedless-depth-review.yaml +59 -0
  44. package/chunks/agent/skills/techniques/spec-differential.yaml +48 -0
  45. package/chunks/agent/skills/techniques/variant-hunting.yaml +55 -0
  46. package/chunks/agent/skills/vulnerabilities/cardano-eutxo-validators.yaml +72 -0
  47. package/chunks/agent/skills/vulnerabilities/command-injection.yaml +99 -0
  48. package/chunks/agent/skills/vulnerabilities/deserialization-chains.yaml +106 -0
  49. package/chunks/agent/skills/vulnerabilities/native-memory-safety.yaml +93 -0
  50. package/chunks/agent/skills/vulnerabilities/path-traversal.yaml +78 -0
  51. package/chunks/agent/skills/vulnerabilities/prototype-pollution.yaml +132 -0
  52. package/chunks/agent/skills/vulnerabilities/request-smuggling.yaml +129 -0
  53. package/chunks/agent/skills/vulnerabilities/sqli-advanced.yaml +102 -0
  54. package/chunks/agent/skills/vulnerabilities/ssrf-bypass.yaml +84 -0
  55. package/chunks/agent/skills/vulnerabilities/ssti-exploitation.yaml +112 -0
  56. package/chunks/agent/skills/vulnerabilities/structural-sqli.yaml +91 -0
  57. package/chunks/appsec-catalog-CBWHGTEL.js +24 -0
  58. package/chunks/artifact-scraper-HQ7HLP6V.js +34 -0
  59. package/chunks/assumption-mining-5SNIVVDN.js +79 -0
  60. package/chunks/chunk-242TMK6G.js +110 -0
  61. package/chunks/chunk-2JCCA2JL.js +381 -0
  62. package/chunks/chunk-2RMLOJVB.js +47 -0
  63. package/chunks/chunk-3KPWWJCI.js +1422 -0
  64. package/chunks/chunk-3MOLBTLS.js +953 -0
  65. package/chunks/chunk-53G27VPS.js +136 -0
  66. package/chunks/chunk-57ZENEX2.js +169 -0
  67. package/chunks/chunk-5G3ZXFBW.js +182 -0
  68. package/chunks/chunk-5JI7L7KV.js +74240 -0
  69. package/chunks/chunk-6L26OBWI.js +1094 -0
  70. package/chunks/chunk-6LKRLK2R.js +542 -0
  71. package/chunks/chunk-72CISA2D.js +2597 -0
  72. package/chunks/chunk-7DQEV5QI.js +287 -0
  73. package/chunks/chunk-AD7WXDH6.js +3250 -0
  74. package/chunks/chunk-ADOLI6DT.js +102 -0
  75. package/chunks/chunk-AHTBZC3E.js +596 -0
  76. package/chunks/chunk-ASUR522M.js +518 -0
  77. package/chunks/chunk-ATATQACO.js +471 -0
  78. package/chunks/chunk-BATRQBOR.js +1732 -0
  79. package/chunks/chunk-BFR2CDV5.js +5735 -0
  80. package/chunks/chunk-BFRB6HB2.js +235 -0
  81. package/chunks/chunk-BKZFDZ23.js +361 -0
  82. package/chunks/chunk-BVAZO4WA.js +193 -0
  83. package/chunks/chunk-BZNEY2ZS.js +33225 -0
  84. package/chunks/chunk-C2WXWFBB.js +1070 -0
  85. package/chunks/chunk-CMKXP5RR.js +917 -0
  86. package/chunks/chunk-DW5UWPFY.js +275 -0
  87. package/chunks/chunk-F3WBKITT.js +687 -0
  88. package/chunks/chunk-GMT2AZKM.js +2439 -0
  89. package/chunks/chunk-H2FFLZNK.js +3 -0
  90. package/chunks/chunk-IOL7D5YV.js +515 -0
  91. package/chunks/chunk-IR537GON.js +49 -0
  92. package/chunks/chunk-JQHOLLTD.js +3729 -0
  93. package/chunks/chunk-KLTNTE2Z.js +121 -0
  94. package/chunks/chunk-LP3HHYQU.js +245 -0
  95. package/chunks/chunk-MYVT64FN.js +16 -0
  96. package/chunks/chunk-N7BOUYEV.js +852 -0
  97. package/chunks/chunk-OEFNRYI2.js +150 -0
  98. package/chunks/chunk-P6WKNFWX.js +1968 -0
  99. package/chunks/chunk-QI233I24.js +333 -0
  100. package/chunks/chunk-RJWOYEOG.js +271 -0
  101. package/chunks/chunk-RRMJC3ZE.js +105 -0
  102. package/chunks/chunk-RWONANDA.js +973 -0
  103. package/chunks/chunk-SAFFWQW4.js +5921 -0
  104. package/chunks/chunk-SF4KZ4O3.js +502 -0
  105. package/chunks/chunk-SVJEDVYK.js +808 -0
  106. package/chunks/chunk-TSK2AG6J.js +5108 -0
  107. package/chunks/chunk-U5FLXGHT.js +2010 -0
  108. package/chunks/chunk-UCYRA73C.js +662 -0
  109. package/chunks/chunk-UR4ELX2Y.js +4784 -0
  110. package/chunks/chunk-VQG4FT5D.js +685 -0
  111. package/chunks/chunk-WEBMRWEG.js +9337 -0
  112. package/chunks/chunk-WU6AFRAZ.js +1167 -0
  113. package/chunks/chunk-WVTBZEQO.js +1531 -0
  114. package/chunks/chunk-WXQ6BJ6A.js +596 -0
  115. package/chunks/chunk-XM5NLHYU.js +600 -0
  116. package/chunks/chunk-YLMN3N25.js +4054 -0
  117. package/chunks/chunk-YYRBQVFE.js +2572 -0
  118. package/chunks/chunk-Z5FA2XOX.js +2798 -0
  119. package/chunks/commands-TBC2F5CE.js +16999 -0
  120. package/chunks/corpus-v1.json +403 -0
  121. package/chunks/cost-ledger-NU63MYZM.js +13 -0
  122. package/chunks/data/appsec-archetypes.json +102 -0
  123. package/chunks/data/chromium-archetypes.json +204 -0
  124. package/chunks/data/freebsd-archetypes.json +171 -0
  125. package/chunks/data/kernel-archetypes.json +611 -0
  126. package/chunks/db-KFWOAUUB.js +16 -0
  127. package/chunks/disclose-EKTWSCGW.js +132 -0
  128. package/chunks/dist-4MAW5X6L.js +74 -0
  129. package/chunks/dist-4YICS2J2.js +2776 -0
  130. package/chunks/dist-FMGSR3DW.js +159 -0
  131. package/chunks/eval-runner-7ZF472KV.js +27 -0
  132. package/chunks/example-manifest.json +95 -0
  133. package/chunks/exploit-agent-45YQOHPJ.js +98 -0
  134. package/chunks/exploit-autoclimb-UZGEMGYT.js +81 -0
  135. package/chunks/exploit-climb-SG23KB2G.js +358 -0
  136. package/chunks/fix-IG7LYXH3.js +12 -0
  137. package/chunks/github-issues-MJ6OYOOU.js +157 -0
  138. package/chunks/harness-7ODOLOWU.js +22 -0
  139. package/chunks/http-conformance-DU66MZIU.js +11 -0
  140. package/chunks/http-sender-GWH2IYEA.js +10 -0
  141. package/chunks/hunt-scan-MMKUOETU.js +38 -0
  142. package/chunks/identity-6ZAIIWOR.js +191 -0
  143. package/chunks/kernel-primitive-7U6CWHXD.js +44 -0
  144. package/chunks/kernel-vm-runner-3JAU4O6J.js +62 -0
  145. package/chunks/memsafety-scan-J7SECV5W.js +16 -0
  146. package/chunks/native-loop-RJCJPOUS.js +54 -0
  147. package/chunks/npm-detectors-KB5Y5ZFX.js +69 -0
  148. package/chunks/npm-dynamic-discovery-AHROOZDB.js +13 -0
  149. package/chunks/orchestrate-OC2VZN2X.js +62 -0
  150. package/chunks/pipeline-BZK5BLJR.js +14 -0
  151. package/chunks/pre-recon-cve-66EB6G4M.js +351 -0
  152. package/chunks/prepare-MYY743TK.js +13 -0
  153. package/chunks/process-LQRCS5OY.js +13 -0
  154. package/chunks/replay-runner-45G3VU7X.js +39 -0
  155. package/chunks/run-D56MPDKY.js +32716 -0
  156. package/chunks/runtime-G7YWIO3B.js +43 -0
  157. package/chunks/runtime-J7PLXZNM.js +12 -0
  158. package/chunks/scan-stream-FHI2FYZE.js +96 -0
  159. package/chunks/scope-BI7BF4ZY.js +18 -0
  160. package/chunks/session-store-GZ4A4KYT.js +28 -0
  161. package/chunks/source-files-PWRQR6LY.js +12 -0
  162. package/chunks/specdrift-WCLH6TTQ.js +19 -0
  163. package/chunks/variant-candidates-YERCYMPD.js +13 -0
  164. package/chunks/web-recon-prepass-JJGPEOLE.js +1148 -0
  165. package/dashboard/assets/0sec-icon-66SreztZ.gif +0 -0
  166. package/dashboard/assets/bot-yILPnRgD.js +1 -0
  167. package/dashboard/assets/chevron-down-BeDJ-Vka.js +1 -0
  168. package/dashboard/assets/circle-alert-BEnMBMT-.js +1 -0
  169. package/dashboard/assets/client-DRYsuOGl.js +9 -0
  170. package/dashboard/assets/copy-Bi5vzoF9.js +1 -0
  171. package/dashboard/assets/desktop-CXTQonNm.js +32 -0
  172. package/dashboard/assets/desktop-NOekk4QS.css +1 -0
  173. package/dashboard/assets/dist-CmX-h7a4.js +1 -0
  174. package/dashboard/assets/dist-DAwYQmf_.js +45 -0
  175. package/dashboard/assets/findings-page-CACW9nw2.js +5 -0
  176. package/dashboard/assets/format-PoISqsES.js +1 -0
  177. package/dashboard/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  178. package/dashboard/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  179. package/dashboard/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  180. package/dashboard/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  181. package/dashboard/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  182. package/dashboard/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
  183. package/dashboard/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
  184. package/dashboard/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
  185. package/dashboard/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
  186. package/dashboard/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  187. package/dashboard/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  188. package/dashboard/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
  189. package/dashboard/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
  190. package/dashboard/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
  191. package/dashboard/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
  192. package/dashboard/assets/jsx-runtime-C7oxC63R.js +1 -0
  193. package/dashboard/assets/live-page-DrgQqQK5.js +1 -0
  194. package/dashboard/assets/meta-tile-CcskIV_o.js +1 -0
  195. package/dashboard/assets/operations-BKE8T-vY.css +2 -0
  196. package/dashboard/assets/operations-D8nUP5_m.js +4 -0
  197. package/dashboard/assets/operations-app-CQQhG54n.js +2 -0
  198. package/dashboard/assets/overview-page-DTGS8dPo.js +1 -0
  199. package/dashboard/assets/page-header-MFVvEtZW.js +1 -0
  200. package/dashboard/assets/play-Dwo-9xt6.js +1 -0
  201. package/dashboard/assets/scans-page-ChVtq8Us.js +1 -0
  202. package/dashboard/assets/search-CiooTLck.js +1 -0
  203. package/dashboard/assets/siren-CCBwTSuk.js +1 -0
  204. package/dashboard/assets/table-BYS-nIVA.js +1 -0
  205. package/dashboard/assets/tabs-DivxbhQy.js +1 -0
  206. package/dashboard/desktop.html +21 -0
  207. package/dashboard/index.html +15 -0
  208. package/package.json +30 -18
  209. package/bin/0sec.cjs +0 -361
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __0secCreateRequire } from "node:module";
3
+ const require = __0secCreateRequire(import.meta.url);
4
+ import {
5
+ homeStateDir
6
+ } from "./chunk-6L26OBWI.js";
7
+
8
+ // packages/cli/src/tui/session-store.ts
9
+ import { chmodSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ var SESSIONS_DIRNAME = "console-sessions";
12
+ var FILE_MODE = 384;
13
+ var DIR_MODE = 448;
14
+ var DEFAULT_PRUNE_KEEP = 20;
15
+ var PREVIEW_MAX_LENGTH = 120;
16
+ var SUMMARY_MAX_LENGTH = 120;
17
+ var SESSION_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
18
+ var FILE_SUFFIX = ".json";
19
+ function sessionsDir(homeDir) {
20
+ return join(homeStateDir(homeDir), SESSIONS_DIRNAME);
21
+ }
22
+ function isValidSessionId(id) {
23
+ return typeof id === "string" && SESSION_ID_PATTERN.test(id);
24
+ }
25
+ function sessionFilePath(id, homeDir) {
26
+ if (!isValidSessionId(id)) return null;
27
+ return join(sessionsDir(homeDir), `${id}${FILE_SUFFIX}`);
28
+ }
29
+ function rawValue(raw, key) {
30
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return void 0;
31
+ return raw[key];
32
+ }
33
+ function optionalString(raw, key) {
34
+ const value = rawValue(raw, key);
35
+ return typeof value === "string" && value.length > 0 ? value : void 0;
36
+ }
37
+ var CONTROL_CHARS = /[\u0000-\u001F\u007F-\u009F]+/g;
38
+ function sanitizeLine(value, maxLength) {
39
+ if (typeof value !== "string") return "";
40
+ const flattened = value.replace(CONTROL_CHARS, " ").replace(/\s+/g, " ").trim();
41
+ return flattened.length > maxLength ? `${flattened.slice(0, maxLength - 1)}\u2026` : flattened;
42
+ }
43
+ function sanitizePreview(value) {
44
+ return sanitizeLine(value, PREVIEW_MAX_LENGTH);
45
+ }
46
+ function sanitizeSummary(value) {
47
+ const flattened = sanitizeLine(value, SUMMARY_MAX_LENGTH);
48
+ return flattened.length > 0 ? flattened : void 0;
49
+ }
50
+ function toMeta(id, raw, messageCount) {
51
+ const savedAt = rawValue(raw, "savedAt");
52
+ const cwd = rawValue(raw, "cwd");
53
+ const target = optionalString(raw, "target");
54
+ const model = optionalString(raw, "model");
55
+ const mode = optionalString(raw, "mode");
56
+ const summary = sanitizeSummary(rawValue(raw, "summary"));
57
+ return {
58
+ id,
59
+ savedAt: typeof savedAt === "number" && Number.isFinite(savedAt) ? savedAt : 0,
60
+ ...target !== void 0 ? { target } : {},
61
+ ...model !== void 0 ? { model } : {},
62
+ ...mode !== void 0 ? { mode } : {},
63
+ cwd: typeof cwd === "string" ? cwd : "",
64
+ messageCount,
65
+ preview: sanitizePreview(rawValue(raw, "preview")),
66
+ ...summary !== void 0 ? { summary } : {}
67
+ };
68
+ }
69
+ function parseSession(id, text) {
70
+ let raw;
71
+ try {
72
+ raw = JSON.parse(text);
73
+ } catch {
74
+ return null;
75
+ }
76
+ const messages = rawValue(raw, "messages");
77
+ if (!Array.isArray(messages)) return null;
78
+ return { ...toMeta(id, raw, messages.length), messages };
79
+ }
80
+ function saveSession(session, homeDir) {
81
+ try {
82
+ if (!Array.isArray(session?.messages)) return false;
83
+ const path = sessionFilePath(session.id, homeDir);
84
+ if (path === null) return false;
85
+ const dir = sessionsDir(homeDir);
86
+ mkdirSync(dir, { recursive: true, mode: DIR_MODE });
87
+ chmodSync(dir, DIR_MODE);
88
+ const payload = {
89
+ ...toMeta(session.id, session, session.messages.length),
90
+ messages: session.messages
91
+ };
92
+ writeFileSync(path, `${JSON.stringify(payload)}
93
+ `, { encoding: "utf8", mode: FILE_MODE });
94
+ chmodSync(path, FILE_MODE);
95
+ return true;
96
+ } catch {
97
+ return false;
98
+ }
99
+ }
100
+ function listSessions(homeDir, opts) {
101
+ const dir = sessionsDir(homeDir);
102
+ let entries;
103
+ try {
104
+ entries = readdirSync(dir);
105
+ } catch {
106
+ return [];
107
+ }
108
+ const out = [];
109
+ for (const entry of entries) {
110
+ if (!entry.endsWith(FILE_SUFFIX)) continue;
111
+ const id = entry.slice(0, -FILE_SUFFIX.length);
112
+ if (!isValidSessionId(id)) continue;
113
+ let text;
114
+ try {
115
+ text = readFileSync(join(dir, entry), "utf8");
116
+ } catch {
117
+ continue;
118
+ }
119
+ const parsed = parseSession(id, text);
120
+ if (parsed === null) continue;
121
+ if (opts?.cwd !== void 0 && parsed.cwd !== opts.cwd) continue;
122
+ const { messages: _messages, ...meta } = parsed;
123
+ out.push(meta);
124
+ }
125
+ out.sort((a, b) => b.savedAt - a.savedAt || a.id.localeCompare(b.id));
126
+ const limit = opts?.limit;
127
+ if (typeof limit === "number" && Number.isFinite(limit)) {
128
+ return out.slice(0, Math.max(0, Math.floor(limit)));
129
+ }
130
+ return out;
131
+ }
132
+ var SECONDS_PER_MINUTE = 60;
133
+ var MINUTES_PER_HOUR = 60;
134
+ var HOURS_PER_DAY = 24;
135
+ var DAYS_PER_WEEK = 7;
136
+ var FUTURE_SKEW_MS = 6e4;
137
+ function relativeAge(savedAt, now) {
138
+ if (!Number.isFinite(savedAt) || savedAt <= 0) return "";
139
+ if (savedAt > now + FUTURE_SKEW_MS) return "";
140
+ const seconds = Math.max(0, Math.floor((now - savedAt) / 1e3));
141
+ if (seconds < SECONDS_PER_MINUTE) return `${seconds}s`;
142
+ const minutes = Math.floor(seconds / SECONDS_PER_MINUTE);
143
+ if (minutes < MINUTES_PER_HOUR) return `${minutes}m`;
144
+ const hours = Math.floor(minutes / MINUTES_PER_HOUR);
145
+ if (hours < HOURS_PER_DAY) return `${hours}h`;
146
+ const days = Math.floor(hours / HOURS_PER_DAY);
147
+ if (days < DAYS_PER_WEEK) return `${days}d`;
148
+ return `${Math.floor(days / DAYS_PER_WEEK)}w`;
149
+ }
150
+ function loadSession(id, homeDir) {
151
+ const path = sessionFilePath(id, homeDir);
152
+ if (path === null) return null;
153
+ try {
154
+ return parseSession(id, readFileSync(path, "utf8"));
155
+ } catch {
156
+ return null;
157
+ }
158
+ }
159
+ function deleteSession(id, homeDir, opts) {
160
+ if (opts?.protectedIds?.has(id)) return false;
161
+ const path = sessionFilePath(id, homeDir);
162
+ if (path === null) return false;
163
+ try {
164
+ rmSync(path, { force: false });
165
+ return true;
166
+ } catch {
167
+ return false;
168
+ }
169
+ }
170
+ function pruneSessions(homeDir, opts) {
171
+ const requested = opts?.keep;
172
+ const keep = typeof requested === "number" && Number.isFinite(requested) ? Math.max(0, Math.floor(requested)) : DEFAULT_PRUNE_KEEP;
173
+ let removed = 0;
174
+ let archived = 0;
175
+ for (const meta of listSessions(homeDir)) {
176
+ if (opts?.protectedIds?.has(meta.id)) continue;
177
+ if (archived++ < keep) continue;
178
+ if (deleteSession(meta.id, homeDir, opts)) removed += 1;
179
+ }
180
+ return removed;
181
+ }
182
+
183
+ export {
184
+ DEFAULT_PRUNE_KEEP,
185
+ sessionsDir,
186
+ isValidSessionId,
187
+ saveSession,
188
+ listSessions,
189
+ relativeAge,
190
+ loadSession,
191
+ deleteSession,
192
+ pruneSessions
193
+ };