recoder-code 2.5.2 → 2.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/index.js +0 -0
  2. package/dist/src/commands/context/index.js +2 -2
  3. package/dist/src/commands/mcp/marketplace.d.ts +6 -0
  4. package/dist/src/commands/mcp/marketplace.js +448 -0
  5. package/dist/src/commands/mcp.js +2 -0
  6. package/dist/src/commands/parallel.d.ts +20 -0
  7. package/dist/src/commands/parallel.js +133 -0
  8. package/dist/src/commands/recoderWeb.js +184 -5
  9. package/dist/src/commands/web/diff.d.ts +13 -0
  10. package/dist/src/commands/web/diff.js +235 -0
  11. package/dist/src/commands/web/link.d.ts +11 -0
  12. package/dist/src/commands/web/link.js +96 -0
  13. package/dist/src/commands/web/pull.d.ts +13 -0
  14. package/dist/src/commands/web/pull.js +203 -0
  15. package/dist/src/commands/web/status.d.ts +10 -0
  16. package/dist/src/commands/web/status.js +104 -0
  17. package/dist/src/commands/web/unlink.d.ts +10 -0
  18. package/dist/src/commands/web/unlink.js +45 -0
  19. package/dist/src/commands/web/watch.d.ts +14 -0
  20. package/dist/src/commands/web/watch.js +360 -0
  21. package/dist/src/commands/web.js +12 -0
  22. package/dist/src/config/config.js +6 -2
  23. package/dist/src/config/defaultMcpServers.d.ts +1 -0
  24. package/dist/src/config/defaultMcpServers.js +46 -0
  25. package/dist/src/gemini.js +10 -0
  26. package/dist/src/parallel/git-utils.d.ts +42 -0
  27. package/dist/src/parallel/git-utils.js +161 -0
  28. package/dist/src/parallel/index.d.ts +14 -0
  29. package/dist/src/parallel/index.js +14 -0
  30. package/dist/src/parallel/parallel-mode.d.ts +48 -0
  31. package/dist/src/parallel/parallel-mode.js +224 -0
  32. package/dist/src/services/AgentBridgeService.d.ts +61 -0
  33. package/dist/src/services/AgentBridgeService.js +253 -0
  34. package/dist/src/services/BuiltinCommandLoader.js +7 -0
  35. package/dist/src/services/PlatformSyncService.d.ts +154 -0
  36. package/dist/src/services/PlatformSyncService.js +588 -0
  37. package/dist/src/ui/commands/workflowCommands.d.ts +16 -0
  38. package/dist/src/ui/commands/workflowCommands.js +291 -0
  39. package/dist/src/ui/commands/workspaceCommand.d.ts +11 -0
  40. package/dist/src/ui/commands/workspaceCommand.js +329 -0
  41. package/dist/src/zed-integration/schema.d.ts +30 -30
  42. package/package.json +29 -10
  43. package/src/postinstall.cjs +3 -2
  44. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -5,11 +5,21 @@
5
5
  import open from 'open';
6
6
  import { RecoderWebService } from '../services/RecoderWebService.js';
7
7
  import { RecoderAuthService } from '../services/RecoderAuthService.js';
