@phenx-inc/ctlsurf 0.3.14 → 0.3.15

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 +49 -3
  2. package/out/headless/index.mjs.map +2 -2
  3. package/out/main/index.js +48 -2
  4. package/out/preload/index.js +6 -0
  5. package/out/renderer/assets/{cssMode-G_SDogBL.js → cssMode-D5dPwEy5.js} +3 -3
  6. package/out/renderer/assets/{freemarker2-BzEus0h2.js → freemarker2-c5jJjQ9s.js} +1 -1
  7. package/out/renderer/assets/{handlebars-Et995f6O.js → handlebars-BTbmOxx9.js} +1 -1
  8. package/out/renderer/assets/{html-D4wgKxPD.js → html-3cIIQcxO.js} +1 -1
  9. package/out/renderer/assets/{htmlMode-DSxpefzL.js → htmlMode-DYbpW1yY.js} +3 -3
  10. package/out/renderer/assets/{index-AQ346NMi.css → index-6KvOnYL1.css} +18 -0
  11. package/out/renderer/assets/{index-ByJTqkiQ.js → index-D2MUZin7.js} +36 -23
  12. package/out/renderer/assets/{javascript-CzLoo8aq.js → javascript-CDuCMm-6.js} +2 -2
  13. package/out/renderer/assets/{jsonMode-BrwPy7fY.js → jsonMode-COLqbq0s.js} +3 -3
  14. package/out/renderer/assets/{liquid-BsfPf6YG.js → liquid-BFcqZizB.js} +1 -1
  15. package/out/renderer/assets/{lspLanguageFeatures-CxLZ421s.js → lspLanguageFeatures-CbkEcL-z.js} +1 -1
  16. package/out/renderer/assets/{mdx-CPvHIsAR.js → mdx-DyK93oEE.js} +1 -1
  17. package/out/renderer/assets/{python-Dr7dCUjG.js → python-D4lCwSVr.js} +1 -1
  18. package/out/renderer/assets/{razor-a7zjD7Y3.js → razor-DdkE9XVt.js} +1 -1
  19. package/out/renderer/assets/{tsMode-B7KLV2X6.js → tsMode-BrQ4Fsc-.js} +1 -1
  20. package/out/renderer/assets/{typescript-Cjuzf37q.js → typescript-BakbYMnC.js} +1 -1
  21. package/out/renderer/assets/{xml-Yz9xINtk.js → xml-DHDW9Xhp.js} +1 -1
  22. package/out/renderer/assets/{yaml-DtKnp5J0.js → yaml-1Ayv_J3q.js} +1 -1
  23. package/out/renderer/index.html +2 -2
  24. package/package.json +1 -1
  25. package/src/main/agents.ts +36 -1
  26. package/src/main/headless.ts +5 -3
  27. package/src/main/index.ts +4 -2
  28. package/src/main/orchestrator.ts +29 -0
  29. package/src/preload/index.ts +7 -0
  30. package/src/renderer/App.tsx +19 -1
  31. package/src/renderer/styles.css +18 -0
