@luckydraw/cumulus 0.5.1

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 (142) hide show
  1. package/README.md +148 -0
  2. package/dist/cli/cumulus.d.ts +3 -0
  3. package/dist/cli/cumulus.d.ts.map +1 -0
  4. package/dist/cli/cumulus.js +233 -0
  5. package/dist/cli/cumulus.js.map +1 -0
  6. package/dist/index.d.ts +33 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +43 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/lib/config.d.ts +86 -0
  11. package/dist/lib/config.d.ts.map +1 -0
  12. package/dist/lib/config.js +241 -0
  13. package/dist/lib/config.js.map +1 -0
  14. package/dist/lib/content-detector.d.ts +46 -0
  15. package/dist/lib/content-detector.d.ts.map +1 -0
  16. package/dist/lib/content-detector.js +359 -0
  17. package/dist/lib/content-detector.js.map +1 -0
  18. package/dist/lib/content-store.d.ts +255 -0
  19. package/dist/lib/content-store.d.ts.map +1 -0
  20. package/dist/lib/content-store.js +955 -0
  21. package/dist/lib/content-store.js.map +1 -0
  22. package/dist/lib/context-budget.d.ts +83 -0
  23. package/dist/lib/context-budget.d.ts.map +1 -0
  24. package/dist/lib/context-budget.js +101 -0
  25. package/dist/lib/context-budget.js.map +1 -0
  26. package/dist/lib/embeddings.d.ts +64 -0
  27. package/dist/lib/embeddings.d.ts.map +1 -0
  28. package/dist/lib/embeddings.js +176 -0
  29. package/dist/lib/embeddings.js.map +1 -0
  30. package/dist/lib/history.d.ts +120 -0
  31. package/dist/lib/history.d.ts.map +1 -0
  32. package/dist/lib/history.js +205 -0
  33. package/dist/lib/history.js.map +1 -0
  34. package/dist/lib/image-utils.d.ts +41 -0
  35. package/dist/lib/image-utils.d.ts.map +1 -0
  36. package/dist/lib/image-utils.js +288 -0
  37. package/dist/lib/image-utils.js.map +1 -0
  38. package/dist/lib/migrate.d.ts +35 -0
  39. package/dist/lib/migrate.d.ts.map +1 -0
  40. package/dist/lib/migrate.js +196 -0
  41. package/dist/lib/migrate.js.map +1 -0
  42. package/dist/lib/retriever.d.ts +56 -0
  43. package/dist/lib/retriever.d.ts.map +1 -0
  44. package/dist/lib/retriever.js +644 -0
  45. package/dist/lib/retriever.js.map +1 -0
  46. package/dist/lib/revert.d.ts +23 -0
  47. package/dist/lib/revert.d.ts.map +1 -0
  48. package/dist/lib/revert.js +75 -0
  49. package/dist/lib/revert.js.map +1 -0
  50. package/dist/lib/session.d.ts +65 -0
  51. package/dist/lib/session.d.ts.map +1 -0
  52. package/dist/lib/session.js +289 -0
  53. package/dist/lib/session.js.map +1 -0
  54. package/dist/lib/snapshots.d.ts +39 -0
  55. package/dist/lib/snapshots.d.ts.map +1 -0
  56. package/dist/lib/snapshots.js +99 -0
  57. package/dist/lib/snapshots.js.map +1 -0
  58. package/dist/lib/stream-processor.d.ts +149 -0
  59. package/dist/lib/stream-processor.d.ts.map +1 -0
  60. package/dist/lib/stream-processor.js +389 -0
  61. package/dist/lib/stream-processor.js.map +1 -0
  62. package/dist/lib/summarizer.d.ts +67 -0
  63. package/dist/lib/summarizer.d.ts.map +1 -0
  64. package/dist/lib/summarizer.js +213 -0
  65. package/dist/lib/summarizer.js.map +1 -0
  66. package/dist/mcp/index.d.ts +3 -0
  67. package/dist/mcp/index.d.ts.map +1 -0
  68. package/dist/mcp/index.js +16 -0
  69. package/dist/mcp/index.js.map +1 -0
  70. package/dist/mcp/proxy.d.ts +19 -0
  71. package/dist/mcp/proxy.d.ts.map +1 -0
  72. package/dist/mcp/proxy.js +120 -0
  73. package/dist/mcp/proxy.js.map +1 -0
  74. package/dist/mcp/server.d.ts +6 -0
  75. package/dist/mcp/server.d.ts.map +1 -0
  76. package/dist/mcp/server.js +29 -0
  77. package/dist/mcp/server.js.map +1 -0
  78. package/dist/mcp/shared-server.d.ts +21 -0
  79. package/dist/mcp/shared-server.d.ts.map +1 -0
  80. package/dist/mcp/shared-server.js +210 -0
  81. package/dist/mcp/shared-server.js.map +1 -0
  82. package/dist/mcp/tool-handler.d.ts +20 -0
  83. package/dist/mcp/tool-handler.d.ts.map +1 -0
  84. package/dist/mcp/tool-handler.js +1405 -0
  85. package/dist/mcp/tool-handler.js.map +1 -0
  86. package/dist/tui/components/App.d.ts +11 -0
  87. package/dist/tui/components/App.d.ts.map +1 -0
  88. package/dist/tui/components/App.js +607 -0
  89. package/dist/tui/components/App.js.map +1 -0
  90. package/dist/tui/components/DebugContextView.d.ts +13 -0
  91. package/dist/tui/components/DebugContextView.d.ts.map +1 -0
  92. package/dist/tui/components/DebugContextView.js +78 -0
  93. package/dist/tui/components/DebugContextView.js.map +1 -0
  94. package/dist/tui/components/IncludeMenu.d.ts +12 -0
  95. package/dist/tui/components/IncludeMenu.d.ts.map +1 -0
  96. package/dist/tui/components/IncludeMenu.js +127 -0
  97. package/dist/tui/components/IncludeMenu.js.map +1 -0
  98. package/dist/tui/components/InputArea.d.ts +27 -0
  99. package/dist/tui/components/InputArea.d.ts.map +1 -0
  100. package/dist/tui/components/InputArea.js +366 -0
  101. package/dist/tui/components/InputArea.js.map +1 -0
  102. package/dist/tui/components/MarkdownText.d.ts +38 -0
  103. package/dist/tui/components/MarkdownText.d.ts.map +1 -0
  104. package/dist/tui/components/MarkdownText.js +234 -0
  105. package/dist/tui/components/MarkdownText.js.map +1 -0
  106. package/dist/tui/components/MessageBubble.d.ts +11 -0
  107. package/dist/tui/components/MessageBubble.d.ts.map +1 -0
  108. package/dist/tui/components/MessageBubble.js +16 -0
  109. package/dist/tui/components/MessageBubble.js.map +1 -0
  110. package/dist/tui/components/MessageHistory.d.ts +11 -0
  111. package/dist/tui/components/MessageHistory.d.ts.map +1 -0
  112. package/dist/tui/components/MessageHistory.js +12 -0
  113. package/dist/tui/components/MessageHistory.js.map +1 -0
  114. package/dist/tui/components/RevertMenu.d.ts +17 -0
  115. package/dist/tui/components/RevertMenu.d.ts.map +1 -0
  116. package/dist/tui/components/RevertMenu.js +144 -0
  117. package/dist/tui/components/RevertMenu.js.map +1 -0
  118. package/dist/tui/components/StatusBar.d.ts +14 -0
  119. package/dist/tui/components/StatusBar.d.ts.map +1 -0
  120. package/dist/tui/components/StatusBar.js +13 -0
  121. package/dist/tui/components/StatusBar.js.map +1 -0
  122. package/dist/tui/components/StreamingResponse.d.ts +15 -0
  123. package/dist/tui/components/StreamingResponse.d.ts.map +1 -0
  124. package/dist/tui/components/StreamingResponse.js +52 -0
  125. package/dist/tui/components/StreamingResponse.js.map +1 -0
  126. package/dist/tui/hooks/useAppState.d.ts +147 -0
  127. package/dist/tui/hooks/useAppState.d.ts.map +1 -0
  128. package/dist/tui/hooks/useAppState.js +110 -0
  129. package/dist/tui/hooks/useAppState.js.map +1 -0
  130. package/dist/tui/hooks/useClaudeProcess.d.ts +19 -0
  131. package/dist/tui/hooks/useClaudeProcess.d.ts.map +1 -0
  132. package/dist/tui/hooks/useClaudeProcess.js +185 -0
  133. package/dist/tui/hooks/useClaudeProcess.js.map +1 -0
  134. package/dist/tui/index.d.ts +10 -0
  135. package/dist/tui/index.d.ts.map +1 -0
  136. package/dist/tui/index.js +11 -0
  137. package/dist/tui/index.js.map +1 -0
  138. package/dist/tui/utils/streamParser.d.ts +31 -0
  139. package/dist/tui/utils/streamParser.d.ts.map +1 -0
  140. package/dist/tui/utils/streamParser.js +63 -0
  141. package/dist/tui/utils/streamParser.js.map +1 -0
  142. package/package.json +94 -0
@@ -0,0 +1,205 @@
1
+ import * as fs from 'fs/promises';
2
+ import * as path from 'path';
3
+ import { nanoid } from 'nanoid';
4
+ /**
5
+ * HistoryStore manages conversation history persistence in JSONL format.
6
+ *
7
+ * Each conversation thread is stored as a separate JSONL file where each line
8
+ * is a JSON-serialized Message object. This format is append-only, crash-safe,
9
+ * and efficient for streaming reads.
10
+ */
11
+ export class HistoryStore {
12
+ threadPath;
13
+ /**
14
+ * Create a new HistoryStore instance.
15
+ * @param threadPath - Absolute path to the JSONL file for this thread
16
+ */
17
+ constructor(threadPath) {
18
+ this.threadPath = threadPath;
19
+ }
20
+ /**
21
+ * Estimate token count from content using whitespace-based approximation.
22
+ * Multiplies word count by 1.3 to account for subword tokenization.
23
+ */
24
+ estimateTokens(content) {
25
+ const words = content.split(/\s+/).filter(w => w.length > 0);
26
+ return Math.ceil(words.length * 1.3);
27
+ }
28
+ /**
29
+ * Append a new message to the history.
30
+ * Automatically generates id (nanoid) and timestamp.
31
+ * Creates parent directories if they don't exist.
32
+ * @param msg - Message without id and timestamp
33
+ * @returns The complete message with generated fields
34
+ */
35
+ async append(msg) {
36
+ // Ensure parent directory exists
37
+ const dir = path.dirname(this.threadPath);
38
+ await fs.mkdir(dir, { recursive: true });
39
+ // Build complete message
40
+ const message = {
41
+ id: nanoid(),
42
+ timestamp: Date.now(),
43
+ role: msg.role,
44
+ content: msg.content,
45
+ tokenCount: msg.tokenCount ?? this.estimateTokens(msg.content),
46
+ ...(msg.metadata && { metadata: msg.metadata }),
47
+ };
48
+ // Persist attachments: copy from ephemeral source to thread's attachments/ dir
49
+ if (msg.attachments?.length) {
50
+ const attachDir = path.join(dir, 'attachments');
51
+ await fs.mkdir(attachDir, { recursive: true });
52
+ const persisted = [];
53
+ for (const att of msg.attachments) {
54
+ const destName = `${message.id}-${att.name}`;
55
+ const destPath = path.join(attachDir, destName);
56
+ try {
57
+ await fs.copyFile(att.storedPath, destPath);
58
+ persisted.push({
59
+ name: att.name,
60
+ type: att.type,
61
+ mimeType: att.mimeType,
62
+ storedPath: path.join('attachments', destName),
63
+ });
64
+ }
65
+ catch {
66
+ // Source file missing (e.g., $TMPDIR already cleaned) — skip this attachment
67
+ }
68
+ }
69
+ if (persisted.length > 0) {
70
+ message.attachments = persisted;
71
+ }
72
+ }
73
+ // Append to file with newline
74
+ const line = JSON.stringify(message) + '\n';
75
+ await fs.appendFile(this.threadPath, line, 'utf-8');
76
+ return message;
77
+ }
78
+ /**
79
+ * Resolve relative attachment storedPaths to absolute paths.
80
+ * Called before returning messages to clients so they can use paths directly.
81
+ */
82
+ resolveAttachmentPaths(messages) {
83
+ const dir = path.dirname(this.threadPath);
84
+ return messages.map(msg => {
85
+ if (!msg.attachments?.length)
86
+ return msg;
87
+ return {
88
+ ...msg,
89
+ attachments: msg.attachments.map(att => ({
90
+ ...att,
91
+ storedPath: path.join(dir, att.storedPath),
92
+ })),
93
+ };
94
+ });
95
+ }
96
+ /**
97
+ * Load all messages from the history file.
98
+ * Returns empty array if file doesn't exist.
99
+ * @returns Array of all messages in chronological order
100
+ */
101
+ async getAll() {
102
+ try {
103
+ const content = await fs.readFile(this.threadPath, 'utf-8');
104
+ if (!content.trim()) {
105
+ return [];
106
+ }
107
+ const messages = content
108
+ .trim()
109
+ .split('\n')
110
+ .filter(line => line.trim())
111
+ .map(line => JSON.parse(line));
112
+ return this.resolveAttachmentPaths(messages);
113
+ }
114
+ catch (error) {
115
+ // File doesn't exist - return empty array
116
+ if (error.code === 'ENOENT') {
117
+ return [];
118
+ }
119
+ throw error;
120
+ }
121
+ }
122
+ /**
123
+ * Get messages by index range (0-based, inclusive start, exclusive end).
124
+ * Follows JavaScript slice() semantics.
125
+ * @param startIdx - Starting index (inclusive)
126
+ * @param endIdx - Ending index (exclusive)
127
+ * @returns Array of messages in the range
128
+ */
129
+ async getRange(startIdx, endIdx) {
130
+ const all = await this.getAll();
131
+ return all.slice(startIdx, endIdx);
132
+ }
133
+ /**
134
+ * Get the N most recent messages.
135
+ * @param n - Number of messages to retrieve
136
+ * @returns Array of recent messages, oldest first
137
+ */
138
+ async getRecent(n) {
139
+ if (n <= 0) {
140
+ return [];
141
+ }
142
+ const all = await this.getAll();
143
+ // Return last n messages, maintaining chronological order
144
+ return all.slice(-n);
145
+ }
146
+ /**
147
+ * Get statistics about the conversation history.
148
+ * @returns Stats object with counts and timestamps
149
+ */
150
+ async getStats() {
151
+ const messages = await this.getAll();
152
+ if (messages.length === 0) {
153
+ return {
154
+ count: 0,
155
+ totalTokens: 0,
156
+ };
157
+ }
158
+ const totalTokens = messages.reduce((sum, msg) => sum + (msg.tokenCount ?? 0), 0);
159
+ return {
160
+ count: messages.length,
161
+ totalTokens,
162
+ oldestTimestamp: messages[0]?.timestamp,
163
+ newestTimestamp: messages[messages.length - 1]?.timestamp,
164
+ };
165
+ }
166
+ /**
167
+ * Search messages by content (case-insensitive substring match).
168
+ * @param query - Search string
169
+ * @param limit - Maximum results to return (default: 10)
170
+ * @returns Matching messages, most recent first
171
+ */
172
+ async search(query, limit = 10) {
173
+ const messages = await this.getAll();
174
+ const lowerQuery = query.toLowerCase();
175
+ // Filter matches and reverse for most-recent-first
176
+ const matches = messages
177
+ .filter(msg => msg.content.toLowerCase().includes(lowerQuery))
178
+ .reverse()
179
+ .slice(0, limit);
180
+ return matches;
181
+ }
182
+ /**
183
+ * Truncate history to keep only messages up to and including the target message.
184
+ * Uses atomic write (write to .tmp then rename) to prevent data loss.
185
+ * @param messageId - ID of the last message to keep
186
+ * @returns Array of removed message IDs
187
+ * @throws If messageId is not found
188
+ */
189
+ async truncateTo(messageId) {
190
+ const messages = await this.getAll();
191
+ const targetIndex = messages.findIndex(m => m.id === messageId);
192
+ if (targetIndex === -1) {
193
+ throw new Error(`Message not found: ${messageId}`);
194
+ }
195
+ const kept = messages.slice(0, targetIndex + 1);
196
+ const removed = messages.slice(targetIndex + 1);
197
+ // Atomic write: write to temp file, then rename over original
198
+ const tmpPath = this.threadPath + '.tmp';
199
+ const content = kept.map(m => JSON.stringify(m)).join('\n') + '\n';
200
+ await fs.writeFile(tmpPath, content, 'utf-8');
201
+ await fs.rename(tmpPath, this.threadPath);
202
+ return removed.map(m => m.id);
203
+ }
204
+ }
205
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/lib/history.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Image detection, validation, caching, and clipboard reading utilities.
3
+ */
4
+ export interface DetectedImage {
5
+ /** As written in message, or "clipboard" */
6
+ originalPath: string;
7
+ /** Absolute path to source file */
8
+ resolvedPath: string;
9
+ /** Path in content store images directory */
10
+ cachedPath: string;
11
+ /** MIME type (image/png, image/jpeg, etc.) */
12
+ mediaType: string;
13
+ /** Base64-encoded file contents */
14
+ base64Data: string;
15
+ /** File size in bytes */
16
+ sizeBytes: number;
17
+ }
18
+ export interface ImageProcessingResult {
19
+ /** Successfully processed images */
20
+ images: DetectedImage[];
21
+ /** User text with image paths replaced by [IMAGE: filename] */
22
+ cleanedText: string;
23
+ /** Errors encountered during processing */
24
+ errors: Array<{
25
+ path: string;
26
+ error: string;
27
+ }>;
28
+ }
29
+ /**
30
+ * Process all image references in a message text.
31
+ * Detects file paths, validates, caches, and encodes images.
32
+ * Returns processed images and cleaned text with paths replaced by [IMAGE: filename].
33
+ */
34
+ export declare function processImagesInMessage(text: string, contentBasePath: string): Promise<ImageProcessingResult>;
35
+ /**
36
+ * Read image data from the system clipboard.
37
+ * Platform-specific: macOS (osascript), Linux X11 (xclip), Linux Wayland (wl-paste).
38
+ * Returns null if clipboard has no image data.
39
+ */
40
+ export declare function readClipboardImage(contentBasePath: string): Promise<DetectedImage | null>;
41
+ //# sourceMappingURL=image-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-utils.d.ts","sourceRoot":"","sources":["../../src/lib/image-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAwCH,MAAM,WAAW,aAAa;IAC5B,4CAA4C;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,oCAAoC;IACpC,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AA2JD;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,EACZ,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,qBAAqB,CAAC,CA0BhC;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAyB/F"}
@@ -0,0 +1,288 @@
1
+ /**
2
+ * Image detection, validation, caching, and clipboard reading utilities.
3
+ */
4
+ import { execFile } from 'child_process';
5
+ import * as crypto from 'crypto';
6
+ import * as fs from 'fs/promises';
7
+ import * as os from 'os';
8
+ import * as path from 'path';
9
+ import { promisify } from 'util';
10
+ import { nanoid } from 'nanoid';
11
+ const execFileAsync = promisify(execFile);
12
+ /** Supported image formats with their MIME types and magic bytes */
13
+ const IMAGE_FORMATS = {
14
+ '.png': {
15
+ mediaType: 'image/png',
16
+ magic: [Buffer.from([0x89, 0x50, 0x4e, 0x47])],
17
+ },
18
+ '.jpg': {
19
+ mediaType: 'image/jpeg',
20
+ magic: [Buffer.from([0xff, 0xd8, 0xff])],
21
+ },
22
+ '.jpeg': {
23
+ mediaType: 'image/jpeg',
24
+ magic: [Buffer.from([0xff, 0xd8, 0xff])],
25
+ },
26
+ '.gif': {
27
+ mediaType: 'image/gif',
28
+ magic: [Buffer.from('GIF87a'), Buffer.from('GIF89a')],
29
+ },
30
+ '.webp': {
31
+ mediaType: 'image/webp',
32
+ magic: [Buffer.from('RIFF')],
33
+ },
34
+ };
35
+ /** Maximum image file size: 5MB */
36
+ const MAX_IMAGE_SIZE = 5 * 1024 * 1024;
37
+ /**
38
+ * Regex patterns for detecting image file paths in text.
39
+ * Matches:
40
+ * - Quoted paths: "~/my photos/cat.png"
41
+ * - Unquoted paths starting with /, ~/, ./, ../
42
+ */
43
+ const IMAGE_PATH_PATTERNS = [
44
+ // Quoted paths with spaces: "path/to/image.ext"
45
+ /"([^"]+\.(?:png|jpg|jpeg|gif|webp))"/gi,
46
+ // Unquoted absolute or relative paths (no spaces allowed)
47
+ /(?:^|\s)((?:\/|~\/|\.\/|\.\.\/)[^\s]+\.(?:png|jpg|jpeg|gif|webp))\b/gi,
48
+ ];
49
+ /**
50
+ * Detect image file paths in a text message.
51
+ * Returns deduplicated resolved absolute paths.
52
+ */
53
+ function detectImagePaths(text) {
54
+ const found = new Map(); // resolved -> original
55
+ for (const pattern of IMAGE_PATH_PATTERNS) {
56
+ // Reset lastIndex for global regex
57
+ pattern.lastIndex = 0;
58
+ let match;
59
+ while ((match = pattern.exec(text)) !== null) {
60
+ const rawPath = match[1];
61
+ const expanded = rawPath.startsWith('~/')
62
+ ? path.join(os.homedir(), rawPath.slice(2))
63
+ : rawPath;
64
+ const resolved = path.resolve(expanded);
65
+ if (!found.has(resolved)) {
66
+ found.set(resolved, rawPath);
67
+ }
68
+ }
69
+ }
70
+ return Array.from(found.entries()).map(([resolved, original]) => ({
71
+ original,
72
+ resolved,
73
+ }));
74
+ }
75
+ /**
76
+ * Validate an image file: exists, readable, supported format, under size limit, magic bytes match.
77
+ */
78
+ async function validateImage(filePath) {
79
+ try {
80
+ const stat = await fs.stat(filePath);
81
+ if (!stat.isFile()) {
82
+ return { valid: false, error: 'Not a file' };
83
+ }
84
+ if (stat.size > MAX_IMAGE_SIZE) {
85
+ const sizeMB = (stat.size / (1024 * 1024)).toFixed(1);
86
+ return { valid: false, error: `Image too large: ${sizeMB}MB (max 5MB)` };
87
+ }
88
+ if (stat.size === 0) {
89
+ return { valid: false, error: 'Empty file' };
90
+ }
91
+ const ext = path.extname(filePath).toLowerCase();
92
+ const format = IMAGE_FORMATS[ext];
93
+ if (!format) {
94
+ return { valid: false, error: `Unsupported format: ${ext}` };
95
+ }
96
+ // Check magic bytes
97
+ const header = Buffer.alloc(8);
98
+ const fh = await fs.open(filePath, 'r');
99
+ try {
100
+ await fh.read(header, 0, 8, 0);
101
+ }
102
+ finally {
103
+ await fh.close();
104
+ }
105
+ const magicMatch = format.magic.some(magic => header.subarray(0, magic.length).equals(magic));
106
+ if (!magicMatch) {
107
+ return { valid: false, error: `File content doesn't match ${ext} format` };
108
+ }
109
+ return { valid: true, mediaType: format.mediaType, sizeBytes: stat.size };
110
+ }
111
+ catch (err) {
112
+ if (err.code === 'ENOENT') {
113
+ return { valid: false, error: 'File not found' };
114
+ }
115
+ if (err.code === 'EACCES') {
116
+ return { valid: false, error: 'Permission denied' };
117
+ }
118
+ return { valid: false, error: `Cannot read file: ${err.message}` };
119
+ }
120
+ }
121
+ /**
122
+ * Cache an image file to the content store images directory.
123
+ * Deduplicates by SHA-256 hash.
124
+ */
125
+ async function cacheImage(sourcePath, imagesDir) {
126
+ await fs.mkdir(imagesDir, { recursive: true });
127
+ const fileData = await fs.readFile(sourcePath);
128
+ const hash = crypto.createHash('sha256').update(fileData).digest('hex').slice(0, 16);
129
+ const ext = path.extname(sourcePath).toLowerCase();
130
+ // Use hash prefix in filename for O(1) dedup via file existence check
131
+ const cachedName = `img_${hash}${ext}`;
132
+ const cachedPath = path.join(imagesDir, cachedName);
133
+ try {
134
+ await fs.access(cachedPath);
135
+ // File already exists with same content hash
136
+ return cachedPath;
137
+ }
138
+ catch {
139
+ // Not cached yet, copy it
140
+ await fs.copyFile(sourcePath, cachedPath);
141
+ return cachedPath;
142
+ }
143
+ }
144
+ /**
145
+ * Process a single detected image path: validate, cache, encode.
146
+ */
147
+ async function processImagePath(original, resolved, imagesDir) {
148
+ const validation = await validateImage(resolved);
149
+ if (!validation.valid) {
150
+ return { error: validation.error };
151
+ }
152
+ const cachedPath = await cacheImage(resolved, imagesDir);
153
+ const fileData = await fs.readFile(resolved);
154
+ const base64Data = fileData.toString('base64');
155
+ return {
156
+ originalPath: original,
157
+ resolvedPath: resolved,
158
+ cachedPath,
159
+ mediaType: validation.mediaType,
160
+ base64Data,
161
+ sizeBytes: validation.sizeBytes,
162
+ };
163
+ }
164
+ /**
165
+ * Process all image references in a message text.
166
+ * Detects file paths, validates, caches, and encodes images.
167
+ * Returns processed images and cleaned text with paths replaced by [IMAGE: filename].
168
+ */
169
+ export async function processImagesInMessage(text, contentBasePath) {
170
+ const imagesDir = path.join(contentBasePath, 'images');
171
+ const detected = detectImagePaths(text);
172
+ if (detected.length === 0) {
173
+ return { images: [], cleanedText: text, errors: [] };
174
+ }
175
+ const images = [];
176
+ const errors = [];
177
+ let cleanedText = text;
178
+ for (const { original, resolved } of detected) {
179
+ const result = await processImagePath(original, resolved, imagesDir);
180
+ if ('error' in result) {
181
+ errors.push({ path: original, error: result.error });
182
+ }
183
+ else {
184
+ images.push(result);
185
+ const filename = path.basename(resolved);
186
+ // Replace the path in the text with an image indicator
187
+ cleanedText = cleanedText.replace(original, `[IMAGE: ${filename}]`);
188
+ }
189
+ }
190
+ return { images, cleanedText, errors };
191
+ }
192
+ /**
193
+ * Read image data from the system clipboard.
194
+ * Platform-specific: macOS (osascript), Linux X11 (xclip), Linux Wayland (wl-paste).
195
+ * Returns null if clipboard has no image data.
196
+ */
197
+ export async function readClipboardImage(contentBasePath) {
198
+ const platform = os.platform();
199
+ const imagesDir = path.join(contentBasePath, 'images');
200
+ await fs.mkdir(imagesDir, { recursive: true });
201
+ const tempPath = path.join(os.tmpdir(), `cumulus-clip-${nanoid(8)}.png`);
202
+ try {
203
+ if (platform === 'darwin') {
204
+ return await readMacClipboard(tempPath, imagesDir);
205
+ }
206
+ else if (platform === 'linux') {
207
+ return await readLinuxClipboard(tempPath, imagesDir);
208
+ }
209
+ return null;
210
+ }
211
+ catch {
212
+ // Clipboard read failed — no image data or command not available
213
+ return null;
214
+ }
215
+ finally {
216
+ // Clean up temp file
217
+ try {
218
+ await fs.unlink(tempPath);
219
+ }
220
+ catch {
221
+ // Ignore
222
+ }
223
+ }
224
+ }
225
+ async function readMacClipboard(tempPath, imagesDir) {
226
+ // Check if clipboard has image data (PNGf class)
227
+ try {
228
+ const { stdout } = await execFileAsync('osascript', ['-e', 'clipboard info']);
229
+ if (!stdout.includes('PNGf') && !stdout.includes('«class PNGf»')) {
230
+ return null;
231
+ }
232
+ }
233
+ catch {
234
+ return null;
235
+ }
236
+ // Write clipboard image to temp file
237
+ const script = `
238
+ set theFile to open for access POSIX file "${tempPath}" with write permission
239
+ set eof theFile to 0
240
+ write (the clipboard as «class PNGf») to theFile
241
+ close access theFile
242
+ `;
243
+ try {
244
+ await execFileAsync('osascript', ['-e', script]);
245
+ }
246
+ catch {
247
+ return null;
248
+ }
249
+ return await finishClipboardImage(tempPath, imagesDir);
250
+ }
251
+ async function readLinuxClipboard(tempPath, imagesDir) {
252
+ const isWayland = !!process.env.WAYLAND_DISPLAY;
253
+ try {
254
+ if (isWayland) {
255
+ const { stdout } = await execFileAsync('wl-paste', ['--type', 'image/png'], {
256
+ encoding: 'buffer',
257
+ maxBuffer: MAX_IMAGE_SIZE,
258
+ });
259
+ await fs.writeFile(tempPath, stdout);
260
+ }
261
+ else {
262
+ const { stdout } = await execFileAsync('xclip', ['-selection', 'clipboard', '-t', 'image/png', '-o'], { encoding: 'buffer', maxBuffer: MAX_IMAGE_SIZE });
263
+ await fs.writeFile(tempPath, stdout);
264
+ }
265
+ }
266
+ catch {
267
+ return null;
268
+ }
269
+ return await finishClipboardImage(tempPath, imagesDir);
270
+ }
271
+ async function finishClipboardImage(tempPath, imagesDir) {
272
+ const validation = await validateImage(tempPath);
273
+ if (!validation.valid) {
274
+ return null;
275
+ }
276
+ const cachedPath = await cacheImage(tempPath, imagesDir);
277
+ const fileData = await fs.readFile(tempPath);
278
+ const base64Data = fileData.toString('base64');
279
+ return {
280
+ originalPath: 'clipboard',
281
+ resolvedPath: tempPath,
282
+ cachedPath,
283
+ mediaType: validation.mediaType,
284
+ base64Data,
285
+ sizeBytes: validation.sizeBytes,
286
+ };
287
+ }
288
+ //# sourceMappingURL=image-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-utils.js","sourceRoot":"","sources":["../../src/lib/image-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Migration utilities for upgrading old threads to support new features.
3
+ */
4
+ export interface MigrationResult {
5
+ threadName: string;
6
+ messagesTotal: number;
7
+ embeddingsGenerated: number;
8
+ sessionCreated: boolean;
9
+ sessionExchanges: number;
10
+ errors: string[];
11
+ }
12
+ export interface MigrationProgress {
13
+ phase: 'loading' | 'embeddings' | 'sessions' | 'done';
14
+ current: number;
15
+ total: number;
16
+ message: string;
17
+ }
18
+ export type ProgressCallback = (progress: MigrationProgress) => void;
19
+ /**
20
+ * Migrate a thread to support RAG features.
21
+ * - Generates embeddings for all messages
22
+ * - Creates a legacy session document from old conversations
23
+ */
24
+ export declare function migrateThread(threadPath: string, onProgress?: ProgressCallback): Promise<MigrationResult>;
25
+ /**
26
+ * Check if a thread needs migration.
27
+ * Returns true if embeddings or sessions are missing.
28
+ */
29
+ export declare function needsMigration(threadPath: string): Promise<{
30
+ needsEmbeddings: boolean;
31
+ needsSessions: boolean;
32
+ messageCount: number;
33
+ embeddingCount: number;
34
+ }>;
35
+ //# sourceMappingURL=migrate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/lib/migrate.ts"],"names":[],"mappings":"AAAA;;GAEG;AASH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,SAAS,GAAG,YAAY,GAAG,UAAU,GAAG,MAAM,CAAC;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,iBAAiB,KAAK,IAAI,CAAC;AAErE;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,gBAAgB,GAC5B,OAAO,CAAC,eAAe,CAAC,CAqK1B;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;IAChE,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC,CAgCD"}