codeam-cli 1.0.8 → 1.0.9

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/index.js +84 -21
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -110,7 +110,7 @@ var import_picocolors = __toESM(require("picocolors"));
110
110
  // package.json
111
111
  var package_default = {
112
112
  name: "codeam-cli",
113
- version: "1.0.8",
113
+ version: "1.0.9",
114
114
  description: "Remote control Claude Code from your mobile device",
115
115
  main: "dist/index.js",
116
116
  bin: {
@@ -510,13 +510,70 @@ var CommandRelayService = class {
510
510
  // src/services/claude.service.ts
511
511
  var import_child_process = require("child_process");
512
512
  var fs2 = __toESM(require("fs"));
513
+ var os3 = __toESM(require("os"));
513
514
  var path2 = __toESM(require("path"));
515
+ var PYTHON_PTY_HELPER = `import os,pty,sys,select,signal,struct,fcntl,termios,errno
516
+ m,s=pty.openpty()
517
+ try:
518
+ cols=int(os.environ.get('COLUMNS','220'))
519
+ rows=int(os.environ.get('LINES','50'))
520
+ fcntl.ioctl(s,termios.TIOCSWINSZ,struct.pack('HHHH',rows,cols,0,0))
521
+ except Exception:pass
522
+ pid=os.fork()
523
+ if pid==0:
524
+ os.close(m)
525
+ os.setsid()
526
+ try:fcntl.ioctl(s,termios.TIOCSCTTY,0)
527
+ except Exception:pass
528
+ for fd in[0,1,2]:os.dup2(s,fd)
529
+ if s>2:os.close(s)
530
+ os.execvp(sys.argv[1],sys.argv[1:])
531
+ sys.exit(127)
532
+ os.close(s)
533
+ done=[False]
534
+ def onchld(n,f):
535
+ try:os.waitpid(pid,os.WNOHANG)
536
+ except Exception:pass
537
+ done[0]=True
538
+ def onwinch(n,f):
539
+ try:
540
+ sz=os.get_terminal_size(2)
541
+ fcntl.ioctl(m,termios.TIOCSWINSZ,struct.pack('HHHH',sz.lines,sz.columns,0,0))
542
+ except Exception:pass
543
+ signal.signal(signal.SIGCHLD,onchld)
544
+ signal.signal(signal.SIGWINCH,onwinch)
545
+ i=sys.stdin.fileno()
546
+ o=sys.stdout.fileno()
547
+ while not done[0]:
548
+ try:r,_,_=select.select([i,m],[],[],0.1)
549
+ except OSError as e:
550
+ if e.errno==errno.EINTR:continue
551
+ break
552
+ if i in r:
553
+ try:
554
+ d=os.read(i,4096)
555
+ if d:os.write(m,d)
556
+ else:break
557
+ except OSError:break
558
+ if m in r:
559
+ try:
560
+ d=os.read(m,4096)
561
+ if d:os.write(o,d)
562
+ except OSError:done[0]=True
563
+ try:os.kill(pid,signal.SIGTERM)
564
+ except Exception:pass
565
+ try:
566
+ _,st=os.waitpid(pid,0)
567
+ sys.exit((st>>8)&0xFF)
568
+ except Exception:sys.exit(0)
569
+ `;
514
570
  var ClaudeService = class {
515
571
  constructor(opts) {
516
572
  this.opts = opts;
517
573
  }
518
574
  opts;
519
575
  proc = null;
576
+ helperPath = null;
520
577
  spawn() {
521
578
  if (this.proc) {
522
579
  this.cleanup();
@@ -537,20 +594,24 @@ var ClaudeService = class {
537
594
  }
538
595
  }
539
596
  /**
540
- * macOS / Linux: wrap Claude in `script` to give it a real PTY.
541
- *
542
- * Why: Claude Code (React Ink) checks stdin.isTTY. When stdin is a pipe it
543
- * shows a "no stdin data in 3s" warning and then stops reading stdin entirely.
544
- * `script` creates a PTY for the child so Claude sees stdin as a terminal,
545
- * while the parent (`codeam`) still controls script's stdin/stdout as pipes —
546
- * enabling mobile command injection and output capture.
597
+ * macOS / Linux: use a Python PTY helper to give Claude a real TTY.
598
+ * Falls back to direct spawn if python3 is not available.
547
599
  */
548
600
  spawnWithPty(claudeCmd) {
601
+ const python = findInPath("python3") ?? findInPath("python");
602
+ if (!python) {
603
+ console.error(
604
+ " \xB7 python3 not found; mobile command injection may be limited.\n"
605
+ );
606
+ this.spawnDirect(claudeCmd);
607
+ return;
608
+ }
549
609
  const shell = process.env.SHELL || "/bin/sh";
550
610
  const cols = process.stdout.columns || 220;
551
611
  const rows = process.stdout.rows || 50;
552
- const scriptArgs = buildScriptArgs(shell, claudeCmd);
553
- this.proc = (0, import_child_process.spawn)("script", scriptArgs, {
612
+ this.helperPath = path2.join(os3.tmpdir(), "codeam-pty-helper.py");
613
+ fs2.writeFileSync(this.helperPath, PYTHON_PTY_HELPER, { mode: 420 });
614
+ this.proc = (0, import_child_process.spawn)(python, [this.helperPath, shell, "-c", `exec ${claudeCmd}`], {
554
615
  stdio: ["pipe", "pipe", "inherit"],
555
616
  cwd: this.opts.cwd,
556
617
  env: {
@@ -578,14 +639,14 @@ var ClaudeService = class {
578
639
  process.stdin.on("data", this.stdinHandler);
579
640
  process.on("SIGWINCH", this.handleResize);
580
641
  this.proc.on("exit", (code) => {
642
+ this.removeTempFile();
581
643
  this.cleanup();
582
644
  this.opts.onExit(code ?? 0);
583
645
  });
584
646
  }
585
647
  /**
586
- * Windows fallback: direct spawn without PTY.
587
- * Mobile command injection is limited (Claude may ignore pipe stdin after 3 s),
588
- * but the terminal session still works for manual use.
648
+ * Windows (or Python-unavailable) fallback: direct spawn without PTY.
649
+ * Mobile command injection is limited on Windows.
589
650
  */
590
651
  spawnDirect(claudeCmd) {
591
652
  this.proc = (0, import_child_process.spawn)(claudeCmd, [], {
@@ -623,6 +684,7 @@ var ClaudeService = class {
623
684
  }
624
685
  kill() {
625
686
  this.proc?.kill();
687
+ this.removeTempFile();
626
688
  this.cleanup();
627
689
  }
628
690
  stdinHandler = (chunk) => {
@@ -646,15 +708,16 @@ var ClaudeService = class {
646
708
  }
647
709
  }
648
710
  }
649
- };
650
- function buildScriptArgs(shell, claudeCmd) {
651
- const innerCmd = `exec ${claudeCmd}`;
652
- if (process.platform === "darwin") {
653
- return ["-q", "/dev/null", shell, "-c", innerCmd];
711
+ removeTempFile() {
712
+ if (this.helperPath) {
713
+ try {
714
+ fs2.unlinkSync(this.helperPath);
715
+ } catch {
716
+ }
717
+ this.helperPath = null;
718
+ }
654
719
  }
655
- const shellCmd = `${shell} -c ${JSON.stringify(innerCmd)}`;
656
- return ["-q", "-e", "-c", shellCmd, "/dev/null"];
657
- }
720
+ };
658
721
  function findInPath(name) {
659
722
  const dirs = (process.env.PATH ?? "").split(path2.delimiter);
660
723
  for (const dir of dirs) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "Remote control Claude Code from your mobile device",
5
5
  "main": "dist/index.js",
6
6
  "bin": {