remoat 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 (200) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +297 -0
  3. package/dist/bin/cli.d.ts +2 -0
  4. package/dist/bin/cli.js +80 -0
  5. package/dist/bin/cli.js.map +1 -0
  6. package/dist/bin/commands/doctor.d.ts +1 -0
  7. package/dist/bin/commands/doctor.js +211 -0
  8. package/dist/bin/commands/doctor.js.map +1 -0
  9. package/dist/bin/commands/open.d.ts +1 -0
  10. package/dist/bin/commands/open.js +187 -0
  11. package/dist/bin/commands/open.js.map +1 -0
  12. package/dist/bin/commands/setup.d.ts +1 -0
  13. package/dist/bin/commands/setup.js +267 -0
  14. package/dist/bin/commands/setup.js.map +1 -0
  15. package/dist/bin/commands/start.d.ts +2 -0
  16. package/dist/bin/commands/start.js +39 -0
  17. package/dist/bin/commands/start.js.map +1 -0
  18. package/dist/bot/index.d.ts +2 -0
  19. package/dist/bot/index.js +1393 -0
  20. package/dist/bot/index.js.map +1 -0
  21. package/dist/commands/chatCommandHandler.d.ts +20 -0
  22. package/dist/commands/chatCommandHandler.js +30 -0
  23. package/dist/commands/chatCommandHandler.js.map +1 -0
  24. package/dist/commands/cleanupCommandHandler.d.ts +21 -0
  25. package/dist/commands/cleanupCommandHandler.js +40 -0
  26. package/dist/commands/cleanupCommandHandler.js.map +1 -0
  27. package/dist/commands/joinCommandHandler.d.ts +19 -0
  28. package/dist/commands/joinCommandHandler.js +27 -0
  29. package/dist/commands/joinCommandHandler.js.map +1 -0
  30. package/dist/commands/messageParser.d.ts +7 -0
  31. package/dist/commands/messageParser.js +29 -0
  32. package/dist/commands/messageParser.js.map +1 -0
  33. package/dist/commands/slashCommandHandler.d.ts +21 -0
  34. package/dist/commands/slashCommandHandler.js +105 -0
  35. package/dist/commands/slashCommandHandler.js.map +1 -0
  36. package/dist/commands/workspaceCommandHandler.d.ts +16 -0
  37. package/dist/commands/workspaceCommandHandler.js +29 -0
  38. package/dist/commands/workspaceCommandHandler.js.map +1 -0
  39. package/dist/database/chatSessionRepository.d.ts +59 -0
  40. package/dist/database/chatSessionRepository.js +110 -0
  41. package/dist/database/chatSessionRepository.js.map +1 -0
  42. package/dist/database/scheduleRepository.d.ts +60 -0
  43. package/dist/database/scheduleRepository.js +106 -0
  44. package/dist/database/scheduleRepository.js.map +1 -0
  45. package/dist/database/templateRepository.d.ts +51 -0
  46. package/dist/database/templateRepository.js +90 -0
  47. package/dist/database/templateRepository.js.map +1 -0
  48. package/dist/database/workspaceBindingRepository.d.ts +48 -0
  49. package/dist/database/workspaceBindingRepository.js +92 -0
  50. package/dist/database/workspaceBindingRepository.js.map +1 -0
  51. package/dist/index.d.ts +1 -0
  52. package/dist/index.js +11 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/middleware/auth.d.ts +5 -0
  55. package/dist/middleware/auth.js +14 -0
  56. package/dist/middleware/auth.js.map +1 -0
  57. package/dist/middleware/sanitize.d.ts +1 -0
  58. package/dist/middleware/sanitize.js +18 -0
  59. package/dist/middleware/sanitize.js.map +1 -0
  60. package/dist/services/antigravityLauncher.d.ts +7 -0
  61. package/dist/services/antigravityLauncher.js +94 -0
  62. package/dist/services/antigravityLauncher.js.map +1 -0
  63. package/dist/services/approvalDetector.d.ts +97 -0
  64. package/dist/services/approvalDetector.js +394 -0
  65. package/dist/services/approvalDetector.js.map +1 -0
  66. package/dist/services/assistantDomExtractor.d.ts +49 -0
  67. package/dist/services/assistantDomExtractor.js +340 -0
  68. package/dist/services/assistantDomExtractor.js.map +1 -0
  69. package/dist/services/autoAcceptService.d.ts +14 -0
  70. package/dist/services/autoAcceptService.js +81 -0
  71. package/dist/services/autoAcceptService.js.map +1 -0
  72. package/dist/services/cdpBridgeManager.d.ts +50 -0
  73. package/dist/services/cdpBridgeManager.js +355 -0
  74. package/dist/services/cdpBridgeManager.js.map +1 -0
  75. package/dist/services/cdpConnectionPool.d.ts +88 -0
  76. package/dist/services/cdpConnectionPool.js +235 -0
  77. package/dist/services/cdpConnectionPool.js.map +1 -0
  78. package/dist/services/cdpService.d.ts +214 -0
  79. package/dist/services/cdpService.js +1423 -0
  80. package/dist/services/cdpService.js.map +1 -0
  81. package/dist/services/chatSessionService.d.ts +89 -0
  82. package/dist/services/chatSessionService.js +738 -0
  83. package/dist/services/chatSessionService.js.map +1 -0
  84. package/dist/services/errorPopupDetector.d.ts +89 -0
  85. package/dist/services/errorPopupDetector.js +274 -0
  86. package/dist/services/errorPopupDetector.js.map +1 -0
  87. package/dist/services/modeService.d.ts +44 -0
  88. package/dist/services/modeService.js +74 -0
  89. package/dist/services/modeService.js.map +1 -0
  90. package/dist/services/modelService.d.ts +36 -0
  91. package/dist/services/modelService.js +64 -0
  92. package/dist/services/modelService.js.map +1 -0
  93. package/dist/services/planningDetector.d.ts +87 -0
  94. package/dist/services/planningDetector.js +321 -0
  95. package/dist/services/planningDetector.js.map +1 -0
  96. package/dist/services/processManager.d.ts +18 -0
  97. package/dist/services/processManager.js +62 -0
  98. package/dist/services/processManager.js.map +1 -0
  99. package/dist/services/progressSender.d.ts +20 -0
  100. package/dist/services/progressSender.js +65 -0
  101. package/dist/services/progressSender.js.map +1 -0
  102. package/dist/services/promptDispatcher.d.ts +38 -0
  103. package/dist/services/promptDispatcher.js +42 -0
  104. package/dist/services/promptDispatcher.js.map +1 -0
  105. package/dist/services/quotaService.d.ts +21 -0
  106. package/dist/services/quotaService.js +191 -0
  107. package/dist/services/quotaService.js.map +1 -0
  108. package/dist/services/responseMonitor.d.ts +129 -0
  109. package/dist/services/responseMonitor.js +996 -0
  110. package/dist/services/responseMonitor.js.map +1 -0
  111. package/dist/services/scheduleService.d.ts +58 -0
  112. package/dist/services/scheduleService.js +135 -0
  113. package/dist/services/scheduleService.js.map +1 -0
  114. package/dist/services/screenshotService.d.ts +55 -0
  115. package/dist/services/screenshotService.js +86 -0
  116. package/dist/services/screenshotService.js.map +1 -0
  117. package/dist/services/telegramTopicManager.d.ts +40 -0
  118. package/dist/services/telegramTopicManager.js +103 -0
  119. package/dist/services/telegramTopicManager.js.map +1 -0
  120. package/dist/services/titleGeneratorService.d.ts +32 -0
  121. package/dist/services/titleGeneratorService.js +114 -0
  122. package/dist/services/titleGeneratorService.js.map +1 -0
  123. package/dist/services/updateCheckService.d.ts +16 -0
  124. package/dist/services/updateCheckService.js +148 -0
  125. package/dist/services/updateCheckService.js.map +1 -0
  126. package/dist/services/userMessageDetector.d.ts +57 -0
  127. package/dist/services/userMessageDetector.js +222 -0
  128. package/dist/services/userMessageDetector.js.map +1 -0
  129. package/dist/services/workspaceService.d.ts +33 -0
  130. package/dist/services/workspaceService.js +65 -0
  131. package/dist/services/workspaceService.js.map +1 -0
  132. package/dist/ui/autoAcceptUi.d.ts +6 -0
  133. package/dist/ui/autoAcceptUi.js +22 -0
  134. package/dist/ui/autoAcceptUi.js.map +1 -0
  135. package/dist/ui/modeUi.d.ts +12 -0
  136. package/dist/ui/modeUi.js +40 -0
  137. package/dist/ui/modeUi.js.map +1 -0
  138. package/dist/ui/modelsUi.d.ts +12 -0
  139. package/dist/ui/modelsUi.js +101 -0
  140. package/dist/ui/modelsUi.js.map +1 -0
  141. package/dist/ui/projectListUi.d.ts +11 -0
  142. package/dist/ui/projectListUi.js +59 -0
  143. package/dist/ui/projectListUi.js.map +1 -0
  144. package/dist/ui/screenshotUi.d.ts +6 -0
  145. package/dist/ui/screenshotUi.js +28 -0
  146. package/dist/ui/screenshotUi.js.map +1 -0
  147. package/dist/ui/sessionPickerUi.d.ts +8 -0
  148. package/dist/ui/sessionPickerUi.js +32 -0
  149. package/dist/ui/sessionPickerUi.js.map +1 -0
  150. package/dist/ui/templateUi.d.ts +5 -0
  151. package/dist/ui/templateUi.js +44 -0
  152. package/dist/ui/templateUi.js.map +1 -0
  153. package/dist/utils/cdpPorts.d.ts +2 -0
  154. package/dist/utils/cdpPorts.js +6 -0
  155. package/dist/utils/cdpPorts.js.map +1 -0
  156. package/dist/utils/config.d.ts +14 -0
  157. package/dist/utils/config.js +12 -0
  158. package/dist/utils/config.js.map +1 -0
  159. package/dist/utils/configLoader.d.ts +23 -0
  160. package/dist/utils/configLoader.js +153 -0
  161. package/dist/utils/configLoader.js.map +1 -0
  162. package/dist/utils/htmlToTelegramMarkdown.d.ts +6 -0
  163. package/dist/utils/htmlToTelegramMarkdown.js +189 -0
  164. package/dist/utils/htmlToTelegramMarkdown.js.map +1 -0
  165. package/dist/utils/i18n.d.ts +3 -0
  166. package/dist/utils/i18n.js +78 -0
  167. package/dist/utils/i18n.js.map +1 -0
  168. package/dist/utils/imageHandler.d.ts +35 -0
  169. package/dist/utils/imageHandler.js +155 -0
  170. package/dist/utils/imageHandler.js.map +1 -0
  171. package/dist/utils/lockfile.d.ts +7 -0
  172. package/dist/utils/lockfile.js +117 -0
  173. package/dist/utils/lockfile.js.map +1 -0
  174. package/dist/utils/logger.d.ts +23 -0
  175. package/dist/utils/logger.js +85 -0
  176. package/dist/utils/logger.js.map +1 -0
  177. package/dist/utils/logo.d.ts +1 -0
  178. package/dist/utils/logo.js +14 -0
  179. package/dist/utils/logo.js.map +1 -0
  180. package/dist/utils/metadataExtractor.d.ts +5 -0
  181. package/dist/utils/metadataExtractor.js +16 -0
  182. package/dist/utils/metadataExtractor.js.map +1 -0
  183. package/dist/utils/pathUtils.d.ts +23 -0
  184. package/dist/utils/pathUtils.js +58 -0
  185. package/dist/utils/pathUtils.js.map +1 -0
  186. package/dist/utils/processLogBuffer.d.ts +17 -0
  187. package/dist/utils/processLogBuffer.js +108 -0
  188. package/dist/utils/processLogBuffer.js.map +1 -0
  189. package/dist/utils/streamMessageFormatter.d.ts +18 -0
  190. package/dist/utils/streamMessageFormatter.js +91 -0
  191. package/dist/utils/streamMessageFormatter.js.map +1 -0
  192. package/dist/utils/telegramFormatter.d.ts +37 -0
  193. package/dist/utils/telegramFormatter.js +445 -0
  194. package/dist/utils/telegramFormatter.js.map +1 -0
  195. package/dist/utils/voiceHandler.d.ts +23 -0
  196. package/dist/utils/voiceHandler.js +169 -0
  197. package/dist/utils/voiceHandler.js.map +1 -0
  198. package/locales/en.json +85 -0
  199. package/locales/ja.json +109 -0
  200. package/package.json +84 -0
