@creatoria/miniapp-mcp 0.1.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 (160) hide show
  1. package/README.md +469 -0
  2. package/dist/cli.d.ts +6 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +144 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/config/defaults.d.ts +73 -0
  7. package/dist/config/defaults.d.ts.map +1 -0
  8. package/dist/config/defaults.js +118 -0
  9. package/dist/config/defaults.js.map +1 -0
  10. package/dist/config/loader.d.ts +50 -0
  11. package/dist/config/loader.d.ts.map +1 -0
  12. package/dist/config/loader.js +189 -0
  13. package/dist/config/loader.js.map +1 -0
  14. package/dist/core/element-ref.d.ts +44 -0
  15. package/dist/core/element-ref.d.ts.map +1 -0
  16. package/dist/core/element-ref.js +213 -0
  17. package/dist/core/element-ref.js.map +1 -0
  18. package/dist/core/logger.d.ts +55 -0
  19. package/dist/core/logger.d.ts.map +1 -0
  20. package/dist/core/logger.js +378 -0
  21. package/dist/core/logger.js.map +1 -0
  22. package/dist/core/output.d.ts +21 -0
  23. package/dist/core/output.d.ts.map +1 -0
  24. package/dist/core/output.js +56 -0
  25. package/dist/core/output.js.map +1 -0
  26. package/dist/core/report-generator.d.ts +24 -0
  27. package/dist/core/report-generator.d.ts.map +1 -0
  28. package/dist/core/report-generator.js +212 -0
  29. package/dist/core/report-generator.js.map +1 -0
  30. package/dist/core/session.d.ts +83 -0
  31. package/dist/core/session.d.ts.map +1 -0
  32. package/dist/core/session.js +306 -0
  33. package/dist/core/session.js.map +1 -0
  34. package/dist/core/timeout.d.ts +49 -0
  35. package/dist/core/timeout.d.ts.map +1 -0
  36. package/dist/core/timeout.js +67 -0
  37. package/dist/core/timeout.js.map +1 -0
  38. package/dist/core/tool-logger.d.ts +83 -0
  39. package/dist/core/tool-logger.d.ts.map +1 -0
  40. package/dist/core/tool-logger.js +453 -0
  41. package/dist/core/tool-logger.js.map +1 -0
  42. package/dist/core/validation.d.ts +39 -0
  43. package/dist/core/validation.d.ts.map +1 -0
  44. package/dist/core/validation.js +93 -0
  45. package/dist/core/validation.js.map +1 -0
  46. package/dist/index.d.ts +7 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +6 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/server.d.ts +7 -0
  51. package/dist/server.d.ts.map +1 -0
  52. package/dist/server.js +85 -0
  53. package/dist/server.js.map +1 -0
  54. package/dist/tools/assert.d.ts +108 -0
  55. package/dist/tools/assert.d.ts.map +1 -0
  56. package/dist/tools/assert.js +291 -0
  57. package/dist/tools/assert.js.map +1 -0
  58. package/dist/tools/automator.d.ts +45 -0
  59. package/dist/tools/automator.d.ts.map +1 -0
  60. package/dist/tools/automator.js +186 -0
  61. package/dist/tools/automator.js.map +1 -0
  62. package/dist/tools/element.d.ts +253 -0
  63. package/dist/tools/element.d.ts.map +1 -0
  64. package/dist/tools/element.js +615 -0
  65. package/dist/tools/element.js.map +1 -0
  66. package/dist/tools/index.d.ts +97 -0
  67. package/dist/tools/index.d.ts.map +1 -0
  68. package/dist/tools/index.js +1565 -0
  69. package/dist/tools/index.js.map +1 -0
  70. package/dist/tools/miniprogram.d.ts +79 -0
  71. package/dist/tools/miniprogram.d.ts.map +1 -0
  72. package/dist/tools/miniprogram.js +245 -0
  73. package/dist/tools/miniprogram.js.map +1 -0
  74. package/dist/tools/network.d.ts +65 -0
  75. package/dist/tools/network.d.ts.map +1 -0
  76. package/dist/tools/network.js +205 -0
  77. package/dist/tools/network.js.map +1 -0
  78. package/dist/tools/page.d.ts +108 -0
  79. package/dist/tools/page.d.ts.map +1 -0
  80. package/dist/tools/page.js +307 -0
  81. package/dist/tools/page.js.map +1 -0
  82. package/dist/tools/record.d.ts +86 -0
  83. package/dist/tools/record.d.ts.map +1 -0
  84. package/dist/tools/record.js +316 -0
  85. package/dist/tools/record.js.map +1 -0
  86. package/dist/tools/snapshot.d.ts +82 -0
  87. package/dist/tools/snapshot.d.ts.map +1 -0
  88. package/dist/tools/snapshot.js +258 -0
  89. package/dist/tools/snapshot.js.map +1 -0
  90. package/dist/types.d.ts +240 -0
  91. package/dist/types.d.ts.map +1 -0
  92. package/dist/types.js +5 -0
  93. package/dist/types.js.map +1 -0
  94. package/docs/SIMPLE_USAGE.md +210 -0
  95. package/docs/api/README.md +244 -0
  96. package/docs/api/assert.md +1015 -0
  97. package/docs/api/automator.md +345 -0
  98. package/docs/api/element.md +1454 -0
  99. package/docs/api/miniprogram.md +558 -0
  100. package/docs/api/network.md +883 -0
  101. package/docs/api/page.md +909 -0
  102. package/docs/api/record.md +963 -0
  103. package/docs/api/snapshot.md +792 -0
  104. package/docs/architecture.E-Docs.md +1359 -0
  105. package/docs/architecture.F1.md +720 -0
  106. package/docs/architecture.F2.md +871 -0
  107. package/docs/architecture.F3.md +905 -0
  108. package/docs/architecture.md +90 -0
  109. package/docs/charter.A1.align.yaml +170 -0
  110. package/docs/charter.A2.align.yaml +199 -0
  111. package/docs/charter.A3.align.yaml +242 -0
  112. package/docs/charter.A4.align.yaml +227 -0
  113. package/docs/charter.B1.align.yaml +179 -0
  114. package/docs/charter.B2.align.yaml +200 -0
  115. package/docs/charter.B3.align.yaml +200 -0
  116. package/docs/charter.B4.align.yaml +188 -0
  117. package/docs/charter.C1.align.yaml +190 -0
  118. package/docs/charter.C2.align.yaml +202 -0
  119. package/docs/charter.C3.align.yaml +211 -0
  120. package/docs/charter.C4.align.yaml +263 -0
  121. package/docs/charter.C5.align.yaml +220 -0
  122. package/docs/charter.D1.align.yaml +190 -0
  123. package/docs/charter.D2.align.yaml +234 -0
  124. package/docs/charter.D3.align.yaml +206 -0
  125. package/docs/charter.E-Docs.align.yaml +294 -0
  126. package/docs/charter.F1.align.yaml +193 -0
  127. package/docs/charter.F2.align.yaml +248 -0
  128. package/docs/charter.F3.align.yaml +287 -0
  129. package/docs/charter.G.align.yaml +174 -0
  130. package/docs/charter.align.yaml +111 -0
  131. package/docs/examples/session-report-usage.md +449 -0
  132. package/docs/maintenance.md +682 -0
  133. package/docs/playwright-mcp/350/260/203/347/240/224.md +53 -0
  134. package/docs/setup-guide.md +775 -0
  135. package/docs/tasks.A1.atomize.md +296 -0
  136. package/docs/tasks.A2.atomize.md +408 -0
  137. package/docs/tasks.A3.atomize.md +564 -0
  138. package/docs/tasks.A4.atomize.md +496 -0
  139. package/docs/tasks.B1.atomize.md +352 -0
  140. package/docs/tasks.B2.atomize.md +561 -0
  141. package/docs/tasks.B3.atomize.md +508 -0
  142. package/docs/tasks.B4.atomize.md +504 -0
  143. package/docs/tasks.C1.atomize.md +540 -0
  144. package/docs/tasks.C2.atomize.md +665 -0
  145. package/docs/tasks.C3.atomize.md +745 -0
  146. package/docs/tasks.C4.atomize.md +908 -0
  147. package/docs/tasks.C5.atomize.md +755 -0
  148. package/docs/tasks.D1.atomize.md +547 -0
  149. package/docs/tasks.D2.atomize.md +619 -0
  150. package/docs/tasks.D3.atomize.md +790 -0
  151. package/docs/tasks.E-Docs.atomize.md +1204 -0
  152. package/docs/tasks.atomize.md +189 -0
  153. package/docs/troubleshooting.md +855 -0
  154. package/docs//345/256/214/346/225/264/345/256/236/347/216/260/346/226/271/346/241/210.md +155 -0
  155. package/docs//345/274/200/345/217/221/344/273/273/345/212/241/350/256/241/345/210/222.md +110 -0
  156. package/docs//345/276/256/344/277/241/345/260/217/347/250/213/345/272/217/350/207/252/345/212/250/345/214/226API/345/256/214/346/225/264/346/226/207/346/241/243.md +894 -0
  157. package/docs//345/276/256/344/277/241/345/260/217/347/250/213/345/272/217/350/207/252/345/212/250/345/214/226/345/256/214/346/225/264/346/223/215/344/275/234/346/211/213/345/206/214.md +1885 -0
  158. package/docs//346/216/245/345/217/243/346/226/271/346/241/210.md +565 -0
  159. package/docs//347/254/254/344/270/200/347/211/210/346/234/254/346/226/271/346/241/210.md +380 -0
  160. package/package.json +87 -0
