codeam-cli 2.4.7 → 2.4.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.
- package/CHANGELOG.md +6 -0
- package/dist/index.js +92 -41
- package/package.json +1 -1
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.
|
|
182
|
+
version: "2.4.9",
|
|
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,38 @@ async function deploy() {
|
|
|
5737
5741
|
process.exit(1);
|
|
5738
5742
|
}
|
|
5739
5743
|
}
|
|
5744
|
+
const localClaudeDir = path9.join(os6.homedir(), ".claude");
|
|
5745
|
+
const localCredsKind = await detectLocalClaudeCredentials(localClaudeDir);
|
|
5746
|
+
let bridged = "none";
|
|
5747
|
+
if (localCredsKind !== "none") {
|
|
5748
|
+
const sourceLabel = localCredsKind === "flat-file" ? "~/.claude/.credentials.json" : "macOS Keychain";
|
|
5749
|
+
const useLocal = await ot2({
|
|
5750
|
+
message: `Copy your local Claude credentials (${sourceLabel}) to the workspace?`,
|
|
5751
|
+
active: "Yes \u2014 same account, no re-auth",
|
|
5752
|
+
inactive: "No \u2014 log in with a different account",
|
|
5753
|
+
initialValue: true
|
|
5754
|
+
});
|
|
5755
|
+
if (q(useLocal)) {
|
|
5756
|
+
pt("Cancelled.");
|
|
5757
|
+
process.exit(0);
|
|
5758
|
+
}
|
|
5759
|
+
if (useLocal) {
|
|
5760
|
+
const credStep = fe();
|
|
5761
|
+
credStep.start("Bridging Claude credentials\u2026");
|
|
5762
|
+
bridged = await bridgeClaudeCredentials(provider, workspace.id, localClaudeDir);
|
|
5763
|
+
switch (bridged) {
|
|
5764
|
+
case "flat-file":
|
|
5765
|
+
credStep.stop("\u2713 Local credentials staged");
|
|
5766
|
+
break;
|
|
5767
|
+
case "macos-keychain":
|
|
5768
|
+
credStep.stop("\u2713 Credentials extracted from macOS Keychain and staged");
|
|
5769
|
+
break;
|
|
5770
|
+
case "none":
|
|
5771
|
+
credStep.stop("\u26A0 Could not extract local credentials \u2014 falling back to remote login");
|
|
5772
|
+
break;
|
|
5773
|
+
}
|
|
5774
|
+
}
|
|
5775
|
+
}
|
|
5740
5776
|
const claudeStep = fe();
|
|
5741
5777
|
claudeStep.start("Installing Claude CLI on workspace\u2026");
|
|
5742
5778
|
const installResult = await provider.exec(
|
|
@@ -5749,12 +5785,10 @@ async function deploy() {
|
|
|
5749
5785
|
process.exit(1);
|
|
5750
5786
|
}
|
|
5751
5787
|
claudeStep.stop("\u2713 Claude CLI installed");
|
|
5752
|
-
const localClaudeDir = path9.join(os6.homedir(), ".claude");
|
|
5753
5788
|
const haveLocalClaude = fs8.existsSync(localClaudeDir) && fs8.statSync(localClaudeDir).isDirectory();
|
|
5754
5789
|
if (haveLocalClaude) {
|
|
5755
5790
|
const copyStep = fe();
|
|
5756
5791
|
copyStep.start("Copying local Claude config to workspace\u2026");
|
|
5757
|
-
let configUploaded = false;
|
|
5758
5792
|
try {
|
|
5759
5793
|
await provider.uploadDirectory(
|
|
5760
5794
|
workspace.id,
|
|
@@ -5788,52 +5822,36 @@ async function deploy() {
|
|
|
5788
5822
|
// local IDE bridge state
|
|
5789
5823
|
"./todos",
|
|
5790
5824
|
// local todo state
|
|
5791
|
-
"./tasks"
|
|
5825
|
+
"./tasks",
|
|
5792
5826
|
// local task state
|
|
5827
|
+
// Don't overwrite the credentials we already staged in
|
|
5828
|
+
// step 4 — the local dir on macOS doesn't have a flat
|
|
5829
|
+
// credentials file anyway, but on Linux it would, and a
|
|
5830
|
+
// re-write here would be redundant.
|
|
5831
|
+
"./.credentials.json"
|
|
5793
5832
|
]
|
|
5794
5833
|
}
|
|
5795
5834
|
);
|
|
5796
|
-
configUploaded = true;
|
|
5797
5835
|
copyStep.stop("\u2713 Claude config uploaded");
|
|
5798
5836
|
} catch (err) {
|
|
5799
|
-
copyStep.stop("\u26A0 Could not upload Claude config
|
|
5837
|
+
copyStep.stop("\u26A0 Could not upload Claude config (continuing)");
|
|
5800
5838
|
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
5839
|
}
|
|
5840
|
+
}
|
|
5841
|
+
const verifyStep = fe();
|
|
5842
|
+
verifyStep.start("Verifying Claude auth on workspace\u2026");
|
|
5843
|
+
const verified = await verifyClaudeAuth(provider, workspace.id);
|
|
5844
|
+
if (verified) {
|
|
5845
|
+
verifyStep.stop("\u2713 Claude is logged in \u2014 no re-auth needed");
|
|
5820
5846
|
} else {
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
"
|
|
5827
|
-
"
|
|
5828
|
-
|
|
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);
|
|
5847
|
+
verifyStep.stop("\xB7 Claude not yet authenticated \u2014 running login flow");
|
|
5848
|
+
await runRemoteClaudeLogin(provider, workspace.id);
|
|
5849
|
+
const reverified = await verifyClaudeAuth(provider, workspace.id);
|
|
5850
|
+
if (!reverified) {
|
|
5851
|
+
wt(
|
|
5852
|
+
"Claude auth could not be confirmed. You may need to run `claude /login` manually inside the codespace.",
|
|
5853
|
+
"Heads up"
|
|
5854
|
+
);
|
|
5837
5855
|
}
|
|
5838
5856
|
}
|
|
5839
5857
|
const cliStep = fe();
|
|
@@ -5881,6 +5899,39 @@ async function runRemoteClaudeLogin(provider, workspaceId) {
|
|
|
5881
5899
|
);
|
|
5882
5900
|
}
|
|
5883
5901
|
}
|
|
5902
|
+
async function detectLocalClaudeCredentials(localClaudeDir) {
|
|
5903
|
+
if (fs8.existsSync(path9.join(localClaudeDir, ".credentials.json"))) {
|
|
5904
|
+
return "flat-file";
|
|
5905
|
+
}
|
|
5906
|
+
if (process.platform === "darwin") {
|
|
5907
|
+
try {
|
|
5908
|
+
await execFileP3(
|
|
5909
|
+
"security",
|
|
5910
|
+
["find-generic-password", "-s", "Claude Code-credentials"],
|
|
5911
|
+
{ maxBuffer: 1024 * 1024 }
|
|
5912
|
+
);
|
|
5913
|
+
return "macos-keychain";
|
|
5914
|
+
} catch {
|
|
5915
|
+
return "none";
|
|
5916
|
+
}
|
|
5917
|
+
}
|
|
5918
|
+
return "none";
|
|
5919
|
+
}
|
|
5920
|
+
async function verifyClaudeAuth(provider, workspaceId) {
|
|
5921
|
+
const result = await provider.exec(
|
|
5922
|
+
workspaceId,
|
|
5923
|
+
'bash -lc "claude auth status 2>/dev/null || true"'
|
|
5924
|
+
);
|
|
5925
|
+
if (result.code !== 0) return false;
|
|
5926
|
+
const jsonStart = result.stdout.indexOf("{");
|
|
5927
|
+
if (jsonStart < 0) return false;
|
|
5928
|
+
try {
|
|
5929
|
+
const parsed = JSON.parse(result.stdout.slice(jsonStart));
|
|
5930
|
+
return parsed.loggedIn === true;
|
|
5931
|
+
} catch {
|
|
5932
|
+
return false;
|
|
5933
|
+
}
|
|
5934
|
+
}
|
|
5884
5935
|
async function bridgeClaudeCredentials(provider, workspaceId, localClaudeDir) {
|
|
5885
5936
|
const fileBased = path9.join(localClaudeDir, ".credentials.json");
|
|
5886
5937
|
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.
|
|
3
|
+
"version": "2.4.9",
|
|
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": {
|