@@ -0,0 +1,87 @@
1
+ import { CdpService } from './cdpService';
2
+ /** Planning mode button information */
3
+ export interface PlanningInfo {
4
+ /** Open button text */
5
+ openText: string;
6
+ /** Proceed button text */
7
+ proceedText: string;
8
+ /** Plan title (file name shown in the card) */
9
+ planTitle: string;
10
+ /** Plan summary text */
11
+ planSummary: string;
12
+ /** Plan description (markdown rendered in leading-relaxed container) */
13
+ description: string;
14
+ }
15
+ export interface PlanningDetectorOptions {
16
+ /** CDP service instance */
17
+ cdpService: CdpService;
18
+ /** Poll interval in milliseconds (default: 2000ms) */
19
+ pollIntervalMs?: number;
20
+ /** Callback when planning buttons are detected */
21
+ onPlanningRequired: (info: PlanningInfo) => void;
22
+ /** Callback when a previously detected planning state is resolved (buttons disappeared) */
23
+ onResolved?: () => void;
24
+ }
25
+ /**
26
+ * Detects planning mode buttons (Open/Proceed) in the Antigravity UI via polling.
27
+ *
28
+ * Follows the same polling pattern as ApprovalDetector:
29
+ * - start()/stop() lifecycle
30
+ * - Duplicate notification prevention via lastDetectedKey
31
+ * - CDP error tolerance (continues polling on error)
32
+ */
33
+ export declare class PlanningDetector {
34
+ private cdpService;
35
+ private pollIntervalMs;
36
+ private onPlanningRequired;
37
+ private onResolved?;
38
+ private pollTimer;
39
+ private isRunning;
40
+ /** Key of the last detected planning info (for duplicate notification prevention) */
41
+ private lastDetectedKey;
42
+ /** Full PlanningInfo from the last detection */
43
+ private lastDetectedInfo;
44
+ /** Timestamp of last notification (for cooldown-based dedup) */
45
+ private lastNotifiedAt;
46
+ /** Cooldown period in ms to suppress duplicate notifications */
47
+ private static readonly COOLDOWN_MS;
48
+ constructor(options: PlanningDetectorOptions);
49
+ /** Start monitoring. */
50
+ start(): void;
51
+ /** Stop monitoring. */
52
+ stop(): Promise<void>;
53
+ /** Return the last detected planning info. Returns null if nothing has been detected. */
54
+ getLastDetectedInfo(): PlanningInfo | null;
55
+ /** Returns whether monitoring is currently active. */
56
+ isActive(): boolean;
57
+ /**
58
+ * Click the Open button via CDP.
59
+ * @param buttonText Text of the button to click (default: detected openText or "Open")
60
+ * @returns true if click succeeded
61
+ */
62
+ clickOpenButton(buttonText?: string): Promise<boolean>;
63
+ /**
64
+ * Click the Proceed button via CDP.
65
+ * @param buttonText Text of the button to click (default: detected proceedText or "Proceed")
66
+ * @returns true if click succeeded
67
+ */
68
+ clickProceedButton(buttonText?: string): Promise<boolean>;
69
+ /**
70
+ * Extract plan content from the DOM after Open has been clicked.
71
+ * @returns Plan content text or null if not found
72
+ */
73
+ extractPlanContent(): Promise<string | null>;
74
+ /** Schedule the next poll. */
75
+ private schedulePoll;
76
+ /**
77
+ * Single poll iteration:
78
+ * 1. Get planning button info from DOM (with contextId)
79
+ * 2. Notify via callback only on new detection (prevent duplicates)
80
+ * 3. Reset lastDetectedKey / lastDetectedInfo when buttons disappear
81
+ */
82
+ private poll;
83
+ /** Internal click handler using buildClickScript from approvalDetector. */
84
+ private clickButton;
85
+ /** Execute Runtime.evaluate with contextId and return result.value. */
86
+ private runEvaluateScript;
87
+ }
@@ -0,0 +1,321 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PlanningDetector = void 0;
4
+ const logger_1 = require("../utils/logger");
5
+ const approvalDetector_1 = require("./approvalDetector");
6
+ /**
7
+ * Detection script for the Antigravity UI planning mode.
8
+ *
9
+ * Looks for Open/Proceed button pairs inside .notify-user-container
10
+ * and extracts plan metadata from the surrounding DOM elements.
11
+ */
12
+ const DETECT_PLANNING_SCRIPT = `(() => {
13
+ const OPEN_PATTERNS = ['open'];
14
+ const PROCEED_PATTERNS = ['proceed'];
15
+
16
+ const normalize = (text) => (text || '').toLowerCase().replace(/\\s+/g, ' ').trim();
17
+
18
+ // Find the notify container that holds planning UI
19
+ const container = document.querySelector('.notify-user-container');
20
+ if (!container) return null;
21
+
22
+ const allButtons = Array.from(container.querySelectorAll('button'))
23
+ .filter(btn => btn.offsetParent !== null);
24
+
25
+ const openBtn = allButtons.find(btn => {
26
+ const t = normalize(btn.textContent || '');
27
+ return OPEN_PATTERNS.some(p => t === p || t.includes(p));
28
+ }) || null;
29
+
30
+ const proceedBtn = allButtons.find(btn => {
31
+ const t = normalize(btn.textContent || '');
32
+ return PROCEED_PATTERNS.some(p => t === p || t.includes(p));
33
+ }) || null;
34
+
35
+ // Both buttons must exist for this to be a planning UI
36
+ if (!openBtn || !proceedBtn) return null;
37
+
38
+ const openText = (openBtn.textContent || '').trim();
39
+ const proceedText = (proceedBtn.textContent || '').trim();
40
+
41
+ // Extract plan title from .inline-flex.break-all
42
+ const titleEl = container.querySelector('span.inline-flex.break-all, .inline-flex.break-all');
43
+ const planTitle = titleEl ? (titleEl.textContent || '').trim() : '';
44
+
45
+ // Extract plan summary from span.text-sm (excluding buttons text)
46
+ const summaryEls = Array.from(container.querySelectorAll('span.text-sm'));
47
+ const planSummary = summaryEls
48
+ .map(el => (el.textContent || '').trim())
49
+ .filter(text => text.length > 0 && text !== openText && text !== proceedText)
50
+ .join(' ');
51
+
52
+ // Extract description from leading-relaxed container, skipping code/style blocks
53
+ const descEl = container.querySelector('.leading-relaxed.select-text');
54
+ let description = '';
55
+ if (descEl) {
56
+ const SKIP_TAGS = new Set(['PRE', 'CODE', 'STYLE', 'SCRIPT']);
57
+ const parts = [];
58
+ const walk = (node) => {
59
+ if (node.nodeType === 3) {
60
+ const t = node.textContent || '';
61
+ if (t.trim()) parts.push(t.trim());
62
+ } else if (node.nodeType === 1 && !SKIP_TAGS.has(node.tagName)) {
63
+ for (const child of node.childNodes) walk(child);
64
+ }
65
+ };
66
+ walk(descEl);
67
+ description = parts.join(' ').slice(0, 500);
68
+ }
69
+
70
+ return { openText, proceedText, planTitle, planSummary, description };
71
+ })()`;
72
+ /**
73
+ * Extract plan content displayed after clicking Open.
74
+ *
75
+ * Looks for the rendered markdown inside the plan content area
76
+ * and returns the text, truncated to 4000 characters for Telegram message limits.
77
+ */
78
+ const EXTRACT_PLAN_CONTENT_SCRIPT = `(() => {
79
+ // Simple HTML-to-Markdown converter for plan content
80
+ const htmlToMd = (el) => {
81
+ const parts = [];
82
+ const process = (node) => {
83
+ if (node.nodeType === 3) {
84
+ parts.push(node.textContent || '');
85
+ return;
86
+ }
87
+ if (node.nodeType !== 1) return;
88
+ const tag = node.tagName;
89
+ if (tag === 'H1') { parts.push('\\n# '); node.childNodes.forEach(process); parts.push('\\n'); return; }
90
+ if (tag === 'H2') { parts.push('\\n## '); node.childNodes.forEach(process); parts.push('\\n'); return; }
91
+ if (tag === 'H3') { parts.push('\\n### '); node.childNodes.forEach(process); parts.push('\\n'); return; }
92
+ if (tag === 'H4') { parts.push('\\n#### '); node.childNodes.forEach(process); parts.push('\\n'); return; }
93
+ if (tag === 'STRONG' || tag === 'B') { parts.push('**'); node.childNodes.forEach(process); parts.push('**'); return; }
94
+ if (tag === 'EM' || tag === 'I') { parts.push('*'); node.childNodes.forEach(process); parts.push('*'); return; }
95
+ if (tag === 'PRE') {
96
+ const code = node.querySelector('code');
97
+ const text = code ? (code.textContent || '') : (node.textContent || '');
98
+ parts.push('\\n\`\`\`\\n' + text + '\\n\`\`\`\\n');
99
+ return;
100
+ }
101
+ if (tag === 'CODE') { parts.push('\`' + (node.textContent || '') + '\`'); return; }
102
+ if (tag === 'A') {
103
+ const href = node.getAttribute('href') || '';
104
+ parts.push('['); node.childNodes.forEach(process); parts.push('](' + href + ')');
105
+ return;
106
+ }
107
+ if (tag === 'LI') { parts.push('\\n- '); node.childNodes.forEach(process); return; }
108
+ if (tag === 'BR') { parts.push('\\n'); return; }
109
+ if (tag === 'P') { parts.push('\\n\\n'); node.childNodes.forEach(process); parts.push('\\n'); return; }
110
+ if (tag === 'UL' || tag === 'OL') { node.childNodes.forEach(process); parts.push('\\n'); return; }
111
+ if (tag === 'STYLE' || tag === 'SCRIPT') return;
112
+ node.childNodes.forEach(process);
113
+ };
114
+ process(el);
115
+ return parts.join('').replace(/\\n{3,}/g, '\\n\\n').trim();
116
+ };
117
+
118
+ // Primary selector: plan content container
119
+ const contentContainer = document.querySelector(
120
+ 'div.relative.pl-4.pr-4.py-1, div.relative.pl-4.pr-4'
121
+ );
122
+ if (contentContainer) {
123
+ const textEl = contentContainer.querySelector('.leading-relaxed.select-text');
124
+ if (textEl) {
125
+ return htmlToMd(textEl);
126
+ }
127
+ }
128
+
129
+ // Fallback: any leading-relaxed.select-text with significant content
130
+ const allLeading = Array.from(document.querySelectorAll('.leading-relaxed.select-text'));
131
+ for (const el of allLeading) {
132
+ const md = htmlToMd(el);
133
+ if (md.length > 100) {
134
+ return md;
135
+ }
136
+ }
137
+
138
+ return null;
139
+ })()`;
140
+ /**
141
+ * Detects planning mode buttons (Open/Proceed) in the Antigravity UI via polling.
142
+ *
143
+ * Follows the same polling pattern as ApprovalDetector:
144
+ * - start()/stop() lifecycle
145
+ * - Duplicate notification prevention via lastDetectedKey
146
+ * - CDP error tolerance (continues polling on error)
147
+ */
148
+ class PlanningDetector {
149
+ cdpService;
150
+ pollIntervalMs;
151
+ onPlanningRequired;
152
+ onResolved;
153
+ pollTimer = null;
154
+ isRunning = false;
155
+ /** Key of the last detected planning info (for duplicate notification prevention) */
156
+ lastDetectedKey = null;
157
+ /** Full PlanningInfo from the last detection */
158
+ lastDetectedInfo = null;
159
+ /** Timestamp of last notification (for cooldown-based dedup) */
160
+ lastNotifiedAt = 0;
161
+ /** Cooldown period in ms to suppress duplicate notifications */
162
+ static COOLDOWN_MS = 5000;
163
+ constructor(options) {
164
+ this.cdpService = options.cdpService;
165
+ this.pollIntervalMs = options.pollIntervalMs ?? 2000;
166
+ this.onPlanningRequired = options.onPlanningRequired;
167
+ this.onResolved = options.onResolved;
168
+ }
169
+ /** Start monitoring. */
170
+ start() {
171
+ if (this.isRunning)
172
+ return;
173
+ this.isRunning = true;
174
+ this.lastDetectedKey = null;
175
+ this.lastDetectedInfo = null;
176
+ this.lastNotifiedAt = 0;
177
+ this.schedulePoll();
178
+ }
179
+ /** Stop monitoring. */
180
+ async stop() {
181
+ this.isRunning = false;
182
+ if (this.pollTimer) {
183
+ clearTimeout(this.pollTimer);
184
+ this.pollTimer = null;
185
+ }
186
+ }
187
+ /** Return the last detected planning info. Returns null if nothing has been detected. */
188
+ getLastDetectedInfo() {
189
+ return this.lastDetectedInfo;
190
+ }
191
+ /** Returns whether monitoring is currently active. */
192
+ isActive() {
193
+ return this.isRunning;
194
+ }
195
+ /**
196
+ * Click the Open button via CDP.
197
+ * @param buttonText Text of the button to click (default: detected openText or "Open")
198
+ * @returns true if click succeeded
199
+ */
200
+ async clickOpenButton(buttonText) {
201
+ const text = buttonText ?? this.lastDetectedInfo?.openText ?? 'Open';
202
+ return this.clickButton(text);
203
+ }
204
+ /**
205
+ * Click the Proceed button via CDP.
206
+ * @param buttonText Text of the button to click (default: detected proceedText or "Proceed")
207
+ * @returns true if click succeeded
208
+ */
209
+ async clickProceedButton(buttonText) {
210
+ const text = buttonText ?? this.lastDetectedInfo?.proceedText ?? 'Proceed';
211
+ return this.clickButton(text);
212
+ }
213
+ /**
214
+ * Extract plan content from the DOM after Open has been clicked.
215
+ * @returns Plan content text or null if not found
216
+ */
217
+ async extractPlanContent() {
218
+ try {
219
+ const result = await this.runEvaluateScript(EXTRACT_PLAN_CONTENT_SCRIPT);
220
+ return typeof result === 'string' ? result : null;
221
+ }
222
+ catch (error) {
223
+ logger_1.logger.error('[PlanningDetector] Error extracting plan content:', error);
224
+ return null;
225
+ }
226
+ }
227
+ /** Schedule the next poll. */
228
+ schedulePoll() {
229
+ if (!this.isRunning)
230
+ return;
231
+ this.pollTimer = setTimeout(async () => {
232
+ await this.poll();
233
+ if (this.isRunning) {
234
+ this.schedulePoll();
235
+ }
236
+ }, this.pollIntervalMs);
237
+ }
238
+ /**
239
+ * Single poll iteration:
240
+ * 1. Get planning button info from DOM (with contextId)
241
+ * 2. Notify via callback only on new detection (prevent duplicates)
242
+ * 3. Reset lastDetectedKey / lastDetectedInfo when buttons disappear
243
+ */
244
+ async poll() {
245
+ try {
246
+ const contextId = this.cdpService.getPrimaryContextId();
247
+ const callParams = {
248
+ expression: DETECT_PLANNING_SCRIPT,
249
+ returnByValue: true,
250
+ awaitPromise: false,
251
+ };
252
+ if (contextId !== null) {
253
+ callParams.contextId = contextId;
254
+ }
255
+ const result = await this.cdpService.call('Runtime.evaluate', callParams);
256
+ const info = result?.result?.value ?? null;
257
+ if (info) {
258
+ // Duplicate prevention: use button text pair as key (stable across DOM redraws)
259
+ const key = `${info.openText}::${info.proceedText}`;
260
+ const now = Date.now();
261
+ const withinCooldown = (now - this.lastNotifiedAt) < PlanningDetector.COOLDOWN_MS;
262
+ if (key !== this.lastDetectedKey && !withinCooldown) {
263
+ this.lastDetectedKey = key;
264
+ this.lastDetectedInfo = info;
265
+ this.lastNotifiedAt = now;
266
+ Promise.resolve(this.onPlanningRequired(info)).catch((err) => {
267
+ logger_1.logger.error('[PlanningDetector] onPlanningRequired callback failed:', err);
268
+ });
269
+ }
270
+ else if (key === this.lastDetectedKey) {
271
+ // Same key — update stored info silently
272
+ this.lastDetectedInfo = info;
273
+ }
274
+ }
275
+ else {
276
+ // Reset when buttons disappear (prepare for next planning detection)
277
+ const wasDetected = this.lastDetectedKey !== null;
278
+ this.lastDetectedKey = null;
279
+ this.lastDetectedInfo = null;
280
+ if (wasDetected && this.onResolved) {
281
+ this.onResolved();
282
+ }
283
+ }
284
+ }
285
+ catch (error) {
286
+ // Ignore CDP errors and continue monitoring
287
+ const message = error instanceof Error ? error.message : String(error);
288
+ if (message.includes('WebSocket is not connected')) {
289
+ return;
290
+ }
291
+ logger_1.logger.error('[PlanningDetector] Error during polling:', error);
292
+ }
293
+ }
294
+ /** Internal click handler using buildClickScript from approvalDetector. */
295
+ async clickButton(buttonText) {
296
+ try {
297
+ const result = await this.runEvaluateScript((0, approvalDetector_1.buildClickScript)(buttonText));
298
+ return result?.ok === true;
299
+ }
300
+ catch (error) {
301
+ logger_1.logger.error('[PlanningDetector] Error while clicking button:', error);
302
+ return false;
303
+ }
304
+ }
305
+ /** Execute Runtime.evaluate with contextId and return result.value. */
306
+ async runEvaluateScript(expression) {
307
+ const contextId = this.cdpService.getPrimaryContextId();
308
+ const callParams = {
309
+ expression,
310
+ returnByValue: true,
311
+ awaitPromise: false,
312
+ };
313
+ if (contextId !== null) {
314
+ callParams.contextId = contextId;
315
+ }
316
+ const result = await this.cdpService.call('Runtime.evaluate', callParams);
317
+ return result?.result?.value;
318
+ }
319
+ }
320
+ exports.PlanningDetector = PlanningDetector;
321
+ //# sourceMappingURL=planningDetector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planningDetector.js","sourceRoot":"","sources":["../../src/services/planningDetector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export interface TaskOptions {
2
+ id: string;
3
+ command: string;
4
+ args: string[];
5
+ cwd: string;
6
+ onStdout?: (data: string) => void;
7
+ onStderr?: (data: string) => void;
8
+ onClose?: (code: number) => void;
9
+ }
10
+ export declare class ProcessManager {
11
+ private maxConcurrentTasks;
12
+ private queue;
13
+ private runningProcesses;
14
+ constructor(maxConcurrentTasks?: number);
15
+ submitTask(options: TaskOptions): Promise<void>;
16
+ private runNext;
17
+ stopTask(taskId: string): boolean;
18
+ }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ProcessManager = void 0;
4
+ const child_process_1 = require("child_process");
5
+ class ProcessManager {
6
+ maxConcurrentTasks;
7
+ queue = [];
8
+ runningProcesses = new Map();
9
+ constructor(maxConcurrentTasks = 1) {
10
+ this.maxConcurrentTasks = maxConcurrentTasks;
11
+ }
12
+ async submitTask(options) {
13
+ this.queue.push(options);
14
+ this.runNext();
15
+ }
16
+ runNext() {
17
+ if (this.runningProcesses.size >= this.maxConcurrentTasks) {
18
+ return;
19
+ }
20
+ const nextTask = this.queue.shift();
21
+ if (!nextTask) {
22
+ return;
23
+ }
24
+ const { id, command, args, cwd, onStdout, onStderr, onClose } = nextTask;
25
+ const child = (0, child_process_1.spawn)(command, args, { cwd });
26
+ this.runningProcesses.set(id, child);
27
+ child.stdout?.on('data', (data) => {
28
+ if (onStdout) {
29
+ onStdout(data.toString());
30
+ }
31
+ });
32
+ child.stderr?.on('data', (data) => {
33
+ if (onStderr) {
34
+ onStderr(data.toString());
35
+ }
36
+ });
37
+ child.on('close', (code) => {
38
+ this.runningProcesses.delete(id);
39
+ if (onClose) {
40
+ onClose(code ?? 0);
41
+ }
42
+ this.runNext();
43
+ });
44
+ }
45
+ stopTask(taskId) {
46
+ const child = this.runningProcesses.get(taskId);
47
+ if (child) {
48
+ child.kill();
49
+ this.runningProcesses.delete(taskId);
50
+ return true;
51
+ }
52
+ // Check if queued
53
+ const indexInQueue = this.queue.findIndex((task) => task.id === taskId);
54
+ if (indexInQueue !== -1) {
55
+ this.queue.splice(indexInQueue, 1);
56
+ return true;
57
+ }
58
+ return false;
59
+ }
60
+ }
61
+ exports.ProcessManager = ProcessManager;
62
+ //# sourceMappingURL=processManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processManager.js","sourceRoot":"","sources":["../../src/services/processManager.ts"],"names":[],"mappings":";;;AAAA,iDAAoD;AAYpD,MAAa,cAAc;IACf,kBAAkB,CAAS;IAC3B,KAAK,GAAkB,EAAE,CAAC;IAC1B,gBAAgB,GAA8B,IAAI,GAAG,EAAE,CAAC;IAEhE,YAAY,qBAA6B,CAAC;QACtC,IAAI,CAAC,kBAAkB,GAAG,kBAAkB,CAAC;IACjD,CAAC;IAEM,KAAK,CAAC,UAAU,CAAC,OAAoB;QACxC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzB,IAAI,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC;IAEO,OAAO;QACX,IAAI,IAAI,CAAC,gBAAgB,CAAC,IAAI,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACxD,OAAO;QACX,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACpC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACZ,OAAO;QACX,CAAC;QAED,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC;QAEzE,MAAM,KAAK,GAAG,IAAA,qBAAK,EAAC,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5C,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAErC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,IAAqB,EAAE,EAAE;YAC/C,IAAI,QAAQ,EAAE,CAAC;gBACX,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC9B,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,IAAqB,EAAE,EAAE;YAC/C,IAAI,QAAQ,EAAE,CAAC;gBACX,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC9B,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAmB,EAAE,EAAE;YACtC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACjC,IAAI,OAAO,EAAE,CAAC;gBACV,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;YACvB,CAAC;YACD,IAAI,CAAC,OAAO,EAAE,CAAC;QACnB,CAAC,CAAC,CAAC;IACP,CAAC;IAEM,QAAQ,CAAC,MAAc;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAChD,IAAI,KAAK,EAAE,CAAC;YACR,KAAK,CAAC,IAAI,EAAE,CAAC;YACb,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACrC,OAAO,IAAI,CAAC;QAChB,CAAC;QACD,kBAAkB;QAClB,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;QACxE,IAAI,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;YACnC,OAAO,IAAI,CAAC;QAChB,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;CACJ;AAjED,wCAiEC"}
@@ -0,0 +1,20 @@
1
+ export interface ProgressSenderOptions {
2
+ send: (content: string) => Promise<unknown>;
3
+ throttleMs?: number;
4
+ maxLength?: number;
5
+ wrapInCodeBlock?: boolean;
6
+ }
7
+ export declare class ProgressSender {
8
+ private throttleMs;
9
+ private maxLength;
10
+ private wrapInCodeBlock;
11
+ private buffer;
12
+ private timer;
13
+ private sendContent;
14
+ constructor(options: ProgressSenderOptions);
15
+ append(text: string): void;
16
+ forceEmit(): void;
17
+ dispose(): void;
18
+ private emit;
19
+ private splitByLength;
20
+ }
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ProgressSender = void 0;
4
+ class ProgressSender {
5
+ throttleMs;
6
+ maxLength;
7
+ wrapInCodeBlock;
8
+ buffer = '';
9
+ timer = null;
10
+ sendContent;
11
+ constructor(options) {
12
+ this.sendContent = options.send;
13
+ this.throttleMs = options.throttleMs ?? 3000;
14
+ this.maxLength = options.maxLength ?? 4000;
15
+ this.wrapInCodeBlock = options.wrapInCodeBlock ?? true;
16
+ }
17
+ append(text) {
18
+ this.buffer += text;
19
+ if (!this.timer) {
20
+ this.timer = setTimeout(() => {
21
+ this.emit();
22
+ }, this.throttleMs);
23
+ }
24
+ }
25
+ forceEmit() {
26
+ this.emit();
27
+ }
28
+ dispose() {
29
+ if (this.timer) {
30
+ clearTimeout(this.timer);
31
+ this.timer = null;
32
+ }
33
+ this.buffer = '';
34
+ }
35
+ emit() {
36
+ if (this.timer) {
37
+ clearTimeout(this.timer);
38
+ this.timer = null;
39
+ }
40
+ if (!this.buffer)
41
+ return;
42
+ const payload = this.buffer;
43
+ this.buffer = '';
44
+ const chunks = this.splitByLength(payload, this.maxLength);
45
+ for (const chunk of chunks) {
46
+ const escaped = chunk.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
47
+ const content = this.wrapInCodeBlock ? `<pre>${escaped}</pre>` : chunk;
48
+ this.sendContent(content).catch(() => { });
49
+ }
50
+ }
51
+ splitByLength(text, maxLength) {
52
+ if (text.length <= maxLength) {
53
+ return [text];
54
+ }
55
+ const result = [];
56
+ let cursor = 0;
57
+ while (cursor < text.length) {
58
+ result.push(text.slice(cursor, cursor + maxLength));
59
+ cursor += maxLength;
60
+ }
61
+ return result;
62
+ }
63
+ }
64
+ exports.ProgressSender = ProgressSender;
65
+ //# sourceMappingURL=progressSender.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progressSender.js","sourceRoot":"","sources":["../../src/services/progressSender.ts"],"names":[],"mappings":";;;AAOA,MAAa,cAAc;IACf,UAAU,CAAS;IACnB,SAAS,CAAS;IAClB,eAAe,CAAU;IAEzB,MAAM,GAAW,EAAE,CAAC;IACpB,KAAK,GAA0B,IAAI,CAAC;IAEpC,WAAW,CAAwC;IAE3D,YAAY,OAA8B;QACtC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;QAChC,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC;QAC7C,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC;QAC3C,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,IAAI,CAAC;IAC3D,CAAC;IAEM,MAAM,CAAC,IAAY;QACtB,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC;QACpB,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YACd,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBACzB,IAAI,CAAC,IAAI,EAAE,CAAC;YAChB,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACxB,CAAC;IACL,CAAC;IAEM,SAAS;QACZ,IAAI,CAAC,IAAI,EAAE,CAAC;IAChB,CAAC;IAEM,OAAO;QACV,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACzB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACtB,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;IACrB,CAAC;IAEO,IAAI;QACR,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACzB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACtB,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QAEjB,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC3D,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YACzB,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACzF,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,QAAQ,OAAO,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;YACvE,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;QAC/C,CAAC;IACL,CAAC;IAEO,aAAa,CAAC,IAAY,EAAE,SAAiB;QACjD,IAAI,IAAI,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC;YAC3B,OAAO,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,OAAO,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YAC1B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;YACpD,MAAM,IAAI,SAAS,CAAC;QACxB,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ;AArED,wCAqEC"}
@@ -0,0 +1,38 @@
1
+ import { ChatSessionRepository } from '../database/chatSessionRepository';
2
+ import { CdpBridge, TelegramChannel } from './cdpBridgeManager';
3
+ import { CdpService } from './cdpService';
4
+ import { ModeService } from './modeService';
5
+ import { ModelService } from './modelService';
6
+ import { TitleGeneratorService } from './titleGeneratorService';
7
+ import { TelegramTopicManager } from './telegramTopicManager';
8
+ import { ChatSessionService } from './chatSessionService';
9
+ import { InboundImageAttachment } from '../utils/imageHandler';
10
+ export interface PromptDispatchOptions {
11
+ chatSessionService: ChatSessionService;
12
+ chatSessionRepo: ChatSessionRepository;
13
+ topicManager: TelegramTopicManager;
14
+ titleGenerator: TitleGeneratorService;
15
+ }
16
+ export interface PromptDispatchRequest {
17
+ channel: TelegramChannel;
18
+ prompt: string;
19
+ cdp: CdpService;
20
+ inboundImages?: InboundImageAttachment[];
21
+ options?: PromptDispatchOptions;
22
+ }
23
+ export interface PromptDispatcherDeps {
24
+ bridge: CdpBridge;
25
+ modeService: ModeService;
26
+ modelService: ModelService;
27
+ sendPromptImpl: (bridge: CdpBridge, channel: TelegramChannel, prompt: string, cdp: CdpService, modeService: ModeService, modelService: ModelService, inboundImages?: InboundImageAttachment[], options?: PromptDispatchOptions) => Promise<void>;
28
+ }
29
+ export declare class PromptDispatcher {
30
+ private readonly deps;
31
+ /** Per-channel lock to prevent concurrent prompt dispatch */
32
+ private channelLocks;
33
+ /** Per-workspace lock to prevent cross-topic races on the same workspace */
34
+ private workspaceLocks;
35
+ constructor(deps: PromptDispatcherDeps);
36
+ private channelKey;
37
+ send(req: PromptDispatchRequest): Promise<void>;
38
+ }