codeam-cli 1.1.1 → 1.1.2

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 +147 -32
  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.1.1",
113
+ version: "1.1.2",
114
114
  description: "Remote control Claude Code from your mobile device",
115
115
  main: "dist/index.js",
116
116
  bin: {
@@ -735,8 +735,131 @@ function findInPath(name) {
735
735
  var https2 = __toESM(require("https"));
736
736
  var http2 = __toESM(require("http"));
737
737
  var API_BASE4 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
738
- function stripAnsi(raw) {
739
- return raw.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "").replace(/\x1B\][^\x07\x1B]*(?:\x07|\x1B\\)/g, "").replace(/\x1B[@-Z\\-_]/g, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
738
+ function renderToLines(raw) {
739
+ const screen = [""];
740
+ let row = 0;
741
+ let col = 0;
742
+ function ensureRow() {
743
+ while (screen.length <= row) screen.push("");
744
+ }
745
+ function writeChar(ch) {
746
+ ensureRow();
747
+ if (col < screen[row].length) {
748
+ screen[row] = screen[row].slice(0, col) + ch + screen[row].slice(col + 1);
749
+ } else {
750
+ while (screen[row].length < col) screen[row] += " ";
751
+ screen[row] += ch;
752
+ }
753
+ col++;
754
+ }
755
+ let i = 0;
756
+ while (i < raw.length) {
757
+ const ch = raw[i];
758
+ if (ch === "\x1B") {
759
+ i++;
760
+ if (i >= raw.length) break;
761
+ if (raw[i] === "[") {
762
+ i++;
763
+ let param = "";
764
+ while (i < raw.length && !/[@-~]/.test(raw[i])) param += raw[i++];
765
+ const cmd = raw[i] ?? "";
766
+ const n = parseInt(param) || 1;
767
+ if (cmd === "A") {
768
+ row = Math.max(0, row - n);
769
+ } else if (cmd === "B") {
770
+ row += n;
771
+ ensureRow();
772
+ } else if (cmd === "C") {
773
+ col += n;
774
+ } else if (cmd === "D") {
775
+ col = Math.max(0, col - n);
776
+ } else if (cmd === "G") {
777
+ col = Math.max(0, n - 1);
778
+ } else if (cmd === "H" || cmd === "f") {
779
+ const p2 = param.split(";");
780
+ row = Math.max(0, (parseInt(p2[0] ?? "1") || 1) - 1);
781
+ col = Math.max(0, (parseInt(p2[1] ?? "1") || 1) - 1);
782
+ ensureRow();
783
+ } else if (cmd === "J") {
784
+ if (param === "2" || param === "3") {
785
+ screen.length = 1;
786
+ screen[0] = "";
787
+ row = 0;
788
+ col = 0;
789
+ } else if (param === "1") {
790
+ for (let r = 0; r < row; r++) screen[r] = "";
791
+ screen[row] = " ".repeat(col) + screen[row].slice(col);
792
+ } else {
793
+ screen[row] = screen[row].slice(0, col);
794
+ screen.splice(row + 1);
795
+ }
796
+ } else if (cmd === "K") {
797
+ ensureRow();
798
+ if (param === "" || param === "0") screen[row] = screen[row].slice(0, col);
799
+ else if (param === "1") screen[row] = " ".repeat(col) + screen[row].slice(col);
800
+ else if (param === "2") screen[row] = "";
801
+ } else if (cmd === "h" && (param === "?1049" || param === "?47")) {
802
+ screen.length = 1;
803
+ screen[0] = "";
804
+ row = 0;
805
+ col = 0;
806
+ } else if (cmd === "l" && (param === "?1049" || param === "?47")) {
807
+ screen.length = 1;
808
+ screen[0] = "";
809
+ row = 0;
810
+ col = 0;
811
+ }
812
+ } else if (raw[i] === "]") {
813
+ i++;
814
+ while (i < raw.length) {
815
+ if (raw[i] === "\x07") break;
816
+ if (raw[i] === "\x1B" && i + 1 < raw.length && raw[i + 1] === "\\") {
817
+ i++;
818
+ break;
819
+ }
820
+ i++;
821
+ }
822
+ }
823
+ } else if (ch === "\r") {
824
+ if (i + 1 < raw.length && raw[i + 1] === "\n") {
825
+ row++;
826
+ col = 0;
827
+ ensureRow();
828
+ i++;
829
+ } else {
830
+ col = 0;
831
+ }
832
+ } else if (ch === "\n") {
833
+ row++;
834
+ col = 0;
835
+ ensureRow();
836
+ } else if (ch >= " " || ch === " ") {
837
+ writeChar(ch);
838
+ }
839
+ i++;
840
+ }
841
+ return screen;
842
+ }
843
+ function filterChrome(lines) {
844
+ return lines.filter((line) => {
845
+ const t = line.trim();
846
+ if (!t) return false;
847
+ if (/^[─━—═\-─]{3,}$/.test(t)) return false;
848
+ if (/^[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]\s/.test(t)) return false;
849
+ if (/esc.{0,5}to.{0,5}interrupt/i.test(t)) return false;
850
+ if (/high\s*[·•]\s*\/effort/i.test(t)) return false;
851
+ if (/^[❯>]\s*$/.test(t)) return false;
852
+ if (/^[❯>]\s+\S/.test(t)) return false;
853
+ if (/^\(thinking\)\s*$/.test(t)) return false;
854
+ if (/^\?\s.*shortcut/i.test(t)) return false;
855
+ if (/spending limit|usage limit/i.test(t) && t.length < 80) return false;
856
+ return true;
857
+ });
858
+ }
859
+ function extractContent(raw) {
860
+ const lines = renderToLines(raw);
861
+ const filtered = filterChrome(lines);
862
+ return filtered.join("\n").replace(/\n{3,}/g, "\n\n").trim();
740
863
  }
741
864
  var OutputService = class _OutputService {
742
865
  constructor(sessionId, pluginId) {
@@ -745,28 +868,25 @@ var OutputService = class _OutputService {
745
868
  }
746
869
  sessionId;
747
870
  pluginId;
748
- buffer = "";
749
- lastSentBuffer = "";
871
+ /** Raw PTY bytes — processed only when we need to send content. */
872
+ rawBuffer = "";
873
+ lastSentContent = "";
750
874
  pollTimer = null;
751
875
  startTime = 0;
752
876
  active = false;
753
- /** Time since last meaningful push() when this exceeds IDLE_MS, response is done. */
877
+ /** When the last chunk of printable raw content arrived. */
754
878
  lastPushTime = 0;
755
- static POLL_MS = 500;
756
- /** No new content for 3 s = Claude finished responding. */
879
+ static POLL_MS = 1e3;
880
+ /** No new printable content for 3 s Claude finished responding. */
757
881
  static IDLE_MS = 3e3;
758
- /** No content at all for 30 s = give up (connection issue, etc.). */
882
+ /** No content at all for 30 s give up. */
759
883
  static EMPTY_TIMEOUT_MS = 3e4;
760
- /** Hard cap — always clear typing state after 2 minutes. */
884
+ /** Hard cap — clear typing state after 2 min no matter what. */
761
885
  static MAX_MS = 12e4;
762
- /**
763
- * Call before sending a command from mobile.
764
- * Clears previous output, sends new_turn event, starts polling.
765
- */
766
886
  newTurn() {
767
887
  this.stopPoll();
768
- this.buffer = "";
769
- this.lastSentBuffer = "";
888
+ this.rawBuffer = "";
889
+ this.lastSentContent = "";
770
890
  this.lastPushTime = 0;
771
891
  this.active = true;
772
892
  this.startTime = Date.now();
@@ -774,14 +894,11 @@ var OutputService = class _OutputService {
774
894
  });
775
895
  this.pollTimer = setInterval(() => this.tick(), _OutputService.POLL_MS);
776
896
  }
777
- /** Feed raw terminal output from Claude (called on every stdout chunk). */
778
897
  push(raw) {
779
898
  if (!this.active) return;
780
- const text = stripAnsi(raw).replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n");
781
- if (text.trim()) {
782
- this.buffer += text;
783
- this.lastPushTime = Date.now();
784
- }
899
+ this.rawBuffer += raw;
900
+ const printable = raw.replace(/\x1B\[[^@-~]*[@-~]/g, "").replace(/[\x00-\x1F\x7F]/g, "");
901
+ if (printable.trim()) this.lastPushTime = Date.now();
785
902
  }
786
903
  dispose() {
787
904
  this.stopPoll();
@@ -795,11 +912,9 @@ var OutputService = class _OutputService {
795
912
  this.finalize();
796
913
  return;
797
914
  }
798
- const hasContent = this.buffer.trim().length > 0;
799
- if (!hasContent) {
800
- if (elapsed >= _OutputService.EMPTY_TIMEOUT_MS) {
801
- this.finalize();
802
- }
915
+ const content = extractContent(this.rawBuffer);
916
+ if (!content) {
917
+ if (elapsed >= _OutputService.EMPTY_TIMEOUT_MS) this.finalize();
803
918
  return;
804
919
  }
805
920
  const idleMs = this.lastPushTime > 0 ? now - this.lastPushTime : elapsed;
@@ -807,17 +922,17 @@ var OutputService = class _OutputService {
807
922
  this.finalize();
808
923
  return;
809
924
  }
810
- if (this.buffer !== this.lastSentBuffer) {
811
- this.lastSentBuffer = this.buffer;
812
- this.postChunk({ type: "text", content: this.buffer, done: false }).catch(() => {
925
+ if (content !== this.lastSentContent) {
926
+ this.lastSentContent = content;
927
+ this.postChunk({ type: "text", content, done: false }).catch(() => {
813
928
  });
814
929
  }
815
930
  }
816
931
  finalize() {
817
- const text = this.buffer;
932
+ const content = extractContent(this.rawBuffer);
818
933
  this.stopPoll();
819
934
  this.active = false;
820
- this.postChunk({ type: "text", content: text, done: true }).catch(() => {
935
+ this.postChunk({ type: "text", content, done: true }).catch(() => {
821
936
  });
822
937
  }
823
938
  stopPoll() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Remote control Claude Code from your mobile device",
5
5
  "main": "dist/index.js",
6
6
  "bin": {