@@ -0,0 +1,213 @@
1
+ /**
2
+ * ElementRef resolver for locating elements in miniProgram pages
3
+ * Supports multiple location strategies: refId, selector, xpath, index
4
+ *
5
+ * Cache Invalidation:
6
+ * - Element cache is automatically cleared when page navigation is detected
7
+ * - Cached elements are validated against current page before use
8
+ * - Stale elements (from different pages) are removed automatically
9
+ */
10
+ /**
11
+ * Generate a unique reference ID for caching elements
12
+ */
13
+ export function generateRefId() {
14
+ return Math.random().toString(36).slice(2) + Date.now().toString(36);
15
+ }
16
+ /**
17
+ * Check if page has changed and clear stale element cache
18
+ * Called before each element resolution to ensure cache validity
19
+ *
20
+ * @param state - Session state with element cache
21
+ * @param currentPagePath - Current page path
22
+ */
23
+ export function invalidateStaleCacheEntries(state, currentPagePath) {
24
+ // If page has changed, clear entire cache (safe approach)
25
+ if (state.currentPagePath && state.currentPagePath !== currentPagePath) {
26
+ const previousPath = state.currentPagePath;
27
+ const clearedCount = state.elements.size;
28
+ state.elements.clear();
29
+ state.logger?.info('Element cache cleared due to page navigation', {
30
+ previousPage: previousPath,
31
+ currentPage: currentPagePath,
32
+ clearedElements: clearedCount,
33
+ });
34
+ }
35
+ // Update current page path
36
+ state.currentPagePath = currentPagePath;
37
+ }
38
+ /**
39
+ * Resolve page object from sessionState
40
+ * @param state - Session state containing miniProgram instance
41
+ * @param pagePath - Optional page path. If not provided, uses currentPage
42
+ * @returns Page object
43
+ * @throws Error if miniProgram not connected or page not found
44
+ */
45
+ export async function resolvePage(state, pagePath) {
46
+ if (!state.miniProgram) {
47
+ throw new Error('MiniProgram not launched or connected. Call miniprogram_launch or miniprogram_connect first.');
48
+ }
49
+ // If no pagePath specified, use current page
50
+ if (!pagePath) {
51
+ const currentPage = await state.miniProgram.currentPage();
52
+ if (!currentPage) {
53
+ throw new Error('No current page found. Ensure the miniProgram has navigated to a page.');
54
+ }
55
+ return currentPage;
56
+ }
57
+ // Find page in pageStack by path
58
+ const pageStack = await state.miniProgram.pageStack();
59
+ if (!Array.isArray(pageStack) || pageStack.length === 0) {
60
+ throw new Error('Page stack is empty. Ensure miniProgram has navigated to pages.');
61
+ }
62
+ // Normalize pagePath (support both '/path' and 'path')
63
+ const normalizedPath = pagePath.startsWith('/') ? pagePath.slice(1) : pagePath;
64
+ const found = pageStack.find((p) => p.path === normalizedPath || p.path === pagePath);
65
+ if (!found) {
66
+ const availablePaths = pageStack.map((p) => p.path).join(', ');
67
+ throw new Error(`Page not found in stack: ${pagePath}. Available pages: ${availablePaths}`);
68
+ }
69
+ return found;
70
+ }
71
+ /**
72
+ * Resolve element using various location strategies
73
+ * @param state - Session state containing element cache
74
+ * @param ref - Element reference input with location strategy
75
+ * @returns Resolved element with page, element object, and optional refId
76
+ * @throws Error if element not found or invalid reference
77
+ */
78
+ export async function resolveElement(state, ref) {
79
+ // Get the page first
80
+ const page = await resolvePage(state, ref.pagePath);
81
+ // Get current page path for cache validation
82
+ const currentPagePath = page.path || (await state.miniProgram?.currentPage())?.path;
83
+ // Invalidate stale cache entries if page has changed
84
+ if (currentPagePath) {
85
+ invalidateStaleCacheEntries(state, currentPagePath);
86
+ }
87
+ let element = null;
88
+ // Strategy 1: Use cached refId
89
+ if (ref.refId) {
90
+ const cached = state.elements.get(ref.refId);
91
+ if (!cached) {
92
+ const availableRefs = Array.from(state.elements.keys()).join(', ');
93
+ throw new Error(`Invalid refId: ${ref.refId}. ` +
94
+ `Available refIds: ${availableRefs || '(none)'}. ` +
95
+ `The element may have been removed or the page has changed.`);
96
+ }
97
+ // Validate that cached element is from current page
98
+ if (currentPagePath && cached.pagePath !== currentPagePath) {
99
+ // Remove stale entry
100
+ state.elements.delete(ref.refId);
101
+ throw new Error(`Cached element refId ${ref.refId} is stale. ` +
102
+ `Element was cached on page '${cached.pagePath}' but current page is '${currentPagePath}'. ` +
103
+ `Please re-query the element on the current page.`);
104
+ }
105
+ element = cached.element;
106
+ }
107
+ // Strategy 2: Use XPath (requires SDK 0.11.0+)
108
+ else if (ref.xpath) {
109
+ const anyPage = page;
110
+ // Check if xpath is supported
111
+ if (typeof anyPage.xpath !== 'function' && typeof anyPage.getElementByXpath !== 'function') {
112
+ throw new Error('XPath is not supported by current miniprogram-automator SDK version. ' +
113
+ 'Please upgrade to SDK 0.11.0 or later, or use selector instead.');
114
+ }
115
+ // If index is specified, use getElementsByXpath
116
+ if (typeof ref.index === 'number') {
117
+ if (typeof anyPage.getElementsByXpath === 'function') {
118
+ const elements = await anyPage.getElementsByXpath(ref.xpath);
119
+ if (!Array.isArray(elements) || elements.length === 0) {
120
+ throw new Error(`No elements found with XPath: ${ref.xpath}`);
121
+ }
122
+ if (ref.index < 0 || ref.index >= elements.length) {
123
+ throw new Error(`Index ${ref.index} out of range. Found ${elements.length} elements with XPath: ${ref.xpath}`);
124
+ }
125
+ element = elements[ref.index];
126
+ }
127
+ else {
128
+ throw new Error('getElementsByXpath is not supported. Cannot use index with xpath in this SDK version.');
129
+ }
130
+ }
131
+ else {
132
+ // Use getElementByXpath or xpath (fallback)
133
+ if (typeof anyPage.getElementByXpath === 'function') {
134
+ element = await anyPage.getElementByXpath(ref.xpath);
135
+ }
136
+ else if (typeof anyPage.xpath === 'function') {
137
+ element = await anyPage.xpath(ref.xpath);
138
+ }
139
+ }
140
+ if (!element) {
141
+ throw new Error(`Element not found with XPath: ${ref.xpath}`);
142
+ }
143
+ }
144
+ // Strategy 3: Use CSS selector
145
+ else if (ref.selector) {
146
+ // If index is specified, use $$
147
+ if (typeof ref.index === 'number') {
148
+ const elements = await page.$$(ref.selector);
149
+ if (!Array.isArray(elements) || elements.length === 0) {
150
+ throw new Error(`No elements found with selector: ${ref.selector}`);
151
+ }
152
+ if (ref.index < 0 || ref.index >= elements.length) {
153
+ throw new Error(`Index ${ref.index} out of range. Found ${elements.length} elements with selector: ${ref.selector}`);
154
+ }
155
+ element = elements[ref.index];
156
+ }
157
+ else {
158
+ // Use $ for single element
159
+ element = await page.$(ref.selector);
160
+ if (!element) {
161
+ throw new Error(`Element not found with selector: ${ref.selector}`);
162
+ }
163
+ }
164
+ }
165
+ // No valid location strategy provided
166
+ else {
167
+ throw new Error('Invalid ElementRef: must provide one of: refId, selector, or xpath');
168
+ }
169
+ // Cache element if save=true
170
+ let newRefId;
171
+ if (ref.save && element) {
172
+ newRefId = generateRefId();
173
+ // Store element with page metadata for cache invalidation
174
+ state.elements.set(newRefId, {
175
+ element,
176
+ pagePath: currentPagePath || page.path || 'unknown',
177
+ cachedAt: new Date(),
178
+ });
179
+ state.logger?.info('Element cached with refId', {
180
+ refId: newRefId,
181
+ pagePath: currentPagePath || page.path,
182
+ });
183
+ }
184
+ return {
185
+ page,
186
+ element,
187
+ refId: newRefId,
188
+ };
189
+ }
190
+ /**
191
+ * Validate ElementRefInput and provide helpful error messages
192
+ */
193
+ export function validateElementRef(ref) {
194
+ if (!ref || typeof ref !== 'object') {
195
+ throw new Error('ElementRef must be an object');
196
+ }
197
+ const hasRefId = typeof ref.refId === 'string' && ref.refId.length > 0;
198
+ const hasSelector = typeof ref.selector === 'string' && ref.selector.length > 0;
199
+ const hasXpath = typeof ref.xpath === 'string' && ref.xpath.length > 0;
200
+ if (!hasRefId && !hasSelector && !hasXpath) {
201
+ throw new Error('ElementRef must provide one of: refId, selector, or xpath');
202
+ }
203
+ if (ref.index !== undefined && typeof ref.index !== 'number') {
204
+ throw new Error('ElementRef.index must be a number');
205
+ }
206
+ if (ref.pagePath !== undefined && typeof ref.pagePath !== 'string') {
207
+ throw new Error('ElementRef.pagePath must be a string');
208
+ }
209
+ if (ref.save !== undefined && typeof ref.save !== 'boolean') {
210
+ throw new Error('ElementRef.save must be a boolean');
211
+ }
212
+ }
213
+ //# sourceMappingURL=element-ref.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element-ref.js","sourceRoot":"","sources":["../../src/core/element-ref.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Logger implementation for structured logging with file support
3
+ */
4
+ import type { Logger, LoggerConfig } from '../types.js';
5
+ /**
6
+ * Console-based logger implementation
7
+ */
8
+ export declare class ConsoleLogger implements Logger {
9
+ private sessionId?;
10
+ private toolName?;
11
+ private config;
12
+ private fileWriter?;
13
+ constructor(sessionId?: string, toolName?: string, config?: LoggerConfig);
14
+ /**
15
+ * Merge user config with defaults and validate values (Issue #7)
16
+ */
17
+ private mergeConfig;
18
+ /**
19
+ * Check if log level should be output
20
+ */
21
+ private shouldLog;
22
+ /**
23
+ * Create log entry with metadata
24
+ */
25
+ private createEntry;
26
+ /**
27
+ * Format log entry for console output
28
+ */
29
+ private format;
30
+ info(message: string, context?: Record<string, any>): void;
31
+ warn(message: string, context?: Record<string, any>): void;
32
+ error(message: string, context?: Record<string, any>): void;
33
+ debug(message: string, context?: Record<string, any>): void;
34
+ /**
35
+ * Log entry to console and optionally to file
36
+ */
37
+ private log;
38
+ /**
39
+ * Flush log buffer to file
40
+ */
41
+ flush(): Promise<void>;
42
+ /**
43
+ * Dispose logger and flush buffer
44
+ */
45
+ dispose(): Promise<void>;
46
+ /**
47
+ * Create a child logger with specific tool name
48
+ */
49
+ child(toolName: string): Logger;
50
+ }
51
+ /**
52
+ * Create a logger for a session
53
+ */
54
+ export declare function createLogger(sessionId: string, config?: LoggerConfig): Logger;
55
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAsB,YAAY,EAAE,MAAM,aAAa,CAAA;AA8P3E;;GAEG;AACH,qBAAa,aAAc,YAAW,MAAM;IAC1C,OAAO,CAAC,SAAS,CAAC,CAAQ;IAC1B,OAAO,CAAC,QAAQ,CAAC,CAAQ;IACzB,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,UAAU,CAAC,CAAY;gBAEnB,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,YAAY;IAgBxE;;OAEG;IACH,OAAO,CAAC,WAAW;IA2CnB;;OAEG;IACH,OAAO,CAAC,SAAS;IAIjB;;OAEG;IACH,OAAO,CAAC,WAAW;IAWnB;;OAEG;IACH,OAAO,CAAC,MAAM;IAUd,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAM1D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAM1D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAM3D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAM3D;;OAEG;IACH,OAAO,CAAC,GAAG;IAQX;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B;;OAEG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;CAGhC;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,CAE7E"}
@@ -0,0 +1,378 @@
1
+ /**
2
+ * Logger implementation for structured logging with file support
3
+ */
4
+ import { open, mkdir, rename } from 'fs/promises';
5
+ import { statfs } from 'fs/promises'; // Issue #6: Disk space check (Node 18+)
6
+ import { join } from 'path';
7
+ /**
8
+ * Default logger configuration
9
+ */
10
+ const DEFAULT_LOGGER_CONFIG = {
11
+ level: 'info',
12
+ enableFileLog: false,
13
+ outputDir: '.mcp-artifacts',
14
+ bufferSize: 100,
15
+ flushInterval: 5000,
16
+ enableFailureSnapshot: false,
17
+ };
18
+ /**
19
+ * Log level priorities for filtering
20
+ */
21
+ const LOG_LEVELS = {
22
+ debug: 0,
23
+ info: 1,
24
+ warn: 2,
25
+ error: 3,
26
+ };
27
+ /**
28
+ * Internal file writer with buffering for async log writing
29
+ */
30
+ class FileWriter {
31
+ sessionId;
32
+ outputDir;
33
+ bufferSize;
34
+ flushInterval;
35
+ buffer = [];
36
+ flushTimer;
37
+ fileHandle;
38
+ disposed = false;
39
+ filePath;
40
+ flushInProgress = false; // Issue #2: Prevent concurrent flushes
41
+ failureCount = 0; // Issue #1: Circuit breaker for persistent failures
42
+ MAX_FAILURES = 3; // Disable after 3 consecutive failures
43
+ constructor(sessionId, outputDir, bufferSize, flushInterval) {
44
+ this.sessionId = sessionId;
45
+ this.outputDir = outputDir;
46
+ this.bufferSize = bufferSize;
47
+ this.flushInterval = flushInterval;
48
+ this.startFlushTimer();
49
+ }
50
+ /**
51
+ * Add log entry to buffer (non-blocking)
52
+ */
53
+ write(entry) {
54
+ if (this.disposed)
55
+ return;
56
+ this.buffer.push(entry);
57
+ // Trigger flush if buffer is full
58
+ if (this.buffer.length >= this.bufferSize) {
59
+ // Fire-and-forget flush (non-blocking)
60
+ void this.flush().catch((err) => {
61
+ console.error('Failed to flush log buffer:', err);
62
+ });
63
+ }
64
+ }
65
+ /**
66
+ * Flush buffer to file
67
+ */
68
+ async flush() {
69
+ // Issue #2: Prevent concurrent flushes
70
+ if (this.buffer.length === 0 || this.disposed || this.flushInProgress)
71
+ return;
72
+ this.flushInProgress = true;
73
+ try {
74
+ // Extract buffer entries
75
+ const entries = this.buffer.splice(0, this.buffer.length);
76
+ const lines = entries
77
+ .map((e) => {
78
+ try {
79
+ // Issue #13: Handle circular references
80
+ return JSON.stringify(e);
81
+ }
82
+ catch (error) {
83
+ return JSON.stringify({
84
+ ...e,
85
+ context: '<Serialization failed: circular reference>',
86
+ _serializationError: error instanceof Error ? error.message : String(error),
87
+ });
88
+ }
89
+ })
90
+ .join('\n') + '\n';
91
+ try {
92
+ // Ensure file handle is open
93
+ if (!this.fileHandle) {
94
+ await this.ensureLogDirectory();
95
+ this.filePath = join(this.outputDir, 'logs', `session-${this.sessionId}.log`);
96
+ this.fileHandle = await open(this.filePath, 'a');
97
+ }
98
+ // Issue #6: Check disk space before writing
99
+ await this.checkDiskSpace();
100
+ if (this.disposed)
101
+ return; // Disk space check may have disabled logging
102
+ // Issue #8: Rotate file if too large
103
+ await this.rotateIfNeeded();
104
+ // Write to file
105
+ await this.fileHandle.write(lines);
106
+ // Issue #1: Reset failure count on success
107
+ this.failureCount = 0;
108
+ }
109
+ catch (error) {
110
+ const err = error instanceof Error ? error.message : String(error);
111
+ // Handle disk full error
112
+ if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOSPC') {
113
+ console.error('Disk full, disabling file logging');
114
+ // Issue #4: Close file handle before disposing to prevent leak
115
+ if (this.fileHandle) {
116
+ await this.fileHandle.close().catch(() => { });
117
+ this.fileHandle = undefined;
118
+ }
119
+ this.disposed = true;
120
+ return;
121
+ }
122
+ console.error('Failed to write logs to file:', err);
123
+ // Issue #1: Circuit breaker - disable after too many failures
124
+ this.failureCount++;
125
+ if (this.failureCount >= this.MAX_FAILURES) {
126
+ console.error(`Too many flush failures (${this.failureCount}), disabling file logging`);
127
+ // Issue #4: Close file handle before disposing
128
+ if (this.fileHandle) {
129
+ await this.fileHandle.close().catch(() => { });
130
+ this.fileHandle = undefined;
131
+ }
132
+ this.disposed = true;
133
+ return;
134
+ }
135
+ // Issue #1: Smart retry with buffer size limit
136
+ // Only keep the most recent entries to prevent unbounded growth
137
+ const entriesToKeep = Math.min(entries.length, this.bufferSize - this.buffer.length);
138
+ if (entriesToKeep > 0) {
139
+ // Keep newest entries, drop oldest
140
+ this.buffer.unshift(...entries.slice(-entriesToKeep));
141
+ if (entriesToKeep < entries.length) {
142
+ console.warn(`Dropped ${entries.length - entriesToKeep} old log entries due to buffer limit`);
143
+ }
144
+ }
145
+ else {
146
+ console.warn(`Buffer full, dropped ${entries.length} failed log entries`);
147
+ }
148
+ }
149
+ }
150
+ finally {
151
+ this.flushInProgress = false;
152
+ }
153
+ }
154
+ /**
155
+ * Dispose writer and flush remaining buffer
156
+ */
157
+ async dispose() {
158
+ this.disposed = true;
159
+ if (this.flushTimer) {
160
+ clearInterval(this.flushTimer);
161
+ }
162
+ await this.flush();
163
+ if (this.fileHandle) {
164
+ await this.fileHandle.close();
165
+ this.fileHandle = undefined;
166
+ }
167
+ }
168
+ /**
169
+ * Start periodic flush timer
170
+ */
171
+ startFlushTimer() {
172
+ this.flushTimer = setInterval(() => {
173
+ void this.flush().catch((err) => {
174
+ console.error('Scheduled flush failed:', err);
175
+ });
176
+ }, this.flushInterval);
177
+ }
178
+ /**
179
+ * Ensure log directory exists
180
+ */
181
+ async ensureLogDirectory() {
182
+ const logDir = join(this.outputDir, 'logs');
183
+ await mkdir(logDir, { recursive: true });
184
+ }
185
+ /**
186
+ * Check disk space and warn/disable if low (Issue #6)
187
+ */
188
+ async checkDiskSpace() {
189
+ if (!this.fileHandle || !this.filePath)
190
+ return;
191
+ try {
192
+ const stats = await statfs(this.outputDir);
193
+ const freeBytes = BigInt(stats.bsize) * BigInt(stats.bavail);
194
+ const freeMB = Number(freeBytes / BigInt(1024 * 1024));
195
+ if (freeMB < 100) {
196
+ console.warn(`[Logger] Low disk space: ${freeMB.toFixed(2)}MB remaining`);
197
+ }
198
+ if (freeMB < 10) {
199
+ console.error('[Logger] Critical disk space (<10MB), disabling file logging');
200
+ // Close file handle and dispose
201
+ await this.fileHandle.close().catch(() => { });
202
+ this.fileHandle = undefined;
203
+ this.disposed = true;
204
+ }
205
+ }
206
+ catch (error) {
207
+ // statfs may not be available on all platforms/Node versions
208
+ // Silently ignore to avoid breaking on older Node versions
209
+ }
210
+ }
211
+ /**
212
+ * Rotate log file if it exceeds 10MB (Issue #8)
213
+ */
214
+ async rotateIfNeeded() {
215
+ if (!this.fileHandle || !this.filePath)
216
+ return;
217
+ try {
218
+ const stats = await this.fileHandle.stat();
219
+ const sizeMB = stats.size / (1024 * 1024);
220
+ if (sizeMB > 10) {
221
+ console.warn(`[Logger] Log file exceeds 10MB (${sizeMB.toFixed(2)}MB), rotating...`);
222
+ // Close current file
223
+ await this.fileHandle.close();
224
+ // Rename to timestamped backup
225
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5); // Remove .xxxZ
226
+ const rotatedPath = `${this.filePath}.${timestamp}`;
227
+ await rename(this.filePath, rotatedPath);
228
+ console.error(`[Logger] Rotated log file to: ${rotatedPath}`);
229
+ // Reopen new file
230
+ this.fileHandle = await open(this.filePath, 'a');
231
+ }
232
+ }
233
+ catch (error) {
234
+ console.error('[Logger] Failed to rotate log file:', error);
235
+ // Continue with existing file handle
236
+ }
237
+ }
238
+ }
239
+ /**
240
+ * Console-based logger implementation
241
+ */
242
+ export class ConsoleLogger {
243
+ sessionId;
244
+ toolName;
245
+ config;
246
+ fileWriter;
247
+ constructor(sessionId, toolName, config) {
248
+ this.sessionId = sessionId;
249
+ this.toolName = toolName;
250
+ this.config = this.mergeConfig(config);
251
+ // Initialize file writer if enabled
252
+ if (this.config.enableFileLog && sessionId) {
253
+ this.fileWriter = new FileWriter(sessionId, this.config.outputDir, this.config.bufferSize, this.config.flushInterval);
254
+ }
255
+ }
256
+ /**
257
+ * Merge user config with defaults and validate values (Issue #7)
258
+ */
259
+ mergeConfig(userConfig) {
260
+ // Start with defaults
261
+ const merged = {
262
+ ...DEFAULT_LOGGER_CONFIG,
263
+ ...userConfig,
264
+ };
265
+ // Issue #7: Validate and clamp buffer size (10 - 10000)
266
+ const rawBufferSize = merged.bufferSize;
267
+ merged.bufferSize = Math.max(10, Math.min(merged.bufferSize, 10000));
268
+ if (rawBufferSize !== merged.bufferSize) {
269
+ console.warn(`Logger bufferSize clamped to ${merged.bufferSize} (was ${rawBufferSize}). Valid range: 10-10000`);
270
+ }
271
+ // Issue #7: Validate and clamp flush interval (100ms - 60s)
272
+ const rawFlushInterval = merged.flushInterval;
273
+ merged.flushInterval = Math.max(100, Math.min(merged.flushInterval, 60000));
274
+ if (rawFlushInterval !== merged.flushInterval) {
275
+ console.warn(`Logger flushInterval clamped to ${merged.flushInterval}ms (was ${rawFlushInterval}ms). Valid range: 100-60000ms`);
276
+ }
277
+ // Issue #7: Validate log level
278
+ const validLevels = ['debug', 'info', 'warn', 'error'];
279
+ if (!validLevels.includes(merged.level)) {
280
+ console.warn(`Invalid log level "${merged.level}", using default "info". Valid levels: ${validLevels.join(', ')}`);
281
+ merged.level = 'info';
282
+ }
283
+ // Issue #7: Validate outputDir is non-empty string
284
+ if (typeof merged.outputDir !== 'string' || merged.outputDir.trim() === '') {
285
+ console.warn(`Invalid outputDir "${merged.outputDir}", using default ".mcp-artifacts"`);
286
+ merged.outputDir = '.mcp-artifacts';
287
+ }
288
+ return merged;
289
+ }
290
+ /**
291
+ * Check if log level should be output
292
+ */
293
+ shouldLog(level) {
294
+ return LOG_LEVELS[level] >= LOG_LEVELS[this.config.level];
295
+ }
296
+ /**
297
+ * Create log entry with metadata
298
+ */
299
+ createEntry(level, message, context) {
300
+ return {
301
+ timestamp: new Date(),
302
+ level,
303
+ message,
304
+ sessionId: this.sessionId,
305
+ toolName: this.toolName,
306
+ context,
307
+ };
308
+ }
309
+ /**
310
+ * Format log entry for console output
311
+ */
312
+ format(entry) {
313
+ const timestamp = entry.timestamp.toISOString();
314
+ const level = entry.level.toUpperCase().padEnd(5);
315
+ const sessionInfo = entry.sessionId ? ` [${entry.sessionId}]` : '';
316
+ const toolInfo = entry.toolName ? ` [${entry.toolName}]` : '';
317
+ const contextInfo = entry.context ? ` ${JSON.stringify(entry.context)}` : '';
318
+ return `${timestamp} ${level}${sessionInfo}${toolInfo}: ${entry.message}${contextInfo}`;
319
+ }
320
+ info(message, context) {
321
+ if (!this.shouldLog('info'))
322
+ return;
323
+ const entry = this.createEntry('info', message, context);
324
+ this.log(entry);
325
+ }
326
+ warn(message, context) {
327
+ if (!this.shouldLog('warn'))
328
+ return;
329
+ const entry = this.createEntry('warn', message, context);
330
+ this.log(entry);
331
+ }
332
+ error(message, context) {
333
+ if (!this.shouldLog('error'))
334
+ return;
335
+ const entry = this.createEntry('error', message, context);
336
+ this.log(entry);
337
+ }
338
+ debug(message, context) {
339
+ if (!this.shouldLog('debug'))
340
+ return;
341
+ const entry = this.createEntry('debug', message, context);
342
+ this.log(entry);
343
+ }
344
+ /**
345
+ * Log entry to console and optionally to file
346
+ */
347
+ log(entry) {
348
+ // Console output (always)
349
+ console.error(this.format(entry));
350
+ // File output (if enabled)
351
+ this.fileWriter?.write(entry);
352
+ }
353
+ /**
354
+ * Flush log buffer to file
355
+ */
356
+ async flush() {
357
+ await this.fileWriter?.flush();
358
+ }
359
+ /**
360
+ * Dispose logger and flush buffer
361
+ */
362
+ async dispose() {
363
+ await this.fileWriter?.dispose();
364
+ }
365
+ /**
366
+ * Create a child logger with specific tool name
367
+ */
368
+ child(toolName) {
369
+ return new ConsoleLogger(this.sessionId, toolName, this.config);
370
+ }
371
+ }
372
+ /**
373
+ * Create a logger for a session
374
+ */
375
+ export function createLogger(sessionId, config) {
376
+ return new ConsoleLogger(sessionId, undefined, config);
377
+ }
378
+ //# sourceMappingURL=logger.js.map