claude-chrome-parallel 2.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 (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +353 -0
  3. package/dist/cdp/client.d.ts +120 -0
  4. package/dist/cdp/client.d.ts.map +1 -0
  5. package/dist/cdp/client.js +345 -0
  6. package/dist/cdp/client.js.map +1 -0
  7. package/dist/chrome/launcher.d.ts +42 -0
  8. package/dist/chrome/launcher.d.ts.map +1 -0
  9. package/dist/chrome/launcher.js +256 -0
  10. package/dist/chrome/launcher.js.map +1 -0
  11. package/dist/cli/claude-session.d.ts +11 -0
  12. package/dist/cli/claude-session.js +349 -0
  13. package/dist/cli/claude-session.js.map +1 -0
  14. package/dist/cli/index.d.ts +14 -0
  15. package/dist/cli/index.js +562 -0
  16. package/dist/cli/index.js.map +1 -0
  17. package/dist/cli/install.d.ts +16 -0
  18. package/dist/cli/install.js +185 -0
  19. package/dist/cli/install.js.map +1 -0
  20. package/dist/cli/uninstall.d.ts +7 -0
  21. package/dist/cli/uninstall.js +126 -0
  22. package/dist/cli/uninstall.js.map +1 -0
  23. package/dist/config/config-recovery.d.ts +69 -0
  24. package/dist/config/config-recovery.d.ts.map +1 -0
  25. package/dist/config/config-recovery.js +302 -0
  26. package/dist/config/config-recovery.js.map +1 -0
  27. package/dist/config/index.d.ts +6 -0
  28. package/dist/config/index.d.ts.map +1 -0
  29. package/dist/config/index.js +22 -0
  30. package/dist/config/index.js.map +1 -0
  31. package/dist/config/session-isolator.d.ts +76 -0
  32. package/dist/config/session-isolator.d.ts.map +1 -0
  33. package/dist/config/session-isolator.js +268 -0
  34. package/dist/config/session-isolator.js.map +1 -0
  35. package/dist/index.d.ts +10 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +118 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/master/index.d.ts +18 -0
  40. package/dist/master/index.d.ts.map +1 -0
  41. package/dist/master/index.js +75 -0
  42. package/dist/master/index.js.map +1 -0
  43. package/dist/master/ipc-server.d.ts +21 -0
  44. package/dist/master/ipc-server.d.ts.map +1 -0
  45. package/dist/master/ipc-server.js +175 -0
  46. package/dist/master/ipc-server.js.map +1 -0
  47. package/dist/master/request-handler.d.ts +17 -0
  48. package/dist/master/request-handler.d.ts.map +1 -0
  49. package/dist/master/request-handler.js +134 -0
  50. package/dist/master/request-handler.js.map +1 -0
  51. package/dist/master/session-registry.d.ts +120 -0
  52. package/dist/master/session-registry.d.ts.map +1 -0
  53. package/dist/master/session-registry.js +247 -0
  54. package/dist/master/session-registry.js.map +1 -0
  55. package/dist/mcp-server.d.ts +69 -0
  56. package/dist/mcp-server.d.ts.map +1 -0
  57. package/dist/mcp-server.js +301 -0
  58. package/dist/mcp-server.js.map +1 -0
  59. package/dist/session-manager.d.ts +107 -0
  60. package/dist/session-manager.d.ts.map +1 -0
  61. package/dist/session-manager.js +322 -0
  62. package/dist/session-manager.js.map +1 -0
  63. package/dist/shared/ipc-constants.d.ts +16 -0
  64. package/dist/shared/ipc-constants.d.ts.map +1 -0
  65. package/dist/shared/ipc-constants.js +66 -0
  66. package/dist/shared/ipc-constants.js.map +1 -0
  67. package/dist/shared/ipc-protocol.d.ts +33 -0
  68. package/dist/shared/ipc-protocol.d.ts.map +1 -0
  69. package/dist/shared/ipc-protocol.js +20 -0
  70. package/dist/shared/ipc-protocol.js.map +1 -0
  71. package/dist/tools/computer.d.ts +6 -0
  72. package/dist/tools/computer.d.ts.map +1 -0
  73. package/dist/tools/computer.js +426 -0
  74. package/dist/tools/computer.js.map +1 -0
  75. package/dist/tools/find.d.ts +6 -0
  76. package/dist/tools/find.d.ts.map +1 -0
  77. package/dist/tools/find.js +242 -0
  78. package/dist/tools/find.js.map +1 -0
  79. package/dist/tools/form-input.d.ts +6 -0
  80. package/dist/tools/form-input.d.ts.map +1 -0
  81. package/dist/tools/form-input.js +181 -0
  82. package/dist/tools/form-input.js.map +1 -0
  83. package/dist/tools/index.d.ts +6 -0
  84. package/dist/tools/index.d.ts.map +1 -0
  85. package/dist/tools/index.js +26 -0
  86. package/dist/tools/index.js.map +1 -0
  87. package/dist/tools/javascript.d.ts +6 -0
  88. package/dist/tools/javascript.d.ts.map +1 -0
  89. package/dist/tools/javascript.js +134 -0
  90. package/dist/tools/javascript.js.map +1 -0
  91. package/dist/tools/navigate.d.ts +6 -0
  92. package/dist/tools/navigate.d.ts.map +1 -0
  93. package/dist/tools/navigate.js +155 -0
  94. package/dist/tools/navigate.js.map +1 -0
  95. package/dist/tools/read-page.d.ts +6 -0
  96. package/dist/tools/read-page.d.ts.map +1 -0
  97. package/dist/tools/read-page.js +182 -0
  98. package/dist/tools/read-page.js.map +1 -0
  99. package/dist/tools/tabs-context.d.ts +6 -0
  100. package/dist/tools/tabs-context.d.ts.map +1 -0
  101. package/dist/tools/tabs-context.js +73 -0
  102. package/dist/tools/tabs-context.js.map +1 -0
  103. package/dist/tools/tabs-create.d.ts +6 -0
  104. package/dist/tools/tabs-create.d.ts.map +1 -0
  105. package/dist/tools/tabs-create.js +49 -0
  106. package/dist/tools/tabs-create.js.map +1 -0
  107. package/dist/types/index.d.ts +3 -0
  108. package/dist/types/index.d.ts.map +1 -0
  109. package/dist/types/index.js +19 -0
  110. package/dist/types/index.js.map +1 -0
  111. package/dist/types/mcp.d.ts +54 -0
  112. package/dist/types/mcp.d.ts.map +1 -0
  113. package/dist/types/mcp.js +14 -0
  114. package/dist/types/mcp.js.map +1 -0
  115. package/dist/types/session.d.ts +28 -0
  116. package/dist/types/session.d.ts.map +1 -0
  117. package/dist/types/session.js +6 -0
  118. package/dist/types/session.js.map +1 -0
  119. package/dist/utils/atomic-file.d.ts +50 -0
  120. package/dist/utils/atomic-file.d.ts.map +1 -0
  121. package/dist/utils/atomic-file.js +217 -0
  122. package/dist/utils/atomic-file.js.map +1 -0
  123. package/dist/utils/index.d.ts +6 -0
  124. package/dist/utils/index.d.ts.map +1 -0
  125. package/dist/utils/index.js +22 -0
  126. package/dist/utils/index.js.map +1 -0
  127. package/dist/utils/json-validator.d.ts +40 -0
  128. package/dist/utils/json-validator.d.ts.map +1 -0
  129. package/dist/utils/json-validator.js +295 -0
  130. package/dist/utils/json-validator.js.map +1 -0
  131. package/dist/utils/ref-id-manager.d.ts +26 -0
  132. package/dist/utils/ref-id-manager.d.ts.map +1 -0
  133. package/dist/utils/ref-id-manager.js +81 -0
  134. package/dist/utils/ref-id-manager.js.map +1 -0
  135. package/dist/utils/request-queue.d.ts +37 -0
  136. package/dist/utils/request-queue.d.ts.map +1 -0
  137. package/dist/utils/request-queue.js +110 -0
  138. package/dist/utils/request-queue.js.map +1 -0
  139. package/dist/worker/auto-master.d.ts +24 -0
  140. package/dist/worker/auto-master.d.ts.map +1 -0
  141. package/dist/worker/auto-master.js +135 -0
  142. package/dist/worker/auto-master.js.map +1 -0
  143. package/dist/worker/index.d.ts +25 -0
  144. package/dist/worker/index.d.ts.map +1 -0
  145. package/dist/worker/index.js +93 -0
  146. package/dist/worker/index.js.map +1 -0
  147. package/dist/worker/ipc-client.d.ts +26 -0
  148. package/dist/worker/ipc-client.d.ts.map +1 -0
  149. package/dist/worker/ipc-client.js +211 -0
  150. package/dist/worker/ipc-client.js.map +1 -0
  151. package/dist/worker/remote-session-manager.d.ts +114 -0
  152. package/dist/worker/remote-session-manager.d.ts.map +1 -0
  153. package/dist/worker/remote-session-manager.js +151 -0
  154. package/dist/worker/remote-session-manager.js.map +1 -0
  155. package/dist/worker/tools.d.ts +7 -0
  156. package/dist/worker/tools.d.ts.map +1 -0
  157. package/dist/worker/tools.js +340 -0
  158. package/dist/worker/tools.js.map +1 -0
  159. package/dist/worker/worker-mcp-server.d.ts +70 -0
  160. package/dist/worker/worker-mcp-server.d.ts.map +1 -0
  161. package/dist/worker/worker-mcp-server.js +295 -0
  162. package/dist/worker/worker-mcp-server.js.map +1 -0
  163. package/package.json +73 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/types/session.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,iBAAiB,GAAG,iBAAiB,GAAG,sBAAsB,GAAG,wBAAwB,CAAC;IAChG,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB"}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ /**
3
+ * Session Types
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/types/session.ts"],"names":[],"mappings":";AAAA;;GAEG"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Atomic file operations for safe concurrent access
3
+ * Prevents race conditions when multiple processes access the same file
4
+ */
5
+ export interface WriteOptions {
6
+ /** Create backup before writing */
7
+ backup?: boolean;
8
+ /** Timeout for acquiring lock (ms) */
9
+ lockTimeout?: number;
10
+ }
11
+ export interface ReadResult<T> {
12
+ success: boolean;
13
+ data?: T;
14
+ error?: string;
15
+ corrupted?: boolean;
16
+ }
17
+ /**
18
+ * Write file atomically using temp file + rename pattern
19
+ * This ensures the file is never in a partial/corrupt state
20
+ */
21
+ export declare function writeFileAtomicSafe(filePath: string, data: string | object, options?: WriteOptions): Promise<void>;
22
+ /**
23
+ * Read file safely with JSON validation
24
+ */
25
+ export declare function readFileSafe<T = unknown>(filePath: string): Promise<ReadResult<T>>;
26
+ /**
27
+ * Create a backup of a file
28
+ */
29
+ export declare function backupFile(filePath: string, backupDir?: string): Promise<string>;
30
+ /**
31
+ * Restore a file from backup
32
+ */
33
+ export declare function restoreFromBackup(backupPath: string, targetPath: string): Promise<void>;
34
+ /**
35
+ * Get list of available backups for a file
36
+ */
37
+ export declare function listBackups(originalFilename: string, backupDir?: string): string[];
38
+ /**
39
+ * Clean up old backups, keeping only the specified number
40
+ */
41
+ export declare function cleanupBackups(originalFilename: string, keepCount?: number, backupDir?: string): number;
42
+ /**
43
+ * Acquire a file lock with timeout
44
+ */
45
+ export declare function acquireLock(filePath: string, timeout?: number): Promise<() => Promise<void>>;
46
+ /**
47
+ * Check if a file is currently locked
48
+ */
49
+ export declare function isLocked(filePath: string): Promise<boolean>;
50
+ //# sourceMappingURL=atomic-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-file.d.ts","sourceRoot":"","sources":["../../src/utils/atomic-file.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,MAAM,WAAW,YAAY;IAC3B,mCAAmC;IACnC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAiBf;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,CAAC,GAAG,OAAO,EAC5C,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAgCxB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CA4BjB;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAcf;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,gBAAgB,EAAE,MAAM,EACxB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,EAAE,CAoBV;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,gBAAgB,EAAE,MAAM,EACxB,SAAS,GAAE,MAAU,EACrB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAqBR;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,MAAc,GACtB,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAoB9B;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAKjE"}
@@ -0,0 +1,217 @@
1
+ "use strict";
2
+ /**
3
+ * Atomic file operations for safe concurrent access
4
+ * Prevents race conditions when multiple processes access the same file
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ var __importDefault = (this && this.__importDefault) || function (mod) {
40
+ return (mod && mod.__esModule) ? mod : { "default": mod };
41
+ };
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.writeFileAtomicSafe = writeFileAtomicSafe;
44
+ exports.readFileSafe = readFileSafe;
45
+ exports.backupFile = backupFile;
46
+ exports.restoreFromBackup = restoreFromBackup;
47
+ exports.listBackups = listBackups;
48
+ exports.cleanupBackups = cleanupBackups;
49
+ exports.acquireLock = acquireLock;
50
+ exports.isLocked = isLocked;
51
+ const fs = __importStar(require("fs"));
52
+ const path = __importStar(require("path"));
53
+ const os = __importStar(require("os"));
54
+ const write_file_atomic_1 = __importDefault(require("write-file-atomic"));
55
+ const lockfile = __importStar(require("proper-lockfile"));
56
+ /**
57
+ * Write file atomically using temp file + rename pattern
58
+ * This ensures the file is never in a partial/corrupt state
59
+ */
60
+ async function writeFileAtomicSafe(filePath, data, options = {}) {
61
+ const { backup = false, lockTimeout = 10000 } = options;
62
+ const content = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
63
+ // Ensure directory exists
64
+ const dir = path.dirname(filePath);
65
+ if (!fs.existsSync(dir)) {
66
+ fs.mkdirSync(dir, { recursive: true });
67
+ }
68
+ // Create backup if requested and file exists
69
+ if (backup && fs.existsSync(filePath)) {
70
+ await backupFile(filePath);
71
+ }
72
+ // Use write-file-atomic for safe writing
73
+ await (0, write_file_atomic_1.default)(filePath, content, { encoding: 'utf8' });
74
+ }
75
+ /**
76
+ * Read file safely with JSON validation
77
+ */
78
+ async function readFileSafe(filePath) {
79
+ try {
80
+ if (!fs.existsSync(filePath)) {
81
+ return { success: false, error: 'File does not exist' };
82
+ }
83
+ const content = fs.readFileSync(filePath, 'utf8');
84
+ // Try to parse as JSON
85
+ try {
86
+ const data = JSON.parse(content);
87
+ return { success: true, data };
88
+ }
89
+ catch (parseError) {
90
+ // Check if it's a corruption pattern (two JSON objects concatenated)
91
+ if (content.includes('}{')) {
92
+ return {
93
+ success: false,
94
+ error: 'JSON parse error - corruption detected',
95
+ corrupted: true,
96
+ };
97
+ }
98
+ return {
99
+ success: false,
100
+ error: `JSON parse error: ${parseError.message}`,
101
+ };
102
+ }
103
+ }
104
+ catch (error) {
105
+ return {
106
+ success: false,
107
+ error: `Read error: ${error.message}`,
108
+ };
109
+ }
110
+ }
111
+ /**
112
+ * Create a backup of a file
113
+ */
114
+ async function backupFile(filePath, backupDir) {
115
+ if (!fs.existsSync(filePath)) {
116
+ throw new Error(`File does not exist: ${filePath}`);
117
+ }
118
+ // Default backup directory
119
+ const defaultBackupDir = path.join(os.homedir(), '.claude-chrome-parallel', 'backups');
120
+ const targetDir = backupDir || defaultBackupDir;
121
+ // Ensure backup directory exists
122
+ if (!fs.existsSync(targetDir)) {
123
+ fs.mkdirSync(targetDir, { recursive: true });
124
+ }
125
+ // Generate backup filename with timestamp
126
+ const basename = path.basename(filePath);
127
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
128
+ const backupName = `${basename}.${timestamp}.bak`;
129
+ const backupPath = path.join(targetDir, backupName);
130
+ // Copy file to backup location
131
+ fs.copyFileSync(filePath, backupPath);
132
+ return backupPath;
133
+ }
134
+ /**
135
+ * Restore a file from backup
136
+ */
137
+ async function restoreFromBackup(backupPath, targetPath) {
138
+ if (!fs.existsSync(backupPath)) {
139
+ throw new Error(`Backup file does not exist: ${backupPath}`);
140
+ }
141
+ // Ensure target directory exists
142
+ const targetDir = path.dirname(targetPath);
143
+ if (!fs.existsSync(targetDir)) {
144
+ fs.mkdirSync(targetDir, { recursive: true });
145
+ }
146
+ // Use atomic write to restore
147
+ const content = fs.readFileSync(backupPath, 'utf8');
148
+ await writeFileAtomicSafe(targetPath, content);
149
+ }
150
+ /**
151
+ * Get list of available backups for a file
152
+ */
153
+ function listBackups(originalFilename, backupDir) {
154
+ const defaultBackupDir = path.join(os.homedir(), '.claude-chrome-parallel', 'backups');
155
+ const targetDir = backupDir || defaultBackupDir;
156
+ if (!fs.existsSync(targetDir)) {
157
+ return [];
158
+ }
159
+ const basename = path.basename(originalFilename);
160
+ const pattern = new RegExp(`^${basename.replace('.', '\\.')}\\..*\\.bak$`);
161
+ return fs
162
+ .readdirSync(targetDir)
163
+ .filter((file) => pattern.test(file))
164
+ .sort()
165
+ .reverse(); // Most recent first
166
+ }
167
+ /**
168
+ * Clean up old backups, keeping only the specified number
169
+ */
170
+ function cleanupBackups(originalFilename, keepCount = 5, backupDir) {
171
+ const backups = listBackups(originalFilename, backupDir);
172
+ const toDelete = backups.slice(keepCount);
173
+ const defaultBackupDir = path.join(os.homedir(), '.claude-chrome-parallel', 'backups');
174
+ const targetDir = backupDir || defaultBackupDir;
175
+ for (const backup of toDelete) {
176
+ const backupPath = path.join(targetDir, backup);
177
+ try {
178
+ fs.unlinkSync(backupPath);
179
+ }
180
+ catch {
181
+ // Ignore deletion errors
182
+ }
183
+ }
184
+ return toDelete.length;
185
+ }
186
+ /**
187
+ * Acquire a file lock with timeout
188
+ */
189
+ async function acquireLock(filePath, timeout = 10000) {
190
+ // Ensure file exists for locking
191
+ const dir = path.dirname(filePath);
192
+ if (!fs.existsSync(dir)) {
193
+ fs.mkdirSync(dir, { recursive: true });
194
+ }
195
+ if (!fs.existsSync(filePath)) {
196
+ fs.writeFileSync(filePath, '{}');
197
+ }
198
+ const release = await lockfile.lock(filePath, {
199
+ retries: {
200
+ retries: Math.ceil(timeout / 100),
201
+ factor: 1,
202
+ minTimeout: 100,
203
+ maxTimeout: 100,
204
+ },
205
+ });
206
+ return release;
207
+ }
208
+ /**
209
+ * Check if a file is currently locked
210
+ */
211
+ async function isLocked(filePath) {
212
+ if (!fs.existsSync(filePath)) {
213
+ return false;
214
+ }
215
+ return lockfile.check(filePath);
216
+ }
217
+ //# sourceMappingURL=atomic-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-file.js","sourceRoot":"","sources":["../../src/utils/atomic-file.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Utility exports
3
+ */
4
+ export * from './atomic-file';
5
+ export * from './json-validator';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ /**
3
+ * Utility exports
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
17
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ __exportStar(require("./atomic-file"), exports);
21
+ __exportStar(require("./json-validator"), exports);
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;;;;;;;;;;;;;AAEH,gDAA8B;AAC9B,mDAAiC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * JSON validation and corruption detection utilities
3
+ * Detects and attempts to recover from common JSON corruption patterns
4
+ */
5
+ export interface ValidationResult {
6
+ valid: boolean;
7
+ error?: string;
8
+ corrupted?: boolean;
9
+ corruptionType?: 'concatenated' | 'truncated' | 'invalid' | 'empty';
10
+ }
11
+ export interface RecoveryResult {
12
+ success: boolean;
13
+ data?: unknown;
14
+ method?: string;
15
+ error?: string;
16
+ }
17
+ /**
18
+ * Check if a string is valid JSON
19
+ */
20
+ export declare function isValidJson(content: string): boolean;
21
+ /**
22
+ * Detect corruption in JSON content
23
+ * Identifies common corruption patterns from race conditions
24
+ */
25
+ export declare function detectCorruption(content: string): ValidationResult;
26
+ /**
27
+ * Extract valid JSON from corrupted content
28
+ * Attempts various recovery strategies
29
+ */
30
+ export declare function extractValidJson(content: string): RecoveryResult;
31
+ /**
32
+ * Merge two JSON objects, preferring values from the second
33
+ * Useful for recovering from partial writes
34
+ */
35
+ export declare function mergeJsonObjects(obj1: Record<string, unknown>, obj2: Record<string, unknown>): Record<string, unknown>;
36
+ /**
37
+ * Validate .claude.json specific structure
38
+ */
39
+ export declare function validateClaudeConfig(content: unknown): ValidationResult;
40
+ //# sourceMappingURL=json-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-validator.d.ts","sourceRoot":"","sources":["../../src/utils/json-validator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,cAAc,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;CACrE;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAOpD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,CA0DlE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CA4DhE;AA8HD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAuBzB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,CA2BvE"}
@@ -0,0 +1,295 @@
1
+ "use strict";
2
+ /**
3
+ * JSON validation and corruption detection utilities
4
+ * Detects and attempts to recover from common JSON corruption patterns
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.isValidJson = isValidJson;
8
+ exports.detectCorruption = detectCorruption;
9
+ exports.extractValidJson = extractValidJson;
10
+ exports.mergeJsonObjects = mergeJsonObjects;
11
+ exports.validateClaudeConfig = validateClaudeConfig;
12
+ /**
13
+ * Check if a string is valid JSON
14
+ */
15
+ function isValidJson(content) {
16
+ try {
17
+ JSON.parse(content);
18
+ return true;
19
+ }
20
+ catch {
21
+ return false;
22
+ }
23
+ }
24
+ /**
25
+ * Detect corruption in JSON content
26
+ * Identifies common corruption patterns from race conditions
27
+ */
28
+ function detectCorruption(content) {
29
+ if (!content || content.trim() === '') {
30
+ return {
31
+ valid: false,
32
+ corrupted: true,
33
+ corruptionType: 'empty',
34
+ error: 'Empty content',
35
+ };
36
+ }
37
+ // Check for concatenated JSON objects (common race condition pattern)
38
+ // Pattern: }{ indicates two JSON objects were written together
39
+ if (content.includes('}{')) {
40
+ return {
41
+ valid: false,
42
+ corrupted: true,
43
+ corruptionType: 'concatenated',
44
+ error: 'Detected concatenated JSON objects (race condition corruption)',
45
+ };
46
+ }
47
+ // Check for multiple JSON arrays concatenated
48
+ if (content.includes('][')) {
49
+ return {
50
+ valid: false,
51
+ corrupted: true,
52
+ corruptionType: 'concatenated',
53
+ error: 'Detected concatenated JSON arrays',
54
+ };
55
+ }
56
+ // Try to parse
57
+ try {
58
+ JSON.parse(content);
59
+ return { valid: true };
60
+ }
61
+ catch (error) {
62
+ const errorMessage = error.message;
63
+ // Detect truncated JSON
64
+ if (errorMessage.includes('Unexpected end of JSON') ||
65
+ errorMessage.includes('Unexpected end of input')) {
66
+ return {
67
+ valid: false,
68
+ corrupted: true,
69
+ corruptionType: 'truncated',
70
+ error: 'JSON appears to be truncated',
71
+ };
72
+ }
73
+ return {
74
+ valid: false,
75
+ corrupted: true,
76
+ corruptionType: 'invalid',
77
+ error: `Invalid JSON: ${errorMessage}`,
78
+ };
79
+ }
80
+ }
81
+ /**
82
+ * Extract valid JSON from corrupted content
83
+ * Attempts various recovery strategies
84
+ */
85
+ function extractValidJson(content) {
86
+ // Strategy 1: Content is already valid
87
+ if (isValidJson(content)) {
88
+ return {
89
+ success: true,
90
+ data: JSON.parse(content),
91
+ method: 'content_already_valid',
92
+ };
93
+ }
94
+ const trimmed = content.trim();
95
+ // Strategy 2: Handle concatenated JSON objects
96
+ // Take the first complete JSON object
97
+ if (trimmed.includes('}{')) {
98
+ const firstObjectEnd = findMatchingBrace(trimmed, 0);
99
+ if (firstObjectEnd !== -1) {
100
+ const firstObject = trimmed.substring(0, firstObjectEnd + 1);
101
+ if (isValidJson(firstObject)) {
102
+ return {
103
+ success: true,
104
+ data: JSON.parse(firstObject),
105
+ method: 'extract_first_object',
106
+ };
107
+ }
108
+ }
109
+ // Alternative: try to take the second object (might be more recent)
110
+ const secondStart = trimmed.indexOf('}{') + 1;
111
+ const secondObject = trimmed.substring(secondStart);
112
+ if (isValidJson(secondObject)) {
113
+ return {
114
+ success: true,
115
+ data: JSON.parse(secondObject),
116
+ method: 'extract_second_object',
117
+ };
118
+ }
119
+ }
120
+ // Strategy 3: Handle truncated JSON
121
+ // Try to complete the JSON by adding missing brackets
122
+ const result = attemptTruncatedRecovery(trimmed);
123
+ if (result.success) {
124
+ return result;
125
+ }
126
+ // Strategy 4: Look for the largest valid JSON substring
127
+ const largestValid = findLargestValidJson(trimmed);
128
+ if (largestValid) {
129
+ return {
130
+ success: true,
131
+ data: JSON.parse(largestValid),
132
+ method: 'largest_valid_substring',
133
+ };
134
+ }
135
+ return {
136
+ success: false,
137
+ error: 'Could not recover valid JSON from content',
138
+ };
139
+ }
140
+ /**
141
+ * Find the index of the matching closing brace for an opening brace
142
+ */
143
+ function findMatchingBrace(content, startIndex) {
144
+ let depth = 0;
145
+ let inString = false;
146
+ let escapeNext = false;
147
+ for (let i = startIndex; i < content.length; i++) {
148
+ const char = content[i];
149
+ if (escapeNext) {
150
+ escapeNext = false;
151
+ continue;
152
+ }
153
+ if (char === '\\' && inString) {
154
+ escapeNext = true;
155
+ continue;
156
+ }
157
+ if (char === '"') {
158
+ inString = !inString;
159
+ continue;
160
+ }
161
+ if (inString)
162
+ continue;
163
+ if (char === '{') {
164
+ depth++;
165
+ }
166
+ else if (char === '}') {
167
+ depth--;
168
+ if (depth === 0) {
169
+ return i;
170
+ }
171
+ }
172
+ }
173
+ return -1;
174
+ }
175
+ /**
176
+ * Attempt to recover truncated JSON
177
+ */
178
+ function attemptTruncatedRecovery(content) {
179
+ // Count open brackets and braces
180
+ let openBraces = 0;
181
+ let openBrackets = 0;
182
+ let inString = false;
183
+ let escapeNext = false;
184
+ for (const char of content) {
185
+ if (escapeNext) {
186
+ escapeNext = false;
187
+ continue;
188
+ }
189
+ if (char === '\\' && inString) {
190
+ escapeNext = true;
191
+ continue;
192
+ }
193
+ if (char === '"') {
194
+ inString = !inString;
195
+ continue;
196
+ }
197
+ if (inString)
198
+ continue;
199
+ switch (char) {
200
+ case '{':
201
+ openBraces++;
202
+ break;
203
+ case '}':
204
+ openBraces--;
205
+ break;
206
+ case '[':
207
+ openBrackets++;
208
+ break;
209
+ case ']':
210
+ openBrackets--;
211
+ break;
212
+ }
213
+ }
214
+ // If we're inside a string, try to close it
215
+ if (inString) {
216
+ content += '"';
217
+ }
218
+ // Try to complete with missing brackets/braces
219
+ const closing = ']'.repeat(Math.max(0, openBrackets)) +
220
+ '}'.repeat(Math.max(0, openBraces));
221
+ if (closing) {
222
+ const completed = content + closing;
223
+ if (isValidJson(completed)) {
224
+ return {
225
+ success: true,
226
+ data: JSON.parse(completed),
227
+ method: 'complete_truncated',
228
+ };
229
+ }
230
+ }
231
+ return { success: false, error: 'Truncated recovery failed' };
232
+ }
233
+ /**
234
+ * Find the largest valid JSON substring
235
+ */
236
+ function findLargestValidJson(content) {
237
+ // Start from the beginning, try to find complete JSON objects
238
+ for (let end = content.length; end > 1; end--) {
239
+ const substring = content.substring(0, end);
240
+ if (isValidJson(substring)) {
241
+ return substring;
242
+ }
243
+ }
244
+ return null;
245
+ }
246
+ /**
247
+ * Merge two JSON objects, preferring values from the second
248
+ * Useful for recovering from partial writes
249
+ */
250
+ function mergeJsonObjects(obj1, obj2) {
251
+ const result = { ...obj1 };
252
+ for (const [key, value] of Object.entries(obj2)) {
253
+ if (value !== null &&
254
+ typeof value === 'object' &&
255
+ !Array.isArray(value) &&
256
+ result[key] &&
257
+ typeof result[key] === 'object' &&
258
+ !Array.isArray(result[key])) {
259
+ // Recursively merge objects
260
+ result[key] = mergeJsonObjects(result[key], value);
261
+ }
262
+ else if (value !== undefined) {
263
+ result[key] = value;
264
+ }
265
+ }
266
+ return result;
267
+ }
268
+ /**
269
+ * Validate .claude.json specific structure
270
+ */
271
+ function validateClaudeConfig(content) {
272
+ if (typeof content !== 'object' || content === null) {
273
+ return {
274
+ valid: false,
275
+ error: 'Config must be an object',
276
+ };
277
+ }
278
+ const config = content;
279
+ // Check for expected top-level keys (optional validation)
280
+ const expectedKeys = [
281
+ 'numStartups',
282
+ 'tipsHistory',
283
+ 'userID',
284
+ 'firstStartTime',
285
+ ];
286
+ const hasExpectedKeys = expectedKeys.some((key) => key in config);
287
+ if (!hasExpectedKeys && Object.keys(config).length > 0) {
288
+ return {
289
+ valid: false,
290
+ error: 'Config missing expected Claude configuration keys',
291
+ };
292
+ }
293
+ return { valid: true };
294
+ }
295
+ //# sourceMappingURL=json-validator.js.map