codeam-cli 2.61.32 → 2.61.33

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.61.32] — 2026-07-24
8
+
9
+ ### Added
10
+
11
+ - **shared:** Add GitLab as a version_control integration
12
+
7
13
  ## [2.61.31] — 2026-07-24
8
14
 
9
15
  ### Added
package/dist/index.js CHANGED
@@ -6355,7 +6355,7 @@ function readAnonId() {
6355
6355
  }
6356
6356
  function superProperties() {
6357
6357
  return {
6358
- cliVersion: true ? "2.61.32" : "0.0.0-dev",
6358
+ cliVersion: true ? "2.61.33" : "0.0.0-dev",
6359
6359
  nodeVersion: process.version,
6360
6360
  platform: process.platform,
6361
6361
  arch: process.arch,
@@ -6536,7 +6536,7 @@ var os4 = __toESM(require("os"));
6536
6536
  // package.json
6537
6537
  var package_default = {
6538
6538
  name: "codeam-cli",
6539
- version: "2.61.32",
6539
+ version: "2.61.33",
6540
6540
  description: "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device \u2014 async. The terminal companion for CodeAgent Mobile.",
6541
6541
  type: "commonjs",
6542
6542
  main: "dist/index.js",
@@ -7707,7 +7707,7 @@ var CommandRelayService = class _CommandRelayService {
7707
7707
  // fresh + clear the "CLI update available" banner after a self-update
7708
7708
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
7709
7709
  // pair/reconnect). Older backends ignore the extra field.
7710
- ..."2.61.32" ? { ideVersion: "2.61.32" } : {}
7710
+ ..."2.61.33" ? { ideVersion: "2.61.33" } : {}
7711
7711
  }).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
7712
7712
  }
7713
7713
  /**
@@ -17324,8 +17324,24 @@ function githubOwnerRepo(repoRef) {
17324
17324
  if (httpsMatch) return { owner: httpsMatch[1], repo: httpsMatch[2] };
17325
17325
  return null;
17326
17326
  }
17327
- function repoCloneUrl(repoRef, cloneToken) {
17327
+ function gitlabProjectPath(repoRef) {
17328
17328
  const trimmed = repoRef.trim();
17329
+ const https9 = /^https?:\/\/gitlab\.com\/(.+?)(?:\.git)?\/?$/.exec(trimmed);
17330
+ if (https9) return https9[1];
17331
+ if (!/^https?:\/\//.test(trimmed) && !trimmed.startsWith("git@") && trimmed.includes("/")) {
17332
+ return trimmed.replace(/\.git$/, "");
17333
+ }
17334
+ return null;
17335
+ }
17336
+ function repoCloneUrl(repoRef, cloneToken, provider = "github") {
17337
+ const trimmed = repoRef.trim();
17338
+ if (provider === "gitlab") {
17339
+ const glPath = gitlabProjectPath(trimmed);
17340
+ if (glPath) {
17341
+ return cloneToken ? `https://oauth2:${cloneToken}@gitlab.com/${glPath}.git` : `https://gitlab.com/${glPath}.git`;
17342
+ }
17343
+ if (/^https?:\/\//.test(trimmed) || trimmed.startsWith("git@")) return trimmed;
17344
+ }
17329
17345
  if (cloneToken) {
17330
17346
  const gh = githubOwnerRepo(trimmed);
17331
17347
  if (gh) {
@@ -17354,12 +17370,15 @@ async function fetchGithubIdentity(token) {
17354
17370
  return null;
17355
17371
  }
17356
17372
  }
17357
- async function configureGitCredentials(dest, repoRef, cloneToken) {
17358
- const gh = githubOwnerRepo(repoRef.trim());
17359
- if (!gh || !cloneToken) return;
17373
+ async function configureGitCredentials(dest, repoRef, cloneToken, provider = "github") {
17374
+ const isGitlab = provider === "gitlab";
17375
+ if (isGitlab ? !gitlabProjectPath(repoRef.trim()) : !githubOwnerRepo(repoRef.trim())) return;
17376
+ if (!cloneToken) return;
17360
17377
  const credFile = path40.join(dest, ".git", "codeam-credentials");
17361
- fs36.writeFileSync(credFile, `https://x-access-token:${cloneToken}@github.com
17362
- `, { mode: 384 });
17378
+ const credLine = isGitlab ? `https://oauth2:${cloneToken}@gitlab.com
17379
+ ` : `https://x-access-token:${cloneToken}@github.com
17380
+ `;
17381
+ fs36.writeFileSync(credFile, credLine, { mode: 384 });
17363
17382
  restrictToOwner(credFile);
17364
17383
  const env = nonInteractiveGitEnv();
17365
17384
  const git2 = (args2) => execFileP4("git", ["-C", dest, ...args2], { timeout: 3e4, env });
@@ -17374,20 +17393,17 @@ async function configureGitCredentials(dest, repoRef, cloneToken) {
17374
17393
  `store --file=${credFilePosix}`
17375
17394
  ]).catch(() => {
17376
17395
  });
17377
- await git2(["remote", "set-url", "origin", repoCloneUrl(repoRef)]).catch(() => {
17396
+ await git2(["remote", "set-url", "origin", repoCloneUrl(repoRef, void 0, provider)]).catch(() => {
17378
17397
  });
17379
17398
  const hasIdentity = await git2(["config", "user.email"]).then(() => true).catch(() => false);
17380
17399
  if (!hasIdentity) {
17381
- const who = await fetchGithubIdentity(cloneToken);
17382
- if (who?.login) {
17383
- await git2(["config", "--local", "user.name", who.login]).catch(() => {
17384
- });
17385
- }
17386
- const email = who?.email ?? (who?.login ? `${who.login}@users.noreply.github.com` : void 0);
17387
- if (email) {
17388
- await git2(["config", "--local", "user.email", email]).catch(() => {
17389
- });
17390
- }
17400
+ const who = isGitlab ? null : await fetchGithubIdentity(cloneToken);
17401
+ const login = who?.login ?? "CodeAgent";
17402
+ await git2(["config", "--local", "user.name", login]).catch(() => {
17403
+ });
17404
+ const email = who?.email ?? (isGitlab ? "agent@codeagent-mobile.com" : `${login}@users.noreply.github.com`);
17405
+ await git2(["config", "--local", "user.email", email]).catch(() => {
17406
+ });
17391
17407
  }
17392
17408
  }
17393
17409
  function maskToken(text, cloneToken) {
@@ -17397,7 +17413,7 @@ function maskToken(text, cloneToken) {
17397
17413
  }
17398
17414
  return masked;
17399
17415
  }
17400
- async function prepareWorkspace(repoOrPath, deployId, cloneToken) {
17416
+ async function prepareWorkspace(repoOrPath, deployId, cloneToken, provider = "github") {
17401
17417
  if (isAbsolutePathTarget(repoOrPath)) {
17402
17418
  if (!fs36.existsSync(repoOrPath)) {
17403
17419
  throw new Error(`deploy target path does not exist: ${repoOrPath}`);
@@ -17406,11 +17422,11 @@ async function prepareWorkspace(repoOrPath, deployId, cloneToken) {
17406
17422
  }
17407
17423
  const dest = path40.join(selfHostedWorkspaceRoot(), deployId);
17408
17424
  if (fs36.existsSync(path40.join(dest, ".git"))) {
17409
- if (cloneToken) await configureGitCredentials(dest, repoOrPath, cloneToken);
17425
+ if (cloneToken) await configureGitCredentials(dest, repoOrPath, cloneToken, provider);
17410
17426
  return dest;
17411
17427
  }
17412
17428
  fs36.mkdirSync(selfHostedWorkspaceRoot(), { recursive: true, mode: 448 });
17413
- const cloneUrl = repoCloneUrl(repoOrPath, cloneToken);
17429
+ const cloneUrl = repoCloneUrl(repoOrPath, cloneToken, provider);
17414
17430
  try {
17415
17431
  await execFileP4("git", ["clone", "--depth", "1", cloneUrl, dest], {
17416
17432
  timeout: 12e4,
@@ -17421,7 +17437,7 @@ async function prepareWorkspace(repoOrPath, deployId, cloneToken) {
17421
17437
  const reason = err instanceof Error ? err.message : String(err);
17422
17438
  throw new Error(`git clone failed for ${maskCloneUrl(cloneUrl)}: ${maskToken(reason, cloneToken)}`);
17423
17439
  }
17424
- if (cloneToken) await configureGitCredentials(dest, repoOrPath, cloneToken);
17440
+ if (cloneToken) await configureGitCredentials(dest, repoOrPath, cloneToken, provider);
17425
17441
  return dest;
17426
17442
  }
17427
17443
 
@@ -17722,6 +17738,92 @@ async function ensureGhAuth(runner, ghCmd, token) {
17722
17738
  log.warn("host-agent", `gh auth errored: ${e instanceof Error ? e.message : String(e)}`);
17723
17739
  }
17724
17740
  }
17741
+ var FALLBACK_GLAB_VERSION = "1.54.0";
17742
+ var GLAB_RELEASE_API = "https://gitlab.com/api/v4/projects/gitlab-org%2Fcli/releases/permalink/latest";
17743
+ async function resolveLatestGlabVersion() {
17744
+ try {
17745
+ const res = await fetch(GLAB_RELEASE_API, { headers: { Accept: "application/json" } });
17746
+ if (res.ok) {
17747
+ const body = await res.json();
17748
+ const tag = (body.tag_name ?? "").replace(/^v/, "");
17749
+ if (/^\d+\.\d+\.\d+$/.test(tag)) return tag;
17750
+ }
17751
+ } catch {
17752
+ }
17753
+ return FALLBACK_GLAB_VERSION;
17754
+ }
17755
+ async function ensureGlabCli(runner, deps = {}) {
17756
+ if (runner.which("glab")) return "glab";
17757
+ const arch2 = ghArch();
17758
+ const platform3 = process.platform;
17759
+ if (arch2 === null || platform3 !== "linux" && platform3 !== "darwin" && platform3 !== "win32") {
17760
+ log.warn("host-agent", `glab auto-install unsupported on ${platform3}/${process.arch} \u2014 skipping`);
17761
+ return null;
17762
+ }
17763
+ const osToken = platform3 === "darwin" ? "darwin" : platform3 === "win32" ? "windows" : "linux";
17764
+ const ext = platform3 === "win32" ? "zip" : "tar.gz";
17765
+ const binaryName = platform3 === "win32" ? "glab.exe" : "glab";
17766
+ const downloadFn = deps.downloadFn ?? download;
17767
+ try {
17768
+ const version3 = await resolveLatestGlabVersion();
17769
+ const asset = `glab_${version3}_${osToken}_${arch2}`;
17770
+ const url2 = `https://gitlab.com/gitlab-org/cli/-/releases/v${version3}/downloads/${asset}.${ext}`;
17771
+ const tmpRoot = fs38.mkdtempSync(path42.join(os34.tmpdir(), "codeam-glab-"));
17772
+ const archive = path42.join(tmpRoot, `${asset}.${ext}`);
17773
+ if (!await downloadFn(url2, archive)) {
17774
+ log.warn("host-agent", "glab download failed \u2014 skipping (git push/pull still work)");
17775
+ return null;
17776
+ }
17777
+ const extract = await runner.run("tar", ["-xf", archive, "-C", tmpRoot], { timeoutMs: 6e4 });
17778
+ if (extract.code !== 0) {
17779
+ log.warn("host-agent", `glab archive extraction failed (code=${String(extract.code)}) \u2014 skipping`);
17780
+ return null;
17781
+ }
17782
+ const candidates = [
17783
+ path42.join(tmpRoot, "bin", binaryName),
17784
+ path42.join(tmpRoot, asset, "bin", binaryName),
17785
+ path42.join(tmpRoot, binaryName)
17786
+ ];
17787
+ const extractedBin = candidates.find((c2) => fs38.existsSync(c2));
17788
+ if (!extractedBin) {
17789
+ log.warn("host-agent", "glab binary not found in the extracted archive \u2014 skipping");
17790
+ return null;
17791
+ }
17792
+ const binDir = codeamBinDir();
17793
+ fs38.mkdirSync(binDir, { recursive: true });
17794
+ const target = path42.join(binDir, binaryName);
17795
+ fs38.copyFileSync(extractedBin, target);
17796
+ fs38.chmodSync(target, 493);
17797
+ log.info("host-agent", `glab installed to ${target} (v${version3})`);
17798
+ return target;
17799
+ } catch (e) {
17800
+ log.warn("host-agent", `glab auto-install errored: ${e instanceof Error ? e.message : String(e)} \u2014 skipping`);
17801
+ return null;
17802
+ }
17803
+ }
17804
+ async function ensureGlabAuth(runner, glabCmd, token) {
17805
+ if (!token) return;
17806
+ try {
17807
+ const status2 = await runner.run(glabCmd, ["auth", "status"], { timeoutMs: 15e3 });
17808
+ if (status2.code === 0) {
17809
+ log.info("host-agent", "glab already authenticated \u2014 leaving the existing login untouched");
17810
+ return;
17811
+ }
17812
+ const login = await runner.run(
17813
+ glabCmd,
17814
+ ["auth", "login", "--hostname", "gitlab.com", "--stdin"],
17815
+ { timeoutMs: 2e4, input: `${token}
17816
+ ` }
17817
+ );
17818
+ if (login.code === 0) {
17819
+ log.info("host-agent", "glab authenticated with the linked GitLab token");
17820
+ } else {
17821
+ log.warn("host-agent", `glab auth login failed (code=${String(login.code)}) \u2014 glab unauthenticated`);
17822
+ }
17823
+ } catch (e) {
17824
+ log.warn("host-agent", `glab auth errored: ${e instanceof Error ? e.message : String(e)}`);
17825
+ }
17826
+ }
17725
17827
  var defaultGitToolingRunner = {
17726
17828
  which(cmd) {
17727
17829
  try {
@@ -18569,7 +18671,7 @@ async function autoUpgradeBeforeCriticalCommand() {
18569
18671
  if (process.env.NODE_ENV === "test") return;
18570
18672
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
18571
18673
  if (process.env.CI) return;
18572
- const current = true ? "2.61.32" : null;
18674
+ const current = true ? "2.61.33" : null;
18573
18675
  if (!current) return;
18574
18676
  const cache = readCache();
18575
18677
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -18586,7 +18688,7 @@ function checkForUpdates() {
18586
18688
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
18587
18689
  if (process.env.CI) return;
18588
18690
  if (!process.stdout.isTTY) return;
18589
- const current = true ? "2.61.32" : null;
18691
+ const current = true ? "2.61.33" : null;
18590
18692
  if (!current) return;
18591
18693
  const cache = readCache();
18592
18694
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -18606,7 +18708,7 @@ var SELF_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
18606
18708
  var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
18607
18709
  var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
18608
18710
  function currentCliVersion() {
18609
- return true ? "2.61.32" : null;
18711
+ return true ? "2.61.33" : null;
18610
18712
  }
18611
18713
  function runCmd(cmd, args2, timeoutMs) {
18612
18714
  return new Promise((resolve8) => {
@@ -18815,6 +18917,9 @@ function isDeployPayload(p2) {
18815
18917
  if (p2.cloneToken !== void 0 && typeof p2.cloneToken !== "string") {
18816
18918
  return false;
18817
18919
  }
18920
+ if (p2.repoProvider !== void 0 && p2.repoProvider !== "github" && p2.repoProvider !== "gitlab") {
18921
+ return false;
18922
+ }
18818
18923
  if (p2.headroomEnabled !== void 0 && typeof p2.headroomEnabled !== "boolean") {
18819
18924
  return false;
18820
18925
  }
@@ -19463,7 +19568,12 @@ var HostAgentSupervisor = class {
19463
19568
  if (!isAbsolutePathTarget(payload.repoOrPath)) {
19464
19569
  report("cloning", "cloning repository");
19465
19570
  }
19466
- const cwd = await prepareWorkspace(payload.repoOrPath, payload.deployId, payload.cloneToken);
19571
+ const cwd = await prepareWorkspace(
19572
+ payload.repoOrPath,
19573
+ payload.deployId,
19574
+ payload.cloneToken,
19575
+ payload.repoProvider ?? "github"
19576
+ );
19467
19577
  let childEnv;
19468
19578
  let extraArgs = [];
19469
19579
  if (payload.houseProxy) {
@@ -19504,15 +19614,23 @@ var HostAgentSupervisor = class {
19504
19614
  if (payload.cloneToken) {
19505
19615
  try {
19506
19616
  report("preparing", "configuring git tooling");
19507
- const ghCmd = await ensureGhCli(defaultGitToolingRunner, payload.cloneToken);
19508
- if (ghCmd) {
19509
- childEnv.PATH = `${codeamBinDir()}${path46.delimiter}${childEnv.PATH}`;
19510
- await ensureGhAuth(defaultGitToolingRunner, ghCmd, payload.cloneToken);
19617
+ if ((payload.repoProvider ?? "github") === "gitlab") {
19618
+ const glabCmd = await ensureGlabCli(defaultGitToolingRunner);
19619
+ if (glabCmd) {
19620
+ childEnv.PATH = `${codeamBinDir()}${path46.delimiter}${childEnv.PATH}`;
19621
+ await ensureGlabAuth(defaultGitToolingRunner, glabCmd, payload.cloneToken);
19622
+ }
19623
+ } else {
19624
+ const ghCmd = await ensureGhCli(defaultGitToolingRunner, payload.cloneToken);
19625
+ if (ghCmd) {
19626
+ childEnv.PATH = `${codeamBinDir()}${path46.delimiter}${childEnv.PATH}`;
19627
+ await ensureGhAuth(defaultGitToolingRunner, ghCmd, payload.cloneToken);
19628
+ }
19511
19629
  }
19512
19630
  } catch (e) {
19513
19631
  log.warn(
19514
19632
  "host-agent",
19515
- `gh tooling setup skipped: ${e instanceof Error ? e.message : String(e)}`
19633
+ `git tooling setup skipped: ${e instanceof Error ? e.message : String(e)}`
19516
19634
  );
19517
19635
  }
19518
19636
  }
@@ -38599,7 +38717,7 @@ function checkChokidar() {
38599
38717
  }
38600
38718
  async function doctor(args2 = []) {
38601
38719
  const json = args2.includes("--json");
38602
- const cliVersion = true ? "2.61.32" : "0.0.0-dev";
38720
+ const cliVersion = true ? "2.61.33" : "0.0.0-dev";
38603
38721
  const apiBase2 = resolveApiBaseUrl();
38604
38722
  const diagnosticId = (0, import_node_crypto13.randomUUID)();
38605
38723
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -39110,7 +39228,7 @@ async function mcpRun(args2) {
39110
39228
  // src/commands/version.ts
39111
39229
  var import_picocolors15 = __toESM(require("picocolors"));
39112
39230
  function version2() {
39113
- const v = true ? "2.61.32" : "unknown";
39231
+ const v = true ? "2.61.33" : "unknown";
39114
39232
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
39115
39233
  }
39116
39234
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.61.32",
3
+ "version": "2.61.33",
4
4
  "description": "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device — async. The terminal companion for CodeAgent Mobile.",
5
5
  "type": "commonjs",
6
6
  "main": "dist/index.js",