opencodespaces 1.2.0 → 1.8.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.
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Build Test File
3
+ *
4
+ * This file exists to trigger CI/CD builds for testing purposes.
5
+ * Modify the timestamp below to trigger a new build.
6
+ *
7
+ * Last modified: 2026-01-19T00:30:00Z
8
+ */
9
+ export declare const BUILD_TEST_TIMESTAMP = "2026-01-19T00:30:00Z";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Build Test File
3
+ *
4
+ * This file exists to trigger CI/CD builds for testing purposes.
5
+ * Modify the timestamp below to trigger a new build.
6
+ *
7
+ * Last modified: 2026-01-19T00:30:00Z
8
+ */
9
+ export const BUILD_TEST_TIMESTAMP = '2026-01-19T00:30:00Z';
@@ -116,33 +116,18 @@ async function startSync(sessionId, localDir) {
116
116
  const spinner = ora('Initializing sync...').start();
117
117
  try {
118
118
  // Initialize sync on server (get SSH credentials)
119
+ // The API auto-cleans stale connections, but handle 409 as fallback
119
120
  let syncInfo;
120
121
  try {
121
122
  syncInfo = await api.initSync(sessionId);
122
123
  }
123
124
  catch (initError) {
124
- // Handle "Sync already active" error (409)
125
125
  if (initError instanceof ApiError && initError.statusCode === 409) {
126
- spinner.stop();
127
- logger.warn('A previous sync connection exists on the server.');
128
- const { cleanup } = await inquirer.prompt([
129
- {
130
- type: 'confirm',
131
- name: 'cleanup',
132
- message: 'Clean up stale connection and retry?',
133
- default: true,
134
- },
135
- ]);
136
- if (cleanup) {
137
- spinner.start('Cleaning up stale connection...');
138
- await api.stopSync(sessionId);
139
- spinner.text = 'Initializing sync...';
140
- syncInfo = await api.initSync(sessionId);
141
- }
142
- else {
143
- logger.info('Run "opencodespaces sync stop" to manually clean up.');
144
- process.exit(0);
145
- }
126
+ // Fallback: force cleanup and retry (API should auto-clean, but just in case)
127
+ spinner.text = 'Cleaning up stale connection...';
128
+ await api.stopSync(sessionId);
129
+ spinner.text = 'Initializing sync...';
130
+ syncInfo = await api.initSync(sessionId);
146
131
  }
147
132
  else {
148
133
  throw initError;
@@ -176,7 +161,20 @@ async function startSync(sessionId, localDir) {
176
161
  'two-way-resolved',
177
162
  ...ignoreArgs,
178
163
  ];
179
- execFileSync('mutagen', mutagenArgs, { stdio: 'pipe' });
164
+ try {
165
+ execFileSync('mutagen', mutagenArgs, { stdio: 'pipe' });
166
+ }
167
+ catch (mutagenError) {
168
+ const errorMsg = mutagenError?.stderr?.toString() ||
169
+ mutagenError.message;
170
+ if (errorMsg.includes('unable to locate agent bundle') ||
171
+ errorMsg.includes('unable to install agent')) {
172
+ throw new Error('Mutagen agent installation failed. Your mutagen installation may be missing agent bundles.\n' +
173
+ 'Fix: Download the full mutagen release from https://github.com/mutagen-io/mutagen/releases\n' +
174
+ 'and ensure mutagen-agents.tar.gz is next to the mutagen binary.');
175
+ }
176
+ throw mutagenError;
177
+ }
180
178
  spinner.succeed(`Syncing ${localPath} ↔ ${hostAlias}:${remotePath}`);
181
179
  logger.log('');
182
180
  logger.warn('Note: node_modules and other large directories are excluded by default.');
@@ -366,18 +364,36 @@ async function ensureSshConfigIncludes() {
366
364
  }
367
365
  }
368
366
  /**
369
- * Check if Mutagen is installed
367
+ * Check if Mutagen is installed and warn about missing agent bundles on Linux
370
368
  */
371
369
  function checkDependencies() {
372
370
  try {
373
371
  execSync('mutagen version', { stdio: 'pipe' });
372
+ // Warn about missing agent bundles (common Linux issue)
373
+ if (process.platform === 'linux') {
374
+ try {
375
+ const mutagenPath = execSync('which mutagen', { encoding: 'utf-8' }).trim();
376
+ const mutagenDir = path.dirname(mutagenPath);
377
+ const bundlePaths = [
378
+ path.join(mutagenDir, 'mutagen-agents.tar.gz'),
379
+ '/usr/local/libexec/mutagen-agents.tar.gz',
380
+ ];
381
+ if (!bundlePaths.some(p => fs.existsSync(p))) {
382
+ logger.warn('mutagen-agents.tar.gz not found. Sync relies on the container\'s pre-installed agent.');
383
+ logger.dim('For best compatibility, download the full mutagen release archive.');
384
+ }
385
+ }
386
+ catch {
387
+ // Non-critical, continue
388
+ }
389
+ }
374
390
  }
375
391
  catch {
376
392
  logger.error('Mutagen not found. Please install it:');
377
393
  logger.log('');
378
394
  logger.log(' macOS: brew install mutagen-io/mutagen/mutagen');
379
- logger.log(' Linux: Download from https://mutagen.io/documentation/introduction/installation');
380
- logger.log(' Windows: Download from https://mutagen.io/documentation/introduction/installation');
395
+ logger.log(' Linux: Download FULL archive from https://github.com/mutagen-io/mutagen/releases');
396
+ logger.log(' (must include mutagen-agents.tar.gz)');
381
397
  logger.log('');
382
398
  process.exit(1);
383
399
  }
package/dist/index.js CHANGED
@@ -36,6 +36,3 @@ sessionsCommand(program);
36
36
  syncCommand(program);
37
37
  sshCommand(program);
38
38
  program.parse();
39
- // test
40
- // v2
41
- // Workflow test 2026-01-18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencodespaces",
3
- "version": "1.2.0",
3
+ "version": "1.8.0",
4
4
  "description": "CLI for OpenCodeSpaces - Connect your local IDE to cloud sessions",
5
5
  "type": "module",
6
6
  "bin": {