@adhdev/daemon-core 0.5.3

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 (217) hide show
  1. package/dist/index.d.ts +2662 -0
  2. package/dist/index.js +11341 -0
  3. package/dist/index.js.map +1 -0
  4. package/package.json +48 -0
  5. package/providers/_builtin/.github/workflows/generate-registry.yml +57 -0
  6. package/providers/_builtin/COMPATIBILITY.md +217 -0
  7. package/providers/_builtin/CONTRIBUTING.md +200 -0
  8. package/providers/_builtin/README.md +119 -0
  9. package/providers/_builtin/_helpers/index.js +188 -0
  10. package/providers/_builtin/acp/agentpool/provider.json +54 -0
  11. package/providers/_builtin/acp/amp/provider.json +52 -0
  12. package/providers/_builtin/acp/auggie/provider.json +57 -0
  13. package/providers/_builtin/acp/autodev/provider.json +54 -0
  14. package/providers/_builtin/acp/autohand/provider.json +52 -0
  15. package/providers/_builtin/acp/blackbox-ai/provider.json +54 -0
  16. package/providers/_builtin/acp/claude-agent/provider.json +57 -0
  17. package/providers/_builtin/acp/cline-acp/provider.json +54 -0
  18. package/providers/_builtin/acp/codebuddy/provider.json +54 -0
  19. package/providers/_builtin/acp/codex-cli/provider.json +57 -0
  20. package/providers/_builtin/acp/corust-agent/provider.json +52 -0
  21. package/providers/_builtin/acp/crow-cli/provider.json +54 -0
  22. package/providers/_builtin/acp/cursor-acp/provider.json +54 -0
  23. package/providers/_builtin/acp/deepagents/provider.json +52 -0
  24. package/providers/_builtin/acp/dimcode/provider.json +54 -0
  25. package/providers/_builtin/acp/docker-cagent/provider.json +57 -0
  26. package/providers/_builtin/acp/factory-droid/provider.json +60 -0
  27. package/providers/_builtin/acp/fast-agent/provider.json +52 -0
  28. package/providers/_builtin/acp/gemini-cli/provider.json +114 -0
  29. package/providers/_builtin/acp/github-copilot/provider.json +54 -0
  30. package/providers/_builtin/acp/goose/provider.json +57 -0
  31. package/providers/_builtin/acp/junie/provider.json +52 -0
  32. package/providers/_builtin/acp/kilo/provider.json +54 -0
  33. package/providers/_builtin/acp/kimi-cli/provider.json +57 -0
  34. package/providers/_builtin/acp/minion-code/provider.json +52 -0
  35. package/providers/_builtin/acp/mistral-vibe/provider.json +57 -0
  36. package/providers/_builtin/acp/nova/provider.json +54 -0
  37. package/providers/_builtin/acp/openclaw/provider.json +54 -0
  38. package/providers/_builtin/acp/opencode/provider.json +52 -0
  39. package/providers/_builtin/acp/openhands/provider.json +54 -0
  40. package/providers/_builtin/acp/pi-acp/provider.json +52 -0
  41. package/providers/_builtin/acp/qoder/provider.json +54 -0
  42. package/providers/_builtin/acp/qwen-code/provider.json +60 -0
  43. package/providers/_builtin/acp/stakpak/provider.json +54 -0
  44. package/providers/_builtin/acp/vtcode/provider.json +54 -0
  45. package/providers/_builtin/cli/claude-cli/provider.json +100 -0
  46. package/providers/_builtin/cli/codex-cli/provider.json +89 -0
  47. package/providers/_builtin/cli/gemini-cli/provider.json +93 -0
  48. package/providers/_builtin/docs/CDP_SELECTOR_GUIDE.md +370 -0
  49. package/providers/_builtin/docs/PROVIDER_GUIDE.md +916 -0
  50. package/providers/_builtin/extension/cline/provider.json +35 -0
  51. package/providers/_builtin/extension/cline/scripts/focus_editor.js +48 -0
  52. package/providers/_builtin/extension/cline/scripts/list_chats.js +100 -0
  53. package/providers/_builtin/extension/cline/scripts/list_models.js +43 -0
  54. package/providers/_builtin/extension/cline/scripts/list_modes.js +35 -0
  55. package/providers/_builtin/extension/cline/scripts/new_session.js +85 -0
  56. package/providers/_builtin/extension/cline/scripts/open_panel.js +25 -0
  57. package/providers/_builtin/extension/cline/scripts/read_chat.js +257 -0
  58. package/providers/_builtin/extension/cline/scripts/resolve_action.js +83 -0
  59. package/providers/_builtin/extension/cline/scripts/send_message.js +95 -0
  60. package/providers/_builtin/extension/cline/scripts/set_mode.js +36 -0
  61. package/providers/_builtin/extension/cline/scripts/set_model.js +36 -0
  62. package/providers/_builtin/extension/cline/scripts/switch_session.js +206 -0
  63. package/providers/_builtin/extension/cline/scripts.js +73 -0
  64. package/providers/_builtin/extension/roo-code/provider.json +35 -0
  65. package/providers/_builtin/extension/roo-code/scripts.js +659 -0
  66. package/providers/_builtin/ide/antigravity/provider.json +68 -0
  67. package/providers/_builtin/ide/antigravity/scripts/1.106/focus_editor.js +20 -0
  68. package/providers/_builtin/ide/antigravity/scripts/1.106/list_chats.js +137 -0
  69. package/providers/_builtin/ide/antigravity/scripts/1.106/list_models.js +38 -0
  70. package/providers/_builtin/ide/antigravity/scripts/1.106/list_modes.js +48 -0
  71. package/providers/_builtin/ide/antigravity/scripts/1.106/new_session.js +75 -0
  72. package/providers/_builtin/ide/antigravity/scripts/1.106/read_chat.js +262 -0
  73. package/providers/_builtin/ide/antigravity/scripts/1.106/resolve_action.js +68 -0
  74. package/providers/_builtin/ide/antigravity/scripts/1.106/scripts.js +57 -0
  75. package/providers/_builtin/ide/antigravity/scripts/1.106/send_message.js +56 -0
  76. package/providers/_builtin/ide/antigravity/scripts/1.106/set_mode.js +34 -0
  77. package/providers/_builtin/ide/antigravity/scripts/1.106/set_model.js +47 -0
  78. package/providers/_builtin/ide/antigravity/scripts/1.106/switch_session.js +114 -0
  79. package/providers/_builtin/ide/antigravity/scripts/1.107/focus_editor.js +20 -0
  80. package/providers/_builtin/ide/antigravity/scripts/1.107/list_chats.js +137 -0
  81. package/providers/_builtin/ide/antigravity/scripts/1.107/list_models.js +61 -0
  82. package/providers/_builtin/ide/antigravity/scripts/1.107/list_modes.js +72 -0
  83. package/providers/_builtin/ide/antigravity/scripts/1.107/new_session.js +75 -0
  84. package/providers/_builtin/ide/antigravity/scripts/1.107/read_chat.js +262 -0
  85. package/providers/_builtin/ide/antigravity/scripts/1.107/resolve_action.js +68 -0
  86. package/providers/_builtin/ide/antigravity/scripts/1.107/scripts.js +67 -0
  87. package/providers/_builtin/ide/antigravity/scripts/1.107/send_message.js +56 -0
  88. package/providers/_builtin/ide/antigravity/scripts/1.107/set_mode.js +67 -0
  89. package/providers/_builtin/ide/antigravity/scripts/1.107/set_model.js +72 -0
  90. package/providers/_builtin/ide/antigravity/scripts/1.107/switch_session.js +114 -0
  91. package/providers/_builtin/ide/cursor/provider.json +70 -0
  92. package/providers/_builtin/ide/cursor/scripts/0.49/dismiss_notification.js +30 -0
  93. package/providers/_builtin/ide/cursor/scripts/0.49/focus_editor.js +13 -0
  94. package/providers/_builtin/ide/cursor/scripts/0.49/list_models.js +78 -0
  95. package/providers/_builtin/ide/cursor/scripts/0.49/list_modes.js +40 -0
  96. package/providers/_builtin/ide/cursor/scripts/0.49/list_notifications.js +23 -0
  97. package/providers/_builtin/ide/cursor/scripts/0.49/list_sessions.js +42 -0
  98. package/providers/_builtin/ide/cursor/scripts/0.49/new_session.js +20 -0
  99. package/providers/_builtin/ide/cursor/scripts/0.49/open_panel.js +23 -0
  100. package/providers/_builtin/ide/cursor/scripts/0.49/read_chat.js +75 -0
  101. package/providers/_builtin/ide/cursor/scripts/0.49/resolve_action.js +19 -0
  102. package/providers/_builtin/ide/cursor/scripts/0.49/scripts.js +78 -0
  103. package/providers/_builtin/ide/cursor/scripts/0.49/send_message.js +23 -0
  104. package/providers/_builtin/ide/cursor/scripts/0.49/set_mode.js +38 -0
  105. package/providers/_builtin/ide/cursor/scripts/0.49/set_model.js +81 -0
  106. package/providers/_builtin/ide/cursor/scripts/0.49/switch_session.js +28 -0
  107. package/providers/_builtin/ide/kiro/provider.json +67 -0
  108. package/providers/_builtin/ide/kiro/scripts/focus_editor.js +20 -0
  109. package/providers/_builtin/ide/kiro/scripts/open_panel.js +47 -0
  110. package/providers/_builtin/ide/kiro/scripts/resolve_action.js +54 -0
  111. package/providers/_builtin/ide/kiro/scripts/send_message.js +29 -0
  112. package/providers/_builtin/ide/kiro/scripts/webview_list_models.js +39 -0
  113. package/providers/_builtin/ide/kiro/scripts/webview_list_modes.js +39 -0
  114. package/providers/_builtin/ide/kiro/scripts/webview_list_sessions.js +21 -0
  115. package/providers/_builtin/ide/kiro/scripts/webview_new_session.js +34 -0
  116. package/providers/_builtin/ide/kiro/scripts/webview_read_chat.js +68 -0
  117. package/providers/_builtin/ide/kiro/scripts/webview_send_message.js +72 -0
  118. package/providers/_builtin/ide/kiro/scripts/webview_set_mode.js +15 -0
  119. package/providers/_builtin/ide/kiro/scripts/webview_set_model.js +15 -0
  120. package/providers/_builtin/ide/kiro/scripts/webview_switch_session.js +26 -0
  121. package/providers/_builtin/ide/kiro/scripts.js +62 -0
  122. package/providers/_builtin/ide/pearai/provider.json +67 -0
  123. package/providers/_builtin/ide/pearai/scripts/focus_editor.js +20 -0
  124. package/providers/_builtin/ide/pearai/scripts/list_sessions.js +38 -0
  125. package/providers/_builtin/ide/pearai/scripts/new_session.js +55 -0
  126. package/providers/_builtin/ide/pearai/scripts/open_panel.js +46 -0
  127. package/providers/_builtin/ide/pearai/scripts/resolve_action.js +54 -0
  128. package/providers/_builtin/ide/pearai/scripts/send_message.js +29 -0
  129. package/providers/_builtin/ide/pearai/scripts/webview_list_models.js +43 -0
  130. package/providers/_builtin/ide/pearai/scripts/webview_list_modes.js +35 -0
  131. package/providers/_builtin/ide/pearai/scripts/webview_list_sessions.js +62 -0
  132. package/providers/_builtin/ide/pearai/scripts/webview_new_session.js +49 -0
  133. package/providers/_builtin/ide/pearai/scripts/webview_read_chat.js +92 -0
  134. package/providers/_builtin/ide/pearai/scripts/webview_resolve_action.js +59 -0
  135. package/providers/_builtin/ide/pearai/scripts/webview_send_message.js +72 -0
  136. package/providers/_builtin/ide/pearai/scripts/webview_set_mode.js +36 -0
  137. package/providers/_builtin/ide/pearai/scripts/webview_set_model.js +36 -0
  138. package/providers/_builtin/ide/pearai/scripts/webview_switch_session.js +34 -0
  139. package/providers/_builtin/ide/pearai/scripts.js +74 -0
  140. package/providers/_builtin/ide/trae/provider.json +66 -0
  141. package/providers/_builtin/ide/trae/scripts/focus_editor.js +20 -0
  142. package/providers/_builtin/ide/trae/scripts/list_chats.js +24 -0
  143. package/providers/_builtin/ide/trae/scripts/list_models.js +39 -0
  144. package/providers/_builtin/ide/trae/scripts/list_modes.js +39 -0
  145. package/providers/_builtin/ide/trae/scripts/new_session.js +30 -0
  146. package/providers/_builtin/ide/trae/scripts/open_panel.js +44 -0
  147. package/providers/_builtin/ide/trae/scripts/read_chat.js +113 -0
  148. package/providers/_builtin/ide/trae/scripts/resolve_action.js +54 -0
  149. package/providers/_builtin/ide/trae/scripts/send_message.js +69 -0
  150. package/providers/_builtin/ide/trae/scripts/set_mode.js +15 -0
  151. package/providers/_builtin/ide/trae/scripts/set_model.js +15 -0
  152. package/providers/_builtin/ide/trae/scripts/switch_session.js +23 -0
  153. package/providers/_builtin/ide/trae/scripts.js +57 -0
  154. package/providers/_builtin/ide/vscode/provider.json +64 -0
  155. package/providers/_builtin/ide/vscode-insiders/provider.json +62 -0
  156. package/providers/_builtin/ide/vscodium/provider.json +63 -0
  157. package/providers/_builtin/ide/windsurf/provider.json +53 -0
  158. package/providers/_builtin/ide/windsurf/scripts/focus_editor.js +30 -0
  159. package/providers/_builtin/ide/windsurf/scripts/list_chats.js +117 -0
  160. package/providers/_builtin/ide/windsurf/scripts/list_models.js +39 -0
  161. package/providers/_builtin/ide/windsurf/scripts/list_modes.js +39 -0
  162. package/providers/_builtin/ide/windsurf/scripts/new_session.js +69 -0
  163. package/providers/_builtin/ide/windsurf/scripts/open_panel.js +58 -0
  164. package/providers/_builtin/ide/windsurf/scripts/read_chat.js +297 -0
  165. package/providers/_builtin/ide/windsurf/scripts/resolve_action.js +68 -0
  166. package/providers/_builtin/ide/windsurf/scripts/send_message.js +87 -0
  167. package/providers/_builtin/ide/windsurf/scripts/set_mode.js +15 -0
  168. package/providers/_builtin/ide/windsurf/scripts/set_model.js +15 -0
  169. package/providers/_builtin/ide/windsurf/scripts/switch_session.js +58 -0
  170. package/providers/_builtin/ide/windsurf/scripts.js +57 -0
  171. package/providers/_builtin/registry.json +266 -0
  172. package/providers/_builtin/validate.js +156 -0
  173. package/src/agent-stream/index.ts +6 -0
  174. package/src/agent-stream/manager.ts +286 -0
  175. package/src/agent-stream/poller.ts +154 -0
  176. package/src/agent-stream/provider-adapter.ts +138 -0
  177. package/src/agent-stream/types.ts +61 -0
  178. package/src/boot/daemon-lifecycle.ts +252 -0
  179. package/src/cdp/devtools.ts +335 -0
  180. package/src/cdp/initializer.ts +191 -0
  181. package/src/cdp/manager.ts +897 -0
  182. package/src/cdp/scanner.ts +185 -0
  183. package/src/cdp/setup.ts +150 -0
  184. package/src/cli-adapter-types.ts +25 -0
  185. package/src/cli-adapters/provider-cli-adapter.ts +448 -0
  186. package/src/commands/cdp-commands.ts +208 -0
  187. package/src/commands/chat-commands.ts +675 -0
  188. package/src/commands/cli-manager.ts +353 -0
  189. package/src/commands/handler.ts +328 -0
  190. package/src/commands/router.ts +258 -0
  191. package/src/commands/stream-commands.ts +325 -0
  192. package/src/config/chat-history.ts +211 -0
  193. package/src/config/config.ts +219 -0
  194. package/src/daemon/dev-server.ts +2378 -0
  195. package/src/daemon/scaffold-template.ts +394 -0
  196. package/src/daemon-core.ts +50 -0
  197. package/src/detection/cli-detector.ts +89 -0
  198. package/src/detection/ide-detector.ts +157 -0
  199. package/src/index.ts +103 -0
  200. package/src/installer.ts +263 -0
  201. package/src/ipc-protocol.ts +133 -0
  202. package/src/launch.ts +433 -0
  203. package/src/logging/command-log.ts +180 -0
  204. package/src/logging/logger.ts +316 -0
  205. package/src/providers/acp-provider-instance.ts +1140 -0
  206. package/src/providers/cli-provider-instance.ts +207 -0
  207. package/src/providers/contracts.ts +524 -0
  208. package/src/providers/extension-provider-instance.ts +156 -0
  209. package/src/providers/ide-provider-instance.ts +377 -0
  210. package/src/providers/index.ts +18 -0
  211. package/src/providers/provider-instance-manager.ts +182 -0
  212. package/src/providers/provider-instance.ts +112 -0
  213. package/src/providers/provider-loader.ts +1031 -0
  214. package/src/providers/status-monitor.ts +125 -0
  215. package/src/providers/version-archive.ts +266 -0
  216. package/src/status/reporter.ts +294 -0
  217. package/src/types.ts +206 -0