8
+ import { platformSync } from '../services/PlatformSyncService.js';
8
9
  export async function handleRecoderWebCommand(args) {
9
10
  const webService = new RecoderWebService();
10
11
  const authService = new RecoderAuthService();
11
12
  const command = args[0];
12
- // Check authentication for all commands
13
+ // Sync command doesn't require auth (uses container context)
14
+ if (command === 'sync') {
15
+ await handleSync(args.slice(1));
16
+ return;
17
+ }
18
+ if (command === 'status') {
19
+ await handleStatus();
20
+ return;
21
+ }
22
+ // Check authentication for all other commands
13
23
  const isAuth = await authService.isAuthenticated();
14
24
  if (!isAuth) {
15
25
  console.error('āŒ Not authenticated');
@@ -29,6 +39,9 @@ export async function handleRecoderWebCommand(args) {
29
39
  case 'info':
30
40
  await handleInfo(webService, args.slice(1));
31
41
  break;
42
+ case 'push':
43
+ await handlePush(webService, args.slice(1));
44
+ break;
32
45
  default:
33
46
  showHelp();
34
47
  break;
@@ -172,6 +185,160 @@ async function handleInfo(webService, args) {
172
185
  process.exit(1);
173
186
  }
174
187
  }
188
+ async function handleSync(args) {
189
+ const subCommand = args[0];
190
+ switch (subCommand) {
191
+ case 'start':
192
+ await startSync();
193
+ break;
194
+ case 'stop':
195
+ stopSync();
196
+ break;
197
+ default:
198
+ await showSyncStatus();
199
+ break;
200
+ }
201
+ }
202
+ async function startSync() {
203
+ console.log('šŸ”„ Starting platform sync...\n');
204
+ const platform = await platformSync.detectPlatform();
205
+ if (!platform.isContainer) {
206
+ console.log('šŸ“ Not running in recoder.xyz container');
207
+ console.log('');
208
+ console.log('šŸ’” Platform sync works when you run recoder-code inside:');
209
+ console.log(' - recoder.xyz web terminal');
210
+ console.log(' - recoder.xyz Docker container');
211
+ console.log('');
212
+ console.log('For local development, changes will sync to web when you');
213
+ console.log('push with: recoder web push <urlId>');
214
+ return;
215
+ }
216
+ console.log('🐳 Detected recoder.xyz container');
217
+ console.log(`šŸ“ Project ID: ${platform.projectId || 'Unknown'}`);
218
+ const connected = await platformSync.connect();
219
+ if (connected) {
220
+ console.log('āœ… Connected to platform sync');
221
+ console.log('');
222
+ console.log('šŸ“” File changes will now sync bidirectionally:');
223
+ console.log(' - CLI edits → Web editor');
224
+ console.log(' - Web edits → CLI workspace');
225
+ console.log('');
226
+ if (platform.previewUrl) {
227
+ console.log(`🌐 Live Preview: ${platform.previewUrl}`);
228
+ }
229
+ // Start file watcher
230
+ platformSync.startFileWatcher();
231
+ // Listen for remote changes
232
+ platformSync.on('remoteChange', (changes) => {
233
+ console.log(`\nšŸ“„ Remote changes: ${changes.length} files updated`);
234
+ for (const change of changes) {
235
+ console.log(` ${change.type}: ${change.path}`);
236
+ }
237
+ });
238
+ platformSync.on('localChange', (change) => {
239
+ console.log(`šŸ“¤ Synced: ${change.type} ${change.path}`);
240
+ });
241
+ console.log('');
242
+ console.log('Press Ctrl+C to stop sync');
243
+ // Keep process running
244
+ await new Promise(() => { });
245
+ }
246
+ else {
247
+ console.log('āŒ Failed to connect to platform');
248
+ console.log('šŸ’” Make sure docker-backend is running');
249
+ }
250
+ }
251
+ function stopSync() {
252
+ platformSync.disconnect();
253
+ console.log('āœ… Platform sync stopped');
254
+ }
255
+ async function showSyncStatus() {
256
+ const platform = await platformSync.detectPlatform();
257
+ platformSync.displayStatus();
258
+ if (platform.isContainer) {
259
+ console.log('');
260
+ console.log('Commands:');
261
+ console.log(' recoder web sync start - Start bidirectional sync');
262
+ console.log(' recoder web sync stop - Stop sync');
263
+ }
264
+ }
265
+ async function handleStatus() {
266
+ console.log('šŸ“Š Platform Status\n');
267
+ const platform = await platformSync.detectPlatform();
268
+ console.log(`Environment: ${platform.isContainer ? '🐳 Container' : 'šŸ’» Local'}`);
269
+ if (platform.isContainer) {
270
+ console.log(`Project ID: ${platform.projectId || 'Not detected'}`);
271
+ console.log(`Backend: ${platform.backendUrl}`);
272
+ console.log(`Web URL: ${platform.webUrl}`);
273
+ console.log(`Sync: ${platformSync.isConnected() ? 'āœ… Connected' : 'āŒ Disconnected'}`);
274
+ if (platform.previewUrl) {
275
+ console.log(`Preview: ${platform.previewUrl}`);
276
+ }
277
+ console.log('');
278
+ console.log('šŸ’” Start sync: recoder web sync start');
279
+ }
280
+ else {
281
+ console.log('');
282
+ console.log('šŸ’” Run recoder-code in recoder.xyz terminal for live sync');
283
+ console.log(' Or use "recoder web push <urlId>" to upload local files');
284
+ }
285
+ }
286
+ async function handlePush(webService, args) {
287
+ const urlId = args[0];
288
+ const directory = args[1] || process.cwd();
289
+ if (!urlId) {
290
+ console.error('āŒ Please provide a project URL ID');
291
+ console.log('\nUsage:');
292
+ console.log(' recoder web push <urlId> [directory]');
293
+ console.log('\nExample:');
294
+ console.log(' recoder web push 1762542265823');
295
+ console.log(' recoder web push 1762542265823 ./my-project');
296
+ process.exit(1);
297
+ }
298
+ try {
299
+ console.log(`šŸ“¤ Scanning files in: ${directory}\n`);
300
+ const files = await webService.scanDirectory(directory);
301
+ const fileCount = Object.keys(files).length;
302
+ if (fileCount === 0) {
303
+ console.log('āŒ No files found to upload');
304
+ return;
305
+ }
306
+ console.log(`Found ${fileCount} files to upload`);
307
+ // Get remote files for comparison
308
+ let changes = { added: [], modified: [], deleted: [], unchanged: [] };
309
+ try {
310
+ const project = await webService.getProject(urlId);
311
+ changes = await webService.detectChanges(files, project.fileSnapshot);
312
+ console.log('');
313
+ if (changes.added.length)
314
+ console.log(` + ${changes.added.length} new files`);
315
+ if (changes.modified.length)
316
+ console.log(` ~ ${changes.modified.length} modified`);
317
+ if (changes.deleted.length)
318
+ console.log(` - ${changes.deleted.length} deleted`);
319
+ if (changes.unchanged.length)
320
+ console.log(` = ${changes.unchanged.length} unchanged`);
321
+ }
322
+ catch {
323
+ // Project doesn't exist yet
324
+ console.log(' Creating new project...');
325
+ }
326
+ console.log('');
327
+ const result = await webService.uploadProject(urlId, files);
328
+ if (result.success) {
329
+ console.log('āœ… Upload complete!');
330
+ console.log(`šŸ“ ${result.fileCount} files synced`);
331
+ console.log(`🌐 View at: ${webService.getProjectUrl(urlId)}`);
332
+ }
333
+ else {
334
+ console.log('āŒ Upload failed');
335
+ }
336
+ }
337
+ catch (error) {
338
+ console.error(`āŒ ${error.message}`);
339
+ process.exit(1);
340
+ }
341
+ }
175
342
  function showHelp() {
176
343
  console.log('Recoder.xyz Web Platform');
177
344
  console.log('\nManage projects from your web IDE');
@@ -184,19 +351,31 @@ function showHelp() {
184
351
  console.log(' download <urlId> Download project files to local machine');
185
352
  console.log(' [directory] Optional output directory');
186
353
  console.log('');
354
+ console.log(' push <urlId> Upload local files to web project');
355
+ console.log(' [directory] Optional source directory (default: cwd)');
356
+ console.log('');
357
+ console.log(' sync Platform sync (when in container)');
358
+ console.log(' start Start bidirectional sync');
359
+ console.log(' stop Stop sync');
360
+ console.log('');
361
+ console.log(' status Show platform connection status');
187
362
  console.log(' open <urlId> Open project in web browser');
188
363
  console.log(' info <urlId> Show detailed project information');
189
364
  console.log('\nExamples:');
190
365
  console.log(' recoder web list');
191
366
  console.log(' recoder web list --limit 10');
192
367
  console.log(' recoder web download 1762542265823');
193
- console.log(' recoder web download 1762542265823 ./my-project');
194
- console.log(' recoder web open 1762542265823');
195
- console.log(' recoder web info 1762542265823');
368
+ console.log(' recoder web push 1762542265823 ./my-project');
369
+ console.log(' recoder web sync start');
370
+ console.log(' recoder web status');
196
371
  console.log('\nšŸ’” Workflow:');
197
- console.log(' 1. Create project in web IDE (http://localhost:5173)');
372
+ console.log(' 1. Create project in web IDE (https://web.recoder.xyz)');
198
373
  console.log(' 2. List projects: recoder web list');
199
374
  console.log(' 3. Download locally: recoder web download <urlId>');
200
375
  console.log(' 4. Edit with full file system access');
376
+ console.log(' 5. Push changes: recoder web push <urlId>');
377
+ console.log('');
378
+ console.log('šŸ’” In recoder.xyz terminal:');
379
+ console.log(' Run "recoder web sync start" for live bidirectional sync');
201
380
  console.log('\nFor more information, visit: https://recoder.xyz/docs');
202
381
  }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * 'recoder web diff' command
3
+ * Show differences between local and web project
4
+ */
5
+ import type { CommandModule } from 'yargs';
6
+ interface DiffArgs {
7
+ urlId?: string;
8
+ directory?: string;
9
+ verbose?: boolean;
10
+ 'show-content'?: boolean;
11
+ }
12
+ export declare const diffCommand: CommandModule<{}, DiffArgs>;
13
+ export {};
@@ -0,0 +1,235 @@
1
+ /**
2
+ * 'recoder web diff' command
3
+ * Show differences between local and web project
4
+ */
5
+ import path from 'node:path';
6
+ import fs from 'node:fs/promises';
7
+ import { RecoderWebService } from '../../services/RecoderWebService.js';
8
+ import { RecoderAuthService } from '../../services/RecoderAuthService.js';
9
+ const DOCKER_BACKEND_URL = process.env['RECODER_DOCKER_URL'] || 'https://docker.recoder.xyz';
10
+ // ANSI color codes
11
+ const colors = {
12
+ reset: '\x1b[0m',
13
+ red: '\x1b[31m',
14
+ green: '\x1b[32m',
15
+ yellow: '\x1b[33m',
16
+ blue: '\x1b[34m',
17
+ cyan: '\x1b[36m',
18
+ gray: '\x1b[90m',
19
+ };
20
+ export const diffCommand = {
21
+ command: 'diff [urlId]',
22
+ describe: 'Show differences between local and web project',
23
+ builder: (yargs) => yargs
24
+ .positional('urlId', {
25
+ type: 'string',
26
+ description: 'Project URL ID (optional, will read from .recoder-web if present)',
27
+ })
28
+ .option('directory', {
29
+ type: 'string',
30
+ alias: 'd',
31
+ description: 'Directory to compare (defaults to current directory)',
32
+ default: process.cwd(),
33
+ })
34
+ .option('verbose', {
35
+ type: 'boolean',
36
+ alias: 'v',
37
+ description: 'Show detailed diff for each file',
38
+ default: false,
39
+ })
40
+ .option('show-content', {
41
+ type: 'boolean',
42
+ description: 'Show unified diff content for modified files',
43
+ default: false,
44
+ }),
45
+ handler: async (argv) => {
46
+ const webService = new RecoderWebService();
47
+ const authService = new RecoderAuthService();
48
+ try {
49
+ // Check authentication
50
+ const isAuth = await authService.isAuthenticated();
51
+ if (!isAuth) {
52
+ console.error('Not authenticated');
53
+ console.log('Run: recoder auth login');
54
+ process.exit(1);
55
+ }
56
+ const targetDir = path.resolve(argv.directory || process.cwd());
57
+ let urlId = argv.urlId;
58
+ // Try to read .recoder-web metadata if no urlId provided
59
+ if (!urlId) {
60
+ try {
61
+ const metadataPath = path.join(targetDir, '.recoder-web');
62
+ const metadata = JSON.parse(await fs.readFile(metadataPath, 'utf-8'));
63
+ urlId = metadata.urlId || metadata.projectId;
64
+ console.log(`Found project ID from .recoder-web: ${urlId}`);
65
+ }
66
+ catch {
67
+ console.error('No project ID provided and no .recoder-web file found');
68
+ console.log('Usage: recoder web diff <urlId>');
69
+ console.log(' Or run from a linked directory');
70
+ process.exit(1);
71
+ }
72
+ }
73
+ console.log(`Comparing project: ${urlId}`);
74
+ console.log(`Local directory: ${targetDir}\n`);
75
+ // Scan local files
76
+ console.log('Scanning local files...');
77
+ const localFiles = await webService.scanDirectory(targetDir);
78
+ console.log(`Found ${Object.keys(localFiles).length} local files`);
79
+ // Get token for backend request
80
+ const token = await authService.getAccessToken();
81
+ if (!token) {
82
+ console.error('Failed to get access token');
83
+ process.exit(1);
84
+ }
85
+ // Try to get diff from docker backend first (has unified diff support)
86
+ let diffResult = null;
87
+ try {
88
+ const response = await fetch(`${DOCKER_BACKEND_URL}/api/sync/${urlId}/diff`, {
89
+ method: 'POST',
90
+ headers: {
91
+ 'Authorization': `Bearer ${token}`,
92
+ 'Content-Type': 'application/json',
93
+ },
94
+ body: JSON.stringify({ localFiles }),
95
+ });
96
+ if (response.ok) {
97
+ const data = await response.json();
98
+ if (data?.success) {
99
+ diffResult = data;
100
+ if (data.containerRunning) {
101
+ console.log('Comparing with running container');
102
+ }
103
+ }
104
+ }
105
+ }
106
+ catch {
107
+ // Docker backend not available
108
+ }
109
+ // Fall back to local comparison with web API
110
+ if (!diffResult) {
111
+ console.log('Fetching remote files from web API...');
112
+ let remoteFiles = {};
113
+ try {
114
+ const project = await webService.getProject(urlId);
115
+ remoteFiles = project.fileSnapshot || {};
116
+ }
117
+ catch (error) {
118
+ console.error(`Failed to fetch project: ${error.message}`);
119
+ process.exit(1);
120
+ }
121
+ const changes = await webService.detectChanges(localFiles, remoteFiles);
122
+ diffResult = {
123
+ diff: changes,
124
+ stats: {
125
+ totalChanges: changes.added.length + changes.modified.length + changes.deleted.length,
126
+ addedCount: changes.added.length,
127
+ modifiedCount: changes.modified.length,
128
+ deletedCount: changes.deleted.length,
129
+ },
130
+ };
131
+ }
132
+ // Display results
133
+ console.log('\n' + '='.repeat(60));
134
+ console.log(' DIFF SUMMARY');
135
+ console.log('='.repeat(60));
136
+ const { diff, stats } = diffResult;
137
+ if (stats.totalChanges === 0) {
138
+ console.log(`\n${colors.green}No differences - local and remote are in sync!${colors.reset}`);
139
+ return;
140
+ }
141
+ // Summary box
142
+ console.log(`
143
+ ${colors.gray}+----------------------------------+${colors.reset}
144
+ ${colors.gray}|${colors.reset} ${colors.green}+ ${stats.addedCount} added${colors.reset} (local only) ${colors.gray}|${colors.reset}
145
+ ${colors.gray}|${colors.reset} ${colors.yellow}~ ${stats.modifiedCount} modified${colors.reset} ${colors.gray}|${colors.reset}
146
+ ${colors.gray}|${colors.reset} ${colors.red}- ${stats.deletedCount} deleted${colors.reset} (remote only) ${colors.gray}|${colors.reset}
147
+ ${colors.gray}+----------------------------------+${colors.reset}
148
+ `);
149
+ // Added files (local only)
150
+ if (diff.added.length > 0) {
151
+ console.log(`${colors.green}Added (local only):${colors.reset}`);
152
+ const toShow = argv.verbose ? diff.added : diff.added.slice(0, 15);
153
+ for (const file of toShow) {
154
+ console.log(` ${colors.green}+${colors.reset} ${file}`);
155
+ }
156
+ if (!argv.verbose && diff.added.length > 15) {
157
+ console.log(` ${colors.gray}... and ${diff.added.length - 15} more${colors.reset}`);
158
+ }
159
+ console.log('');
160
+ }
161
+ // Modified files
162
+ if (diff.modified.length > 0) {
163
+ console.log(`${colors.yellow}Modified:${colors.reset}`);
164
+ const toShow = argv.verbose ? diff.modified : diff.modified.slice(0, 15);
165
+ for (const file of toShow) {
166
+ console.log(` ${colors.yellow}~${colors.reset} ${file}`);
167
+ }
168
+ if (!argv.verbose && diff.modified.length > 15) {
169
+ console.log(` ${colors.gray}... and ${diff.modified.length - 15} more${colors.reset}`);
170
+ }
171
+ console.log('');
172
+ }
173
+ // Deleted files (remote only)
174
+ if (diff.deleted.length > 0) {
175
+ console.log(`${colors.red}Deleted (remote only):${colors.reset}`);
176
+ const toShow = argv.verbose ? diff.deleted : diff.deleted.slice(0, 15);
177
+ for (const file of toShow) {
178
+ console.log(` ${colors.red}-${colors.reset} ${file}`);
179
+ }
180
+ if (!argv.verbose && diff.deleted.length > 15) {
181
+ console.log(` ${colors.gray}... and ${diff.deleted.length - 15} more${colors.reset}`);
182
+ }
183
+ console.log('');
184
+ }
185
+ // Show unified diffs if requested
186
+ if (argv['show-content'] && diffResult.unifiedDiffs) {
187
+ console.log('='.repeat(60));
188
+ console.log(' UNIFIED DIFFS');
189
+ console.log('='.repeat(60));
190
+ for (const [filePath, unifiedDiff] of Object.entries(diffResult.unifiedDiffs)) {
191
+ console.log(`\n${colors.cyan}--- ${filePath} ---${colors.reset}`);
192
+ // Color the diff output
193
+ for (const line of unifiedDiff.split('\n')) {
194
+ if (line.startsWith('+') && !line.startsWith('+++')) {
195
+ console.log(`${colors.green}${line}${colors.reset}`);
196
+ }
197
+ else if (line.startsWith('-') && !line.startsWith('---')) {
198
+ console.log(`${colors.red}${line}${colors.reset}`);
199
+ }
200
+ else if (line.startsWith('@@')) {
201
+ console.log(`${colors.cyan}${line}${colors.reset}`);
202
+ }
203
+ else {
204
+ console.log(line);
205
+ }
206
+ }
207
+ }
208
+ if (diff.modified.length > Object.keys(diffResult.unifiedDiffs).length) {
209
+ console.log(`\n${colors.gray}(Showing first ${Object.keys(diffResult.unifiedDiffs).length} of ${diff.modified.length} modified files)${colors.reset}`);
210
+ }
211
+ }
212
+ // Suggestions
213
+ console.log('='.repeat(60));
214
+ console.log('ACTIONS:');
215
+ console.log('-'.repeat(60));
216
+ if (diff.added.length > 0 || diff.modified.length > 0) {
217
+ console.log(` ${colors.blue}recoder web push${colors.reset} - Upload local changes to web`);
218
+ }
219
+ if (diff.deleted.length > 0 || diff.modified.length > 0) {
220
+ console.log(` ${colors.blue}recoder web pull${colors.reset} - Download remote changes to local`);
221
+ }
222
+ console.log(` ${colors.blue}recoder web sync${colors.reset} - Two-way sync`);
223
+ if (!argv['show-content'] && diff.modified.length > 0) {
224
+ console.log(`\n${colors.gray}Tip: Use --show-content to see unified diff of modified files${colors.reset}`);
225
+ }
226
+ if (!argv.verbose && stats.totalChanges > 15) {
227
+ console.log(`${colors.gray}Tip: Use --verbose to see all changed files${colors.reset}`);
228
+ }
229
+ }
230
+ catch (error) {
231
+ console.error(`${error.message}`);
232
+ process.exit(1);
233
+ }
234
+ },
235
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 'recoder web link' command
3
+ * Link current directory to a recoder.xyz web project for real-time sync
4
+ */
5
+ import type { CommandModule } from 'yargs';
6
+ interface LinkArgs {
7
+ projectId?: string;
8
+ directory?: string;
9
+ }
10
+ export declare const linkCommand: CommandModule<{}, LinkArgs>;
11
+ export {};
@@ -0,0 +1,96 @@
1
+ /**
2
+ * 'recoder web link' command
3
+ * Link current directory to a recoder.xyz web project for real-time sync
4
+ */
5
+ import path from 'node:path';
6
+ import fs from 'node:fs/promises';
7
+ import { RecoderAuthService } from '../../services/RecoderAuthService.js';
8
+ import { RecoderWebService } from '../../services/RecoderWebService.js';
9
+ export const linkCommand = {
10
+ command: 'link [projectId]',
11
+ describe: 'Link current directory to a recoder.xyz project for real-time sync',
12
+ builder: (yargs) => yargs
13
+ .positional('projectId', {
14
+ type: 'string',
15
+ description: 'Project URL ID (e.g., abc123 from web.recoder.xyz/chat/abc123)',
16
+ })
17
+ .option('directory', {
18
+ type: 'string',
19
+ alias: 'd',
20
+ description: 'Directory to link (defaults to current directory)',
21
+ default: process.cwd(),
22
+ }),
23
+ handler: async (argv) => {
24
+ const authService = new RecoderAuthService();
25
+ const webService = new RecoderWebService();
26
+ try {
27
+ // Check authentication
28
+ const isAuth = await authService.isAuthenticated();
29
+ if (!isAuth) {
30
+ console.error('āŒ Not authenticated');
31
+ console.log('šŸ’” Run: recoder auth login');
32
+ process.exit(1);
33
+ }
34
+ const targetDir = path.resolve(argv.directory || process.cwd());
35
+ let projectId = argv.projectId;
36
+ // If no projectId, try to read from existing .recoder-web
37
+ if (!projectId) {
38
+ try {
39
+ const metadataPath = path.join(targetDir, '.recoder-web');
40
+ const metadata = JSON.parse(await fs.readFile(metadataPath, 'utf-8'));
41
+ projectId = metadata.urlId || metadata.projectId;
42
+ console.log(`šŸ“‹ Found existing link: ${projectId}`);
43
+ }
44
+ catch {
45
+ console.error('āŒ No project ID provided');
46
+ console.log('šŸ’” Usage: recoder web link <projectId>');
47
+ console.log(' Example: recoder web link abc123');
48
+ console.log('\nšŸ“ Get project ID from your recoder.xyz URL:');
49
+ console.log(' https://web.recoder.xyz/chat/abc123 → projectId is "abc123"');
50
+ process.exit(1);
51
+ }
52
+ }
53
+ // Verify project exists
54
+ console.log(`šŸ” Verifying project: ${projectId}...`);
55
+ let project;
56
+ try {
57
+ project = await webService.getProject(projectId);
58
+ }
59
+ catch {
60
+ console.error(`āŒ Project not found: ${projectId}`);
61
+ console.log('šŸ’” Make sure you have access to this project');
62
+ process.exit(1);
63
+ }
64
+ // Create .recoder-web metadata file
65
+ const metadataFile = path.join(targetDir, '.recoder-web');
66
+ const metadata = {
67
+ urlId: projectId,
68
+ projectId: projectId,
69
+ description: project.description || '',
70
+ linkedAt: new Date().toISOString(),
71
+ webUrl: webService.getProjectUrl(projectId),
72
+ previewUrl: `https://docker.recoder.xyz/preview/${projectId}`,
73
+ syncEnabled: true,
74
+ };
75
+ await fs.writeFile(metadataFile, JSON.stringify(metadata, null, 2), 'utf-8');
76
+ console.log('\nā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”');
77
+ console.log('│ āœ… Project Linked Successfully! │');
78
+ console.log('ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤');
79
+ console.log(`│ šŸ“ Directory: ${targetDir.slice(-30).padEnd(30)}│`);
80
+ console.log(`│ šŸ”— Project: ${projectId.slice(0, 32).padEnd(32)}│`);
81
+ console.log('ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤');
82
+ console.log('│ Available commands: │');
83
+ console.log('│ • recoder web sync - Sync files │');
84
+ console.log('│ • recoder web push - Upload local changes │');
85
+ console.log('│ • recoder web info - Show project info │');
86
+ console.log('│ • recoder web unlink - Remove link │');
87
+ console.log('ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜');
88
+ console.log('\n🌐 Web IDE:', metadata.webUrl);
89
+ console.log('šŸ”— Preview:', metadata.previewUrl);
90
+ }
91
+ catch (error) {
92
+ console.error(`āŒ ${error.message}`);
93
+ process.exit(1);
94
+ }
95
+ },
96
+ };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * 'recoder web pull' command
3
+ * Pull web project files to local directory
4
+ */
5
+ import type { CommandModule } from 'yargs';
6
+ interface PullArgs {
7
+ urlId?: string;
8
+ directory?: string;
9
+ 'dry-run'?: boolean;
10
+ force?: boolean;
11
+ }
12
+ export declare const pullCommand: CommandModule<{}, PullArgs>;
13
+ export {};