opencode-zellij 0.0.15 → 0.0.16

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.
package/dist/index.mjs CHANGED
@@ -663,6 +663,28 @@ function findTabName(value, tabId) {
663
663
  if (found !== void 0) return found;
664
664
  }
665
665
  }
666
+ function activeTabNameProperty(object) {
667
+ if (object.active !== true || object.is_plugin === true) return void 0;
668
+ const name = stringProperty$2(object, ["name", "title"]);
669
+ return typeof name === "string" ? name : void 0;
670
+ }
671
+ function findActiveTabName(value) {
672
+ if (Array.isArray(value)) {
673
+ for (const item of value) {
674
+ const found = findActiveTabName(item);
675
+ if (found !== void 0) return found;
676
+ }
677
+ return;
678
+ }
679
+ if (typeof value !== "object" || value === null) return void 0;
680
+ const object = value;
681
+ const name = activeTabNameProperty(object);
682
+ if (name !== void 0) return name;
683
+ for (const nested of Object.values(object)) {
684
+ const found = findActiveTabName(nested);
685
+ if (found !== void 0) return found;
686
+ }
687
+ }
666
688
  function parseTabName(listTabsJson, tabId) {
667
689
  try {
668
690
  return findTabName(JSON.parse(listTabsJson), tabId);
@@ -671,6 +693,14 @@ function parseTabName(listTabsJson, tabId) {
671
693
  return;
672
694
  }
673
695
  }
696
+ function parseActiveTabName(listTabsJson) {
697
+ try {
698
+ return findActiveTabName(JSON.parse(listTabsJson));
699
+ } catch (error) {
700
+ debug("parseActiveTabName failed", errorMessage(error));
701
+ return;
702
+ }
703
+ }
674
704
  //#endregion
675
705
  //#region src/zellij/cli.ts
676
706
  const execFileAsync$1 = promisify(execFile);
@@ -738,25 +768,28 @@ async function runZellij(actionArgs, options = {}) {
738
768
  }
739
769
  }
