@timetotest/cli 0.1.10 → 0.2.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 (187) hide show
  1. package/README.md +201 -190
  2. package/dist/bin/ttt.js +4 -2
  3. package/dist/bin/ttt.js.map +1 -1
  4. package/dist/package.json +20 -3
  5. package/dist/src/commands/ask/AskApp.js +121 -0
  6. package/dist/src/commands/ask/AskApp.js.map +1 -0
  7. package/dist/src/commands/ask/components/AssistantResponse.js +31 -0
  8. package/dist/src/commands/ask/components/AssistantResponse.js.map +1 -0
  9. package/dist/src/commands/ask/components/Banner.js +15 -0
  10. package/dist/src/commands/ask/components/Banner.js.map +1 -0
  11. package/dist/src/commands/ask/components/ChatInput.js +93 -0
  12. package/dist/src/commands/ask/components/ChatInput.js.map +1 -0
  13. package/dist/src/commands/ask/components/Divider.js +17 -0
  14. package/dist/src/commands/ask/components/Divider.js.map +1 -0
  15. package/dist/src/commands/ask/components/IntroTips.js +19 -0
  16. package/dist/src/commands/ask/components/IntroTips.js.map +1 -0
  17. package/dist/src/commands/ask/components/MessageBubble.js +47 -0
  18. package/dist/src/commands/ask/components/MessageBubble.js.map +1 -0
  19. package/dist/src/commands/ask/components/SessionInfo.js +20 -0
  20. package/dist/src/commands/ask/components/SessionInfo.js.map +1 -0
  21. package/dist/src/commands/ask/components/StatusIndicator.js +67 -0
  22. package/dist/src/commands/ask/components/StatusIndicator.js.map +1 -0
  23. package/dist/src/commands/ask-ink.js +380 -0
  24. package/dist/src/commands/ask-ink.js.map +1 -0
  25. package/dist/src/commands/ask.js +138 -50
  26. package/dist/src/commands/ask.js.map +1 -1
  27. package/dist/src/commands/chat/ChatApp.js +125 -0
  28. package/dist/src/commands/chat/ChatApp.js.map +1 -0
  29. package/dist/src/commands/chat-ink.js +436 -0
  30. package/dist/src/commands/chat-ink.js.map +1 -0
  31. package/dist/src/commands/chat.js +82 -0
  32. package/dist/src/commands/chat.js.map +1 -0
  33. package/dist/src/commands/login.js +6 -4
  34. package/dist/src/commands/login.js.map +1 -1
  35. package/dist/src/commands/start-test.js +62 -88
  36. package/dist/src/commands/start-test.js.map +1 -1
  37. package/dist/src/commands/stream.js +9 -9
  38. package/dist/src/commands/stream.js.map +1 -1
  39. package/dist/src/commands/test.js +58 -125
  40. package/dist/src/commands/test.js.map +1 -1
  41. package/dist/src/lib/agent-orchestrator.js +546 -0
  42. package/dist/src/lib/agent-orchestrator.js.map +1 -0
  43. package/dist/src/lib/cloudinary-service.js +65 -0
  44. package/dist/src/lib/cloudinary-service.js.map +1 -0
  45. package/dist/src/lib/config.js +3 -2
  46. package/dist/src/lib/config.js.map +1 -1
  47. package/dist/src/lib/events.js +73 -60
  48. package/dist/src/lib/events.js.map +1 -1
  49. package/dist/src/lib/http.js +34 -1
  50. package/dist/src/lib/http.js.map +1 -1
  51. package/dist/src/lib/legacy-chat-runner.js +37 -0
  52. package/dist/src/lib/legacy-chat-runner.js.map +1 -0
  53. package/dist/src/lib/local-tools/api/api-discovery.js +17 -0
  54. package/dist/src/lib/local-tools/api/api-discovery.js.map +1 -0
  55. package/dist/src/lib/local-tools/api/build-request.js +20 -0
  56. package/dist/src/lib/local-tools/api/build-request.js.map +1 -0
  57. package/dist/src/lib/local-tools/api/extract-response-fields.js +26 -0
  58. package/dist/src/lib/local-tools/api/extract-response-fields.js.map +1 -0
  59. package/dist/src/lib/local-tools/api/generate-api-test.js +20 -0
  60. package/dist/src/lib/local-tools/api/generate-api-test.js.map +1 -0
  61. package/dist/src/lib/local-tools/api/generate-curl.js +8 -0
  62. package/dist/src/lib/local-tools/api/generate-curl.js.map +1 -0
  63. package/dist/src/lib/local-tools/api/get-api-parameters.js +17 -0
  64. package/dist/src/lib/local-tools/api/get-api-parameters.js.map +1 -0
  65. package/dist/src/lib/local-tools/api/index.js +10 -0
  66. package/dist/src/lib/local-tools/api/index.js.map +1 -0
  67. package/dist/src/lib/local-tools/api/request.js +43 -0
  68. package/dist/src/lib/local-tools/api/request.js.map +1 -0
  69. package/dist/src/lib/local-tools/api/set-auth-header.js +8 -0
  70. package/dist/src/lib/local-tools/api/set-auth-header.js.map +1 -0
  71. package/dist/src/lib/local-tools/api/types.js +2 -0
  72. package/dist/src/lib/local-tools/api/types.js.map +1 -0
  73. package/dist/src/lib/local-tools/api/utils.js +33 -0
  74. package/dist/src/lib/local-tools/api/utils.js.map +1 -0
  75. package/dist/src/lib/local-tools/api/validate-response.js +41 -0
  76. package/dist/src/lib/local-tools/api/validate-response.js.map +1 -0
  77. package/dist/src/lib/local-tools/file-tools.js +45 -0
  78. package/dist/src/lib/local-tools/file-tools.js.map +1 -0
  79. package/dist/src/lib/local-tools/general/discover-local-services.js +95 -0
  80. package/dist/src/lib/local-tools/general/discover-local-services.js.map +1 -0
  81. package/dist/src/lib/local-tools/general/index.js +2 -0
  82. package/dist/src/lib/local-tools/general/index.js.map +1 -0
  83. package/dist/src/lib/local-tools/ui/click-element.js +105 -0
  84. package/dist/src/lib/local-tools/ui/click-element.js.map +1 -0
  85. package/dist/src/lib/local-tools/ui/dom-rag.js +201 -0
  86. package/dist/src/lib/local-tools/ui/dom-rag.js.map +1 -0
  87. package/dist/src/lib/local-tools/ui/find-element.js +31 -0
  88. package/dist/src/lib/local-tools/ui/find-element.js.map +1 -0
  89. package/dist/src/lib/local-tools/ui/hover-element.js +94 -0
  90. package/dist/src/lib/local-tools/ui/hover-element.js.map +1 -0
  91. package/dist/src/lib/local-tools/ui/index.js +3 -0
  92. package/dist/src/lib/local-tools/ui/index.js.map +1 -0
  93. package/dist/src/lib/local-tools/ui/manage-tab.js +65 -0
  94. package/dist/src/lib/local-tools/ui/manage-tab.js.map +1 -0
  95. package/dist/src/lib/local-tools/ui/navigate.js +35 -0
  96. package/dist/src/lib/local-tools/ui/navigate.js.map +1 -0
  97. package/dist/src/lib/local-tools/ui/page-discovery.js +32 -0
  98. package/dist/src/lib/local-tools/ui/page-discovery.js.map +1 -0
  99. package/dist/src/lib/local-tools/ui/playwright-mcp.js +217 -0
  100. package/dist/src/lib/local-tools/ui/playwright-mcp.js.map +1 -0
  101. package/dist/src/lib/local-tools/ui/screenshot.js +19 -0
  102. package/dist/src/lib/local-tools/ui/screenshot.js.map +1 -0
  103. package/dist/src/lib/local-tools/ui/search-interactive-elements.js +18 -0
  104. package/dist/src/lib/local-tools/ui/search-interactive-elements.js.map +1 -0
  105. package/dist/src/lib/local-tools/ui/selector-resolver.js +153 -0
  106. package/dist/src/lib/local-tools/ui/selector-resolver.js.map +1 -0
  107. package/dist/src/lib/local-tools/ui/snapshot-query.js +129 -0
  108. package/dist/src/lib/local-tools/ui/snapshot-query.js.map +1 -0
  109. package/dist/src/lib/local-tools/ui/type-text.js +40 -0
  110. package/dist/src/lib/local-tools/ui/type-text.js.map +1 -0
  111. package/dist/src/lib/local-tools/ui/types.js +2 -0
  112. package/dist/src/lib/local-tools/ui/types.js.map +1 -0
  113. package/dist/src/lib/local-tools/utility/finish-overall-test.js +12 -0
  114. package/dist/src/lib/local-tools/utility/finish-overall-test.js.map +1 -0
  115. package/dist/src/lib/local-tools/utility/index.js +2 -0
  116. package/dist/src/lib/local-tools/utility/index.js.map +1 -0
  117. package/dist/src/lib/prompts/builder.js +38 -0
  118. package/dist/src/lib/prompts/builder.js.map +1 -0
  119. package/dist/src/lib/prompts/index.js +7 -0
  120. package/dist/src/lib/prompts/index.js.map +1 -0
  121. package/dist/src/lib/prompts/templates.js +166 -0
  122. package/dist/src/lib/prompts/templates.js.map +1 -0
  123. package/dist/src/lib/session-manager.js +201 -0
  124. package/dist/src/lib/session-manager.js.map +1 -0
  125. package/dist/src/lib/socket.js +78 -6
  126. package/dist/src/lib/socket.js.map +1 -1
  127. package/dist/src/lib/testing-mode.js +33 -0
  128. package/dist/src/lib/testing-mode.js.map +1 -0
  129. package/dist/src/lib/tool-descriptions.js +59 -0
  130. package/dist/src/lib/tool-descriptions.js.map +1 -0
  131. package/dist/src/lib/tool-executor.js +537 -0
  132. package/dist/src/lib/tool-executor.js.map +1 -0
  133. package/dist/src/lib/tool-registry.js +803 -0
  134. package/dist/src/lib/tool-registry.js.map +1 -0
  135. package/dist/src/lib/tool-result-pruner.js +384 -0
  136. package/dist/src/lib/tool-result-pruner.js.map +1 -0
  137. package/dist/src/lib/tui/components/AskIntro.js +6 -0
  138. package/dist/src/lib/tui/components/AskIntro.js.map +1 -0
  139. package/dist/src/lib/tui/components/Banner.js +15 -0
  140. package/dist/src/lib/tui/components/Banner.js.map +1 -0
  141. package/dist/src/lib/tui/components/Divider.js +17 -0
  142. package/dist/src/lib/tui/components/Divider.js.map +1 -0
  143. package/dist/src/lib/tui/components/EventLine.js +110 -0
  144. package/dist/src/lib/tui/components/EventLine.js.map +1 -0
  145. package/dist/src/lib/tui/components/Header.js +15 -0
  146. package/dist/src/lib/tui/components/Header.js.map +1 -0
  147. package/dist/src/lib/tui/components/InputBox.js +9 -0
  148. package/dist/src/lib/tui/components/InputBox.js.map +1 -0
  149. package/dist/src/lib/tui/components/Mapping.js +8 -0
  150. package/dist/src/lib/tui/components/Mapping.js.map +1 -0
  151. package/dist/src/lib/tui/components/ProjectList.js +6 -0
  152. package/dist/src/lib/tui/components/ProjectList.js.map +1 -0
  153. package/dist/src/lib/tui/components/Spinner.js +20 -0
  154. package/dist/src/lib/tui/components/Spinner.js.map +1 -0
  155. package/dist/src/lib/tui/components/StatusBanner.js +12 -0
  156. package/dist/src/lib/tui/components/StatusBanner.js.map +1 -0
  157. package/dist/src/lib/tui/components/StatusBar.js +11 -0
  158. package/dist/src/lib/tui/components/StatusBar.js.map +1 -0
  159. package/dist/src/lib/tui/components/UserBubble.js +6 -0
  160. package/dist/src/lib/tui/components/UserBubble.js.map +1 -0
  161. package/dist/src/lib/tui/components/index.js +16 -0
  162. package/dist/src/lib/tui/components/index.js.map +1 -0
  163. package/dist/src/lib/tui/events.js +716 -76
  164. package/dist/src/lib/tui/events.js.map +1 -1
  165. package/dist/src/lib/tui/icons.js +14 -0
  166. package/dist/src/lib/tui/icons.js.map +1 -1
  167. package/dist/src/lib/tui/ink-print.js +41 -0
  168. package/dist/src/lib/tui/ink-print.js.map +1 -0
  169. package/dist/src/lib/tui/interactive-chat.js +345 -0
  170. package/dist/src/lib/tui/interactive-chat.js.map +1 -0
  171. package/dist/src/lib/tui/print.js +31 -26
  172. package/dist/src/lib/tui/print.js.map +1 -1
  173. package/dist/src/lib/tui/prompt.js +21 -18
  174. package/dist/src/lib/tui/prompt.js.map +1 -1
  175. package/dist/src/test-agent-flow.js +148 -0
  176. package/dist/src/test-agent-flow.js.map +1 -0
  177. package/dist/src/test-browser-session.js +152 -0
  178. package/dist/src/test-browser-session.js.map +1 -0
  179. package/dist/src/test-browser-snapshot.js +187 -0
  180. package/dist/src/test-browser-snapshot.js.map +1 -0
  181. package/dist/src/test-snapshot-detailed.js +219 -0
  182. package/dist/src/test-snapshot-detailed.js.map +1 -0
  183. package/dist/src/test-snapshot-simple.js +85 -0
  184. package/dist/src/test-snapshot-simple.js.map +1 -0
  185. package/dist/src/test-snapshot-tabs.js +110 -0
  186. package/dist/src/test-snapshot-tabs.js.map +1 -0
  187. package/package.json +20 -3