@@ -0,0 +1,211 @@
1
+ /**
2
+ * Chat History Persistence — Persist completed chat messages to local disk
3
+ *
4
+ * Design:
5
+ * - ~/.adhdev/history/{agentType}/YYYY-MM-DD.jsonl
6
+ * - JSONL format (one line = one message, append-friendly)
7
+ * - Track only new messages (hash comparison with previous)
8
+ * - Auto-rotation (delete files older than 30 days)
9
+ * - Async/non-blocking (no impact on chat collection)
10
+ */
11
+
12
+ import * as fs from 'fs';
13
+ import * as path from 'path';
14
+ import * as os from 'os';
15
+
16
+ const HISTORY_DIR = path.join(os.homedir(), '.adhdev', 'history');
17
+ const RETAIN_DAYS = 30;
18
+
19
+ interface HistoryMessage {
20
+ ts: string; // ISO timestamp
21
+ receivedAt: number; // epoch ms
22
+ role: 'user' | 'assistant' | 'system';
23
+ content: string;
24
+ agent: string; // e.g. 'antigravity', 'cursor', 'gemini-cli'
25
+ instanceId?: string; // IDE instance UUID (distinguishes windows of the same agent type)
26
+ sessionTitle?: string;
27
+ }
28
+
29
+ export class ChatHistoryWriter {
30
+ /** Last seen message count per agent (deduplication) */
31
+ private lastSeenCounts = new Map<string, number>();
32
+ /** Last seen message hash per agent (deduplication) */
33
+ private lastSeenHashes = new Map<string, Set<string>>();
34
+ private rotated = false;
35
+
36
+ /**
37
+ * Append new messages to history
38
+ *
39
+ * @param agentType agent type (e.g. 'antigravity', 'cursor')
40
+ * @param messages Message array received from readChat
41
+ * @param sessionTitle Current session title
42
+ * @param instanceId IDE instance UUID (distinguishes windows of the same agent)
43
+ */
44
+ appendNewMessages(
45
+ agentType: string,
46
+ messages: Array<{ role: string; content: string; receivedAt?: number }>,
47
+ sessionTitle?: string,
48
+ instanceId?: string,
49
+ ): void {
50
+ if (!messages || messages.length === 0) return;
51
+
52
+ try {
53
+ // dedup key: agentType + instanceId
54
+ const dedupKey = instanceId ? `${agentType}:${instanceId}` : agentType;
55
+ let seenHashes = this.lastSeenHashes.get(dedupKey);
56
+ if (!seenHashes) {
57
+ seenHashes = new Set<string>();
58
+ this.lastSeenHashes.set(dedupKey, seenHashes);
59
+ }
60
+
61
+ // Filter new messages
62
+ const newMessages: HistoryMessage[] = [];
63
+ for (const msg of messages) {
64
+ const hash = `${msg.role}:${(msg.content || '').slice(0, 50)}`;
65
+ if (seenHashes.has(hash)) continue;
66
+ seenHashes.add(hash);
67
+ newMessages.push({
68
+ ts: new Date(msg.receivedAt || Date.now()).toISOString(),
69
+ receivedAt: msg.receivedAt || Date.now(),
70
+ role: msg.role as 'user' | 'assistant' | 'system',
71
+ content: msg.content || '',
72
+ agent: agentType,
73
+ instanceId,
74
+ sessionTitle,
75
+ });
76
+ }
77
+
78
+ if (newMessages.length === 0) return;
79
+
80
+ // Append to file — separate file if instanceId exists
81
+ const dir = path.join(HISTORY_DIR, this.sanitize(agentType));
82
+ fs.mkdirSync(dir, { recursive: true });
83
+
84
+ const date = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
85
+ const filePrefix = instanceId ? `${this.sanitize(instanceId)}_` : '';
86
+ const filePath = path.join(dir, `${filePrefix}${date}.jsonl`);
87
+ const lines = newMessages.map(m => JSON.stringify(m)).join('\n') + '\n';
88
+ fs.appendFileSync(filePath, lines, 'utf-8');
89
+
90
+ // Detect session switch — reset hash if message count decreases
91
+ const prevCount = this.lastSeenCounts.get(dedupKey) || 0;
92
+ if (messages.length < prevCount * 0.5 && prevCount > 3) {
93
+ seenHashes.clear();
94
+ for (const msg of messages) {
95
+ seenHashes.add(`${msg.role}:${(msg.content || '').slice(0, 50)}`);
96
+ }
97
+ }
98
+ this.lastSeenCounts.set(dedupKey, messages.length);
99
+
100
+ // Rotate only once on first call
101
+ if (!this.rotated) {
102
+ this.rotated = true;
103
+ this.rotateOldFiles().catch(() => {});
104
+ }
105
+ } catch {
106
+ // Ignore history save failures (must not affect main functionality)
107
+ }
108
+ }
109
+
110
+ /** Called when agent session is explicitly changed */
111
+ onSessionChange(agentType: string): void {
112
+ this.lastSeenHashes.delete(agentType);
113
+ this.lastSeenCounts.delete(agentType);
114
+ }
115
+
116
+ /** Delete history files older than 30 days */
117
+ private async rotateOldFiles(): Promise<void> {
118
+ try {
119
+ if (!fs.existsSync(HISTORY_DIR)) return;
120
+ const cutoff = Date.now() - RETAIN_DAYS * 24 * 60 * 60 * 1000;
121
+
122
+ const agentDirs = fs.readdirSync(HISTORY_DIR, { withFileTypes: true })
123
+ .filter(d => d.isDirectory());
124
+
125
+ for (const dir of agentDirs) {
126
+ const dirPath = path.join(HISTORY_DIR, dir.name);
127
+ const files = fs.readdirSync(dirPath)
128
+ .filter(f => f.endsWith('.jsonl'));
129
+
130
+ for (const file of files) {
131
+ const filePath = path.join(dirPath, file);
132
+ const stat = fs.statSync(filePath);
133
+ if (stat.mtimeMs < cutoff) {
134
+ fs.unlinkSync(filePath);
135
+ }
136
+ }
137
+ }
138
+ } catch {
139
+ // Ignore rotate failure
140
+ }
141
+ }
142
+
143
+ /** Allow only filename-safe characters */
144
+ private sanitize(name: string): string {
145
+ return name.replace(/[^a-zA-Z0-9_-]/g, '_');
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Read history (static — called from P2P commands)
151
+ *
152
+ * Read JSONL files in reverse order, returning most recent messages first.
153
+ * When instanceId is specified, reads only that instance file.
154
+ * Offset/limit-based paging.
155
+ */
156
+ export function readChatHistory(
157
+ agentType: string,
158
+ offset: number = 0,
159
+ limit: number = 30,
160
+ instanceId?: string,
161
+ ): { messages: HistoryMessage[]; hasMore: boolean } {
162
+ try {
163
+ const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, '_');
164
+ const dir = path.join(HISTORY_DIR, sanitized);
165
+ if (!fs.existsSync(dir)) return { messages: [], hasMore: false };
166
+
167
+ // JSONL file list — only matching prefix if instanceId is specified
168
+ const sanitizedInstance = instanceId?.replace(/[^a-zA-Z0-9_-]/g, '_');
169
+ const files = fs.readdirSync(dir)
170
+ .filter(f => {
171
+ if (!f.endsWith('.jsonl')) return false;
172
+ if (sanitizedInstance) {
173
+ return f.startsWith(`${sanitizedInstance}_`);
174
+ }
175
+ // Without instanceId, only files without prefix (legacy compatible)
176
+ return !f.includes('_') || f.match(/^\d{4}-\d{2}-\d{2}\.jsonl$/);
177
+ })
178
+ .sort()
179
+ .reverse();
180
+
181
+ // Read lines from all files (reverse order)
182
+ const allMessages: HistoryMessage[] = [];
183
+ const needed = offset + limit + 1; // hasMore check +1
184
+
185
+ for (const file of files) {
186
+ if (allMessages.length >= needed) break;
187
+ const filePath = path.join(dir, file);
188
+ const content = fs.readFileSync(filePath, 'utf-8');
189
+ const lines = content.trim().split('\n').filter(Boolean);
190
+
191
+ // Parse in reverse order
192
+ for (let i = lines.length - 1; i >= 0; i--) {
193
+ if (allMessages.length >= needed) break;
194
+ try {
195
+ allMessages.push(JSON.parse(lines[i]));
196
+ } catch { /* skip invalid lines */ }
197
+ }
198
+ }
199
+
200
+ // offset/limit apply
201
+ const sliced = allMessages.slice(offset, offset + limit);
202
+ const hasMore = allMessages.length > offset + limit;
203
+
204
+ // Sort in chronological order (top→bottom = oldest→newest)
205
+ sliced.reverse();
206
+
207
+ return { messages: sliced, hasMore };
208
+ } catch {
209
+ return { messages: [], hasMore: false };
210
+ }
211
+ }
@@ -0,0 +1,219 @@
1
+ /**
2
+ * ADHDev Launcher — Configuration
3
+ *
4
+ * Manages launcher config, server connection tokens, and user preferences.
5
+ */
6
+
7
+ import { homedir } from 'os';
8
+ import { join } from 'path';
9
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from 'fs';
10
+
11
+ export interface ADHDevConfig {
12
+ // Server connection
13
+ serverUrl: string;
14
+ apiToken: string | null;
15
+ connectionToken: string | null;
16
+
17
+ // Selected IDE (primary)
18
+ selectedIde: string | null;
19
+
20
+ // All configured IDEs (multiple)
21
+ configuredIdes: string[];
22
+
23
+ // Installed extensions
24
+ installedExtensions: string[];
25
+
26
+ // User preferences
27
+ autoConnect: boolean;
28
+ notifications: boolean;
29
+
30
+ // Auth
31
+ userEmail: string | null;
32
+ userName: string | null;
33
+
34
+ // Setup state
35
+ setupCompleted: boolean;
36
+ setupDate: string | null;
37
+
38
+ // Configured CLI agents
39
+ configuredCLIs: string[];
40
+
41
+ // Daemon: which IDEs to connect (empty = all)
42
+ enabledIdes: string[];
43
+ recentCliWorkspaces: string[];
44
+
45
+ // Machine nickname (user-customizable label for this machine)
46
+ machineNickname: string | null;
47
+
48
+ // CLI launch history
49
+ cliHistory: CliHistoryEntry[];
50
+
51
+ // Per-provider user config (public setting values)
52
+ providerSettings: Record<string, Record<string, any>>;
53
+
54
+ // Per-IDE extension config (per-IDE on/off control)
55
+ ideSettings: Record<string, {
56
+ extensions?: Record<string, { enabled: boolean }>;
57
+ }>;
58
+ }
59
+
60
+ export interface CliHistoryEntry {
61
+ cliType: string;
62
+ dir: string;
63
+ cliArgs?: string[];
64
+ timestamp: number;
65
+ label?: string;
66
+ }
67
+
68
+ const DEFAULT_CONFIG: ADHDevConfig = {
69
+ serverUrl: 'https://api.adhf.dev',
70
+ apiToken: null,
71
+ connectionToken: null,
72
+ selectedIde: null,
73
+ configuredIdes: [],
74
+ installedExtensions: [],
75
+ autoConnect: true,
76
+ notifications: true,
77
+ userEmail: null,
78
+ userName: null,
79
+ setupCompleted: false,
80
+ setupDate: null,
81
+ configuredCLIs: [],
82
+ enabledIdes: [],
83
+ recentCliWorkspaces: [],
84
+ machineNickname: null,
85
+ cliHistory: [],
86
+ providerSettings: {},
87
+ ideSettings: {},
88
+ };
89
+
90
+ /**
91
+ * Get the config directory path
92
+ */
93
+ export function getConfigDir(): string {
94
+ const dir = join(homedir(), '.adhdev');
95
+ if (!existsSync(dir)) {
96
+ mkdirSync(dir, { recursive: true });
97
+ }
98
+ return dir;
99
+ }
100
+
101
+ /**
102
+ * Get the config file path
103
+ */
104
+ function getConfigPath(): string {
105
+ return join(getConfigDir(), 'config.json');
106
+ }
107
+
108
+ /**
109
+ * Load configuration from disk
110
+ */
111
+ export function loadConfig(): ADHDevConfig {
112
+ const configPath = getConfigPath();
113
+
114
+ if (!existsSync(configPath)) {
115
+ return { ...DEFAULT_CONFIG };
116
+ }
117
+
118
+ try {
119
+ const raw = readFileSync(configPath, 'utf-8');
120
+ const parsed = JSON.parse(raw);
121
+ return { ...DEFAULT_CONFIG, ...parsed };
122
+ } catch {
123
+ return { ...DEFAULT_CONFIG };
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Save configuration to disk
129
+ */
130
+ export function saveConfig(config: ADHDevConfig): void {
131
+ const configPath = getConfigPath();
132
+ const dir = getConfigDir();
133
+
134
+ if (!existsSync(dir)) {
135
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
136
+ }
137
+
138
+ writeFileSync(configPath, JSON.stringify(config, null, 2), { encoding: 'utf-8', mode: 0o600 });
139
+ try { chmodSync(configPath, 0o600); } catch { /* Windows etc. not supported */ }
140
+ }
141
+
142
+ /**
143
+ * Update specific config fields
144
+ */
145
+ export function updateConfig(updates: Partial<ADHDevConfig>): ADHDevConfig {
146
+ const config = loadConfig();
147
+ const updated = { ...config, ...updates };
148
+ saveConfig(updated);
149
+ return updated;
150
+ }
151
+
152
+ /**
153
+ * Mark setup as completed
154
+ */
155
+ export function markSetupComplete(
156
+ ideId: string | string[],
157
+ extensions: string[]
158
+ ): ADHDevConfig {
159
+ const ideIds = Array.isArray(ideId) ? ideId : [ideId];
160
+ return updateConfig({
161
+ selectedIde: ideIds[0],
162
+ configuredIdes: ideIds,
163
+ installedExtensions: extensions,
164
+ setupCompleted: true,
165
+ setupDate: new Date().toISOString(),
166
+ });
167
+ }
168
+
169
+ /**
170
+ * Check if setup has been completed before
171
+ */
172
+ export function isSetupComplete(): boolean {
173
+ const config = loadConfig();
174
+ return config.setupCompleted;
175
+ }
176
+
177
+ /**
178
+ * Reset configuration
179
+ */
180
+ export function resetConfig(): void {
181
+ saveConfig({ ...DEFAULT_CONFIG });
182
+ }
183
+
184
+ /**
185
+ * Generate a connection token for server authentication
186
+ */
187
+ export function generateConnectionToken(): string {
188
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
189
+ let token = 'db_';
190
+ for (let i = 0; i < 32; i++) {
191
+ token += chars.charAt(Math.floor(Math.random() * chars.length));
192
+ }
193
+ return token;
194
+ }
195
+ /**
196
+ * Add CLI launch to history (max 20, dedup by cliType+dir+args)
197
+ */
198
+ export function addCliHistory(entry: Omit<CliHistoryEntry, 'timestamp'>): void {
199
+ const config = loadConfig();
200
+ const history = config.cliHistory || [];
201
+ const argsKey = (entry.cliArgs || []).join(' ');
202
+
203
+ // Remove duplicate (same cliType + dir + args)
204
+ const filtered = history.filter(h => {
205
+ const hArgsKey = (h.cliArgs || []).join(' ');
206
+ return !(h.cliType === entry.cliType && h.dir === entry.dir && hArgsKey === argsKey);
207
+ });
208
+
209
+ // Add to front
210
+ filtered.unshift({
211
+ ...entry,
212
+ timestamp: Date.now(),
213
+ label: entry.label || `${entry.cliType} · ${entry.dir.split('/').filter(Boolean).pop() || 'root'}${argsKey ? ` (${argsKey})` : ''}`,
214
+ });
215
+
216
+ // Keep max 20
217
+ config.cliHistory = filtered.slice(0, 20);
218
+ saveConfig(config);
219
+ }