740
770
  var ZellijCli = class {
771
+ constructor(run = runZellij) {
772
+ this.run = run;
773
+ }
741
774
  async newPane(options) {
742
- return parsePaneId((await runZellij(buildNewPaneActionArgs(options))).stdout);
775
+ return parsePaneId((await this.run(buildNewPaneActionArgs(options))).stdout);
743
776
  }
744
777
  async writeChars(paneId, data) {
745
- await runZellij(zellijActionArgs("write-chars", [
778
+ await this.run(zellijActionArgs("write-chars", [
746
779
  "--pane-id",
747
780
  paneId,
748
781
  data
749
782
  ]));
750
783
  }
751
784
  async sendCtrlC(paneId) {
752
- await runZellij(zellijActionArgs("send-keys", [
785
+ await this.run(zellijActionArgs("send-keys", [
753
786
  "--pane-id",
754
787
  paneId,
755
788
  "Ctrl c"
756
789
  ]));
757
790
  }
758
791
  async closePane(paneId) {
759
- await runZellij(zellijActionArgs("close-pane", ["--pane-id", paneId]));
792
+ await this.run(zellijActionArgs("close-pane", ["--pane-id", paneId]));
760
793
  }
761
794
  closePaneSync(paneId) {
762
795
  ensureZellijTarget();
@@ -767,10 +800,10 @@ var ZellijCli = class {
767
800
  });
768
801
  }
769
802
  async focusPane(paneId) {
770
- await runZellij(zellijActionArgs("focus-pane-id", [paneId]));
803
+ await this.run(zellijActionArgs("focus-pane-id", [paneId]));
771
804
  }
772
805
  async dumpScreen(paneId) {
773
- return (await runZellij(zellijActionArgs("dump-screen", [
806
+ return (await this.run(zellijActionArgs("dump-screen", [
774
807
  "--pane-id",
775
808
  paneId,
776
809
  "--full"
@@ -779,21 +812,24 @@ var ZellijCli = class {
779
812
  async currentPaneTabId() {
780
813
  const paneId = process.env.ZELLIJ_PANE_ID;
781
814
  if (!paneId) return void 0;
782
- return parseCurrentPaneTabId((await runZellij(zellijActionArgs("list-panes", ["--json"]), { timeoutMs: 5e3 })).stdout, paneId);
815
+ return parseCurrentPaneTabId((await this.run(zellijActionArgs("list-panes", ["--json"]), { timeoutMs: 5e3 })).stdout, paneId);
783
816
  }
784
817
  async paneExists(paneId) {
785
- return parsePaneExists((await runZellij(zellijActionArgs("list-panes", ["--json"]), { timeoutMs: 5e3 })).stdout, paneId);
818
+ return parsePaneExists((await this.run(zellijActionArgs("list-panes", ["--json"]), { timeoutMs: 5e3 })).stdout, paneId);
786
819
  }
787
820
  async renameTab(title) {
788
821
  const tabId = await this.currentPaneTabId();
789
822
  if (tabId === void 0 && process.env.ZELLIJ) throw new Error(`Could not resolve Zellij tab id for pane ${process.env.ZELLIJ_PANE_ID ?? "<missing>"}`);
790
- await runZellij(tabId === void 0 ? buildRenameTabActionArgs(title) : buildRenameTabActionArgs(title, { tabId }));
823
+ await this.run(tabId === void 0 ? buildRenameTabActionArgs(title) : buildRenameTabActionArgs(title, { tabId }));
791
824
  }
792
825
  async currentTabTitle() {
793
- if (!process.env.ZELLIJ_PANE_ID) return void 0;
826
+ if (!process.env.ZELLIJ_PANE_ID) {
827
+ if (!process.env.ZELLIJ_SESSION_NAME?.trim()) return void 0;
828
+ return parseActiveTabName((await this.run(zellijActionArgs("list-tabs", ["--json"]), { timeoutMs: 5e3 })).stdout);
829
+ }
794
830
  const tabId = await this.currentPaneTabId();
795
831
  if (tabId === void 0) return void 0;
796
- return parseTabName((await runZellij(zellijActionArgs("list-tabs", ["--json"]), { timeoutMs: 5e3 })).stdout, tabId);
832
+ return parseTabName((await this.run(zellijActionArgs("list-tabs", ["--json"]), { timeoutMs: 5e3 })).stdout, tabId);
797
833
  }
798
834
  };
799
835
  const zellijCli = new ZellijCli();
@@ -1038,13 +1074,20 @@ function createExitCodeToken() {
1038
1074
  return randomUUID().replaceAll("-", "");
1039
1075
  }
1040
1076
  function parseExitCodeMarker(line) {
1041
- const match = line.replace(ansiPattern, "").trim().match(markerPattern);
1077
+ const match = line.replace(ansiPattern, "").replace(/\r?\n/g, "").trim().match(markerPattern);
1042
1078
  if (!match?.[1] || !match[2]) return null;
1043
1079
  return {
1044
1080
  token: match[1],
1045
1081
  exitCode: Number(match[2])
1046
1082
  };
1047
1083
  }
1084
+ function parseExitCodeMarkerLines(lines, maxWindowLines = 8) {
1085
+ for (let start = 0; start < lines.length; start += 1) for (let size = 1; size <= maxWindowLines && start + size <= lines.length; size += 1) {
1086
+ const marker = parseExitCodeMarker(lines.slice(start, start + size).join("\n"));
1087
+ if (marker) return marker;
1088
+ }
1089
+ return null;
1090
+ }
1048
1091
  //#endregion
1049
1092
  //#region src/zellij/subscribe.ts
1050
1093
  const maxStderrLines = 200;
@@ -1107,9 +1150,9 @@ var SubscriberManager = class {
1107
1150
  this.sessions = sessions;
1108
1151
  this.maxBufferLines = maxBufferLines;
1109
1152
  this.spawnProcess = dependencies.spawn ?? spawn;
1110
- this.dumpScreen = dependencies.dumpScreen ?? zellijCli.dumpScreen;
1111
- this.paneExists = dependencies.paneExists ?? zellijCli.paneExists;
1112
- this.closePane = dependencies.closePane ?? zellijCli.closePane;
1153
+ this.dumpScreen = dependencies.dumpScreen ?? ((paneId) => zellijCli.dumpScreen(paneId));
1154
+ this.paneExists = dependencies.paneExists ?? ((paneId) => zellijCli.paneExists(paneId));
1155
+ this.closePane = dependencies.closePane ?? ((paneId) => zellijCli.closePane(paneId));
1113
1156
  this.lifecycleHooks = dependencies.lifecycleHooks;
1114
1157
  this.terminalTailLines = dependencies.terminalTailLines ?? 200;
1115
1158
  }
@@ -1268,12 +1311,9 @@ var SubscriberManager = class {
1268
1311
  captureExitCode(sessionId, lines) {
1269
1312
  const session = this.sessions.get(sessionId);
1270
1313
  if (!session.exitCodeToken) return;
1271
- for (const line of lines) {
1272
- const marker = parseExitCodeMarker(line);
1273
- if (!marker || marker.token !== session.exitCodeToken) continue;
1274
- this.markSessionTerminal(sessionId, "exit_marker", { exitCode: marker.exitCode });
1275
- return;
1276
- }
1314
+ const marker = parseExitCodeMarkerLines(lines);
1315
+ if (!marker || marker.token !== session.exitCodeToken) return;
1316
+ this.markSessionTerminal(sessionId, "exit_marker", { exitCode: marker.exitCode });
1277
1317
  }
1278
1318
  handleStderr(sessionId, child, chunk) {
1279
1319
  const state = this.subscribers.get(sessionId);
@@ -1538,7 +1578,7 @@ async function executeZellijPtyRead(args, dependencies = {}) {
1538
1578
  const nextAdviceApi = dependencies.nextAdvice ?? nextAdvice;
1539
1579
  const readOutputSnapshotApi = dependencies.readOutputSnapshot ?? readOutputSnapshot;
1540
1580
  const validateGrepApi = dependencies.validateGrep ?? validateGrep;
1541
- const paneExistsApi = dependencies.paneExists ?? zellijCli.paneExists;
1581
+ const paneExistsApi = dependencies.paneExists ?? ((paneId) => zellijCli.paneExists(paneId));
1542
1582
  const session = sessionManagerApi.get(args.id);
1543
1583
  const grepError = validateGrepApi(args.grep);
1544
1584
  if (grepError) return {
@@ -1714,13 +1754,6 @@ const requestSudoTool = tool({
1714
1754
  floating: true,
1715
1755
  exitCodeToken
1716
1756
  });
1717
- const warnings = [];
1718
- try {
1719
- await zellijCli.focusPane(paneId);
1720
- } catch (error) {
1721
- if (!(error instanceof Error ? error.message : String(error)).includes("already focused")) throw error;
1722
- warnings.push("Pane was already focused after creation.");
1723
- }
1724
1757
  const session = sessionManager.create({
1725
1758
  openCodeSessionId: context.sessionID,
1726
1759
  paneId,
@@ -1738,7 +1771,7 @@ const requestSudoTool = tool({
1738
1771
  session: publicSession(session),
1739
1772
  output: readOutputSnapshot(session.id),
1740
1773
  next: nextAdvice(false, "The user must review the summary and commands in Zellij, then type YES and any required credentials directly in the pane."),
1741
- warnings
1774
+ warnings: []
1742
1775
  });
1743
1776
  }
1744
1777
  });
@@ -1987,6 +2020,9 @@ function parseSessionStatus(value) {
1987
2020
  const completionTitle = "Zellij PTY session completed";
1988
2021
  const completionMessage = "A Zellij PTY session completed. Review the finished pane if needed.";
1989
2022
  const queuedNoticeHeader = "[OpenCode] Zellij PTY completion notice";
2023
+ function supportsActivePrompt(mode) {
2024
+ return mode === "prompt" || mode === "queue+toast";
2025
+ }
1990
2026
  function buildQueuedCompletionNotice(events) {
1991
2027
  return [queuedNoticeHeader, ...events.map((event) => `- ${event.session.id} (${event.session.paneId}) 已完成,請使用 zellij_pty_read 讀取最終輸出並清理 pane。`)].join("\n");
1992
2028
  }
@@ -2028,7 +2064,7 @@ function injectQueuedCompletionNotice(input, notice) {
2028
2064
  };
2029
2065
  }
2030
2066
  function evaluateCompletionPromptDecision(input) {
2031
- if (input.config.mode !== "prompt") return {
2067
+ if (!supportsActivePrompt(input.config.mode)) return {
2032
2068
  shouldPrompt: false,
2033
2069
  shouldQueue: false,
2034
2070
  reason: "prompt mode disabled"
@@ -2113,6 +2149,7 @@ var SessionCompletionNotificationQueue = class {
2113
2149
  const state = {
2114
2150
  event,
2115
2151
  queued: false,
2152
+ sent: false,
2116
2153
  toastSent: false,
2117
2154
  promptAttempts: 0,
2118
2155
  promptAttemptedAt: null
@@ -2127,7 +2164,7 @@ var SessionCompletionNotificationQueue = class {
2127
2164
  this.finalize(state);
2128
2165
  return;
2129
2166
  case "queue+toast":
2130
- state.queued = true;
2167
+ await this.tryPromptOrQueue(state);
2131
2168
  await this.sendToast(state);
2132
2169
  return;
2133
2170
  case "prompt":
@@ -2141,7 +2178,7 @@ var SessionCompletionNotificationQueue = class {
2141
2178
  if (pending.length === 0) return input;
2142
2179
  const notice = buildQueuedCompletionNotice(pending.map((state) => state.event));
2143
2180
  for (const state of pending) {
2144
- if (!state.toastSent) this.context.markSent(state.event.sessionId);
2181
+ this.markStateSent(state);
2145
2182
  this.finalize(state);
2146
2183
  }
2147
2184
  return injectQueuedCompletionNotice(input, notice);
@@ -2187,7 +2224,7 @@ var SessionCompletionNotificationQueue = class {
2187
2224
  state.queued = true;
2188
2225
  return;
2189
2226
  }
2190
- this.context.markSent(state.event.sessionId);
2227
+ this.markStateSent(state);
2191
2228
  this.finalize(state);
2192
2229
  } catch (error) {
2193
2230
  debug("completion notification prompt failed", errorMessage(error));
@@ -2208,12 +2245,17 @@ var SessionCompletionNotificationQueue = class {
2208
2245
  duration: 1e4
2209
2246
  } });
2210
2247
  state.toastSent = true;
2211
- this.context.markSent(state.event.sessionId);
2248
+ this.markStateSent(state);
2212
2249
  if (!state.queued) this.finalize(state);
2213
2250
  } catch (error) {
2214
2251
  debug("completion notification toast failed", errorMessage(error));
2215
2252
  }
2216
2253
  }
2254
+ markStateSent(state) {
2255
+ if (state.sent) return;
2256
+ this.context.markSent(state.event.sessionId);
2257
+ state.sent = true;
2258
+ }
2217
2259
  finalize(state) {
2218
2260
  this.states.delete(state.event.sessionId);
2219
2261
  }