codeam-cli 2.4.7 → 2.4.8

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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,12 @@ All notable changes to `codeam-cli` are documented here.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [2.4.7] — 2026-05-03
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** Cross-platform Claude credential bridge for codeam deploy (v2.4.7)
12
+
7
13
  ## [2.4.5] — 2026-05-03
8
14
 
9
15
  ### Fixed
package/dist/index.js CHANGED
@@ -179,7 +179,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
179
179
  // package.json
180
180
  var package_default = {
181
181
  name: "codeam-cli",
182
- version: "2.4.7",
182
+ version: "2.4.8",
183
183
  description: "Remote control Claude Code (and other AI coding agents) from your mobile phone. Pair your device, send prompts, stream responses in real-time, and approve commands \u2014 from anywhere.",
184
184
  main: "dist/index.js",
185
185
  bin: {
@@ -5478,8 +5478,11 @@ var GitHubCodespacesProvider = class {
5478
5478
  const tarArgs = ["-czf", "-", "-C", localDir];
5479
5479
  for (const pattern of options.exclude ?? []) {
5480
5480
  tarArgs.push(`--exclude=${pattern}`);
5481
+ const stripped = pattern.replace(/^\.\/+/, "");
5482
+ if (stripped !== pattern) tarArgs.push(`--exclude=${stripped}`);
5481
5483
  }
5482
5484
  tarArgs.push(".");
5485
+ const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
5483
5486
  const sshArgs = [
5484
5487
  "codespace",
5485
5488
  "ssh",
@@ -5490,7 +5493,8 @@ var GitHubCodespacesProvider = class {
5490
5493
  ];
5491
5494
  await new Promise((resolve2, reject) => {
5492
5495
  const tar = (0, import_child_process5.spawn)("tar", tarArgs, {
5493
- stdio: ["ignore", "pipe", "pipe"]
5496
+ stdio: ["ignore", "pipe", "pipe"],
5497
+ env: tarEnv
5494
5498
  });
5495
5499
  const ssh = (0, import_child_process5.spawn)("gh", sshArgs, {
5496
5500
  stdio: [tar.stdout, "pipe", "pipe"]
@@ -5737,6 +5741,21 @@ async function deploy() {
5737
5741
  process.exit(1);
5738
5742
  }
5739
5743
  }
5744
+ const localClaudeDir = path9.join(os6.homedir(), ".claude");
5745
+ const credStep = fe();
5746
+ credStep.start("Bridging Claude credentials\u2026");
5747
+ const bridged = await bridgeClaudeCredentials(provider, workspace.id, localClaudeDir);
5748
+ switch (bridged) {
5749
+ case "flat-file":
5750
+ credStep.stop("\u2713 Local credentials staged");
5751
+ break;
5752
+ case "macos-keychain":
5753
+ credStep.stop("\u2713 Credentials extracted from macOS Keychain and staged");
5754
+ break;
5755
+ case "none":
5756
+ credStep.stop("\xB7 No local credentials found \u2014 will run interactive login after install");
5757
+ break;
5758
+ }
5740
5759
  const claudeStep = fe();
5741
5760
  claudeStep.start("Installing Claude CLI on workspace\u2026");
5742
5761
  const installResult = await provider.exec(
@@ -5749,12 +5768,10 @@ async function deploy() {
5749
5768
  process.exit(1);
5750
5769
  }
5751
5770
  claudeStep.stop("\u2713 Claude CLI installed");
5752
- const localClaudeDir = path9.join(os6.homedir(), ".claude");
5753
5771
  const haveLocalClaude = fs8.existsSync(localClaudeDir) && fs8.statSync(localClaudeDir).isDirectory();
5754
5772
  if (haveLocalClaude) {
5755
5773
  const copyStep = fe();
5756
5774
  copyStep.start("Copying local Claude config to workspace\u2026");
5757
- let configUploaded = false;
5758
5775
  try {
5759
5776
  await provider.uploadDirectory(
5760
5777
  workspace.id,
@@ -5788,52 +5805,36 @@ async function deploy() {
5788
5805
  // local IDE bridge state
5789
5806
  "./todos",
5790
5807
  // local todo state
5791
- "./tasks"
5808
+ "./tasks",
5792
5809
  // local task state
5810
+ // Don't overwrite the credentials we already staged in
5811
+ // step 4 — the local dir on macOS doesn't have a flat
5812
+ // credentials file anyway, but on Linux it would, and a
5813
+ // re-write here would be redundant.
5814
+ "./.credentials.json"
5793
5815
  ]
5794
5816
  }
5795
5817
  );
5796
- configUploaded = true;
5797
5818
  copyStep.stop("\u2713 Claude config uploaded");
5798
5819
  } catch (err) {
5799
- copyStep.stop("\u26A0 Could not upload Claude config \u2014 falling back to remote login");
5820
+ copyStep.stop("\u26A0 Could not upload Claude config (continuing)");
5800
5821
  void err;
5801
- await runRemoteClaudeLogin(provider, workspace.id);
5802
- }
5803
- if (configUploaded) {
5804
- const credStep = fe();
5805
- credStep.start("Bridging Claude credentials\u2026");
5806
- const bridged = await bridgeClaudeCredentials(provider, workspace.id, localClaudeDir);
5807
- switch (bridged) {
5808
- case "flat-file":
5809
- credStep.stop("\u2713 Credentials in tar \u2014 no re-auth needed");
5810
- break;
5811
- case "macos-keychain":
5812
- credStep.stop("\u2713 Credentials extracted from macOS Keychain \u2014 no re-auth needed");
5813
- break;
5814
- case "none":
5815
- credStep.stop("\u26A0 No transferable Claude credentials found \u2014 falling back to remote login");
5816
- await runRemoteClaudeLogin(provider, workspace.id);
5817
- break;
5818
- }
5819
5822
  }
5823
+ }
5824
+ const verifyStep = fe();
5825
+ verifyStep.start("Verifying Claude auth on workspace\u2026");
5826
+ const verified = await verifyClaudeAuth(provider, workspace.id);
5827
+ if (verified) {
5828
+ verifyStep.stop("\u2713 Claude is logged in \u2014 no re-auth needed");
5820
5829
  } else {
5821
- wt(
5822
- [
5823
- "No local ~/.claude config found.",
5824
- "We can run `claude login` inside the workspace right now \u2014 the URL",
5825
- "will print here, you open it in your browser, paste the code back,",
5826
- "and the workspace gets authenticated. (Skip if you'd rather do it",
5827
- "manually later from inside the codespace.)"
5828
- ].join("\n"),
5829
- "Claude credentials"
5830
- );
5831
- const proceed = await ot2({
5832
- message: "Run `claude login` on the workspace now?",
5833
- initialValue: true
5834
- });
5835
- if (!q(proceed) && proceed) {
5836
- await runRemoteClaudeLogin(provider, workspace.id);
5830
+ verifyStep.stop("\xB7 Claude not yet authenticated \u2014 running login flow");
5831
+ await runRemoteClaudeLogin(provider, workspace.id);
5832
+ const reverified = await verifyClaudeAuth(provider, workspace.id);
5833
+ if (!reverified) {
5834
+ wt(
5835
+ "Claude auth could not be confirmed. You may need to run `claude /login` manually inside the codespace.",
5836
+ "Heads up"
5837
+ );
5837
5838
  }
5838
5839
  }
5839
5840
  const cliStep = fe();
@@ -5881,6 +5882,21 @@ async function runRemoteClaudeLogin(provider, workspaceId) {
5881
5882
  );
5882
5883
  }
5883
5884
  }
5885
+ async function verifyClaudeAuth(provider, workspaceId) {
5886
+ const result = await provider.exec(
5887
+ workspaceId,
5888
+ 'bash -lc "claude auth status 2>/dev/null || true"'
5889
+ );
5890
+ if (result.code !== 0) return false;
5891
+ const jsonStart = result.stdout.indexOf("{");
5892
+ if (jsonStart < 0) return false;
5893
+ try {
5894
+ const parsed = JSON.parse(result.stdout.slice(jsonStart));
5895
+ return parsed.loggedIn === true;
5896
+ } catch {
5897
+ return false;
5898
+ }
5899
+ }
5884
5900
  async function bridgeClaudeCredentials(provider, workspaceId, localClaudeDir) {
5885
5901
  const fileBased = path9.join(localClaudeDir, ".credentials.json");
5886
5902
  if (fs8.existsSync(fileBased)) return "flat-file";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.4.7",
3
+ "version": "2.4.8",
4
4
  "description": "Remote control Claude Code (and other AI coding agents) from your mobile phone. Pair your device, send prompts, stream responses in real-time, and approve commands — from anywhere.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {