codeam-cli 1.4.37 → 1.4.39

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 +66 -47
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -116,7 +116,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
116
116
  // package.json
117
117
  var package_default = {
118
118
  name: "codeam-cli",
119
- version: "1.4.37",
119
+ version: "1.4.39",
120
120
  description: "Remote control Claude Code from your mobile device",
121
121
  main: "dist/index.js",
122
122
  bin: {
@@ -815,9 +815,22 @@ var ClaudeService = class {
815
815
  const claudeCmd = findInPath("claude") ? "claude" : "claude-code";
816
816
  this.strategy.spawn(claudeCmd, this.opts.cwd);
817
817
  }
818
- /** Send a command to Claude's stdin (remote control from mobile). */
818
+ /**
819
+ * Send a command to Claude's stdin (remote control from mobile).
820
+ *
821
+ * Why two separate writes with a delay?
822
+ * Same batching problem as selectOption: all bytes arriving in one write()
823
+ * call are processed by readline in one synchronous run. React Ink batches
824
+ * the resulting state updates, so when '\r' fires the input's value is still
825
+ * the pre-batch (empty/previous) state → Enter submits nothing and the text
826
+ * stays visible-but-unsubmitted in the input field.
827
+ *
828
+ * Sending '\r' in a separate write() 50 ms later guarantees it arrives on
829
+ * a fresh event-loop tick, after React has flushed the text into input state.
830
+ */
819
831
  sendCommand(text) {
820
- this.strategy.write(text + "\r");
832
+ this.strategy.write(text);
833
+ setTimeout(() => this.strategy.write("\r"), 50);
821
834
  }
822
835
  /**
823
836
  * Navigate a React Ink selector to the given 0-based target index and confirm.
@@ -1116,7 +1129,11 @@ function filterChrome(lines) {
1116
1129
  if (/^\?\s.*shortcut/i.test(t)) continue;
1117
1130
  if (/spending limit|usage limit/i.test(t) && t.length < 80) continue;
1118
1131
  if (/↑\s*\/?\s*↓\s*to\s*navigate/i.test(t)) continue;
1119
- if (/^[❯>]\s+\S/.test(t) && !/^[❯>]\s*\d+\./.test(t)) {
1132
+ if (t.replace(/\s/g, "").length === 1) continue;
1133
+ if ((t.match(/─/g)?.length ?? 0) >= 6) continue;
1134
+ if (/ctrl\+?o\s+to\s+expand/i.test(t)) continue;
1135
+ const stripped = t.replace(/^[│╭╰╮╯┌└┐┘├┤┬┴┼]\s?/, "");
1136
+ if (/^[❯>]\s+\S/.test(stripped) && !/^[❯>]\s*\d+\./.test(stripped)) {
1120
1137
  skipEchoContinuation = true;
1121
1138
  continue;
1122
1139
  }
@@ -1148,7 +1165,15 @@ var OutputService = class _OutputService {
1148
1165
  static IDLE_MS = 3e3;
1149
1166
  /** Shorter idle threshold for selector detection (UI is ready immediately). */
1150
1167
  static SELECTOR_IDLE_MS = 1500;
1151
- static EMPTY_TIMEOUT_MS = 3e4;
1168
+ /**
1169
+ * Grace period before the first tick processes output.
1170
+ * Prevents the raw PTY input echo from being captured before Claude Code
1171
+ * clears and re-renders its TUI (which happens within ~100-200 ms of
1172
+ * receiving the input, but we give a 1.5 s margin for loaded machines).
1173
+ */
1174
+ static WARMUP_MS = 1500;
1175
+ /** Max idle with no visible content (spinner only) before finalizing. */
1176
+ static EMPTY_TIMEOUT_MS = 6e4;
1152
1177
  static MAX_MS = 12e4;
1153
1178
  newTurn() {
1154
1179
  this.stopPoll();
@@ -1222,6 +1247,7 @@ var OutputService = class _OutputService {
1222
1247
  this.finalize();
1223
1248
  return;
1224
1249
  }
1250
+ if (elapsed < _OutputService.WARMUP_MS) return;
1225
1251
  const lines = renderToLines(this.rawBuffer);
1226
1252
  const selector = detectSelector(lines) ?? detectListSelector(lines);
1227
1253
  if (selector) {
@@ -1559,61 +1585,54 @@ var HistoryService = class {
1559
1585
  return { used: inputTokens, total, percent, model: lastModel, outputTokens, cacheReadTokens: lastUsage["cache_read_input_tokens"] ?? 0 };
1560
1586
  }
1561
1587
  /**
1562
- * Estimate the total API cost for the current month across all projects.
1563
- * Scans all JSONL files modified this month under ~/.claude/projects/.
1588
+ * Estimate the API cost for the current month in the current project directory.
1589
+ * Scans only the JSONL files for this project (cwd), so the value reflects
1590
+ * usage from the active Claude Code session rather than the entire machine.
1564
1591
  */
1565
1592
  getMonthlyEstimatedCost() {
1566
- const claudeDir = path4.join(os4.homedir(), ".claude", "projects");
1567
- let projectDirs;
1568
- try {
1569
- projectDirs = fs4.readdirSync(claudeDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path4.join(claudeDir, e.name));
1570
- } catch {
1571
- return 0;
1572
- }
1593
+ const projectDir = this.projectDir;
1573
1594
  const now = /* @__PURE__ */ new Date();
1574
1595
  const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
1575
1596
  const monthStartIso = monthStart.toISOString();
1576
1597
  const monthStartMs = monthStart.getTime();
1577
1598
  let totalCost = 0;
1578
- for (const projectDir of projectDirs) {
1579
- let files;
1599
+ let files;
1600
+ try {
1601
+ files = fs4.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
1602
+ try {
1603
+ return fs4.statSync(path4.join(projectDir, f)).mtimeMs >= monthStartMs;
1604
+ } catch {
1605
+ return false;
1606
+ }
1607
+ });
1608
+ } catch {
1609
+ return 0;
1610
+ }
1611
+ for (const file of files) {
1612
+ let raw;
1580
1613
  try {
1581
- files = fs4.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
1582
- try {
1583
- return fs4.statSync(path4.join(projectDir, f)).mtimeMs >= monthStartMs;
1584
- } catch {
1585
- return false;
1586
- }
1587
- });
1614
+ raw = fs4.readFileSync(path4.join(projectDir, file), "utf8");
1588
1615
  } catch {
1589
1616
  continue;
1590
1617
  }
1591
- for (const file of files) {
1592
- let raw;
1618
+ for (const line of raw.split("\n").filter(Boolean)) {
1593
1619
  try {
1594
- raw = fs4.readFileSync(path4.join(projectDir, file), "utf8");
1620
+ const record = JSON.parse(line);
1621
+ if (record["type"] !== "assistant") continue;
1622
+ const timestamp = record["timestamp"];
1623
+ if (timestamp && timestamp < monthStartIso) continue;
1624
+ const msg = record["message"];
1625
+ if (!msg || msg["model"] === "<synthetic>") continue;
1626
+ const model = msg["model"] || "";
1627
+ const usage = msg["usage"];
1628
+ if (!usage) continue;
1629
+ const pricing = getPricing(model);
1630
+ const input = usage["input_tokens"] ?? 0;
1631
+ const output = usage["output_tokens"] ?? 0;
1632
+ const cacheRead = usage["cache_read_input_tokens"] ?? 0;
1633
+ const cacheWrite = usage["cache_creation_input_tokens"] ?? 0;
1634
+ totalCost += input / 1e6 * pricing.input + output / 1e6 * pricing.output + cacheRead / 1e6 * pricing.cacheRead + cacheWrite / 1e6 * pricing.cacheWrite;
1595
1635
  } catch {
1596
- continue;
1597
- }
1598
- for (const line of raw.split("\n").filter(Boolean)) {
1599
- try {
1600
- const record = JSON.parse(line);
1601
- if (record["type"] !== "assistant") continue;
1602
- const timestamp = record["timestamp"];
1603
- if (timestamp && timestamp < monthStartIso) continue;
1604
- const msg = record["message"];
1605
- if (!msg || msg["model"] === "<synthetic>") continue;
1606
- const model = msg["model"] || "";
1607
- const usage = msg["usage"];
1608
- if (!usage) continue;
1609
- const pricing = getPricing(model);
1610
- const input = usage["input_tokens"] ?? 0;
1611
- const output = usage["output_tokens"] ?? 0;
1612
- const cacheRead = usage["cache_read_input_tokens"] ?? 0;
1613
- const cacheWrite = usage["cache_creation_input_tokens"] ?? 0;
1614
- totalCost += input / 1e6 * pricing.input + output / 1e6 * pricing.output + cacheRead / 1e6 * pricing.cacheRead + cacheWrite / 1e6 * pricing.cacheWrite;
1615
- } catch {
1616
- }
1617
1636
  }
1618
1637
  }
1619
1638
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "1.4.37",
3
+ "version": "1.4.39",
4
4
  "description": "Remote control Claude Code from your mobile device",
5
5
  "main": "dist/index.js",
6
6
  "bin": {