@mod-computer/cli 0.2.3 ā 0.2.5
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.
- package/dist/cli.bundle.js +216 -36371
- package/package.json +3 -3
- package/dist/app.js +0 -227
- package/dist/cli.bundle.js.map +0 -7
- package/dist/cli.js +0 -132
- package/dist/commands/add.js +0 -245
- package/dist/commands/agents-run.js +0 -71
- package/dist/commands/auth.js +0 -259
- package/dist/commands/branch.js +0 -1411
- package/dist/commands/claude-sync.js +0 -772
- package/dist/commands/comment.js +0 -568
- package/dist/commands/diff.js +0 -182
- package/dist/commands/index.js +0 -73
- package/dist/commands/init.js +0 -597
- package/dist/commands/ls.js +0 -135
- package/dist/commands/members.js +0 -687
- package/dist/commands/mv.js +0 -282
- package/dist/commands/recover.js +0 -207
- package/dist/commands/rm.js +0 -257
- package/dist/commands/spec.js +0 -386
- package/dist/commands/status.js +0 -296
- package/dist/commands/sync.js +0 -119
- package/dist/commands/trace.js +0 -1752
- package/dist/commands/workspace.js +0 -447
- package/dist/components/conflict-resolution-ui.js +0 -120
- package/dist/components/messages.js +0 -5
- package/dist/components/thread.js +0 -8
- package/dist/config/features.js +0 -83
- package/dist/containers/branches-container.js +0 -140
- package/dist/containers/directory-container.js +0 -92
- package/dist/containers/thread-container.js +0 -214
- package/dist/containers/threads-container.js +0 -27
- package/dist/containers/workspaces-container.js +0 -27
- package/dist/daemon/conflict-resolution.js +0 -172
- package/dist/daemon/content-hash.js +0 -31
- package/dist/daemon/file-sync.js +0 -985
- package/dist/daemon/index.js +0 -203
- package/dist/daemon/mime-types.js +0 -166
- package/dist/daemon/offline-queue.js +0 -211
- package/dist/daemon/path-utils.js +0 -64
- package/dist/daemon/share-policy.js +0 -83
- package/dist/daemon/wasm-errors.js +0 -189
- package/dist/daemon/worker.js +0 -557
- package/dist/daemon-worker.js +0 -258
- package/dist/errors/workspace-errors.js +0 -48
- package/dist/lib/auth-server.js +0 -216
- package/dist/lib/browser.js +0 -35
- package/dist/lib/diff.js +0 -284
- package/dist/lib/formatters.js +0 -204
- package/dist/lib/git.js +0 -137
- package/dist/lib/local-fs.js +0 -201
- package/dist/lib/prompts.js +0 -56
- package/dist/lib/storage.js +0 -213
- package/dist/lib/trace-formatters.js +0 -314
- package/dist/services/add-service.js +0 -554
- package/dist/services/add-validation.js +0 -124
- package/dist/services/automatic-file-tracker.js +0 -303
- package/dist/services/cli-orchestrator.js +0 -227
- package/dist/services/feature-flags.js +0 -187
- package/dist/services/file-import-service.js +0 -283
- package/dist/services/file-transformation-service.js +0 -218
- package/dist/services/logger.js +0 -44
- package/dist/services/mod-config.js +0 -67
- package/dist/services/modignore-service.js +0 -328
- package/dist/services/sync-daemon.js +0 -244
- package/dist/services/thread-notification-service.js +0 -50
- package/dist/services/thread-service.js +0 -147
- package/dist/stores/use-directory-store.js +0 -96
- package/dist/stores/use-threads-store.js +0 -46
- package/dist/stores/use-workspaces-store.js +0 -54
- package/dist/types/add-types.js +0 -99
- package/dist/types/config.js +0 -16
- package/dist/types/index.js +0 -2
- package/dist/types/workspace-connection.js +0 -53
- package/dist/types.js +0 -1
package/dist/commands/mv.js
DELETED
|
@@ -1,282 +0,0 @@
|
|
|
1
|
-
// glassware[type="implementation", id="impl-cli-mv-cmd--7526e170", requirements="requirement-cli-mv-cmd--01f3db90,requirement-cli-mv-requires-workspace--e8eff602,requirement-cli-mv-workspace--ed3f7751,requirement-cli-mv-create-folders--62adc040,requirement-cli-mv-local--7bdf0396,requirement-cli-mv-no-overwrite--3c71bcba,requirement-cli-mv-folder--af5ef6eb"]
|
|
2
|
-
// spec: packages/mod-cli/specs/file-directory.md
|
|
3
|
-
import { createModWorkspace } from '@mod/mod-core';
|
|
4
|
-
import { readWorkspaceConnection } from '../lib/storage.js';
|
|
5
|
-
import { moveLocalFile } from '../lib/local-fs.js';
|
|
6
|
-
import path from 'path';
|
|
7
|
-
// glassware[type="implementation", id="impl-cli-mv-requires-workspace--35f86c8f", requirements="requirement-cli-mv-requires-workspace--e8eff602,requirement-cli-fd-error-no-workspace--1919b4fb"]
|
|
8
|
-
function requireWorkspaceConnection() {
|
|
9
|
-
const currentDir = process.cwd();
|
|
10
|
-
const connection = readWorkspaceConnection(currentDir);
|
|
11
|
-
if (!connection) {
|
|
12
|
-
console.error('Error: Not connected to a workspace.');
|
|
13
|
-
console.error('Run `mod init` to connect this directory first.');
|
|
14
|
-
process.exit(1);
|
|
15
|
-
}
|
|
16
|
-
return {
|
|
17
|
-
workspaceId: connection.workspaceId,
|
|
18
|
-
workspaceName: connection.workspaceName,
|
|
19
|
-
workspacePath: connection.path,
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
function parseArgs(args) {
|
|
23
|
-
const options = {
|
|
24
|
-
force: false,
|
|
25
|
-
local: false,
|
|
26
|
-
dryRun: false,
|
|
27
|
-
quiet: false,
|
|
28
|
-
};
|
|
29
|
-
const paths = [];
|
|
30
|
-
for (let i = 0; i < args.length; i++) {
|
|
31
|
-
const arg = args[i];
|
|
32
|
-
if (arg === '--force' || arg === '-f') {
|
|
33
|
-
options.force = true;
|
|
34
|
-
}
|
|
35
|
-
else if (arg === '--local') {
|
|
36
|
-
options.local = true;
|
|
37
|
-
}
|
|
38
|
-
else if (arg === '--dry-run') {
|
|
39
|
-
options.dryRun = true;
|
|
40
|
-
}
|
|
41
|
-
else if (arg === '--quiet' || arg === '-q') {
|
|
42
|
-
options.quiet = true;
|
|
43
|
-
}
|
|
44
|
-
else if (!arg.startsWith('-')) {
|
|
45
|
-
paths.push(arg);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
// Last path is destination, rest are sources
|
|
49
|
-
const dest = paths.pop();
|
|
50
|
-
return { sources: paths, dest, options };
|
|
51
|
-
}
|
|
52
|
-
export async function mvCommand(args, repo) {
|
|
53
|
-
const { workspaceId, workspacePath } = requireWorkspaceConnection();
|
|
54
|
-
const { sources, dest, options } = parseArgs(args);
|
|
55
|
-
if (sources.length === 0 || !dest) {
|
|
56
|
-
console.error('Usage: mod mv <source...> <dest>');
|
|
57
|
-
console.error('');
|
|
58
|
-
console.error('Options:');
|
|
59
|
-
console.error(' --force, -f Overwrite existing files');
|
|
60
|
-
console.error(' --local Also move local files');
|
|
61
|
-
console.error(' --dry-run Show what would be moved');
|
|
62
|
-
console.error(' --quiet, -q Only show errors');
|
|
63
|
-
process.exit(1);
|
|
64
|
-
}
|
|
65
|
-
try {
|
|
66
|
-
const modWorkspace = createModWorkspace(repo);
|
|
67
|
-
const workspaceHandle = await modWorkspace.openWorkspace(workspaceId);
|
|
68
|
-
// Get all files and folders
|
|
69
|
-
const fileRefs = await workspaceHandle.file.list();
|
|
70
|
-
const folderRefs = await workspaceHandle.folder.list();
|
|
71
|
-
// Determine if destination is a folder
|
|
72
|
-
const destIsFolder = dest.endsWith('/') ||
|
|
73
|
-
folderRefs.some(f => f.path === dest) ||
|
|
74
|
-
sources.length > 1;
|
|
75
|
-
const moveOps = [];
|
|
76
|
-
const foldersToCreate = new Set();
|
|
77
|
-
for (const source of sources) {
|
|
78
|
-
const normalizedSource = source.replace(/\/$/, '');
|
|
79
|
-
// Find matching file
|
|
80
|
-
const matchingFile = fileRefs.find(f => {
|
|
81
|
-
const filePath = f.metadata?.path || f.name;
|
|
82
|
-
return filePath === normalizedSource;
|
|
83
|
-
});
|
|
84
|
-
if (matchingFile) {
|
|
85
|
-
// Single file move/rename
|
|
86
|
-
const filePath = matchingFile.metadata?.path || matchingFile.name;
|
|
87
|
-
const fileName = path.basename(filePath);
|
|
88
|
-
let toPath;
|
|
89
|
-
let toFolderId = null;
|
|
90
|
-
let newName;
|
|
91
|
-
if (destIsFolder) {
|
|
92
|
-
// Move to folder, keep name
|
|
93
|
-
toPath = path.join(dest.replace(/\/$/, ''), fileName);
|
|
94
|
-
// Find or mark folder for creation
|
|
95
|
-
const destFolder = folderRefs.find(f => f.path === dest.replace(/\/$/, ''));
|
|
96
|
-
if (destFolder) {
|
|
97
|
-
toFolderId = destFolder.id;
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
foldersToCreate.add(dest.replace(/\/$/, ''));
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
// Rename
|
|
105
|
-
toPath = dest;
|
|
106
|
-
newName = path.basename(dest);
|
|
107
|
-
// Determine target folder
|
|
108
|
-
const targetDir = path.dirname(dest);
|
|
109
|
-
if (targetDir && targetDir !== '.') {
|
|
110
|
-
const targetFolder = folderRefs.find(f => f.path === targetDir);
|
|
111
|
-
if (targetFolder) {
|
|
112
|
-
toFolderId = targetFolder.id;
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
foldersToCreate.add(targetDir);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
// Check for existing file at destination (no-overwrite check)
|
|
120
|
-
const existingFile = fileRefs.find(f => {
|
|
121
|
-
const fp = f.metadata?.path || f.name;
|
|
122
|
-
return fp === toPath && f.id !== matchingFile.id;
|
|
123
|
-
});
|
|
124
|
-
if (existingFile && !options.force) {
|
|
125
|
-
console.error(`Error: ${toPath} already exists. Use --force to overwrite.`);
|
|
126
|
-
process.exit(1);
|
|
127
|
-
}
|
|
128
|
-
moveOps.push({
|
|
129
|
-
fileId: matchingFile.id,
|
|
130
|
-
fromPath: filePath,
|
|
131
|
-
toPath,
|
|
132
|
-
toFolderId,
|
|
133
|
-
newName,
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
// Check if it's a folder - move all contents
|
|
138
|
-
const filesInFolder = fileRefs.filter(f => {
|
|
139
|
-
const filePath = f.metadata?.path || f.name;
|
|
140
|
-
return filePath.startsWith(normalizedSource + '/') || filePath === normalizedSource;
|
|
141
|
-
});
|
|
142
|
-
if (filesInFolder.length > 0) {
|
|
143
|
-
for (const file of filesInFolder) {
|
|
144
|
-
const filePath = file.metadata?.path || file.name;
|
|
145
|
-
const relativePath = filePath.startsWith(normalizedSource + '/')
|
|
146
|
-
? filePath.slice(normalizedSource.length + 1)
|
|
147
|
-
: path.basename(filePath);
|
|
148
|
-
const toPath = path.join(dest.replace(/\/$/, ''), relativePath);
|
|
149
|
-
const targetDir = path.dirname(toPath);
|
|
150
|
-
let toFolderId = null;
|
|
151
|
-
if (targetDir && targetDir !== '.') {
|
|
152
|
-
const targetFolder = folderRefs.find(f => f.path === targetDir);
|
|
153
|
-
if (targetFolder) {
|
|
154
|
-
toFolderId = targetFolder.id;
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
foldersToCreate.add(targetDir);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
moveOps.push({
|
|
161
|
-
fileId: file.id,
|
|
162
|
-
fromPath: filePath,
|
|
163
|
-
toPath,
|
|
164
|
-
toFolderId,
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
else {
|
|
169
|
-
console.error(`Error: File not found in workspace: ${source}`);
|
|
170
|
-
console.error("Run `mod ls` to see workspace files.");
|
|
171
|
-
process.exit(1);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
if (moveOps.length === 0) {
|
|
176
|
-
console.log('No files to move.');
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
// Dry run
|
|
180
|
-
if (options.dryRun) {
|
|
181
|
-
if (foldersToCreate.size > 0) {
|
|
182
|
-
console.log('Would create folders:');
|
|
183
|
-
for (const folder of foldersToCreate) {
|
|
184
|
-
console.log(` ${folder}/`);
|
|
185
|
-
}
|
|
186
|
-
console.log('');
|
|
187
|
-
}
|
|
188
|
-
console.log(`Would move ${moveOps.length} file${moveOps.length === 1 ? '' : 's'}:`);
|
|
189
|
-
for (const op of moveOps) {
|
|
190
|
-
console.log(` ${op.fromPath} -> ${op.toPath}`);
|
|
191
|
-
}
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
// Create folders if needed
|
|
195
|
-
const folderIdMap = new Map();
|
|
196
|
-
for (const folderPath of Array.from(foldersToCreate).sort()) {
|
|
197
|
-
try {
|
|
198
|
-
const newFolder = await workspaceHandle.folder.create(folderPath);
|
|
199
|
-
folderIdMap.set(folderPath, newFolder.id);
|
|
200
|
-
if (!options.quiet) {
|
|
201
|
-
console.log(`Created folder: ${folderPath}/`);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
catch (error) {
|
|
205
|
-
// Folder might already exist, try to find it
|
|
206
|
-
const existing = (await workspaceHandle.folder.list()).find(f => f.path === folderPath);
|
|
207
|
-
if (existing) {
|
|
208
|
-
folderIdMap.set(folderPath, existing.id);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
// Execute moves
|
|
213
|
-
let moved = 0;
|
|
214
|
-
let localMoved = 0;
|
|
215
|
-
const errors = [];
|
|
216
|
-
for (const op of moveOps) {
|
|
217
|
-
try {
|
|
218
|
-
// Update folder ID from created folders
|
|
219
|
-
let targetFolderId = op.toFolderId;
|
|
220
|
-
const targetDir = path.dirname(op.toPath);
|
|
221
|
-
if (targetDir && targetDir !== '.' && folderIdMap.has(targetDir)) {
|
|
222
|
-
targetFolderId = folderIdMap.get(targetDir);
|
|
223
|
-
}
|
|
224
|
-
// Move in workspace
|
|
225
|
-
if (op.newName) {
|
|
226
|
-
await workspaceHandle.file.rename(op.fileId, op.newName);
|
|
227
|
-
}
|
|
228
|
-
if (targetFolderId !== undefined) {
|
|
229
|
-
await workspaceHandle.file.move(op.fileId, targetFolderId);
|
|
230
|
-
}
|
|
231
|
-
moved++;
|
|
232
|
-
if (!options.quiet) {
|
|
233
|
-
console.log(`Moved in workspace: ${op.fromPath} -> ${op.toPath}`);
|
|
234
|
-
}
|
|
235
|
-
// Move local file if --local flag is set
|
|
236
|
-
if (options.local) {
|
|
237
|
-
const localFrom = path.join(workspacePath, op.fromPath);
|
|
238
|
-
const localTo = path.join(workspacePath, op.toPath);
|
|
239
|
-
const result = await moveLocalFile(localFrom, localTo);
|
|
240
|
-
if (result.success) {
|
|
241
|
-
localMoved++;
|
|
242
|
-
if (!options.quiet) {
|
|
243
|
-
console.log(`Moved local file: ${op.fromPath} -> ${op.toPath}`);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
else if (result.error) {
|
|
247
|
-
// Permission error handling
|
|
248
|
-
console.warn(`Warning: ${result.error}`);
|
|
249
|
-
console.warn('Workspace updated, but local file not moved.');
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
catch (error) {
|
|
254
|
-
errors.push(`Failed to move ${op.fromPath}: ${error.message}`);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
// Summary
|
|
258
|
-
if (!options.quiet) {
|
|
259
|
-
console.log('');
|
|
260
|
-
if (moveOps.length === 1) {
|
|
261
|
-
console.log(`Moved: ${moveOps[0].fromPath} -> ${moveOps[0].toPath}`);
|
|
262
|
-
}
|
|
263
|
-
else {
|
|
264
|
-
console.log(`Moved ${moved} file${moved === 1 ? '' : 's'}.`);
|
|
265
|
-
}
|
|
266
|
-
if (!options.local && moved > 0) {
|
|
267
|
-
console.log('Note: Local files unchanged. Use --local to also move locally.');
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
if (errors.length > 0) {
|
|
271
|
-
console.error('');
|
|
272
|
-
for (const error of errors) {
|
|
273
|
-
console.error(error);
|
|
274
|
-
}
|
|
275
|
-
process.exit(1);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
catch (error) {
|
|
279
|
-
console.error('Error moving files:', error.message);
|
|
280
|
-
process.exit(1);
|
|
281
|
-
}
|
|
282
|
-
}
|
package/dist/commands/recover.js
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import { readModConfig, writeModConfig } from '../services/mod-config.js';
|
|
4
|
-
export async function recoverCommand(args, repo) {
|
|
5
|
-
const [action] = args;
|
|
6
|
-
if (!action || !['automerge', 'workspace', 'branches'].includes(action)) {
|
|
7
|
-
console.error('Usage: mod recover <automerge|workspace|branches>');
|
|
8
|
-
console.error(' automerge - Attempt to recover corrupted Automerge storage');
|
|
9
|
-
console.error(' workspace - Reset workspace configuration');
|
|
10
|
-
console.error(' branches - Recreate corrupted branch documents');
|
|
11
|
-
process.exit(1);
|
|
12
|
-
}
|
|
13
|
-
switch (action) {
|
|
14
|
-
case 'automerge':
|
|
15
|
-
await handleAutomergeRecovery(repo);
|
|
16
|
-
break;
|
|
17
|
-
case 'workspace':
|
|
18
|
-
await handleWorkspaceRecovery();
|
|
19
|
-
break;
|
|
20
|
-
case 'branches':
|
|
21
|
-
await handleBranchRecovery(repo);
|
|
22
|
-
break;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
async function handleAutomergeRecovery(repo) {
|
|
26
|
-
console.log('š§ Attempting Automerge storage recovery...');
|
|
27
|
-
const storageDir = process.env.MOD_AUTOMERGE_STORAGE_DIR
|
|
28
|
-
? path.resolve(process.env.MOD_AUTOMERGE_STORAGE_DIR)
|
|
29
|
-
: path.resolve('./.automerge-data');
|
|
30
|
-
if (!fs.existsSync(storageDir)) {
|
|
31
|
-
console.log('No Automerge storage directory found. Nothing to recover.');
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
console.log(`Scanning storage directory: ${storageDir}`);
|
|
35
|
-
// Create backup of corrupted storage
|
|
36
|
-
const backupDir = `${storageDir}.backup.${Date.now()}`;
|
|
37
|
-
try {
|
|
38
|
-
console.log(`Creating backup at: ${backupDir}`);
|
|
39
|
-
fs.cpSync(storageDir, backupDir, { recursive: true });
|
|
40
|
-
}
|
|
41
|
-
catch (error) {
|
|
42
|
-
console.warn('Could not create backup:', error);
|
|
43
|
-
}
|
|
44
|
-
// Test document loading to identify corrupted documents
|
|
45
|
-
const corruptedDocs = [];
|
|
46
|
-
const validDocs = [];
|
|
47
|
-
const allDocIds = [];
|
|
48
|
-
const scanDirectory = (dir) => {
|
|
49
|
-
try {
|
|
50
|
-
const items = fs.readdirSync(dir);
|
|
51
|
-
for (const item of items) {
|
|
52
|
-
const itemPath = path.join(dir, item);
|
|
53
|
-
const stat = fs.statSync(itemPath);
|
|
54
|
-
if (stat.isDirectory()) {
|
|
55
|
-
scanDirectory(itemPath);
|
|
56
|
-
}
|
|
57
|
-
else if (itemPath.includes('/incremental/')) {
|
|
58
|
-
// This is an Automerge incremental storage file
|
|
59
|
-
// Extract document ID from path like: 3K/9QuxnF8NM8g71nvphZYzgxsxxJ/incremental/...
|
|
60
|
-
const pathParts = path.relative(storageDir, itemPath).split(path.sep);
|
|
61
|
-
if (pathParts.length >= 3 && pathParts[2] === 'incremental') {
|
|
62
|
-
const docId = `${pathParts[0]}${pathParts[1]}`;
|
|
63
|
-
if (!allDocIds.includes(docId)) {
|
|
64
|
-
allDocIds.push(docId);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
catch (error) {
|
|
71
|
-
console.warn(`Error scanning directory ${dir}:`, error);
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
scanDirectory(storageDir);
|
|
75
|
-
console.log(`Found ${allDocIds.length} document files to test...`);
|
|
76
|
-
// Test each document by actually trying to load it
|
|
77
|
-
for (const docId of allDocIds) {
|
|
78
|
-
try {
|
|
79
|
-
const handle = await repo.find(docId);
|
|
80
|
-
const doc = await handle.doc();
|
|
81
|
-
if (doc) {
|
|
82
|
-
validDocs.push(docId);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
catch (error) {
|
|
86
|
-
const errorMessage = String(error?.message || error || '');
|
|
87
|
-
if (errorMessage.includes('corrupt') || errorMessage.includes('deflate') || errorMessage.includes('unable to parse chunk')) {
|
|
88
|
-
corruptedDocs.push(docId);
|
|
89
|
-
console.log(` š„ Corrupted: ${docId}`);
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
// Other errors might not be corruption
|
|
93
|
-
console.warn(` ā ļø Error loading ${docId}: ${errorMessage.substring(0, 100)}`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
console.log(`Found ${validDocs.length} valid documents and ${corruptedDocs.length} potentially corrupted documents`);
|
|
98
|
-
if (corruptedDocs.length > 0) {
|
|
99
|
-
console.log('\nCorrupted documents detected:');
|
|
100
|
-
corruptedDocs.slice(0, 5).forEach(docId => {
|
|
101
|
-
console.log(` ${docId}`);
|
|
102
|
-
});
|
|
103
|
-
if (corruptedDocs.length > 5) {
|
|
104
|
-
console.log(` ... and ${corruptedDocs.length - 5} more`);
|
|
105
|
-
}
|
|
106
|
-
// Offer to remove corrupted documents
|
|
107
|
-
console.log('\nā ļø This will remove corrupted documents and may cause data loss.');
|
|
108
|
-
console.log('š” Backup created at:', backupDir);
|
|
109
|
-
// Auto-proceed with recovery
|
|
110
|
-
console.log('Proceeding with corruption cleanup...');
|
|
111
|
-
let removedCount = 0;
|
|
112
|
-
for (const docId of corruptedDocs) {
|
|
113
|
-
try {
|
|
114
|
-
// Find and remove the corrupted document files
|
|
115
|
-
// DocId format: 3K9QuxnF8NM8g71nvphZYzgxsxxJ -> 3K/9QuxnF8NM8g71nvphZYzgxsxxJ
|
|
116
|
-
const prefix = docId.substring(0, 2);
|
|
117
|
-
const suffix = docId.substring(2);
|
|
118
|
-
const docDir = path.join(storageDir, prefix, suffix);
|
|
119
|
-
if (fs.existsSync(docDir)) {
|
|
120
|
-
// Remove the entire document directory
|
|
121
|
-
fs.rmSync(docDir, { recursive: true, force: true });
|
|
122
|
-
removedCount++;
|
|
123
|
-
console.log(` šļø Removed document directory: ${prefix}/${suffix}`);
|
|
124
|
-
}
|
|
125
|
-
// Check if parent directory is empty
|
|
126
|
-
const parentDir = path.join(storageDir, prefix);
|
|
127
|
-
try {
|
|
128
|
-
const remainingFiles = fs.readdirSync(parentDir);
|
|
129
|
-
if (remainingFiles.length === 0) {
|
|
130
|
-
fs.rmdirSync(parentDir);
|
|
131
|
-
console.log(` šļø Removed empty prefix directory: ${prefix}`);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
catch { }
|
|
135
|
-
}
|
|
136
|
-
catch (error) {
|
|
137
|
-
console.warn(`Could not remove corrupted document ${docId}:`, error);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
console.log(`ā
Removed ${removedCount} corrupted document files`);
|
|
141
|
-
console.log('š Please restart the application and try your command again');
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
console.log('ā
No corrupted documents detected in Automerge storage');
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
async function handleWorkspaceRecovery() {
|
|
148
|
-
console.log('š§ Resetting workspace configuration...');
|
|
149
|
-
const configPath = '.mod/config.json';
|
|
150
|
-
if (fs.existsSync(configPath)) {
|
|
151
|
-
console.log(`Backing up current config to ${configPath}.backup`);
|
|
152
|
-
fs.copyFileSync(configPath, `${configPath}.backup`);
|
|
153
|
-
}
|
|
154
|
-
// Reset to minimal config
|
|
155
|
-
const minimalConfig = {};
|
|
156
|
-
writeModConfig(minimalConfig);
|
|
157
|
-
console.log('ā
Workspace configuration reset');
|
|
158
|
-
console.log('š” Run "mod workspace list" to see available workspaces');
|
|
159
|
-
console.log('š” Run "mod workspace switch <workspace-name>" to select a workspace');
|
|
160
|
-
}
|
|
161
|
-
async function handleBranchRecovery(repo) {
|
|
162
|
-
console.log('š§ Attempting branch recovery...');
|
|
163
|
-
const cfg = readModConfig();
|
|
164
|
-
if (!cfg?.workspaceId) {
|
|
165
|
-
console.error('No workspace configured. Run "mod recover workspace" first.');
|
|
166
|
-
process.exit(1);
|
|
167
|
-
}
|
|
168
|
-
try {
|
|
169
|
-
// Try to access the workspace
|
|
170
|
-
const wsHandle = await repo.find(cfg.workspaceId);
|
|
171
|
-
const workspace = await wsHandle.doc();
|
|
172
|
-
if (!workspace) {
|
|
173
|
-
console.error('Workspace document is corrupted or missing');
|
|
174
|
-
console.log('š” Run "mod recover automerge" to fix storage corruption');
|
|
175
|
-
process.exit(1);
|
|
176
|
-
}
|
|
177
|
-
const workspaceData = workspace;
|
|
178
|
-
console.log(`ā
Workspace "${workspaceData.title || 'Untitled'}" is accessible`);
|
|
179
|
-
// Try to access branches document
|
|
180
|
-
if (workspaceData.branchesDocId) {
|
|
181
|
-
try {
|
|
182
|
-
const branchesHandle = await repo.find(workspaceData.branchesDocId);
|
|
183
|
-
const branchesDoc = await branchesHandle.doc();
|
|
184
|
-
const branchesData = branchesDoc;
|
|
185
|
-
if (branchesData && branchesData.branches) {
|
|
186
|
-
console.log(`ā
Found ${Object.keys(branchesData.branches).length} branches`);
|
|
187
|
-
console.log('Branches appear to be intact');
|
|
188
|
-
}
|
|
189
|
-
else {
|
|
190
|
-
console.log('ā ļø Branches document exists but appears empty');
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
catch (error) {
|
|
194
|
-
console.error('Branches document is corrupted:', error);
|
|
195
|
-
console.log('š” Run "mod recover automerge" to fix storage corruption');
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
else {
|
|
199
|
-
console.log('ā ļø Workspace has no branches document ID');
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
catch (error) {
|
|
203
|
-
console.error('Failed to access workspace:', error);
|
|
204
|
-
console.log('š” Run "mod recover automerge" to fix storage corruption');
|
|
205
|
-
process.exit(1);
|
|
206
|
-
}
|
|
207
|
-
}
|