osborn 0.9.18 → 0.9.20

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 (2) hide show
  1. package/dist/index.js +44 -8
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -187,8 +187,32 @@ function startApiServer(workingDir, port) {
187
187
  return;
188
188
  }
189
189
  if (req.method === 'GET' && url.pathname === '/health') {
190
+ // Include osborn version — primary signal used by machines.readInstalledOsbornVersion()
191
+ // to detect which agent version is running. Without this the consumer falls back to
192
+ // parsing the Docker image tag (e.g. ":latest" → rejected) and returns null, which
193
+ // breaks the dashboard's version badge + the upgrade-needed comparison.
194
+ // Read once at module load? No — package.json is small and resolveFromPackage() handles
195
+ // both `dist/` (installed) and `src/` (local dev) layouts.
196
+ let version;
197
+ try {
198
+ // Walk up from this file's dirname to find package.json. Works whether running
199
+ // from src/ (tsx local dev) or dist/ (compiled npm install).
200
+ const { readFileSync } = await import('node:fs');
201
+ const { join } = await import('node:path');
202
+ for (const rel of ['../package.json', '../../package.json']) {
203
+ try {
204
+ const pkg = JSON.parse(readFileSync(join(__dirname, rel), 'utf8'));
205
+ if (pkg.name === 'osborn' && pkg.version) {
206
+ version = pkg.version;
207
+ break;
208
+ }
209
+ }
210
+ catch { /* try next */ }
211
+ }
212
+ }
213
+ catch { /* version optional */ }
190
214
  res.writeHead(200, { 'Content-Type': 'application/json' });
191
- res.end(JSON.stringify({ status: 'ok', workingDir }));
215
+ res.end(JSON.stringify({ status: 'ok', workingDir, version }));
192
216
  return;
193
217
  }
194
218
  // POST /webhook/recall — Recall.ai real-time transcript webhooks
@@ -373,9 +397,22 @@ function startApiServer(workingDir, port) {
373
397
  const destSlug = join(projectsDir, effectiveSlug);
374
398
  mkdirSync(destSlug, { recursive: true });
375
399
  const sourceSlugPath = join(effectiveSource, sourceSlug);
376
- const sourceCwd = slugToCwd(sourceSlug);
400
+ // Always rewrite EVERY `"cwd":"<anything>"` to the target workspace dir
401
+ // when targetWorkDir is provided. Earlier version tried to derive the
402
+ // source cwd from the slug name via dash-to-slash substitution, but
403
+ // slug encoding is ambiguous (hyphens in path components like
404
+ // `codespaces-blank` get expanded into directory boundaries). That
405
+ // version only caught the canonical sprite cwd and missed laptop +
406
+ // codespaces references inside the JSONL bodies. Migration test on
407
+ // bj2zgt → fly machine found 12 files still pointing at /Users/...
408
+ // or /workspaces/codespaces-blank after the old rewrite. The brute-
409
+ // force regex below is bulletproof: any `cwd` reference becomes the
410
+ // runtime workspace dir, period.
377
411
  const destCwd = targetWorkDir ?? slugToCwd(effectiveSlug);
378
- const needsCwdRewrite = sourceCwd !== destCwd;
412
+ const needsCwdRewrite = !!targetWorkDir;
413
+ void sourceSlugPath; // silence linter — used below in walk
414
+ const cwdRewriteRe = /"cwd":"[^"]*"/g;
415
+ const cwdReplacement = `"cwd":"${destCwd}"`;
379
416
  // Walk the source slug directory and copy files individually so we can:
380
417
  // (a) skip AppleDouble per-file too (in case nested)
381
418
  // (b) rewrite cwd inside .jsonl files when remapping across workspaces
@@ -408,12 +445,11 @@ function startApiServer(workingDir, port) {
408
445
  if (!shouldWrite)
409
446
  continue;
410
447
  if (needsCwdRewrite && e.name.endsWith('.jsonl')) {
411
- // Read, rewrite "cwd" field, write. JSONL is line-delimited;
412
- // string match on `"cwd":"<sourceCwd>"` is precise enough.
448
+ // Regex-rewrite every `"cwd":"<anything>"` target. Catches all
449
+ // possible source cwds in one pass (laptop, codespaces, sprite,
450
+ // etc.) without depending on slug-derived heuristics.
413
451
  const content = readFileSync(sp, 'utf8');
414
- const find = `"cwd":"${sourceCwd}"`;
415
- const replace = `"cwd":"${destCwd}"`;
416
- const rewritten = content.split(find).join(replace);
452
+ const rewritten = content.replace(cwdRewriteRe, cwdReplacement);
417
453
  writeFileSync(dp, rewritten);
418
454
  }
419
455
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "osborn",
3
- "version": "0.9.18",
3
+ "version": "0.9.20",
4
4
  "description": "Voice AI coding assistant - local agent that connects to Osborn frontend",
5
5
  "type": "module",
6
6
  "bin": {