Parcourir la source

fix(herdr): close orphaned pane when opencode attach fails

When pane split succeeds but the opencode attach (pane run) fails, the
split pane was left in the UI because the session manager receives no
paneId on failure. Close the orphaned pane via closePane before
returning, wrapped in try/catch so a close failure does not mask the
original spawn failure.

Closes the follow-up from PR #697 review.
Michael Henke il y a 4 semaines
Parent
commit
f7a478e95e
2 fichiers modifiés avec 46 ajouts et 0 suppressions
  1. 35 0
      src/multiplexer/herdr/index.test.ts
  2. 11 0
      src/multiplexer/herdr/index.ts

+ 35 - 0
src/multiplexer/herdr/index.test.ts

@@ -346,6 +346,41 @@ describe('HerdrMultiplexer', () => {
     expect(result).toEqual({ success: false });
   });
 
+  test('closes orphaned pane when pane run fails (non-zero exit)', async () => {
+    const { HerdrMultiplexer } = await importFreshHerdr();
+    const herdr = new HerdrMultiplexer('main-vertical', 60);
+
+    crossSpawnMock.mockImplementation((command: string[]) => {
+      if (command[0] === 'which') {
+        return createSpawnResult(0, '/usr/bin/herdr\n');
+      }
+      if (command.includes('split')) {
+        return createSpawnResult(0, `${createSplitResponse('w1:p2')}\n`);
+      }
+      // run returns non-zero; everything else (close, send-keys) succeeds
+      if (command.includes('run')) {
+        return createSpawnResult(1, '', 'run failed');
+      }
+      return createSpawnResult();
+    });
+
+    const result = await herdr.spawnPane(
+      'session-1',
+      'Herdr worker',
+      'http://localhost:4096',
+      '/repo',
+    );
+
+    expect(result).toEqual({ success: false });
+
+    const closeCommands = commands().filter(
+      (c) => c[1] === 'pane' && c[2] === 'close',
+    );
+    expect(closeCommands).toEqual([
+      ['/usr/bin/herdr', 'pane', 'close', 'w1:p2'],
+    ]);
+  });
+
   test('main-horizontal layout opens panes down', async () => {
     const { HerdrMultiplexer } = await importFreshHerdr();
     const herdr = new HerdrMultiplexer('main-horizontal', 60);

+ 11 - 0
src/multiplexer/herdr/index.ts

@@ -133,6 +133,17 @@ export class HerdrMultiplexer implements Multiplexer {
           exitCode: runExitCode,
           stderr: runStderr.trim(),
         });
+        // ponytail: split succeeded but attach failed; close the orphaned pane
+        // so it does not linger in the agent column. Session manager gets no
+        // paneId on failure, so we must clean it up here.
+        try {
+          await this.closePane(paneId);
+        } catch (closeErr) {
+          log('[herdr] spawnPane: failed to close orphaned pane', {
+            paneId,
+            error: String(closeErr),
+          });
+        }
         return { success: false };
       }