@@ -0,0 +1,129 @@
1
+ import { parseSnapshot } from "./playwright-mcp.js";
2
+ import Fuse from "fuse.js";
3
+ function createSearchableText(node) {
4
+ return [
5
+ node.name,
6
+ node.description,
7
+ node.value,
8
+ node.url,
9
+ JSON.stringify(node.attributes ?? {}),
10
+ ]
11
+ .filter(Boolean)
12
+ .join(" ");
13
+ }
14
+ function matchNode(node, args, fuse) {
15
+ const { text, threshold = 0.4 } = args;
16
+ // Text matching with fuzzy search
17
+ if (text) {
18
+ const searchableText = createSearchableText(node);
19
+ // Split text query into individual words and search each one
20
+ const words = text
21
+ .toLowerCase()
22
+ .split(/\s+/)
23
+ .filter((word) => word.length > 0);
24
+ // Check if any word matches this node
25
+ for (const word of words) {
26
+ const results = fuse.search(word);
27
+ const match = results.find((result) => result.item === searchableText && (result.score ?? 1) <= threshold);
28
+ if (match) {
29
+ return true;
30
+ }
31
+ }
32
+ return false;
33
+ }
34
+ return true;
35
+ }
36
+ function collectMatches(node, path, args, out, limit, fuse) {
37
+ if (matchNode(node, args, fuse)) {
38
+ out.push({
39
+ ref: node.ref,
40
+ role: node.role,
41
+ name: node.name,
42
+ description: node.description,
43
+ attributes: node.attributes,
44
+ path: [...path, node.role ?? "node"].filter(Boolean).join(" > ").trim(),
45
+ });
46
+ }
47
+ if (out.length >= limit)
48
+ return;
49
+ if (Array.isArray(node.children)) {
50
+ for (const child of node.children) {
51
+ collectMatches(child, [...path, node.role ?? node.name ?? "node"], args, out, limit, fuse);
52
+ if (out.length >= limit)
53
+ return;
54
+ }
55
+ }
56
+ }
57
+ export function snapshotQuery(ctx, args) {
58
+ const snapshot = ctx.snapshotCache?.ariaSnapshot;
59
+ if (!snapshot) {
60
+ return {
61
+ success: false,
62
+ error: "Snapshot cache empty. Allow the CLI to refresh it (e.g., by navigating or interacting with the page) before querying.",
63
+ };
64
+ }
65
+ // Validate minimum word count for text search
66
+ if (args.text) {
67
+ const wordCount = args.text
68
+ .trim()
69
+ .split(/\s+/)
70
+ .filter((word) => word.length > 0).length;
71
+ if (wordCount < 5) {
72
+ return {
73
+ success: false,
74
+ error: `Text search requires at least 5 words for effective searching. Provided: ${wordCount} words.`,
75
+ };
76
+ }
77
+ }
78
+ const limit = typeof args.limit === "number" && args.limit > 0 ? args.limit : 10;
79
+ const parsed = parseSnapshot(snapshot);
80
+ if (!parsed.length) {
81
+ return {
82
+ success: false,
83
+ error: "Snapshot cache is present but could not be parsed.",
84
+ };
85
+ }
86
+ // Create Fuse instance for fuzzy search
87
+ const searchableTexts = [];
88
+ const nodeTextMap = new Map();
89
+ // Collect all searchable texts and map them to nodes
90
+ function collectTexts(node) {
91
+ const text = createSearchableText(node);
92
+ if (text.trim()) {
93
+ searchableTexts.push(text);
94
+ nodeTextMap.set(text, node);
95
+ }
96
+ if (Array.isArray(node.children)) {
97
+ for (const child of node.children) {
98
+ collectTexts(child);
99
+ }
100
+ }
101
+ }
102
+ for (const node of parsed) {
103
+ collectTexts(node);
104
+ }
105
+ // Configure Fuse for fuzzy search
106
+ const fuse = new Fuse(searchableTexts, {
107
+ includeScore: true,
108
+ threshold: args.threshold || 0.4,
109
+ ignoreLocation: true,
110
+ findAllMatches: true,
111
+ });
112
+ const matches = [];
113
+ for (const node of parsed) {
114
+ collectMatches(node, [], args, matches, limit, fuse);
115
+ if (matches.length >= limit)
116
+ break;
117
+ }
118
+ return {
119
+ success: true,
120
+ query: {
121
+ text: args.text,
122
+ limit,
123
+ threshold: args.threshold || 0.4,
124
+ },
125
+ match_count: matches.length,
126
+ matches,
127
+ };
128
+ }
129
+ //# sourceMappingURL=snapshot-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot-query.js","sourceRoot":"","sources":["../../../../../src/lib/local-tools/ui/snapshot-query.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { ensureDomRagIndex } from "./dom-rag.js";
2
+ export async function typeText(ctx, selectors, texts, clearFirst = true) {
3
+ const page = ctx.page;
4
+ if (!page) {
5
+ return { error: "Browser page not initialized" };
6
+ }
7
+ if (!Array.isArray(selectors) || !Array.isArray(texts) || selectors.length !== texts.length) {
8
+ return { error: "Selectors and texts arrays must be provided and match in length" };
9
+ }
10
+ if (selectors.length === 0) {
11
+ return { error: "No selectors provided" };
12
+ }
13
+ if (selectors.length > 6) {
14
+ return { error: "Maximum 6 inputs at a time" };
15
+ }
16
+ try {
17
+ for (let i = 0; i < selectors.length; i += 1) {
18
+ const selector = selectors[i];
19
+ const text = texts[i] ?? "";
20
+ if (clearFirst) {
21
+ await page.fill(selector, "");
22
+ }
23
+ await page.type(selector, text, { delay: 50 });
24
+ }
25
+ void (async () => {
26
+ try {
27
+ await page.waitForTimeout(200);
28
+ await ensureDomRagIndex(page);
29
+ }
30
+ catch {
31
+ /* best-effort */
32
+ }
33
+ })();
34
+ return { success: true, count: selectors.length };
35
+ }
36
+ catch (error) {
37
+ return { error: error?.message ?? String(error) };
38
+ }
39
+ }
40
+ //# sourceMappingURL=type-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-text.js","sourceRoot":"","sources":["../../../../../src/lib/local-tools/ui/type-text.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,iBAAiB,EAAC,MAAM,cAAc,CAAC;AAE/C,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,GAAwB,EACxB,SAAmB,EACnB,KAAe,EACf,aAAsB,IAAI;IAE1B,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IACtB,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,EAAC,KAAK,EAAE,8BAA8B,EAAC,CAAC;IACjD,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;QAC5F,OAAO,EAAC,KAAK,EAAE,iEAAiE,EAAC,CAAC;IACpF,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAC,KAAK,EAAE,uBAAuB,EAAC,CAAC;IAC1C,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO,EAAC,KAAK,EAAE,4BAA4B,EAAC,CAAC;IAC/C,CAAC;IAED,IAAI,CAAC;QACH,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7C,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;YAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC5B,IAAI,UAAU,EAAE,CAAC;gBACf,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YAChC,CAAC;YACD,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAC,KAAK,EAAE,EAAE,EAAC,CAAC,CAAC;QAC/C,CAAC;QACD,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;gBAC/B,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAChC,CAAC;YAAC,MAAM,CAAC;gBACP,iBAAiB;YACnB,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QACL,OAAO,EAAC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,EAAC,CAAC;IAClD,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,OAAO,EAAC,KAAK,EAAE,KAAK,EAAE,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,EAAC,CAAC;IAClD,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../src/lib/local-tools/ui/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ export async function finishOverallTest(summary) {
2
+ if (!summary || typeof summary !== "string") {
3
+ return { error: "summary is required" };
4
+ }
5
+ return {
6
+ success: true,
7
+ summary,
8
+ timestamp: Date.now(),
9
+ message: "Test session completed",
10
+ };
11
+ }
12
+ //# sourceMappingURL=finish-overall-test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finish-overall-test.js","sourceRoot":"","sources":["../../../../../src/lib/local-tools/utility/finish-overall-test.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAAe;IACrD,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC5C,OAAO,EAAC,KAAK,EAAE,qBAAqB,EAAC,CAAC;IACxC,CAAC;IACD,OAAO;QACL,OAAO,EAAE,IAAI;QACb,OAAO;QACP,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,OAAO,EAAE,wBAAwB;KAClC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { finishOverallTest } from "./finish-overall-test.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/lib/local-tools/utility/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,iBAAiB,EAAC,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * CLI Prompt Builder
3
+ * Builds system prompts for CLI agent reasoning
4
+ */
5
+ import { CLI_AGENT_PROMPT_TEMPLATES } from "./templates.js";
6
+ export function build_cli_system_prompt(prompt_type = "unified_agent_react", context = {}) {
7
+ const template = CLI_AGENT_PROMPT_TEMPLATES[prompt_type];
8
+ // Replace template variables
9
+ let prompt = template
10
+ .replace(/\{\{user_goal\}\}/g, context.user_goal || "unset")
11
+ .replace(/\{\{base_url\}\}/g, context.base_url || "unset")
12
+ .replace(/\{\{ui_base_url\}\}/g, context.ui_base_url || "unset")
13
+ .replace(/\{\{api_base_url\}\}/g, context.api_base_url || "unset")
14
+ .replace(/\{\{documentation_url\}\}/g, context.documentation_url || "unset")
15
+ .replace(/\{\{test_type\}\}/g, context.test_type || "mixed")
16
+ .replace(/\{\{mode\}\}/g, context.mode || "local");
17
+ // Add execution context footer
18
+ const exec_ctx_lines = [
19
+ "\n[Execution Context]",
20
+ `- base_url: ${context.base_url || "unset"}`,
21
+ `- test_type: ${context.test_type || "mixed"}`,
22
+ `- mode: ${context.mode || "local"}`,
23
+ "- Always resolve variables like base_url from context; never use the literal string 'base_url' in tool arguments.",
24
+ "- Local execution mode: You have direct access to the user's environment through tools.",
25
+ "- If testing API endpoints, prefer: api_discovery → discover_endpoints/search; then use api_request with explicit payloads based on documentation",
26
+ ];
27
+ const tail = "\n" + exec_ctx_lines.join("\n");
28
+ return prompt + tail;
29
+ }
30
+ export function get_cli_prompt_template(prompt_type) {
31
+ const valid_types = Object.keys(CLI_AGENT_PROMPT_TEMPLATES);
32
+ if (valid_types.includes(prompt_type)) {
33
+ return CLI_AGENT_PROMPT_TEMPLATES[prompt_type];
34
+ }
35
+ // Default to unified_agent_react
36
+ return CLI_AGENT_PROMPT_TEMPLATES.unified_agent_react;
37
+ }
38
+ //# sourceMappingURL=builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.js","sourceRoot":"","sources":["../../../../src/lib/prompts/builder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAC,0BAA0B,EAAgB,MAAM,gBAAgB,CAAC;AAYzE,MAAM,UAAU,uBAAuB,CACrC,cAA6B,qBAAqB,EAClD,UAA4B,EAAE;IAE9B,MAAM,QAAQ,GAAG,0BAA0B,CAAC,WAAW,CAAC,CAAC;IAEzD,6BAA6B;IAC7B,IAAI,MAAM,GAAG,QAAQ;SAClB,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC;SAC3D,OAAO,CAAC,mBAAmB,EAAE,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC;SACzD,OAAO,CAAC,sBAAsB,EAAE,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC;SAC/D,OAAO,CAAC,uBAAuB,EAAE,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC;SACjE,OAAO,CAAC,4BAA4B,EAAE,OAAO,CAAC,iBAAiB,IAAI,OAAO,CAAC;SAC3E,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC;SAC3D,OAAO,CAAC,eAAe,EAAE,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,CAAC;IAErD,+BAA+B;IAC/B,MAAM,cAAc,GAAG;QACrB,uBAAuB;QACvB,eAAe,OAAO,CAAC,QAAQ,IAAI,OAAO,EAAE;QAC5C,gBAAgB,OAAO,CAAC,SAAS,IAAI,OAAO,EAAE;QAC9C,WAAW,OAAO,CAAC,IAAI,IAAI,OAAO,EAAE;QACpC,mHAAmH;QACnH,yFAAyF;QACzF,mJAAmJ;KACpJ,CAAC;IAEF,MAAM,IAAI,GAAG,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE9C,OAAO,MAAM,GAAG,IAAI,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,WAAmB;IACzD,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAC7B,0BAA0B,CACR,CAAC;IAErB,IAAI,WAAW,CAAC,QAAQ,CAAC,WAA4B,CAAC,EAAE,CAAC;QACvD,OAAO,0BAA0B,CAAC,WAA4B,CAAC,CAAC;IAClE,CAAC;IAED,iCAAiC;IACjC,OAAO,0BAA0B,CAAC,mBAAmB,CAAC;AACxD,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * CLI Prompts Module
3
+ * Exports prompt templates and builders for CLI agent reasoning
4
+ */
5
+ export * from "./templates.js";
6
+ export * from "./builder.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/lib/prompts/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC"}
@@ -0,0 +1,166 @@
1
+ /**
2
+ * CLI Agent Prompt Templates
3
+ * Based on backend prompt patterns but adapted for CLI local execution
4
+ */
5
+ export const CLI_AGENT_SYSTEM_PROMPT = `# 1. Core Identity & Mission
6
+
7
+ You are TimetoTest CLI, a friendly and focused QA automation agent for local testing. You operate with precision, autonomy, and a clear intent in the terminal environment.
8
+
9
+ Your mission is to achieve this user-defined goal:
10
+ Goal: {{user_goal}}
11
+
12
+ <guiding_principles>
13
+ - Observe First, Act Second: Every action is informed by direct evidence from tool outputs. Never operate on assumptions.
14
+ - Autonomy & Drive: Proactively move from one step to the next. Do not ask for permission. If blocked, record the issue, and move to the next task.
15
+ - Clarity & Communication: Narrate your actions with brief, conversational notes for a non-technical audience. Your goal is to make the testing process transparent.
16
+ - Security First: Never expose sensitive data, credentials, or internal system URLs in your communications.
17
+ - Local Execution: You have direct access to the user's local environment through tools. Use this advantage to provide immediate, actionable results.
18
+ </guiding_principles>
19
+
20
+ # 2. The Four-Phase Testing Workflow
21
+
22
+ Follow this structured process from start to finish.
23
+
24
+ <phase_1_setup_discovery>
25
+ - Start Browser Session: If you need to test UI, call browser_session(action="start") to launch a browser before using any UI tools. Do this only once if it was successfully started.
26
+ </phase_1_setup_discovery>
27
+
28
+ <phase_2_orientation_test_generation>
29
+ - Navigate & Observe: Use browser_navigate to reach the target URL. The CLI will maintain a fresh accessibility snapshot automatically; consult it via browser_snapshot_query and add browser_take_screenshot when you need visual confirmation.
30
+ - Validate Context: Before generating tests, ensure all necessary elements (e.g., login forms, buttons) are visible and ready.
31
+ - Generate Test Cases: Once the context is validated, call generate_test_cases() to create test cases and add them to your todo list.
32
+ </phase_2_orientation_test_generation>
33
+
34
+ <phase_3_test_execution>
35
+ - Iterate through each todo item until the list is empty.
36
+ - For each test case, follow the Observe → Act → Verify loop.
37
+ - Observe: Analyze the last screenshot to decide your next action.
38
+ - Act: Use the Playwright MCP tools (browser_click, browser_type, browser_fill_form, browser_press_key, browser_select_option, api_request, etc.) to interact with the system.
39
+ - Verify: Use browser_snapshot_query or examine API responses to confirm outcomes, and call browser_take_screenshot when you need visual evidence. Update the todo status (completed or failed).
40
+ - Reset the application state between test cases as needed to ensure test independence.
41
+ - Use reset_for_next_test_case tool to clear browser state, sign out users, and prepare for the next test case.
42
+ - Retry bounds: Do not call the same tool with essentially identical inputs more than 3 times without progress. After three failed attempts, change strategy (different tool or approach) or record the blocker and move on.
43
+ </phase_3_test_execution>
44
+
45
+ <phase_4_completion_reporting>
46
+ - Do not finish until all todo items are resolved (status is completed or failed).
47
+ - Call finish_overall_test(summary) with a final report, including counts of completed vs. failed tests and key blockers.
48
+ - End Browser Session: Call browser_session(action="stop") to clean up browser resources.
49
+ - Finish Task: Call finish(summary) to stop the agent loop when the task is complete.
50
+ </phase_4_completion_reporting>
51
+
52
+ # 3. Rules of Engagement: Tools & Interaction
53
+
54
+ <element_selection_strategy>
55
+ Snapshot Cache: The CLI refreshes the accessibility snapshot after major UI actions. Query it with browser_snapshot_query using text and/or role filters to retrieve refs instead of capturing new snapshots yourself.
56
+ Snapshot Query: Use browser_snapshot_query to look up refs by text or role from the cached snapshot. If the CLI reports the cache is stale, perform a new UI action (navigate, click, etc.) and it will refresh automatically.
57
+ Interaction Rules:
58
+ - browser_click / browser_hover: supply element (human-friendly reasoning string) plus ref. Only add doubleClick/button/modifiers when necessary.
59
+ - browser_type: provide element, ref, and text. Use slowly or submit only when required. For multiple fields, prefer browser_fill_form with ordered field definitions.
60
+ - browser_select_option: provide element/ref and the list of option labels or values to select.
61
+ - browser_tabs manages windows, browser_handle_dialog clears blocking dialogs, browser_press_key issues global key strokes.
62
+ Verification: After important actions, consult browser_snapshot_query or browser_take_screenshot (when visuals matter) to confirm the state before proceeding.
63
+ Never invent refs or rely on CSS selectors. If you cannot find an element in the latest snapshot, explain the limitation, refresh the snapshot, or try an alternate path.
64
+ </element_selection_strategy>
65
+
66
+ <question_examples>
67
+ - For single-element selection, use descriptive queries with 1-5 terms.
68
+ - Examples for an email field:
69
+ - ["email"]
70
+ Examples:
71
+ - Snapshot snippet:
72
+ \`\`\`
73
+ - button "Sign in"
74
+ ref: 42
75
+ \`\`\`
76
+ Call browser_click with \`element="Sign in button"\` and \`ref="42"\`.
77
+ - Input field snippet:
78
+ \`\`\`
79
+ - textbox "Email address"
80
+ ref: 17
81
+ \`\`\`
82
+ Use browser_type with \`element="Email address input"\`, \`ref="17"\`, and your desired text.
83
+ - Multiple fields:
84
+ Provide an ordered array of \`{name, type, ref, value}\` objects to browser_fill_form, using the refs from the snapshot.
85
+ - If a lookup fails, refresh the snapshot. If the ref still does not exist, acknowledge the blocker and continue.
86
+ </question_examples>
87
+
88
+ <observation_state_awareness>
89
+ - Your world is the last screenshot or API response. Always reason about it before acting.
90
+ - if you see a modal, dismiss it using "skip" or "close" buttons, visible in the screenshot.
91
+ - Never take a screenshot of an empty or browser-internal page (like about:blank). Navigate to a real page first.
92
+ - After an action, only take a new screenshot if you expect the UI to have changed.
93
+ - For API testing, always validate responses and check status codes before proceeding.
94
+ </observation_state_awareness>
95
+
96
+ <input_credentials>
97
+ - Never invent credentials, tokens, or other test data.
98
+ - Use discovery tools and screenshots to understand how features work.
99
+ - If you cannot find the required information through available tools, document the limitation and continue with reasonable assumptions.
100
+ - When testing requires specific values that cannot be discovered, use placeholder values and document this in your test report.
101
+ </input_credentials>
102
+
103
+ # 4. Resilience & Knowledge
104
+
105
+ <persistence_error_handling>
106
+ - If a step fails, retry it up to two times, perhaps with a slightly different approach.
107
+ - If still blocked, record a concise blocker note on the current todo, mark it failed, and move on to the next one.
108
+ - Detect and break out of loops to avoid getting stuck.
109
+ </persistence_error_handling>
110
+
111
+ <tool_preambles>
112
+ - For tool calls: add a brief, natural note only when useful. Vary phrasing, use present tense, and avoid repeating the same opener.
113
+ Examples: "Let me peek at the login page", "Interesting! I'll snap a screenshot to see what's happening", "Ah, looks like I need to click that Sign In button", "Just taking a quick look around", "Time to see what we're working with here".
114
+ - Keep it friendly, concise, and co-worker conversational. Skip narration when the action is obvious.
115
+ - Assume the audience is non-technical; prefer plain, direct language.
116
+ - Add subtle personality touches: use "Let me...", "Time to...", "Interesting...", "Ah, I see...", "Just checking..." to sound more human and engaged.
117
+ - At every step, actively remember what is currently on the screen (last screenshot) and use it to understand where you are before choosing the next action.
118
+ </tool_preambles>
119
+
120
+ <test_case_guidelines>
121
+ - Each test case must be clear, concise, and easy to understand and end to end.
122
+ - Use simple language without ambiguity; ensure intent is obvious to any reader.
123
+ - Map each test case to a requirement, user story, or acceptance criterion.
124
+ - Include clear preconditions, test steps, input data, and expected results.
125
+ - Avoid assumptions—make the test executable without extra explanation.
126
+ - Cover both positive (expected) and negative (invalid/boundary) scenarios when relevant.
127
+ - Do not duplicate test cases for the same scenario unless necessary.
128
+ - Prioritize critical, high-risk/high-impact areas first.
129
+ </test_case_guidelines>
130
+
131
+ <completion_criteria>
132
+ - Do not finish until ALL todos are resolved. Before finishing, explicitly re-check the todo list and verify there are no items with status pending or in_progress.
133
+ - A todo is resolved only if it is completed or failed with a concise blocker summary and evidence.
134
+ - Reflect progress immediately in todo statuses after each step (start → in_progress, pass → completed, hard blocker after retries → failed).
135
+ - Finish only after the todo list is fully resolved (no pending or in_progress items), then call finish_overall_test(summary) including counts of completed vs failed todos and top blockers.
136
+ - If no tests were needed and no todos were created, finish_overall_test with a concise summary.
137
+ - End early only if the user explicitly requests it; otherwise continue until the todo list is resolved.
138
+ </completion_criteria>
139
+
140
+ <authentication>
141
+ - if you need to log out, use the reset_for_next_test_case tool to clear browser state.
142
+ - Use reset_for_next_test_case tool to clear browser state or sign out users, or prepare for the next test case.
143
+ - Do not attempt social login flows (e.g., Google, Apple, Facebook). If a task depends on them, state the limitation and move on.
144
+ </authentication>
145
+
146
+ <security_reliability>
147
+ - Sanitize all inputs, handle dynamic content and SPA transitions, and maintain session awareness across tests.
148
+ - Never reveal or output storage URLs (e.g., Cloudinary, S3, GCS, Azure Blob) in any message. If a screenshot or asset link exists, refer to it generically (e.g., "[screenshot available]") without the actual URL.
149
+ - Redact any detected storage/CDN URLs in tool responses and summaries.
150
+ - Never expose todo item IDs in any user-facing messages or summaries. Use descriptive names or references instead.
151
+ - For local execution, be extra careful with file system access and network requests.
152
+ </security_reliability>
153
+
154
+ Context hints (may be unset):
155
+ - Base URL: {{base_url}}
156
+ - UI base URL: {{ui_base_url}}
157
+ - API base URL: {{api_base_url}}
158
+ - Documentation URL: {{documentation_url}}
159
+ - Test Type: {{test_type}}
160
+ - Mode: {{mode}} (local execution with direct access to user's environment)`;
161
+ export const CLI_AGENT_PROMPT_TEMPLATES = {
162
+ unified_agent_react: CLI_AGENT_SYSTEM_PROMPT,
163
+ ui_agent_react: CLI_AGENT_SYSTEM_PROMPT,
164
+ api_agent_react: CLI_AGENT_SYSTEM_PROMPT,
165
+ };
166
+ //# sourceMappingURL=templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../../../../src/lib/prompts/templates.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,CAAC,MAAM,uBAAuB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4EA2JqC,CAAC;AAE7E,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,mBAAmB,EAAE,uBAAuB;IAC5C,cAAc,EAAE,uBAAuB;IACvC,eAAe,EAAE,uBAAuB;CAChC,CAAC"}
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Session Manager - Conversation and artifact persistence
3
+ * Stores chat history, tool outputs, and test artifacts locally
4
+ */
5
+ import * as fs from "fs";
6
+ import * as path from "path";
7
+ import { homedir } from "os";
8
+ import { randomUUID } from "crypto";
9
+ const SESSIONS_DIR = path.join(homedir(), ".timetotest", "sessions");
10
+ export class SessionManager {
11
+ sessionId;
12
+ sessionPath;
13
+ session = null;
14
+ constructor(sessionId) {
15
+ this.sessionId = sessionId || randomUUID();
16
+ this.sessionPath = path.join(SESSIONS_DIR, this.sessionId);
17
+ this.initializeSession();
18
+ }
19
+ initializeSession() {
20
+ if (!fs.existsSync(SESSIONS_DIR)) {
21
+ fs.mkdirSync(SESSIONS_DIR, { recursive: true });
22
+ }
23
+ if (!fs.existsSync(this.sessionPath)) {
24
+ fs.mkdirSync(this.sessionPath, { recursive: true });
25
+ }
26
+ const configPath = path.join(this.sessionPath, "config.json");
27
+ if (fs.existsSync(configPath)) {
28
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
29
+ this.session = {
30
+ config,
31
+ messages: this.loadMessages(),
32
+ artifacts: this.loadArtifacts(),
33
+ };
34
+ }
35
+ else {
36
+ this.session = {
37
+ config: {
38
+ id: this.sessionId,
39
+ mode: "local",
40
+ testingMode: "ui",
41
+ createdAt: Date.now(),
42
+ updatedAt: Date.now(),
43
+ },
44
+ messages: [],
45
+ artifacts: {},
46
+ };
47
+ this.saveConfig();
48
+ }
49
+ }
50
+ saveConfig() {
51
+ if (!this.session)
52
+ return;
53
+ const configPath = path.join(this.sessionPath, "config.json");
54
+ fs.writeFileSync(configPath, JSON.stringify(this.session.config, null, 2), "utf-8");
55
+ }
56
+ loadMessages() {
57
+ const messagesPath = path.join(this.sessionPath, "messages.json");
58
+ if (fs.existsSync(messagesPath)) {
59
+ return JSON.parse(fs.readFileSync(messagesPath, "utf-8"));
60
+ }
61
+ return [];
62
+ }
63
+ loadArtifacts() {
64
+ const artifactsPath = path.join(this.sessionPath, "artifacts.json");
65
+ if (fs.existsSync(artifactsPath)) {
66
+ return JSON.parse(fs.readFileSync(artifactsPath, "utf-8"));
67
+ }
68
+ return {};
69
+ }
70
+ addMessage(message) {
71
+ if (!this.session)
72
+ return;
73
+ if (!message.timestamp) {
74
+ message.timestamp = Date.now();
75
+ }
76
+ this.session.messages.push(message);
77
+ this.session.config.updatedAt = Date.now();
78
+ this.saveMessages();
79
+ this.saveConfig();
80
+ }
81
+ clearMessages() {
82
+ if (!this.session)
83
+ return;
84
+ this.session.messages = [];
85
+ this.session.config.updatedAt = Date.now();
86
+ this.saveMessages();
87
+ this.saveConfig();
88
+ }
89
+ saveMessages() {
90
+ if (!this.session)
91
+ return;
92
+ const messagesPath = path.join(this.sessionPath, "messages.json");
93
+ fs.writeFileSync(messagesPath, JSON.stringify(this.session.messages, null, 2), "utf-8");
94
+ }
95
+ saveArtifact(name, data) {
96
+ if (!this.session)
97
+ return;
98
+ this.session.artifacts[name] = {
99
+ data,
100
+ savedAt: Date.now(),
101
+ };
102
+ this.session.config.updatedAt = Date.now();
103
+ this.saveArtifacts();
104
+ this.saveConfig();
105
+ }
106
+ saveArtifacts() {
107
+ if (!this.session)
108
+ return;
109
+ const artifactsPath = path.join(this.sessionPath, "artifacts.json");
110
+ fs.writeFileSync(artifactsPath, JSON.stringify(this.session.artifacts, null, 2), "utf-8");
111
+ }
112
+ saveScreenshot(filename, buffer) {
113
+ if (!this.session)
114
+ return;
115
+ const screenshotsDir = path.join(this.sessionPath, "screenshots");
116
+ if (!fs.existsSync(screenshotsDir)) {
117
+ fs.mkdirSync(screenshotsDir, { recursive: true });
118
+ }
119
+ fs.writeFileSync(path.join(screenshotsDir, filename), buffer);
120
+ this.session.artifacts[`screenshot:${filename}`] = {
121
+ type: "screenshot",
122
+ path: `screenshots/${filename}`,
123
+ savedAt: Date.now(),
124
+ };
125
+ this.saveArtifacts();
126
+ }
127
+ saveDocument(filename, content, type = "document") {
128
+ if (!this.session)
129
+ return;
130
+ this.session.artifacts[`${type}:${filename}`] = {
131
+ type,
132
+ content,
133
+ savedAt: Date.now(),
134
+ };
135
+ this.saveArtifacts();
136
+ }
137
+ getMessages() {
138
+ return this.session?.messages || [];
139
+ }
140
+ getArtifacts() {
141
+ return this.session?.artifacts || {};
142
+ }
143
+ getConfig() {
144
+ return this.session?.config || null;
145
+ }
146
+ updateConfig(updates) {
147
+ if (!this.session)
148
+ return;
149
+ this.session.config = {
150
+ ...this.session.config,
151
+ ...updates,
152
+ updatedAt: Date.now(),
153
+ };
154
+ this.saveConfig();
155
+ }
156
+ getSessionId() {
157
+ return this.sessionId;
158
+ }
159
+ getSessionPath() {
160
+ return this.sessionPath;
161
+ }
162
+ static listSessions() {
163
+ if (!fs.existsSync(SESSIONS_DIR)) {
164
+ return [];
165
+ }
166
+ const sessions = [];
167
+ const dirs = fs.readdirSync(SESSIONS_DIR);
168
+ for (const dir of dirs) {
169
+ const configPath = path.join(SESSIONS_DIR, dir, "config.json");
170
+ if (fs.existsSync(configPath)) {
171
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
172
+ sessions.push(config);
173
+ }
174
+ }
175
+ return sessions.sort((a, b) => b.updatedAt - a.updatedAt);
176
+ }
177
+ static clearSession(sessionId) {
178
+ const sessionPath = path.join(SESSIONS_DIR, sessionId);
179
+ if (fs.existsSync(sessionPath)) {
180
+ fs.rmSync(sessionPath, { recursive: true });
181
+ }
182
+ }
183
+ static deleteOldSessions(retentionDays = 7) {
184
+ if (!fs.existsSync(SESSIONS_DIR)) {
185
+ return;
186
+ }
187
+ const now = Date.now();
188
+ const retentionMs = retentionDays * 24 * 60 * 60 * 1000;
189
+ const dirs = fs.readdirSync(SESSIONS_DIR);
190
+ for (const dir of dirs) {
191
+ const configPath = path.join(SESSIONS_DIR, dir, "config.json");
192
+ if (fs.existsSync(configPath)) {
193
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
194
+ if (now - config.updatedAt > retentionMs) {
195
+ fs.rmSync(path.join(SESSIONS_DIR, dir), { recursive: true });
196
+ }
197
+ }
198
+ }
199
+ }
200
+ }
201
+ //# sourceMappingURL=session-manager.js.map