@@ -5471,7 +5471,7 @@ var require_package = __commonJS({
5471
5471
  "package.json"(exports, module) {
5472
5472
  module.exports = {
5473
5473
  name: "@phenx-inc/ctlsurf",
5474
- version: "0.3.14",
5474
+ version: "0.3.15",
5475
5475
  description: "Agent-agnostic terminal and desktop app for ctlsurf \u2014 run Claude Code, Codex, or any coding agent with live session logging and remote control",
5476
5476
  main: "out/main/index.js",
5477
5477
  bin: {
@@ -5619,10 +5619,27 @@ var PtyManager = class {
5619
5619
  };
5620
5620
 
5621
5621
  // src/main/agents.ts
5622
+ import { accessSync, constants } from "fs";
5623
+ import { join, delimiter } from "path";
5622
5624
  function getShellCommand() {
5623
5625
  if (process.platform === "win32") return "powershell.exe";
5624
5626
  return process.env.SHELL || "/bin/zsh";
5625
5627
  }
5628
+ function isCommandAvailable(command) {
5629
+ const dirs = (process.env.PATH || "").split(delimiter).filter(Boolean);
5630
+ const isWin = process.platform === "win32";
5631
+ const exts = isWin ? (process.env.PATHEXT || ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean) : [""];
5632
+ for (const dir of dirs) {
5633
+ for (const ext of exts) {
5634
+ try {
5635
+ accessSync(join(dir, command + ext), isWin ? constants.F_OK : constants.X_OK);
5636
+ return true;
5637
+ } catch {
5638
+ }
5639
+ }
5640
+ }
5641
+ return false;
5642
+ }
5626
5643
  function getBuiltinAgents() {
5627
5644
  return [
5628
5645
  {
@@ -5649,6 +5666,12 @@ function getBuiltinAgents() {
5649
5666
  }
5650
5667
  ];
5651
5668
  }
5669
+ function getAvailableAgents() {
5670
+ const all = getBuiltinAgents();
5671
+ const coding = all.filter((a) => isCodingAgent(a) && isCommandAvailable(a.command));
5672
+ const shell = all.filter((a) => !isCodingAgent(a));
5673
+ return [...coding, ...shell];
5674
+ }
5652
5675
  function isCodingAgent(agent) {
5653
5676
  return agent.id !== "shell";
5654
5677
  }
@@ -6857,6 +6880,7 @@ var Orchestrator = class {
6857
6880
  };
6858
6881
  noProjectPollTimer = null;
6859
6882
  noProjectPollCwd = null;
6883
+ currentProjectName = null;
6860
6884
  constructor(settingsDir, events) {
6861
6885
  this.settingsDir = settingsDir;
6862
6886
  this.events = events;
@@ -6881,12 +6905,14 @@ var Orchestrator = class {
6881
6905
  log(`[worker-ws] Registered: worker_id=${data.worker_id}, folder_id=${data.folder_id}, status=${data.status}`);
6882
6906
  events.onWorkerRegistered(data);
6883
6907
  if (!data.folder_id) {
6908
+ this.setProjectName(null);
6884
6909
  events.onWorkerStatus("no_project");
6885
6910
  if (this.currentCwd && data.status !== "pending_approval") {
6886
6911
  this.startNoProjectPolling(this.currentCwd);
6887
6912
  }
6888
6913
  } else {
6889
6914
  this.stopNoProjectPolling();
6915
+ this.resolveProjectName(data.folder_id);
6890
6916
  }
6891
6917
  },
6892
6918
  onTerminalInput: (data) => {
@@ -6921,6 +6947,26 @@ var Orchestrator = class {
6921
6947
  get cwd() {
6922
6948
  return this.currentCwd;
6923
6949
  }
6950
+ // Name of the connected ctlsurf project (folder) for the desktop header.
6951
+ get projectName() {
6952
+ return this.currentProjectName;
6953
+ }
6954
+ setProjectName(name) {
6955
+ if (this.currentProjectName === name) return;
6956
+ this.currentProjectName = name;
6957
+ this.events.onProjectChanged?.(name);
6958
+ }
6959
+ // Resolve the connected folder's human-readable name. Best-effort: a failed
6960
+ // lookup just leaves the project name unset rather than blocking anything.
6961
+ async resolveProjectName(folderId) {
6962
+ try {
6963
+ const folder = await this.ctlsurfApi.getFolder(folderId);
6964
+ const name = folder?.name ?? folder?.title;
6965
+ this.setProjectName(typeof name === "string" && name ? name : null);
6966
+ } catch (err) {
6967
+ log(`[worker-ws] Failed to resolve project name for folder ${folderId}: ${err}`);
6968
+ }
6969
+ }
6924
6970
  get agent() {
6925
6971
  return this.currentAgent;
6926
6972
  }
@@ -7711,7 +7757,7 @@ async function main() {
7711
7757
  const settingsDir = getSettingsDir(false);
7712
7758
  await checkVersionAndNotify();
7713
7759
  const tui = new Tui();
7714
- const agents = getBuiltinAgents();
7760
+ const agents = getAvailableAgents();
7715
7761
  const orchestrator = new Orchestrator(settingsDir, {
7716
7762
  onPtyData: (_tabId, data) => {
7717
7763
  tui.writePtyData(data);
@@ -7740,7 +7786,7 @@ async function main() {
7740
7786
  let agent;
7741
7787
  let trackTimeOverride;
7742
7788
  if (args.agent) {
7743
- const found = agents.find((a) => a.id === args.agent);
7789
+ const found = getBuiltinAgents().find((a) => a.id === args.agent);
7744
7790
  agent = found || {
7745
7791
  id: args.agent,
7746
7792
  name: args.agent,