@insitue/claude-plugin 0.6.0 → 0.6.1

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "insitue",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Drive a Claude Code session from the InSitue browser overlay. Pick an element in your app, claude reads the file and proposes the edit.",
5
5
  "mcpServers": {
6
6
  "insitue": {
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @insitue/claude-plugin
2
2
 
3
+ ## 0.6.1
4
+
5
+ - **Fix: refuse to reuse a stale companion.** `ensureCompanion` now checks a reachable companion's version via the handshake and, if it is older than the cloud-feature floor (0.7.0), tears it down and spawns the current one — instead of silently attaching to a stale companion left on the port.
6
+
3
7
  ## 0.6.0
4
8
 
5
9
  - **Sign in from Claude.** New `/insitue:login` command + `authenticate` / `complete_authentication` MCP tools open a browser, you approve, and a token is stored automatically — no manual paste.
@@ -291,30 +291,64 @@ var PickBuffer = class {
291
291
  this.waiters.length = 0;
292
292
  }
293
293
  };
294
+ var MIN_COMPANION = "0.7.0";
295
+ function semverGte(version, floor) {
296
+ function parse(v) {
297
+ const m = /^(\d+)\.(\d+)\.(\d+)/.exec(v);
298
+ if (!m) return [0, 0, 0];
299
+ return [parseInt(m[1], 10), parseInt(m[2], 10), parseInt(m[3], 10)];
300
+ }
301
+ const [vMaj, vMin, vPat] = parse(version);
302
+ const [fMaj, fMin, fPat] = parse(floor);
303
+ if (vMaj !== fMaj) return vMaj > fMaj;
304
+ if (vMin !== fMin) return vMin > fMin;
305
+ return vPat >= fPat;
306
+ }
294
307
  async function probeCompanion(session2) {
295
308
  try {
296
309
  process.kill(session2.pid, 0);
297
310
  } catch {
298
- return false;
311
+ return null;
299
312
  }
300
313
  return new Promise((resolve) => {
314
+ let rawBody = "";
301
315
  const req = httpRequest(
302
316
  {
303
317
  host: "127.0.0.1",
304
318
  port: session2.port,
305
319
  path: "/insitue/handshake",
306
320
  method: "GET",
321
+ // Include an Origin so the handshake endpoint returns 200 + JSON
322
+ // instead of 403 (which it returns when Origin is absent). We
323
+ // use the loopback Origin — the companion's allowLocalhost flag
324
+ // accepts any localhost:* Origin, or else this falls back to 403
325
+ // which we still treat as "reachable" (non-null return).
326
+ headers: { origin: "http://localhost:5747" },
307
327
  timeout: 1500
308
328
  },
309
329
  (res) => {
310
- res.resume();
311
- resolve(true);
330
+ res.setEncoding("utf8");
331
+ res.on("data", (chunk) => {
332
+ rawBody += chunk;
333
+ });
334
+ res.on("end", () => {
335
+ if (res.statusCode === 200) {
336
+ try {
337
+ const body = JSON.parse(rawBody);
338
+ resolve(body.companionVersion ?? "0.0.0");
339
+ } catch {
340
+ resolve("0.0.0");
341
+ }
342
+ } else {
343
+ resolve("0.0.0");
344
+ }
345
+ });
312
346
  }
313
347
  );
314
- req.on("error", () => resolve(false));
348
+ req.on("error", () => resolve(null));
315
349
  req.on("timeout", () => {
316
350
  req.destroy();
317
- resolve(false);
351
+ resolve(null);
318
352
  });
319
353
  req.end();
320
354
  });
@@ -322,12 +356,30 @@ async function probeCompanion(session2) {
322
356
  var ownedChild = null;
323
357
  async function ensureCompanion(projectDir2) {
324
358
  const existing = findSession(projectDir2);
325
- if (existing && await probeCompanion(existing.session)) {
326
- process.stderr.write(
327
- `[insitue-mcp] reusing companion at :${existing.session.port} (pid ${existing.session.pid})
359
+ if (existing) {
360
+ const companionVersion = await probeCompanion(existing.session);
361
+ if (companionVersion !== null) {
362
+ if (semverGte(companionVersion, MIN_COMPANION)) {
363
+ process.stderr.write(
364
+ `[insitue-mcp] reusing companion at :${existing.session.port} (pid ${existing.session.pid}, v${companionVersion})
328
365
  `
329
- );
330
- return existing.session;
366
+ );
367
+ return existing.session;
368
+ }
369
+ process.stderr.write(
370
+ `[insitue-mcp] replacing stale companion (v${companionVersion} < ${MIN_COMPANION})
371
+ `
372
+ );
373
+ try {
374
+ process.kill(existing.session.pid);
375
+ } catch {
376
+ }
377
+ const sessionPath = join3(projectDir2, ".insitue", "session.json");
378
+ try {
379
+ rmSync(sessionPath);
380
+ } catch {
381
+ }
382
+ }
331
383
  }
332
384
  process.stderr.write(
333
385
  `[insitue-mcp] starting companion via \`npx -y @insitue/companion@latest dev\` in ${projectDir2}\u2026
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@insitue/claude-plugin",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Drive Claude (Code AND Desktop) from the InSitue browser overlay — pick an element in your app, claude reads the file and proposes the edit.",
5
5
  "keywords": [
6
6
  "insitue",