@termhub/agent 0.1.5 → 0.1.7

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 (2) hide show
  1. package/dist/cli.js +65 -38
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -490,14 +490,26 @@ function buildPasteScript(name) {
490
490
  }
491
491
 
492
492
  // ../../packages/machine-ops/dist/ai-credentials.js
493
+ var CREDENTIAL_SEPARATOR = "---termhub-credential---";
493
494
  function credentialScript(provider) {
494
495
  switch (provider) {
495
496
  case "claude":
496
497
  return [
497
- `if [ -f "$D/.credentials.json" ]; then cat "$D/.credentials.json"`,
498
- `elif [ "$(uname -s)" = Darwin ]; then security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null || true`,
498
+ `SEP=${shellQuote(CREDENTIAL_SEPARATOR)}`,
499
+ // 1. the on-disk copy (Linux, and macOS when the keychain entry is missing/disabled)
500
+ `if [ -f "$D/.credentials.json" ]; then cat "$D/.credentials.json" 2>/dev/null || true; fi`,
501
+ `printf '\\n%s\\n' "$SEP"`,
502
+ `if [ "$(uname -s 2>/dev/null)" = Darwin ]; then`,
503
+ ` D=\${D%/}`,
504
+ // 2a. the per-config-dir keychain item (CLAUDE_CONFIG_DIR accounts)
505
+ ` H=$(printf %s "$D" | shasum -a 256 2>/dev/null | cut -c1-8) || true`,
506
+ ` if [ -n "$H" ]; then security find-generic-password -s "Claude Code-credentials-$H" -w 2>/dev/null || true; fi`,
507
+ ` printf '\\n%s\\n' "$SEP"`,
508
+ // 2b. the unsuffixed item, only for the default dir (it belongs to a different account otherwise)
509
+ ` if [ "$D" = "$HOME/.claude" ]; then security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null || true; fi`,
510
+ ` printf '\\n%s\\n' "$SEP"`,
499
511
  `fi`
500
- ].join("; ");
512
+ ].join("\n");
501
513
  case "chatgpt":
502
514
  return `if [ -f "$D/auth.json" ]; then cat "$D/auth.json"; fi`;
503
515
  case "gemini":
@@ -750,16 +762,49 @@ function createDispatcher(deps) {
750
762
  }
751
763
 
752
764
  // src/pty.ts
765
+ import fs3 from "fs";
766
+ import path3 from "path";
767
+
768
+ // src/pty-health.ts
753
769
  import fs2 from "fs";
754
770
  import path2 from "path";
771
+ import { createRequire } from "module";
772
+ function findSpawnHelper(resolveFrom = import.meta.url) {
773
+ let pkgJson;
774
+ try {
775
+ pkgJson = createRequire(resolveFrom).resolve("node-pty/package.json");
776
+ } catch {
777
+ return null;
778
+ }
779
+ const root = path2.dirname(pkgJson);
780
+ const candidates = [
781
+ path2.join(root, "prebuilds", `${process.platform}-${process.arch}`, "spawn-helper"),
782
+ path2.join(root, "build", "Release", "spawn-helper")
783
+ ];
784
+ return candidates.find((p) => fs2.existsSync(p)) ?? null;
785
+ }
786
+ var EXEC_BITS = 73;
787
+ function ensureSpawnHelperExecutable(helper = findSpawnHelper()) {
788
+ if (!helper) return { path: null, executable: true, repaired: false };
789
+ try {
790
+ const mode = fs2.statSync(helper).mode;
791
+ if (mode & EXEC_BITS) return { path: helper, executable: true, repaired: false };
792
+ fs2.chmodSync(helper, (mode | 493) & 4095);
793
+ return { path: helper, executable: true, repaired: true };
794
+ } catch (err) {
795
+ return { path: helper, executable: false, repaired: false, error: err instanceof Error ? err.message : String(err) };
796
+ }
797
+ }
798
+
799
+ // src/pty.ts
755
800
  function expandHome2(rawCwd, home) {
756
801
  if (rawCwd === "~") return home;
757
- if (rawCwd.startsWith("~/")) return path2.join(home, rawCwd.slice(2));
802
+ if (rawCwd.startsWith("~/")) return path3.join(home, rawCwd.slice(2));
758
803
  return rawCwd;
759
804
  }
760
805
  function existsDir(p) {
761
806
  try {
762
- return fs2.statSync(p).isDirectory();
807
+ return fs3.statSync(p).isDirectory();
763
808
  } catch {
764
809
  return false;
765
810
  }
@@ -769,6 +814,9 @@ function resolveCwd(rawCwd) {
769
814
  const expanded = expandHome2(rawCwd, home);
770
815
  return existsDir(expanded) ? expanded : home;
771
816
  }
817
+ function isSpawnHelperFailure(err) {
818
+ return /posix_spawnp failed/.test(err instanceof Error ? err.message : String(err));
819
+ }
772
820
  function isEnoent(err) {
773
821
  const e = err;
774
822
  if (e?.code === "ENOENT") return true;
@@ -778,6 +826,7 @@ function isEnoent(err) {
778
826
  function createPtyManager(deps) {
779
827
  const procs = /* @__PURE__ */ new Map();
780
828
  const tmux = deps.tmuxPath ?? tmuxPath();
829
+ const repairSpawnHelper = deps.repairSpawnHelper ?? (() => ensureSpawnHelperExecutable());
781
830
  let spawnFn = deps.spawn;
782
831
  async function resolveSpawn() {
783
832
  if (!spawnFn) {
@@ -804,7 +853,16 @@ function createPtyManager(deps) {
804
853
  TERMHUB_SESSION: params.session
805
854
  };
806
855
  const spawn = await resolveSpawn();
807
- proc = spawn(tmux, ["-u", "new-session", "-A", "-s", params.session, "-c", cwd], { name: "xterm-256color", cols, rows, cwd, env });
856
+ const spawnTmux = () => spawn(tmux, ["-u", "new-session", "-A", "-s", params.session, "-c", cwd], { name: "xterm-256color", cols, rows, cwd, env });
857
+ try {
858
+ proc = spawnTmux();
859
+ } catch (err) {
860
+ if (!isSpawnHelperFailure(err)) throw err;
861
+ const helper = repairSpawnHelper();
862
+ if (!helper.repaired) throw err;
863
+ deps.log("spawn-helper exec bit repaired on open", { path: helper.path });
864
+ proc = spawnTmux();
865
+ }
808
866
  } catch (err) {
809
867
  deps.log("pty open failed", { ch, session: params.session, tmux, cwd: params.cwd, error: err instanceof Error ? err.message : String(err) });
810
868
  socket.sendControl({
@@ -888,37 +946,6 @@ function createPtyManager(deps) {
888
946
  };
889
947
  }
890
948
 
891
- // src/pty-health.ts
892
- import fs3 from "fs";
893
- import path3 from "path";
894
- import { createRequire } from "module";
895
- function findSpawnHelper(resolveFrom = import.meta.url) {
896
- let pkgJson;
897
- try {
898
- pkgJson = createRequire(resolveFrom).resolve("node-pty/package.json");
899
- } catch {
900
- return null;
901
- }
902
- const root = path3.dirname(pkgJson);
903
- const candidates = [
904
- path3.join(root, "prebuilds", `${process.platform}-${process.arch}`, "spawn-helper"),
905
- path3.join(root, "build", "Release", "spawn-helper")
906
- ];
907
- return candidates.find((p) => fs3.existsSync(p)) ?? null;
908
- }
909
- var EXEC_BITS = 73;
910
- function ensureSpawnHelperExecutable(helper = findSpawnHelper()) {
911
- if (!helper) return { path: null, executable: true, repaired: false };
912
- try {
913
- const mode = fs3.statSync(helper).mode;
914
- if (mode & EXEC_BITS) return { path: helper, executable: true, repaired: false };
915
- fs3.chmodSync(helper, (mode | 493) & 4095);
916
- return { path: helper, executable: true, repaired: true };
917
- } catch (err) {
918
- return { path: helper, executable: false, repaired: false, error: err instanceof Error ? err.message : String(err) };
919
- }
920
- }
921
-
922
949
  // src/rpc/ai.ts
923
950
  async function credential(params) {
924
951
  let prefix;
@@ -1227,7 +1254,7 @@ async function status(deps = {}) {
1227
1254
  }
1228
1255
 
1229
1256
  // src/version.ts
1230
- var AGENT_VERSION = "0.1.5";
1257
+ var AGENT_VERSION = "0.1.7";
1231
1258
 
1232
1259
  // src/run.ts
1233
1260
  function detectOs(platform = process.platform) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@termhub/agent",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Agente do termhub: conecta esta máquina ao servidor por WebSocket de saída (sem SSH) e expõe os terminais tmux no navegador.",
5
5
  "license": "MIT",
6
6
  "private": false,