@phenx-inc/ctlsurf 0.1.20 → 0.2.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.
Files changed (31) hide show
  1. package/out/headless/index.mjs +91 -57
  2. package/out/headless/index.mjs.map +2 -2
  3. package/out/main/index.js +145 -63
  4. package/out/preload/index.js +9 -8
  5. package/out/renderer/assets/{cssMode-Cxe23-tB.js → cssMode-D3kH1Kju.js} +3 -3
  6. package/out/renderer/assets/{freemarker2-Be0nj7Oa.js → freemarker2-BCHZUSLb.js} +1 -1
  7. package/out/renderer/assets/{handlebars-C0It7_Nu.js → handlebars-DKx-Fw-H.js} +1 -1
  8. package/out/renderer/assets/{html-BW6LB-7J.js → html-BSCM04uL.js} +1 -1
  9. package/out/renderer/assets/{htmlMode-D_V-1VlE.js → htmlMode-BucU1MUc.js} +3 -3
  10. package/out/renderer/assets/{index-D568SpEt.js → index-BsdOeO0U.js} +240 -94
  11. package/out/renderer/assets/{index-DK9wLFFm.css → index-BzF7I1my.css} +111 -0
  12. package/out/renderer/assets/{javascript-D_LoeNc7.js → javascript-bPY5C4uq.js} +2 -2
  13. package/out/renderer/assets/{jsonMode-K3WSinSE.js → jsonMode-BmJotb6E.js} +3 -3
  14. package/out/renderer/assets/{liquid-BqfOd6m8.js → liquid-Cja_Pzh3.js} +1 -1
  15. package/out/renderer/assets/{lspLanguageFeatures-Bjf28WU6.js → lspLanguageFeatures-hoVZfVKv.js} +1 -1
  16. package/out/renderer/assets/{mdx-BoESjI38.js → mdx-C0s81MOq.js} +1 -1
  17. package/out/renderer/assets/{python-DlafOOgB.js → python-CulkBOJr.js} +1 -1
  18. package/out/renderer/assets/{razor-CB6E9DBD.js → razor-czmzhwVZ.js} +1 -1
  19. package/out/renderer/assets/{tsMode-DYu1z_nn.js → tsMode-B90EqYGx.js} +1 -1
  20. package/out/renderer/assets/{typescript-CDjkh0d5.js → typescript-Ckc6emP2.js} +1 -1
  21. package/out/renderer/assets/{xml-C-XlilKZ.js → xml-CKh-JyGN.js} +1 -1
  22. package/out/renderer/assets/{yaml-BTrtxLEo.js → yaml-B49zLim4.js} +1 -1
  23. package/out/renderer/index.html +2 -2
  24. package/package.json +1 -1
  25. package/src/main/headless.ts +8 -7
  26. package/src/main/index.ts +87 -13
  27. package/src/main/orchestrator.ts +98 -54
  28. package/src/preload/index.ts +16 -14
  29. package/src/renderer/App.tsx +161 -43
  30. package/src/renderer/components/TerminalPanel.tsx +113 -48
  31. package/src/renderer/styles.css +111 -0
