@bf-meishijian/aip 0.0.9 → 0.0.11

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/README.md CHANGED
@@ -110,6 +110,8 @@ PLATFORM_WEB_URL=http://10.254.208.127:4173
110
110
 
111
111
  ## 上线准备与 GitLab MR
112
112
 
113
+ Host 管理的开发、部署及 QA 仓库使用 SSH 拉取和推送。平台根据已配置 GitLab 返回的真实 SSH 地址下发,支持独立 SSH 主机名及端口;无需设置全局 Git URL 映射。运行 Host 的系统用户须有可用 SSH 密钥和仓库权限,并完成正常的 SSH 主机身份验证。已有 Host 仓库在核对项目后更新 origin 与显式推送地址,保留分支和修改;失败不会退回 HTTPS。平台 API 与云端源码同步仍使用各自的现有认证。
114
+
113
115
  开发阶段产出 `release_checklist.md`,测试期间随修复维护。人工验收通过后恢复原开发会话,先读取测试交接和最新分支代码,核实清单后提交 MR。Runtime 负责 Git 推送和 MR 写操作;平台仅用自己的读取权限核实、登记和查询状态。
114
116
 
115
117
  Runtime 的 MR 提交能力复用 [GitLab CLI glab](https://docs.gitlab.com/cli/api/)。运行 Runtime 的系统用户需要已安装 `glab`、能从 Runtime 的 `PATH` 找到它,并已通过 [glab auth login](https://docs.gitlab.com/cli/auth/login/) 登录对应 GitLab(或使用 glab 官方支持的凭证环境变量)。该身份需具备项目 MR 创建权限,Git 推送仍使用本机 Git 凭证。不读取平台全局 Token,也不把 Token 传入 Agent 参数。缺少 glab、登录失效、权限不足或网络失败时会返回中文处理提示,不降级使用平台写权限。
@@ -141,3 +143,19 @@ MR 创建成功但平台登记失败时,Runtime 保存待登记结果。重试
141
143
  仓库 manifest 只描述可复用用例,不声明 `ac`。工单的 AC 与 `caseId` 通过本工单测试计划中的 `ac`、`automationCaseIds` 建立关联;正式执行绑定计划版本。独立回归没有工单 AC 关联。旧执行原始证据保留,但目录中的旧 AC 不再参与覆盖或验收判断。
142
144
 
143
145
  移除 AC 契约须与测试仓库、平台 API 和 Host 一起更新。旧格式目录会被新契约拒绝;仍使用旧分支的工单应先将测试资产更新到新契约,再执行,不由平台猜测或补写关联。
146
+
147
+ ## 命令并发与准备排障(0.0.10)
148
+
149
+ 心跳领取与命令执行分离,最多同时处理 4 条任务命令;同一 Run / QA 会话仍按顺序领取。诊断、模型刷新、供应商登录独立于任务槽位,各类型自身串行。多个 Run 使用同一 Git 镜像时,仅该仓库的准备操作串行,避免并发修改镜像缓存。任务命令完成表示已处理命令,不表示后台 Agent 会话已经结束。
150
+
151
+ 准备请求排队超过 30 秒不再判失败。请求仍受原有 24 小时命令有效期约束;Host 已接收后失联或重启则显示中断,不自动重放。消息发送的 30 秒领取期限不受此修改影响。API 发布过程中执行结果回执失败会记录 `command_result_unknown`,不能当作命令执行失败,也不会自动重执行。
152
+
153
+ Windows 计划任务将标准输出和错误追加到 `$HOME\.aip\profiles\default\logs\host.log`;使用 `--local` 时目录为 `profiles\local`,设置 `AIP_HOME` 时基址随之改变。PowerShell 实时查看:
154
+
155
+ ```powershell
156
+ Get-Content "$HOME\.aip\profiles\default\logs\host.log" -Tail 100 -Wait
157
+ ```
158
+
159
+ 按 `commandId` / `runId` 定位;`command_started`、`command_input_loaded`、`workspace_preparation_started`、`repository_preparation_started/completed`、`workspace_materials_ready`、`command_completed` 标识执行进度。停在仓库开始阶段通常需要检查本机 Git 网络和凭证;`heartbeat_failed` / `command_result_unknown` 表明平台通信或回执失败,结合相同标识查询 API 日志。日志不包含命令正文、凭证和环境变量。
160
+
161
+ 需要重新安装新 Runtime 才能获得并发执行与 Windows 日志;仅部署 API/Web 或重启旧 Host 不会更新本地代码。重新运行平台账户页的安装命令会保留本机身份、凭证与工作目录,无需解绑重新注册。
@@ -96,6 +96,7 @@ interface RunInput {
96
96
  id: string;
97
97
  name: string;
98
98
  repositoryUrl: string;
99
+ sshRepositoryUrl?: string | null;
99
100
  baselineCommit: string;
100
101
  baselineBranch: string;
101
102
  devBranch: string;
@@ -139,7 +140,7 @@ export declare function openPiSession(options: {
139
140
  session: AgentSession;
140
141
  sessionId: string;
141
142
  }>;
142
- export declare function prepareDeploymentProjects(runRoot: string, projects: Array<Pick<RunInput["projects"][number], "id" | "name" | "repositoryUrl" | "testBranch">>, configuration?: DeploymentConfigurationSnapshot): Promise<string>;
143
+ export declare function prepareDeploymentProjects(runRoot: string, projects: Array<Pick<RunInput["projects"][number], "id" | "name" | "repositoryUrl" | "sshRepositoryUrl" | "testBranch">>, configuration?: DeploymentConfigurationSnapshot): Promise<string>;
143
144
  export declare function prepareProjects(cacheRoot: string, runRoot: string, workItemKey: string, projects: RunInput["projects"], resume: boolean, verifyRemote?: boolean, uninitializedProjectIds?: string[]): Promise<string>;
144
145
  export declare function initialPrompt(input: RunInput, runRoot: string, _responsible?: boolean, materialRoot?: string): string;
145
146
  export declare function redactHostText(value: string, roots?: string[]): string;
@@ -1 +1 @@
1
- {"version":3,"file":"coding-run-manager.d.ts","sourceRoot":"","sources":["../src/coding-run-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"coding-run-manager.d.ts","sourceRoot":"","sources":["../src/coding-run-manager.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,8 @@ import { basename, dirname, join, resolve } from "node:path";
5
5
  import { promisify } from "node:util";
6
6
  import { createAgentSession, DefaultResourceLoader, SessionManager, } from "@earendil-works/pi-coding-agent";
7
7
  import { deliveryTools } from "./delivery-tools.js";
8
+ import { ensureSshOrigin, gitTransportUrl, matchesRepository, SSH_ACCESS_HINT, } from "./git-transport.js";
9
+ import { logHostEvent } from "./host-log.js";
8
10
  import { inspectHandoffWorkspace, synchronizeLocalIntake, synchronizeRemoteWorkspace, } from "./local-handoff-workspace.js";
9
11
  import { localModelUsage } from "./local-model-usage.js";
10
12
  import { collectProjectFacts, worktreeTreeHash } from "./project-facts.js";
@@ -315,6 +317,7 @@ export class CodingRunManager {
315
317
  const transferNotes = input.transferContinuation && !storedState?.prepared
316
318
  ? await inspectPriorTransferWorkspaces(local.runWorktrees, input.transferWorkspaces?.priorRunIds ?? [], input.projects)
317
319
  : [];
320
+ logHostEvent("workspace_preparation_started", { runId, turnId, phase: input.phase });
318
321
  const cwd = input.discussionTurn
319
322
  ? input.projects.length === 1
320
323
  ? join(runRoot, safeName(input.projects[0].name))
@@ -341,7 +344,9 @@ export class CodingRunManager {
341
344
  })).stdout.trim();
342
345
  await writeSecureJson(stateFile, { ...storedState, preparationBaselines });
343
346
  }
347
+ logHostEvent("workspace_repositories_ready", { runId, turnId });
344
348
  await initializeDeliveryFiles(runRoot, input, Boolean(storedState?.prepared), readMaterial);
349
+ logHostEvent("workspace_materials_ready", { runId, turnId });
345
350
  const deliveryInputHash = createHash("sha256")
346
351
  .update(JSON.stringify([
347
352
  input.primaryProjectId,
@@ -1122,10 +1127,10 @@ export async function prepareDeploymentProjects(runRoot, projects, configuration
1122
1127
  "--branch",
1123
1128
  branch,
1124
1129
  "--",
1125
- project.repositoryUrl,
1130
+ gitTransportUrl(project.repositoryUrl, project.sshRepositoryUrl),
1126
1131
  target,
1127
1132
  ]).catch(() => {
1128
- throw new Error("DEPLOYMENT_REPOSITORY_PREPARE_FAILED");
1133
+ throw new Error(`DEPLOYMENT_REPOSITORY_PREPARE_FAILED:${SSH_ACCESS_HINT}`);
1129
1134
  });
1130
1135
  }
1131
1136
  // Reuse the interrupted deployment as-is; the employee resolves its Git state.
@@ -1133,119 +1138,143 @@ export async function prepareDeploymentProjects(runRoot, projects, configuration
1133
1138
  execFileAsync("git", ["config", "--get", "remote.origin.url"], { cwd: target }),
1134
1139
  execFileAsync("git", ["rev-parse", "--git-common-dir"], { cwd: target }),
1135
1140
  ]);
1136
- if ((await canonicalRepository(origin.trim())) !==
1137
- (await canonicalRepository(project.repositoryUrl)))
1141
+ if (!(await matchesRepository(origin.trim(), project.repositoryUrl, project.sshRepositoryUrl)))
1138
1142
  throw new Error("CODING_REPOSITORY_ORIGIN_MISMATCH");
1139
1143
  if ((await realpath(resolve(target, common.trim()))) !== (await realpath(join(target, ".git"))))
1140
1144
  throw new Error("DEPLOYMENT_REPOSITORY_NOT_ISOLATED");
1145
+ await ensureSshOrigin(target, project.repositoryUrl, project.sshRepositoryUrl);
1141
1146
  }
1142
1147
  return projects.length === 1 ? join(runRoot, safeName(projects[0].name)) : runRoot;
1143
1148
  }
1149
+ const repositoryPreparations = new Map();
1144
1150
  export async function prepareProjects(cacheRoot, runRoot, workItemKey, projects, resume, verifyRemote = false, uninitializedProjectIds = []) {
1145
1151
  await mkdir(cacheRoot, { recursive: true, mode: 0o700 });
1146
1152
  for (const project of projects) {
1147
1153
  const mirror = join(cacheRoot, `${project.id}.git`);
1148
- if (!(await exists(mirror))) {
1149
- await execFileAsync("git", ["clone", "--bare", project.repositoryUrl, mirror]).catch(() => {
1150
- throw new Error("CODING_REPOSITORY_ACCESS_FAILED");
1151
- });
1152
- }
1153
- else {
1154
- const [{ stdout: bare }, { stdout: origin }] = await Promise.all([
1155
- execFileAsync("git", ["--git-dir", mirror, "rev-parse", "--is-bare-repository"]),
1156
- execFileAsync("git", ["--git-dir", mirror, "config", "--get", "remote.origin.url"]),
1157
- ]);
1158
- if (bare.trim() !== "true")
1159
- throw new Error("CODING_REPOSITORY_CACHE_NOT_BARE");
1160
- if ((await canonicalRepository(origin.trim())) !==
1161
- (await canonicalRepository(project.repositoryUrl)))
1162
- throw new Error("CODING_REPOSITORY_ORIGIN_MISMATCH");
1163
- }
1164
- if (!resume || verifyRemote) {
1165
- // Never fetch into local branch refs: stopped worktrees may contain unpublished commits.
1166
- await execFileAsync("git", [
1167
- "--git-dir",
1168
- mirror,
1169
- "config",
1170
- "--replace-all",
1171
- "remote.origin.fetch",
1172
- "+refs/heads/*:refs/remotes/origin/*",
1173
- ]);
1174
- await execFileAsync("git", ["--git-dir", mirror, "fetch", "--prune", "origin"]).catch(() => {
1175
- throw new Error("CODING_REPOSITORY_ACCESS_FAILED");
1176
- });
1177
- }
1178
- const target = join(runRoot, safeName(project.name));
1179
- const expectedBranch = project.devBranch || `aip/${workItemKey}`;
1180
- const existingWorktree = await exists(target);
1181
- if (!existingWorktree) {
1182
- if (resume)
1183
- throw new Error("CODING_RUN_WORKTREE_MISSING");
1184
- const remoteExists = await execFileAsync("git", [
1185
- "--git-dir",
1186
- mirror,
1187
- "show-ref",
1188
- "--verify",
1189
- "--quiet",
1190
- `refs/remotes/origin/${expectedBranch}`,
1191
- ]).then(() => true, () => false);
1192
- const localExists = await execFileAsync("git", [
1193
- "--git-dir",
1194
- mirror,
1195
- "show-ref",
1196
- "--verify",
1197
- "--quiet",
1198
- `refs/heads/${expectedBranch}`,
1199
- ]).then(() => true, () => false);
1200
- if (remoteExists) {
1201
- // A new execution owns its branch ref; an old execution keeps its worktree untouched.
1154
+ // 多个 Run 共享镜像仓库:仅串行此仓库的 clone/fetch/worktree 元数据操作。
1155
+ const previous = repositoryPreparations.get(mirror);
1156
+ let release;
1157
+ const pending = new Promise((resolve) => {
1158
+ release = resolve;
1159
+ });
1160
+ repositoryPreparations.set(mirror, pending);
1161
+ logHostEvent("repository_preparation_waiting", { projectId: project.id, workItemKey });
1162
+ await previous;
1163
+ try {
1164
+ logHostEvent("repository_preparation_started", { projectId: project.id, workItemKey });
1165
+ if (!(await exists(mirror))) {
1202
1166
  await execFileAsync("git", [
1203
1167
  "clone",
1204
- "--single-branch",
1205
- "--branch",
1206
- expectedBranch,
1207
- project.repositoryUrl,
1208
- target,
1168
+ "--bare",
1169
+ gitTransportUrl(project.repositoryUrl, project.sshRepositoryUrl),
1170
+ mirror,
1209
1171
  ]).catch(() => {
1210
- throw new Error("CODING_REPOSITORY_ACCESS_FAILED");
1172
+ throw new Error(`CODING_REPOSITORY_ACCESS_FAILED:${SSH_ACCESS_HINT}`);
1211
1173
  });
1212
1174
  }
1213
1175
  else {
1214
- if (localExists || (verifyRemote && !uninitializedProjectIds.includes(project.id)))
1215
- throw new Error(localExists
1216
- ? "CODING_BRANCH_NOT_PUBLISHED:本机工单分支尚未推送,请检查并同步后再继续。"
1217
- : "CODING_BRANCH_INITIALIZATION_UNCONFIRMED:远端尚无工单分支,原工作区初始化状态未确认,请检查后再继续。");
1176
+ const [{ stdout: bare }, { stdout: origin }] = await Promise.all([
1177
+ execFileAsync("git", ["--git-dir", mirror, "rev-parse", "--is-bare-repository"]),
1178
+ execFileAsync("git", ["--git-dir", mirror, "config", "--get", "remote.origin.url"]),
1179
+ ]);
1180
+ if (bare.trim() !== "true")
1181
+ throw new Error("CODING_REPOSITORY_CACHE_NOT_BARE");
1182
+ if (!(await matchesRepository(origin.trim(), project.repositoryUrl, project.sshRepositoryUrl)))
1183
+ throw new Error("CODING_REPOSITORY_ORIGIN_MISMATCH");
1184
+ }
1185
+ await ensureSshOrigin(mirror, project.repositoryUrl, project.sshRepositoryUrl);
1186
+ if (!resume || verifyRemote) {
1187
+ // Never fetch into local branch refs: stopped worktrees may contain unpublished commits.
1218
1188
  await execFileAsync("git", [
1219
1189
  "--git-dir",
1220
1190
  mirror,
1221
- "cat-file",
1222
- "-e",
1223
- `refs/remotes/origin/${project.baselineBranch}`,
1224
- ]).catch(() => {
1225
- throw new Error("CODING_BASELINE_BRANCH_UNAVAILABLE");
1191
+ "config",
1192
+ "--replace-all",
1193
+ "remote.origin.fetch",
1194
+ "+refs/heads/*:refs/remotes/origin/*",
1195
+ ]);
1196
+ await execFileAsync("git", ["--git-dir", mirror, "fetch", "--prune", "origin"]).catch(() => {
1197
+ throw new Error(`CODING_REPOSITORY_ACCESS_FAILED:${SSH_ACCESS_HINT}`);
1226
1198
  });
1227
- await execFileAsync("git", [
1199
+ }
1200
+ const target = join(runRoot, safeName(project.name));
1201
+ const expectedBranch = project.devBranch || `aip/${workItemKey}`;
1202
+ const existingWorktree = await exists(target);
1203
+ if (!existingWorktree) {
1204
+ if (resume)
1205
+ throw new Error("CODING_RUN_WORKTREE_MISSING");
1206
+ const remoteExists = await execFileAsync("git", [
1228
1207
  "--git-dir",
1229
1208
  mirror,
1230
- "worktree",
1231
- "add",
1232
- "-b",
1233
- expectedBranch,
1234
- target,
1235
- `refs/remotes/origin/${project.baselineBranch}`,
1236
- ]).catch(() => {
1237
- throw new Error("CODING_WORKTREE_CREATE_FAILED");
1238
- });
1209
+ "show-ref",
1210
+ "--verify",
1211
+ "--quiet",
1212
+ `refs/remotes/origin/${expectedBranch}`,
1213
+ ]).then(() => true, () => false);
1214
+ const localExists = await execFileAsync("git", [
1215
+ "--git-dir",
1216
+ mirror,
1217
+ "show-ref",
1218
+ "--verify",
1219
+ "--quiet",
1220
+ `refs/heads/${expectedBranch}`,
1221
+ ]).then(() => true, () => false);
1222
+ if (remoteExists) {
1223
+ // A new execution owns its branch ref; an old execution keeps its worktree untouched.
1224
+ await execFileAsync("git", [
1225
+ "clone",
1226
+ "--single-branch",
1227
+ "--branch",
1228
+ expectedBranch,
1229
+ gitTransportUrl(project.repositoryUrl, project.sshRepositoryUrl),
1230
+ target,
1231
+ ]).catch(() => {
1232
+ throw new Error(`CODING_REPOSITORY_ACCESS_FAILED:${SSH_ACCESS_HINT}`);
1233
+ });
1234
+ }
1235
+ else {
1236
+ if (localExists || (verifyRemote && !uninitializedProjectIds.includes(project.id)))
1237
+ throw new Error(localExists
1238
+ ? "CODING_BRANCH_NOT_PUBLISHED:本机工单分支尚未推送,请检查并同步后再继续。"
1239
+ : "CODING_BRANCH_INITIALIZATION_UNCONFIRMED:远端尚无工单分支,原工作区初始化状态未确认,请检查后再继续。");
1240
+ await execFileAsync("git", [
1241
+ "--git-dir",
1242
+ mirror,
1243
+ "cat-file",
1244
+ "-e",
1245
+ `refs/remotes/origin/${project.baselineBranch}`,
1246
+ ]).catch(() => {
1247
+ throw new Error("CODING_BASELINE_BRANCH_UNAVAILABLE");
1248
+ });
1249
+ await execFileAsync("git", [
1250
+ "--git-dir",
1251
+ mirror,
1252
+ "worktree",
1253
+ "add",
1254
+ "-b",
1255
+ expectedBranch,
1256
+ target,
1257
+ `refs/remotes/origin/${project.baselineBranch}`,
1258
+ ]).catch(() => {
1259
+ throw new Error("CODING_WORKTREE_CREATE_FAILED");
1260
+ });
1261
+ }
1239
1262
  }
1263
+ await validateWorktree(mirror, target, expectedBranch, resume || existingWorktree, project.repositoryUrl, project.sshRepositoryUrl);
1264
+ if (existingWorktree && verifyRemote)
1265
+ await synchronizeRemoteWorkspace(target, expectedBranch);
1266
+ logHostEvent("repository_preparation_completed", { projectId: project.id, workItemKey });
1267
+ }
1268
+ finally {
1269
+ if (repositoryPreparations.get(mirror) === pending)
1270
+ repositoryPreparations.delete(mirror);
1271
+ release();
1240
1272
  }
1241
- await validateWorktree(mirror, target, expectedBranch, resume || existingWorktree, project.repositoryUrl);
1242
- if (existingWorktree && verifyRemote)
1243
- await synchronizeRemoteWorkspace(target, expectedBranch);
1244
1273
  }
1245
1274
  const onlyProject = projects.length === 1 ? projects[0] : null;
1246
1275
  return onlyProject ? join(runRoot, safeName(onlyProject.name)) : runRoot;
1247
1276
  }
1248
- async function validateWorktree(mirror, target, expectedBranch, resume, repositoryUrl) {
1277
+ async function validateWorktree(mirror, target, expectedBranch, resume, repositoryUrl, sshRepositoryUrl) {
1249
1278
  const facts = await Promise.all([
1250
1279
  execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], { cwd: target }),
1251
1280
  execFileAsync("git", ["rev-parse", "--git-common-dir"], { cwd: target }),
@@ -1264,14 +1293,15 @@ async function validateWorktree(mirror, target, expectedBranch, resume, reposito
1264
1293
  if (actualCommon !== expectedCommon) {
1265
1294
  if (actualCommon !== (await realpath(join(target, ".git"))))
1266
1295
  throw new Error("CODING_WORKTREE_OWNERSHIP_INVALID");
1267
- const { stdout: origin } = await execFileAsync("git", ["remote", "get-url", "origin"], {
1296
+ const { stdout: origin } = await execFileAsync("git", ["config", "--get", "remote.origin.url"], {
1268
1297
  cwd: target,
1269
1298
  });
1270
- if ((await canonicalRepository(origin.trim())) !== (await canonicalRepository(repositoryUrl)))
1299
+ if (!(await matchesRepository(origin.trim(), repositoryUrl, sshRepositoryUrl)))
1271
1300
  throw new Error("CODING_REPOSITORY_ORIGIN_MISMATCH");
1272
1301
  }
1273
1302
  if (branch.trim() !== expectedBranch)
1274
1303
  throw new Error("CODING_WORKTREE_BRANCH_MISMATCH");
1304
+ await ensureSshOrigin(target, repositoryUrl, sshRepositoryUrl);
1275
1305
  if (resume)
1276
1306
  return;
1277
1307
  const { stdout: status } = await execFileAsync("git", ["status", "--porcelain"], {
@@ -1280,15 +1310,6 @@ async function validateWorktree(mirror, target, expectedBranch, resume, reposito
1280
1310
  if (status.trim())
1281
1311
  throw new Error("CODING_WORKTREE_NOT_CLEAN");
1282
1312
  }
1283
- async function canonicalRepository(value) {
1284
- if (value.startsWith("/"))
1285
- return realpath(value).catch(() => resolve(value));
1286
- if (value.startsWith("file://")) {
1287
- const path = new URL(value);
1288
- return realpath(path).catch(() => resolve(path.pathname));
1289
- }
1290
- return value.replace(/\/$/u, "");
1291
- }
1292
1313
  function executionInstructions(input, runRoot) {
1293
1314
  // 环境事实与授权单独呈现,业务方法由平台 nodeRules 提供。
1294
1315
  return `本轮执行上下文:\n${JSON.stringify({
@@ -1426,10 +1447,10 @@ export async function inspectPriorTransferWorkspaces(root, runIds, projects) {
1426
1447
  }
1427
1448
  const origin = (await execFileAsync("git", ["config", "--get", "remote.origin.url"], { cwd })).stdout.trim();
1428
1449
  const branch = (await execFileAsync("git", ["branch", "--show-current"], { cwd })).stdout.trim();
1429
- if ((await canonicalRepository(origin)) !==
1430
- (await canonicalRepository(project.repositoryUrl)) ||
1450
+ if (!(await matchesRepository(origin, project.repositoryUrl, project.sshRepositoryUrl)) ||
1431
1451
  branch !== project.devBranch)
1432
1452
  throw new Error("WORKSPACE_IDENTITY_MISMATCH");
1453
+ await ensureSshOrigin(cwd, project.repositoryUrl, project.sshRepositoryUrl);
1433
1454
  await execFileAsync("git", ["fetch", "origin", `+refs/heads/${branch}:refs/remotes/origin/${branch}`], { cwd });
1434
1455
  const counts = (await execFileAsync("git", ["rev-list", "--left-right", "--count", `HEAD...refs/remotes/origin/${branch}`], { cwd })).stdout
1435
1456
  .trim()