codeam-cli 2.4.1 → 2.4.3
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 +12 -0
- package/README.md +22 -0
- package/dist/index.js +267 -29
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ 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.1] — 2026-05-03
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **cli:** Interactive `claude login` fallback when no local config (v2.4.1)
|
|
12
|
+
|
|
13
|
+
## [2.4.0] — 2026-05-03
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **cli:** `codeam deploy` — provision a paired cloud workspace in one command (v2.4.0)
|
|
18
|
+
|
|
7
19
|
## [2.2.2] — 2026-05-02
|
|
8
20
|
|
|
9
21
|
### Fixed
|
package/README.md
CHANGED
|
@@ -46,6 +46,28 @@ That's it. Open the [CodeAgent Mobile app](https://codeagent-mobile.com), enter
|
|
|
46
46
|
| `codeam sessions` | List all paired devices |
|
|
47
47
|
| `codeam status` | Show connection status |
|
|
48
48
|
| `codeam logout` | Remove all paired sessions |
|
|
49
|
+
| `codeam deploy` | Provision a cloud workspace (GitHub Codespaces) and pair it to your phone |
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## `codeam deploy` — drive a cloud workspace from your phone
|
|
54
|
+
|
|
55
|
+
Don't want to keep your laptop running while you control Claude from the train? `codeam deploy` spins up a fresh **GitHub Codespace** for any of your repos, installs Claude Code + `codeam-cli` inside it, copies your local Claude credentials so you skip the re-auth (or runs `claude login` interactively if you don't have a local config yet), and finishes by streaming `codeam pair` from inside the codespace — so you get a pairing code on this terminal already wired to the remote workspace.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
codeam deploy
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
That's it. You'll be guided through:
|
|
62
|
+
|
|
63
|
+
1. **Pick a provider** (GitHub Codespaces today; more coming).
|
|
64
|
+
2. **Pick a repo** from your account.
|
|
65
|
+
3. **Wait ~1 minute** while the codespace boots and tools install.
|
|
66
|
+
4. **Scan the QR / enter the code** on the CodeAgent Mobile app — same flow as `codeam pair`, only the agent is now running in the cloud.
|
|
67
|
+
|
|
68
|
+
Requirements: the [GitHub CLI (`gh`)](https://cli.github.com/) installed and authenticated (`gh auth login`). The deploy flow re-uses `gh`'s OAuth — we don't ask for a separate token.
|
|
69
|
+
|
|
70
|
+
Adding more cloud backends (Gitpod, Coder, your own SSH host, …) is a single new file in `apps/cli/src/services/providers/` — the `CloudProvider` interface keeps it pluggable.
|
|
49
71
|
|
|
50
72
|
---
|
|
51
73
|
|
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.3",
|
|
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: {
|
|
@@ -5080,11 +5080,12 @@ async function logout() {
|
|
|
5080
5080
|
var fs8 = __toESM(require("fs"));
|
|
5081
5081
|
var os6 = __toESM(require("os"));
|
|
5082
5082
|
var path8 = __toESM(require("path"));
|
|
5083
|
-
var
|
|
5083
|
+
var import_picocolors8 = __toESM(require("picocolors"));
|
|
5084
5084
|
|
|
5085
5085
|
// src/services/providers/github-codespaces.ts
|
|
5086
5086
|
var import_child_process5 = require("child_process");
|
|
5087
5087
|
var import_util2 = require("util");
|
|
5088
|
+
var import_picocolors7 = __toESM(require("picocolors"));
|
|
5088
5089
|
var execFileP2 = (0, import_util2.promisify)(import_child_process5.execFile);
|
|
5089
5090
|
var MAX_BUFFER = 8 * 1024 * 1024;
|
|
5090
5091
|
var GitHubCodespacesProvider = class {
|
|
@@ -5096,32 +5097,175 @@ var GitHubCodespacesProvider = class {
|
|
|
5096
5097
|
try {
|
|
5097
5098
|
await execFileP2("gh", ["--version"], { maxBuffer: MAX_BUFFER });
|
|
5098
5099
|
} catch {
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5100
|
+
await this.tryInstallGh();
|
|
5101
|
+
try {
|
|
5102
|
+
await execFileP2("gh", ["--version"], { maxBuffer: MAX_BUFFER });
|
|
5103
|
+
} catch {
|
|
5104
|
+
throw new Error(
|
|
5105
|
+
[
|
|
5106
|
+
"GitHub CLI (`gh`) is still not on PATH.",
|
|
5107
|
+
"Install it manually with:",
|
|
5108
|
+
" \u2022 macOS: brew install gh",
|
|
5109
|
+
" \u2022 Linux: https://github.com/cli/cli/blob/trunk/docs/install_linux.md",
|
|
5110
|
+
" \u2022 Windows: winget install --id GitHub.cli",
|
|
5111
|
+
"Then run `codeam deploy` again."
|
|
5112
|
+
].join("\n")
|
|
5113
|
+
);
|
|
5114
|
+
}
|
|
5109
5115
|
}
|
|
5116
|
+
let isAuthed = false;
|
|
5110
5117
|
try {
|
|
5111
5118
|
await execFileP2("gh", ["auth", "status"], { maxBuffer: MAX_BUFFER });
|
|
5112
|
-
|
|
5119
|
+
isAuthed = true;
|
|
5113
5120
|
} catch {
|
|
5114
5121
|
}
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5122
|
+
if (!isAuthed) {
|
|
5123
|
+
await new Promise((resolve2, reject) => {
|
|
5124
|
+
const proc = (0, import_child_process5.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
|
|
5125
|
+
stdio: "inherit"
|
|
5126
|
+
});
|
|
5127
|
+
proc.on("exit", (code) => {
|
|
5128
|
+
if (code === 0) resolve2();
|
|
5129
|
+
else reject(new Error("gh auth login failed."));
|
|
5130
|
+
});
|
|
5131
|
+
proc.on("error", reject);
|
|
5118
5132
|
});
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5133
|
+
return;
|
|
5134
|
+
}
|
|
5135
|
+
const hasScope = await this.hasCodespaceScope();
|
|
5136
|
+
if (!hasScope) {
|
|
5137
|
+
wt(
|
|
5138
|
+
[
|
|
5139
|
+
"Your existing GitHub login is missing the `codespace` scope.",
|
|
5140
|
+
"I'll run `gh auth refresh` to add it \u2014 your browser will open",
|
|
5141
|
+
"for a one-tap approval."
|
|
5142
|
+
].join("\n"),
|
|
5143
|
+
"One more permission needed"
|
|
5144
|
+
);
|
|
5145
|
+
await new Promise((resolve2, reject) => {
|
|
5146
|
+
const proc = (0, import_child_process5.spawn)(
|
|
5147
|
+
"gh",
|
|
5148
|
+
["auth", "refresh", "-h", "github.com", "-s", "codespace"],
|
|
5149
|
+
{ stdio: "inherit" }
|
|
5150
|
+
);
|
|
5151
|
+
proc.on("exit", (code) => {
|
|
5152
|
+
if (code === 0) resolve2();
|
|
5153
|
+
else reject(new Error("gh auth refresh failed \u2014 re-run `gh auth refresh -h github.com -s codespace` manually."));
|
|
5154
|
+
});
|
|
5155
|
+
proc.on("error", reject);
|
|
5122
5156
|
});
|
|
5123
|
-
|
|
5157
|
+
}
|
|
5158
|
+
}
|
|
5159
|
+
/**
|
|
5160
|
+
* Check whether the current `gh` token includes the `codespace`
|
|
5161
|
+
* OAuth scope. We hit `/user` with `-i` so GitHub echoes the granted
|
|
5162
|
+
* scopes back in the `X-OAuth-Scopes` response header — the most
|
|
5163
|
+
* authoritative source (more reliable than scraping `gh auth status`,
|
|
5164
|
+
* whose format has shifted across `gh` versions).
|
|
5165
|
+
*/
|
|
5166
|
+
async hasCodespaceScope() {
|
|
5167
|
+
try {
|
|
5168
|
+
const { stdout } = await execFileP2(
|
|
5169
|
+
"gh",
|
|
5170
|
+
["api", "-i", "user"],
|
|
5171
|
+
{ maxBuffer: MAX_BUFFER }
|
|
5172
|
+
);
|
|
5173
|
+
const m = stdout.match(/^x-oauth-scopes:\s*(.+)$/im);
|
|
5174
|
+
if (!m) return false;
|
|
5175
|
+
const scopes = m[1].split(",").map((s) => s.trim().toLowerCase());
|
|
5176
|
+
return scopes.includes("codespace");
|
|
5177
|
+
} catch {
|
|
5178
|
+
return false;
|
|
5179
|
+
}
|
|
5180
|
+
}
|
|
5181
|
+
/**
|
|
5182
|
+
* Try to install the `gh` CLI for the user. Opt-in via a confirm
|
|
5183
|
+
* prompt — we never run `brew` / `winget` / `apt` without explicit
|
|
5184
|
+
* consent. Strategy per platform:
|
|
5185
|
+
*
|
|
5186
|
+
* - macOS: `brew install gh` (requires Homebrew)
|
|
5187
|
+
* - Windows: `winget install --id GitHub.cli -e --silent`
|
|
5188
|
+
* - Linux: too many distros / package managers to be safe; we
|
|
5189
|
+
* point the user at the official install doc instead.
|
|
5190
|
+
*
|
|
5191
|
+
* Stdio is inherited so any sudo / authentication prompt the package
|
|
5192
|
+
* manager surfaces (e.g. macOS keychain, Windows UAC) lands in this
|
|
5193
|
+
* terminal. On failure or an unsupported platform we just return —
|
|
5194
|
+
* the caller will re-check `gh --version` and surface the manual-
|
|
5195
|
+
* install error if it's still missing.
|
|
5196
|
+
*/
|
|
5197
|
+
async tryInstallGh() {
|
|
5198
|
+
const platform = process.platform;
|
|
5199
|
+
wt(
|
|
5200
|
+
`GitHub CLI (${import_picocolors7.default.cyan("gh")}) is required for Codespaces deploys but isn't on your PATH.`,
|
|
5201
|
+
"Heads up"
|
|
5202
|
+
);
|
|
5203
|
+
if (platform === "linux") {
|
|
5204
|
+
wt(
|
|
5205
|
+
[
|
|
5206
|
+
"On Linux, please install gh from the official guide:",
|
|
5207
|
+
" https://github.com/cli/cli/blob/trunk/docs/install_linux.md",
|
|
5208
|
+
"Re-run `codeam deploy` once it is on your PATH."
|
|
5209
|
+
].join("\n"),
|
|
5210
|
+
"Install gh on Linux"
|
|
5211
|
+
);
|
|
5212
|
+
return;
|
|
5213
|
+
}
|
|
5214
|
+
let installCmd = null;
|
|
5215
|
+
if (platform === "darwin") {
|
|
5216
|
+
try {
|
|
5217
|
+
await execFileP2("brew", ["--version"], { maxBuffer: MAX_BUFFER });
|
|
5218
|
+
} catch {
|
|
5219
|
+
wt(
|
|
5220
|
+
[
|
|
5221
|
+
"Homebrew (`brew`) is not installed.",
|
|
5222
|
+
"Install it from https://brew.sh and re-run `codeam deploy`,",
|
|
5223
|
+
"or install gh manually: https://cli.github.com/"
|
|
5224
|
+
].join("\n"),
|
|
5225
|
+
"Cannot auto-install on macOS"
|
|
5226
|
+
);
|
|
5227
|
+
return;
|
|
5228
|
+
}
|
|
5229
|
+
installCmd = {
|
|
5230
|
+
exe: "brew",
|
|
5231
|
+
args: ["install", "gh"],
|
|
5232
|
+
describe: "brew install gh"
|
|
5233
|
+
};
|
|
5234
|
+
} else if (platform === "win32") {
|
|
5235
|
+
try {
|
|
5236
|
+
await execFileP2("winget", ["--version"], { maxBuffer: MAX_BUFFER });
|
|
5237
|
+
} catch {
|
|
5238
|
+
wt(
|
|
5239
|
+
[
|
|
5240
|
+
"winget is not available on this machine.",
|
|
5241
|
+
"Install gh manually: https://github.com/cli/cli/releases/latest"
|
|
5242
|
+
].join("\n"),
|
|
5243
|
+
"Cannot auto-install on Windows"
|
|
5244
|
+
);
|
|
5245
|
+
return;
|
|
5246
|
+
}
|
|
5247
|
+
installCmd = {
|
|
5248
|
+
exe: "winget",
|
|
5249
|
+
args: ["install", "--id", "GitHub.cli", "-e", "--silent"],
|
|
5250
|
+
describe: "winget install --id GitHub.cli"
|
|
5251
|
+
};
|
|
5252
|
+
} else {
|
|
5253
|
+
return;
|
|
5254
|
+
}
|
|
5255
|
+
const proceed = await ot2({
|
|
5256
|
+
message: `Run ${import_picocolors7.default.cyan(installCmd.describe)} now?`,
|
|
5257
|
+
initialValue: true
|
|
5258
|
+
});
|
|
5259
|
+
if (q(proceed) || !proceed) return;
|
|
5260
|
+
const installStep = fe();
|
|
5261
|
+
installStep.start(`Installing gh via ${installCmd.describe}\u2026`);
|
|
5262
|
+
const ok = await new Promise((resolve2) => {
|
|
5263
|
+
const proc = (0, import_child_process5.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
|
|
5264
|
+
proc.on("exit", (code) => resolve2(code === 0));
|
|
5265
|
+
proc.on("error", () => resolve2(false));
|
|
5124
5266
|
});
|
|
5267
|
+
if (ok) installStep.stop("\u2713 gh installed");
|
|
5268
|
+
else installStep.stop("\u2717 gh install failed");
|
|
5125
5269
|
}
|
|
5126
5270
|
async listProjects() {
|
|
5127
5271
|
const { stdout } = await execFileP2(
|
|
@@ -5146,10 +5290,51 @@ var GitHubCodespacesProvider = class {
|
|
|
5146
5290
|
private: !!r.isPrivate
|
|
5147
5291
|
}));
|
|
5148
5292
|
}
|
|
5149
|
-
|
|
5293
|
+
/**
|
|
5294
|
+
* Return the machine types available to the user for this repo. The
|
|
5295
|
+
* `gh api /repos/.../codespaces/machines` endpoint reports CPU / RAM /
|
|
5296
|
+
* storage, so we hand all three to the picker for a clean label.
|
|
5297
|
+
*
|
|
5298
|
+
* We filter out anything below 8 GB RAM — Claude Code wants headroom
|
|
5299
|
+
* for `tsc`, build tools, and parallel test runners; the 4 GB tier
|
|
5300
|
+
* (when available) is too tight in practice.
|
|
5301
|
+
*/
|
|
5302
|
+
async listMachineTypes(projectId) {
|
|
5303
|
+
try {
|
|
5304
|
+
const { stdout } = await execFileP2(
|
|
5305
|
+
"gh",
|
|
5306
|
+
["api", `/repos/${projectId}/codespaces/machines`],
|
|
5307
|
+
{ maxBuffer: MAX_BUFFER }
|
|
5308
|
+
);
|
|
5309
|
+
const data = JSON.parse(stdout);
|
|
5310
|
+
const machines = data.machines ?? [];
|
|
5311
|
+
const GB = 1024 ** 3;
|
|
5312
|
+
return machines.map((m) => {
|
|
5313
|
+
const memoryGb = m.memory_in_bytes ? Math.round(m.memory_in_bytes / GB) : 0;
|
|
5314
|
+
const storageGb = m.storage_in_bytes ? Math.round(m.storage_in_bytes / GB) : void 0;
|
|
5315
|
+
const parts = [];
|
|
5316
|
+
if (m.cpus) parts.push(`${m.cpus} ${m.cpus === 1 ? "core" : "cores"}`);
|
|
5317
|
+
if (memoryGb) parts.push(`${memoryGb} GB RAM`);
|
|
5318
|
+
if (storageGb) parts.push(`${storageGb} GB storage`);
|
|
5319
|
+
return {
|
|
5320
|
+
id: m.name,
|
|
5321
|
+
label: m.display_name ?? (parts.join(" \xB7 ") || m.name),
|
|
5322
|
+
memoryGb,
|
|
5323
|
+
cpus: m.cpus,
|
|
5324
|
+
storageGb
|
|
5325
|
+
};
|
|
5326
|
+
}).filter((m) => m.memoryGb >= 8).sort((a, b) => a.memoryGb - b.memoryGb || (a.cpus ?? 0) - (b.cpus ?? 0));
|
|
5327
|
+
} catch {
|
|
5328
|
+
return [];
|
|
5329
|
+
}
|
|
5330
|
+
}
|
|
5331
|
+
async createWorkspace(projectId, machineTypeId) {
|
|
5332
|
+
const machine = machineTypeId ?? await this.pickDefaultMachine(projectId);
|
|
5333
|
+
const args2 = ["codespace", "create", "-R", projectId, "--default-permissions"];
|
|
5334
|
+
if (machine) args2.push("-m", machine);
|
|
5150
5335
|
const { stdout } = await execFileP2(
|
|
5151
5336
|
"gh",
|
|
5152
|
-
|
|
5337
|
+
args2,
|
|
5153
5338
|
{ maxBuffer: MAX_BUFFER, timeout: 12e4 }
|
|
5154
5339
|
);
|
|
5155
5340
|
const name = stdout.trim().split("\n").filter(Boolean).pop() ?? "";
|
|
@@ -5163,6 +5348,27 @@ var GitHubCodespacesProvider = class {
|
|
|
5163
5348
|
webUrl: `https://github.com/codespaces/${name}`
|
|
5164
5349
|
};
|
|
5165
5350
|
}
|
|
5351
|
+
/**
|
|
5352
|
+
* Fallback machine picker for when the orchestrator didn't ask the
|
|
5353
|
+
* user — defaults to the cheapest 8 GB tier (`basicLinux32gb`) and
|
|
5354
|
+
* walks up only if the repo restricts that tier. Returns `null` if
|
|
5355
|
+
* the API call fails entirely; the caller will then omit `-m` and
|
|
5356
|
+
* let `gh` use the repo/org default.
|
|
5357
|
+
*/
|
|
5358
|
+
async pickDefaultMachine(projectId) {
|
|
5359
|
+
const machines = await this.listMachineTypes(projectId);
|
|
5360
|
+
if (machines.length === 0) return null;
|
|
5361
|
+
const preferenceOrder = [
|
|
5362
|
+
"basicLinux32gb",
|
|
5363
|
+
"standardLinux32gb",
|
|
5364
|
+
"premiumLinux",
|
|
5365
|
+
"largePremiumLinux"
|
|
5366
|
+
];
|
|
5367
|
+
for (const pref of preferenceOrder) {
|
|
5368
|
+
if (machines.some((m) => m.id === pref)) return pref;
|
|
5369
|
+
}
|
|
5370
|
+
return machines[0].id;
|
|
5371
|
+
}
|
|
5166
5372
|
async waitUntilAvailable(name) {
|
|
5167
5373
|
const deadline = Date.now() + 5 * 60 * 1e3;
|
|
5168
5374
|
while (Date.now() < deadline) {
|
|
@@ -5231,7 +5437,7 @@ var PROVIDERS = [
|
|
|
5231
5437
|
// src/commands/deploy.ts
|
|
5232
5438
|
async function deploy() {
|
|
5233
5439
|
console.log();
|
|
5234
|
-
mt(
|
|
5440
|
+
mt(import_picocolors8.default.bgMagenta(import_picocolors8.default.white(" codeam deploy ")));
|
|
5235
5441
|
const provider = await pickProvider();
|
|
5236
5442
|
if (!provider) {
|
|
5237
5443
|
pt("No provider selected.");
|
|
@@ -5275,11 +5481,43 @@ async function deploy() {
|
|
|
5275
5481
|
process.exit(0);
|
|
5276
5482
|
}
|
|
5277
5483
|
const project = projects.find((proj) => proj.id === projectId);
|
|
5484
|
+
let machineTypeId;
|
|
5485
|
+
if (provider.listMachineTypes) {
|
|
5486
|
+
const machineStep = fe();
|
|
5487
|
+
machineStep.start("Loading machine types\u2026");
|
|
5488
|
+
let machines = [];
|
|
5489
|
+
try {
|
|
5490
|
+
machines = await provider.listMachineTypes(project.id);
|
|
5491
|
+
machineStep.stop(
|
|
5492
|
+
machines.length > 0 ? `\u2713 ${machines.length} machine type${machines.length === 1 ? "" : "s"} available` : "\xB7 No machine types reported (using provider default)"
|
|
5493
|
+
);
|
|
5494
|
+
} catch {
|
|
5495
|
+
machineStep.stop("\xB7 Could not list machine types \u2014 using provider default");
|
|
5496
|
+
}
|
|
5497
|
+
if (machines.length > 1) {
|
|
5498
|
+
const picked = await _t({
|
|
5499
|
+
message: "Pick a machine size (starts at 8 GB RAM):",
|
|
5500
|
+
initialValue: machines[0].id,
|
|
5501
|
+
options: machines.map((m) => ({
|
|
5502
|
+
value: m.id,
|
|
5503
|
+
label: m.label,
|
|
5504
|
+
hint: `${m.memoryGb} GB RAM`
|
|
5505
|
+
}))
|
|
5506
|
+
});
|
|
5507
|
+
if (q(picked)) {
|
|
5508
|
+
pt("Cancelled.");
|
|
5509
|
+
process.exit(0);
|
|
5510
|
+
}
|
|
5511
|
+
machineTypeId = picked;
|
|
5512
|
+
} else if (machines.length === 1) {
|
|
5513
|
+
machineTypeId = machines[0].id;
|
|
5514
|
+
}
|
|
5515
|
+
}
|
|
5278
5516
|
const createStep = fe();
|
|
5279
5517
|
createStep.start(`Creating workspace for ${project.fullName}\u2026`);
|
|
5280
5518
|
let workspace;
|
|
5281
5519
|
try {
|
|
5282
|
-
workspace = await provider.createWorkspace(project.id);
|
|
5520
|
+
workspace = await provider.createWorkspace(project.id, machineTypeId);
|
|
5283
5521
|
createStep.stop(`\u2713 Workspace ready: ${workspace.displayName ?? workspace.id}`);
|
|
5284
5522
|
} catch (err) {
|
|
5285
5523
|
createStep.stop(`\u2717 Workspace creation failed`);
|
|
@@ -5341,8 +5579,8 @@ async function deploy() {
|
|
|
5341
5579
|
cliStep.stop("\u2713 codeam-cli installed");
|
|
5342
5580
|
wt(
|
|
5343
5581
|
[
|
|
5344
|
-
`Workspace: ${
|
|
5345
|
-
workspace.webUrl ? `Web: ${
|
|
5582
|
+
`Workspace: ${import_picocolors8.default.cyan(workspace.displayName ?? workspace.id)}`,
|
|
5583
|
+
workspace.webUrl ? `Web: ${import_picocolors8.default.cyan(workspace.webUrl)}` : "",
|
|
5346
5584
|
"",
|
|
5347
5585
|
"Starting `codeam pair` on the workspace.",
|
|
5348
5586
|
"Scan the QR code below with the CodeAgent Mobile app to finish pairing."
|
|
@@ -5351,9 +5589,9 @@ async function deploy() {
|
|
|
5351
5589
|
);
|
|
5352
5590
|
const code = (await provider.streamCommand(workspace.id, "codeam pair")).code;
|
|
5353
5591
|
if (code === 0) {
|
|
5354
|
-
gt(
|
|
5592
|
+
gt(import_picocolors8.default.green(`\u2713 Workspace deployed and paired. Drive from your phone, anywhere.`));
|
|
5355
5593
|
} else {
|
|
5356
|
-
gt(
|
|
5594
|
+
gt(import_picocolors8.default.yellow(`Pairing exited with code ${code}. Run "codeam pair" inside the codespace if needed.`));
|
|
5357
5595
|
}
|
|
5358
5596
|
}
|
|
5359
5597
|
async function runRemoteClaudeLogin(provider, workspaceId) {
|
|
@@ -5382,7 +5620,7 @@ async function pickProvider() {
|
|
|
5382
5620
|
message: "Where do you want to deploy?",
|
|
5383
5621
|
options: PROVIDERS.map((prov) => ({
|
|
5384
5622
|
value: prov.id,
|
|
5385
|
-
label: prov.available ? prov.displayName : `${prov.displayName} ${
|
|
5623
|
+
label: prov.available ? prov.displayName : `${prov.displayName} ${import_picocolors8.default.dim("(coming soon)")}`,
|
|
5386
5624
|
hint: prov.tagline
|
|
5387
5625
|
}))
|
|
5388
5626
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.3",
|
|
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": {
|