@@ -591,16 +591,14 @@ var Orchestrator = class {
591
591
  bridge = new ConversationBridge();
592
592
  workerWs;
593
593
  // State
594
- ptyManager = null;
594
+ tabs = /* @__PURE__ */ new Map();
595
+ activeTabId = null;
595
596
  currentAgent = null;
596
597
  currentCwd = null;
597
598
  settings = {
598
599
  activeProfile: "production",
599
600
  profiles: { ...DEFAULT_PROFILES }
600
601
  };
601
- // Terminal stream batching
602
- termStreamBuffer = "";
603
- termStreamTimer = null;
604
602
  constructor(settingsDir, events) {
605
603
  this.settingsDir = settingsDir;
606
604
  this.events = events;
@@ -614,8 +612,9 @@ var Orchestrator = class {
614
612
  events.onWorkerMessage(message);
615
613
  this.workerWs.sendAck(message.id);
616
614
  if (message.type === "prompt" || message.type === "task_dispatch") {
617
- if (this.ptyManager) {
618
- this.ptyManager.write(message.content + "\r");
615
+ const activeTab = this.activeTabId ? this.tabs.get(this.activeTabId) : null;
616
+ if (activeTab) {
617
+ activeTab.ptyManager.write(message.content + "\r");
619
618
  this.bridge.feedInput(message.content);
620
619
  }
621
620
  }
@@ -628,7 +627,8 @@ var Orchestrator = class {
628
627
  }
629
628
  },
630
629
  onTerminalInput: (data) => {
631
- this.ptyManager?.write(data);
630
+ const activeTab = this.activeTabId ? this.tabs.get(this.activeTabId) : null;
631
+ activeTab?.ptyManager.write(data);
632
632
  }
633
633
  });
634
634
  this.bridge.setWsClient(this.workerWs);
@@ -782,31 +782,41 @@ var Orchestrator = class {
782
782
  this.saveSettings();
783
783
  return { ok: true };
784
784
  }
785
- // ─── PTY & Agent ────────────────────────────────
786
- async spawnAgent(agent, cwd) {
787
- if (this.ptyManager) {
788
- this.bridge.endSession();
789
- this.ptyManager.kill();
785
+ // ─── PTY & Agent (multi-tab) ─────────────────────
786
+ async spawnAgent(tabId, agent, cwd) {
787
+ const existing = this.tabs.get(tabId);
788
+ if (existing) {
789
+ if (existing.termStreamTimer) clearTimeout(existing.termStreamTimer);
790
+ existing.ptyManager.kill();
791
+ this.tabs.delete(tabId);
790
792
  }
791
793
  this.currentAgent = agent;
792
794
  const prevCwd = this.currentCwd;
793
795
  this.currentCwd = cwd;
796
+ this.activeTabId = tabId;
794
797
  if (prevCwd !== cwd) {
795
798
  this.events.onCwdChanged();
796
799
  }
797
- this.ptyManager = new PtyManager(agent, cwd);
798
- this.ptyManager.onData((data) => {
799
- this.events.onPtyData(data);
800
- this.bridge.feedOutput(data);
801
- this.streamTerminalData(data);
800
+ const ptyManager = new PtyManager(agent, cwd);
801
+ const tab = { ptyManager, agent, cwd, termStreamBuffer: "", termStreamTimer: null };
802
+ this.tabs.set(tabId, tab);
803
+ ptyManager.onData((data) => {
804
+ this.events.onPtyData(tabId, data);
805
+ if (tabId === this.activeTabId) {
806
+ this.bridge.feedOutput(data);
807
+ this.streamTerminalData(tabId, data);
808
+ }
802
809
  });
803
- const thisPtyManager = this.ptyManager;
804
- this.ptyManager.onExit(async (exitCode) => {
805
- this.events.onPtyExit(exitCode);
806
- this.bridge.endSession();
807
- if (thisPtyManager === this.ptyManager && this.currentAgent && isCodingAgent(this.currentAgent)) {
808
- this.workerWs.disconnect();
810
+ ptyManager.onExit(async (exitCode) => {
811
+ this.events.onPtyExit(tabId, exitCode);
812
+ if (tabId === this.activeTabId) {
813
+ this.bridge.endSession();
814
+ if (this.currentAgent && isCodingAgent(this.currentAgent)) {
815
+ this.workerWs.disconnect();
816
+ }
809
817
  }
818
+ const t = this.tabs.get(tabId);
819
+ if (t?.termStreamTimer) clearTimeout(t.termStreamTimer);
810
820
  });
811
821
  this.bridge.startSession();
812
822
  if (isCodingAgent(agent)) {
@@ -816,23 +826,45 @@ var Orchestrator = class {
816
826
  this.checkProjectStatus(cwd);
817
827
  }
818
828
  }
819
- writePty(data) {
820
- this.ptyManager?.write(data);
821
- this.bridge.feedInput(data);
829
+ writePty(tabId, data) {
830
+ this.tabs.get(tabId)?.ptyManager.write(data);
831
+ if (tabId === this.activeTabId) {
832
+ this.bridge.feedInput(data);
833
+ }
822
834
  }
823
- resizePty(cols, rows) {
824
- this.ptyManager?.resize(cols, rows);
825
- this.bridge.resize(cols, rows);
826
- this.workerWs.sendTerminalResize(cols, rows);
835
+ resizePty(tabId, cols, rows) {
836
+ this.tabs.get(tabId)?.ptyManager.resize(cols, rows);
837
+ if (tabId === this.activeTabId) {
838
+ this.bridge.resize(cols, rows);
839
+ this.workerWs.sendTerminalResize(cols, rows);
840
+ }
827
841
  }
828
- async killAgent() {
829
- this.bridge.endSession();
830
- this.ptyManager?.kill();
831
- this.ptyManager = null;
832
- if (this.currentAgent && isCodingAgent(this.currentAgent)) {
833
- this.workerWs.disconnect();
842
+ async killTab(tabId) {
843
+ const tab = this.tabs.get(tabId);
844
+ if (!tab) return;
845
+ if (tab.termStreamTimer) clearTimeout(tab.termStreamTimer);
846
+ tab.ptyManager.kill();
847
+ this.tabs.delete(tabId);
848
+ if (tabId === this.activeTabId) {
849
+ this.bridge.endSession();
850
+ if (isCodingAgent(tab.agent)) {
851
+ this.workerWs.disconnect();
852
+ }
853
+ const remaining = [...this.tabs.keys()];
854
+ this.activeTabId = remaining.length > 0 ? remaining[remaining.length - 1] : null;
855
+ }
856
+ }
857
+ setActiveTab(tabId) {
858
+ this.activeTabId = tabId;
859
+ const tab = this.tabs.get(tabId);
860
+ if (tab) {
861
+ this.currentAgent = tab.agent;
862
+ this.currentCwd = tab.cwd;
834
863
  }
835
864
  }
865
+ getTabIds() {
866
+ return [...this.tabs.keys()];
867
+ }
836
868
  // ─── Worker WebSocket ───────────────────────────
837
869
  connectWorkerWs(agent, cwd) {
838
870
  const profile = this.getActiveProfile();
@@ -861,28 +893,29 @@ var Orchestrator = class {
861
893
  this.events.onWorkerStatus("no_project");
862
894
  }
863
895
  }
864
- streamTerminalData(data) {
865
- this.termStreamBuffer += data;
866
- if (!this.termStreamTimer) {
867
- this.termStreamTimer = setTimeout(() => {
868
- if (this.termStreamBuffer) {
869
- this.workerWs.sendTerminalData(this.termStreamBuffer);
870
- this.termStreamBuffer = "";
896
+ streamTerminalData(tabId, data) {
897
+ const tab = this.tabs.get(tabId);
898
+ if (!tab) return;
899
+ tab.termStreamBuffer += data;
900
+ if (!tab.termStreamTimer) {
901
+ tab.termStreamTimer = setTimeout(() => {
902
+ if (tab.termStreamBuffer) {
903
+ this.workerWs.sendTerminalData(tab.termStreamBuffer);
904
+ tab.termStreamBuffer = "";
871
905
  }
872
- this.termStreamTimer = null;
906
+ tab.termStreamTimer = null;
873
907
  }, TERM_STREAM_INTERVAL_MS);
874
908
  }
875
909
  }
876
910
  // ─── Shutdown ───────────────────────────────────
877
911
  async shutdown() {
878
912
  this.bridge.endSession();
879
- this.ptyManager?.kill();
880
- this.ptyManager = null;
881
- this.workerWs.disconnect();
882
- if (this.termStreamTimer) {
883
- clearTimeout(this.termStreamTimer);
884
- this.termStreamTimer = null;
913
+ for (const [, tab] of this.tabs) {
914
+ if (tab.termStreamTimer) clearTimeout(tab.termStreamTimer);
915
+ tab.ptyManager.kill();
885
916
  }
917
+ this.tabs.clear();
918
+ this.workerWs.disconnect();
886
919
  }
887
920
  };
888
921
 
@@ -1209,10 +1242,10 @@ async function main() {
1209
1242
  });
1210
1243
  tui.init();
1211
1244
  const orchestrator = new Orchestrator(settingsDir, {
1212
- onPtyData: (data) => {
1245
+ onPtyData: (_tabId, data) => {
1213
1246
  tui.writePtyData(data);
1214
1247
  },
1215
- onPtyExit: (code) => {
1248
+ onPtyExit: (_tabId, code) => {
1216
1249
  tui.destroy();
1217
1250
  console.log(`Agent exited with code ${code}`);
1218
1251
  orchestrator.shutdown().then(() => process.exit(code));
@@ -1233,12 +1266,13 @@ async function main() {
1233
1266
  if (args.profile) orchestrator.switchProfile(args.profile);
1234
1267
  if (args.apiKey) orchestrator.overrideApiKey(args.apiKey);
1235
1268
  if (args.baseUrl) orchestrator.overrideBaseUrl(args.baseUrl);
1269
+ const HEADLESS_TAB = "headless";
1236
1270
  const ptySize = tui.getPtySize();
1237
- await orchestrator.spawnAgent(agent, args.cwd);
1238
- orchestrator.resizePty(ptySize.cols, ptySize.rows);
1271
+ await orchestrator.spawnAgent(HEADLESS_TAB, agent, args.cwd);
1272
+ orchestrator.resizePty(HEADLESS_TAB, ptySize.cols, ptySize.rows);
1239
1273
  if (isCodingAgent(agent)) {
1240
1274
  setTimeout(() => {
1241
- orchestrator.writePty("hello\r");
1275
+ orchestrator.writePty(HEADLESS_TAB, "hello\r");
1242
1276
  }, 1e3);
1243
1277
  }
1244
1278
  const SCROLL_UP_RE = /\x1b\[<64;\d+;\d+M/;
@@ -1255,7 +1289,7 @@ async function main() {
1255
1289
  if (SCROLL_UP_RE.test(str) || SCROLL_DOWN_RE.test(str)) {
1256
1290
  return;
1257
1291
  }
1258
- orchestrator.writePty(str);
1292
+ orchestrator.writePty(HEADLESS_TAB, str);
1259
1293
  });
1260
1294
  }
1261
1295
  process.stdout.on("resize", () => {
@@ -1263,7 +1297,7 @@ async function main() {
1263
1297
  const rows = process.stdout.rows || 24;
1264
1298
  tui.resize(cols, rows);
1265
1299
  const size = tui.getPtySize();
1266
- orchestrator.resizePty(size.cols, size.rows);
1300
+ orchestrator.resizePty(HEADLESS_TAB, size.cols, size.rows);
1267
1301
  });
1268
1302
  const shutdown = async () => {
1269
1303
  if (process.stdin.isTTY) {