@rallycry/conveyor-agent 10.0.5 → 10.1.0
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/dist/{chunk-S72TAMGA.js → chunk-DAHP6JPD.js} +759 -87
- package/dist/chunk-DAHP6JPD.js.map +1 -0
- package/dist/cli.js +78 -31
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +57 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/runtime/entrypoint.sh +58 -27
- package/dist/chunk-S72TAMGA.js.map +0 -1
package/runtime/entrypoint.sh
CHANGED
|
@@ -270,6 +270,34 @@ CONVEYOR_USER_ID="${CONVEYOR_USER_ID:-}"
|
|
|
270
270
|
CONVEYOR_PROJECT_ID_FROM_BUNDLE="${CONVEYOR_PROJECT_ID:-${PROJECT_ID:-}}"
|
|
271
271
|
export CONVEYOR_USER_ID
|
|
272
272
|
|
|
273
|
+
# ── Seed Claude Code's first-run gates ──
|
|
274
|
+
# Pre-answer every interactive gate the CLI shows on a fresh (or partially
|
|
275
|
+
# initialized) config: the onboarding wizard (theme picker), the
|
|
276
|
+
# bypass-permissions warning, and the folder-trust dialog for the repo
|
|
277
|
+
# workspace. Any one of these parks a headless TUI forever — seen live
|
|
278
|
+
# 2026-07-02 as code reviews stuck at "Is this a project you trust?" until the
|
|
279
|
+
# server's cap burned the attempt. conveyor-agent also seeds the same keys before
|
|
280
|
+
# each spawn (ensureClaudeOnboarding), but this shell seed is version-independent
|
|
281
|
+
# (pods can run older baked agents) and lands before the CLI's first read — and
|
|
282
|
+
# it also covers an interactive `claude` opened over an SSH tunnel, which the
|
|
283
|
+
# agent seed never runs for. Merge via jq, never clobber: CLI-owned keys (cache
|
|
284
|
+
# keys, firstStartTime, other projects' trust entries) survive; a missing or
|
|
285
|
+
# corrupt file becomes a fresh seed — Claude Code treats a 0-byte/unparseable
|
|
286
|
+
# file as fatal ("Unexpected end of JSON input"), so replacing is a repair. Theme
|
|
287
|
+
# only seeds fresh configs (a user-picked theme is never overridden). Every step
|
|
288
|
+
# is rescued: config seeding must never kill the entrypoint under `set -eo pipefail`.
|
|
289
|
+
seed_claude_json() {
|
|
290
|
+
SEED_TARGET="$1"
|
|
291
|
+
SEED_GATES='{"hasCompletedOnboarding":true,"bypassPermissionsModeAccepted":true,"projects":{"/workspaces/repo":{"hasTrustDialogAccepted":true}}}'
|
|
292
|
+
if [ -f "${SEED_TARGET}" ] && jq -e . "${SEED_TARGET}" >/dev/null 2>&1; then
|
|
293
|
+
if SEED_MERGED="$(jq -c --argjson gates "${SEED_GATES}" '. * $gates' "${SEED_TARGET}" 2>/dev/null)" && [ -n "${SEED_MERGED}" ]; then
|
|
294
|
+
printf '%s' "${SEED_MERGED}" > "${SEED_TARGET}" 2>/dev/null || true
|
|
295
|
+
fi
|
|
296
|
+
else
|
|
297
|
+
printf '%s' "${SEED_GATES}" | jq -c '. + {theme:"dark"}' > "${SEED_TARGET}" 2>/dev/null || true
|
|
298
|
+
fi
|
|
299
|
+
}
|
|
300
|
+
|
|
273
301
|
# ── Wire per-user Claude state from the GCS FUSE mount ──
|
|
274
302
|
# When the pod has /mnt/conveyor-users mounted (GCS FUSE CSI), symlink
|
|
275
303
|
# ~/.claude, ~/.claude.json, and ~/.config/claude to the user's subdir so
|
|
@@ -282,29 +310,7 @@ if [ -n "${CONVEYOR_USER_ID}" ] && [ -n "${USER_HOME_PROJECT_ID}" ] && [ -d "${U
|
|
|
282
310
|
USER_HOME_ROOT="${USER_HOME_MOUNT}/users/${CONVEYOR_USER_ID}/${USER_HOME_PROJECT_ID}"
|
|
283
311
|
echo "[pool] Linking Claude state to ${USER_HOME_ROOT}"
|
|
284
312
|
mkdir -p "${USER_HOME_ROOT}/.claude" "${USER_HOME_ROOT}/.config/claude" 2>/dev/null || true
|
|
285
|
-
|
|
286
|
-
# corrupted ("Unexpected end of JSON input") and exits code 1, killing the
|
|
287
|
-
# very first query on any fresh user+project mount. We pre-complete onboarding
|
|
288
|
-
# and pick a default theme so the spawned Claude Code TUI doesn't stop at the
|
|
289
|
-
# first-run theme picker / login. Only seed when the file is missing/empty or a
|
|
290
|
-
# bare "{}" placeholder — never overwrite a real config carried over via the
|
|
291
|
-
# GCS-FUSE user-home mount from a prior pod session.
|
|
292
|
-
CLAUDE_JSON="${USER_HOME_ROOT}/.claude.json"
|
|
293
|
-
# Read defensively: on a first-time user+project mount the file does not
|
|
294
|
-
# exist yet. `< "${CLAUDE_JSON}"` on a missing file is a SHELL-level
|
|
295
|
-
# redirection error (the `2>/dev/null` only silences tr, not bash), which
|
|
296
|
-
# under `set -eo pipefail` aborts the whole entrypoint with exit 1 — the
|
|
297
|
-
# agent container then dies seconds after start and the pod is reaped
|
|
298
|
-
# ("deleted externally"). Guard on existence and rescue any read error so a
|
|
299
|
-
# missing/unreadable file falls through to the seed below instead of crashing.
|
|
300
|
-
if [ -f "${CLAUDE_JSON}" ]; then
|
|
301
|
-
CLAUDE_JSON_CURRENT="$(tr -d '[:space:]' < "${CLAUDE_JSON}" 2>/dev/null || true)"
|
|
302
|
-
else
|
|
303
|
-
CLAUDE_JSON_CURRENT=""
|
|
304
|
-
fi
|
|
305
|
-
if [ -z "${CLAUDE_JSON_CURRENT}" ] || [ "${CLAUDE_JSON_CURRENT}" = "{}" ]; then
|
|
306
|
-
echo '{"hasCompletedOnboarding":true,"theme":"dark"}' > "${CLAUDE_JSON}" 2>/dev/null || true
|
|
307
|
-
fi
|
|
313
|
+
seed_claude_json "${USER_HOME_ROOT}/.claude.json"
|
|
308
314
|
|
|
309
315
|
# Replace any image defaults with live symlinks into the mount.
|
|
310
316
|
rm -rf /home/conveyor/.claude /home/conveyor/.claude.json /home/conveyor/.config/claude 2>/dev/null || true
|
|
@@ -314,6 +320,9 @@ if [ -n "${CONVEYOR_USER_ID}" ] && [ -n "${USER_HOME_PROJECT_ID}" ] && [ -d "${U
|
|
|
314
320
|
ln -sfn "${USER_HOME_ROOT}/.config/claude" /home/conveyor/.config/claude
|
|
315
321
|
else
|
|
316
322
|
echo "[pool] Skipping user-home symlink (userId='${CONVEYOR_USER_ID}', projectId='${USER_HOME_PROJECT_ID}', mount present: $([ -d "${USER_HOME_MOUNT}" ] && echo yes || echo no))"
|
|
323
|
+
# No persistent home — the CLI reads the pod-local config; seed it there so
|
|
324
|
+
# non-FUSE pods get the same first-run gate suppression as the mounted path.
|
|
325
|
+
seed_claude_json /home/conveyor/.claude.json
|
|
317
326
|
fi
|
|
318
327
|
|
|
319
328
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -343,6 +352,24 @@ fi
|
|
|
343
352
|
GIT_READY_MARKER="/workspaces/.conveyor-git-ready"
|
|
344
353
|
GIT_FAILED_MARKER="/workspaces/.conveyor-git-failed"
|
|
345
354
|
|
|
355
|
+
reset_tracked_repo_changes_before_assignment_checkout() {
|
|
356
|
+
# The baked/pooled repo is not user-owned until this git gate succeeds. Reset
|
|
357
|
+
# stale tracked image-generated dirt so assignment checkout can move to the
|
|
358
|
+
# requested branch/ref instead of being blocked by files like dependency
|
|
359
|
+
# stamps. Do not `git clean`: untracked prebake artifacts may be intentional.
|
|
360
|
+
# The assignment checkouts additionally pass -f: an UNTRACKED bake artifact
|
|
361
|
+
# can collide with a path the TARGET ref tracks (e.g. a dependency stamp
|
|
362
|
+
# committed on an older PR branch), which this reset cannot clear — checkout
|
|
363
|
+
# then refuses with "untracked working tree files would be overwritten".
|
|
364
|
+
# Forcing is safe here for the same not-user-owned reason.
|
|
365
|
+
if ! _reset_out=$(git -C repo reset --hard HEAD 2>&1); then
|
|
366
|
+
echo "[pool] ERROR: failed to clean tracked repo changes before checkout: ${_reset_out}" >&2
|
|
367
|
+
printf '%s' "pre-checkout reset failed" > "$GIT_FAILED_MARKER"
|
|
368
|
+
return 1
|
|
369
|
+
fi
|
|
370
|
+
return 0
|
|
371
|
+
}
|
|
372
|
+
|
|
346
373
|
# Shared by both the pod-image branch and the repo-present-non-pod-image branch
|
|
347
374
|
# of prepare_workspace_git: refresh the remote token, fetch the base branch,
|
|
348
375
|
# fetch/checkout the task branch (creating it from base if it doesn't exist on
|
|
@@ -365,6 +392,10 @@ sync_task_branch_to_repo() {
|
|
|
365
392
|
echo "[pool] WARN: fetch origin/${DEV_BRANCH} failed: ${_fetch_dev_out}" >&2
|
|
366
393
|
fi
|
|
367
394
|
|
|
395
|
+
if ! reset_tracked_repo_changes_before_assignment_checkout; then
|
|
396
|
+
return 1
|
|
397
|
+
fi
|
|
398
|
+
|
|
368
399
|
if [ -n "${CHECKOUT_REF}" ]; then
|
|
369
400
|
echo "[pool] Fetching checkout ref ${CHECKOUT_REF} for review branch ${BRANCH}..."
|
|
370
401
|
if ! _fetch_checkout_out=$(git -C repo fetch origin "+${CHECKOUT_REF}:refs/remotes/origin/pr-checkout" 2>&1); then
|
|
@@ -372,7 +403,7 @@ sync_task_branch_to_repo() {
|
|
|
372
403
|
printf '%s' "fetch checkout ref ${CHECKOUT_REF} failed" > "$GIT_FAILED_MARKER"
|
|
373
404
|
return 1
|
|
374
405
|
fi
|
|
375
|
-
if ! _checkout_out=$(git -C repo checkout -B "${BRANCH}" "refs/remotes/origin/pr-checkout" 2>&1); then
|
|
406
|
+
if ! _checkout_out=$(git -C repo checkout -f -B "${BRANCH}" "refs/remotes/origin/pr-checkout" 2>&1); then
|
|
376
407
|
echo "[pool] ERROR: Repo checkout of ${CHECKOUT_REF} failed: ${_checkout_out}" >&2
|
|
377
408
|
printf '%s' "checkout of ${CHECKOUT_REF} failed" > "$GIT_FAILED_MARKER"
|
|
378
409
|
return 1
|
|
@@ -387,7 +418,7 @@ sync_task_branch_to_repo() {
|
|
|
387
418
|
if echo "${_fetch_out}" | grep -q "couldn't find remote ref"; then
|
|
388
419
|
echo "[pool] Branch missing on origin — creating from base '${DEV_BRANCH}'"
|
|
389
420
|
# Create the branch locally from the base branch
|
|
390
|
-
if ! _create_out=$(git -C repo checkout -B "${BRANCH}" "origin/${DEV_BRANCH}" 2>&1); then
|
|
421
|
+
if ! _create_out=$(git -C repo checkout -f -B "${BRANCH}" "origin/${DEV_BRANCH}" 2>&1); then
|
|
391
422
|
echo "[pool] ERROR: Failed to create branch '${BRANCH}' from '${DEV_BRANCH}': ${_create_out}" >&2
|
|
392
423
|
printf '%s' "create branch ${BRANCH} from ${DEV_BRANCH} failed" > "$GIT_FAILED_MARKER"
|
|
393
424
|
return 1
|
|
@@ -409,7 +440,7 @@ sync_task_branch_to_repo() {
|
|
|
409
440
|
# Use `checkout -B` to create or reset the local branch tracking origin.
|
|
410
441
|
# This leaves HEAD attached to a real branch (not detached) so `git status`,
|
|
411
442
|
# `git push`, and any tool that keys off branch state work correctly.
|
|
412
|
-
if ! _checkout_out=$(git -C repo checkout -B "${BRANCH}" "origin/${BRANCH}" 2>&1); then
|
|
443
|
+
if ! _checkout_out=$(git -C repo checkout -f -B "${BRANCH}" "origin/${BRANCH}" 2>&1); then
|
|
413
444
|
echo "[pool] ERROR: Repo checkout of ${BRANCH} failed: ${_checkout_out}" >&2
|
|
414
445
|
printf '%s' "checkout of ${BRANCH} failed" > "$GIT_FAILED_MARKER"
|
|
415
446
|
return 1
|
|
@@ -482,7 +513,7 @@ prepare_workspace_git() {
|
|
|
482
513
|
printf '%s' "post-assignment fetch of ${CHECKOUT_REF} failed" > "$GIT_FAILED_MARKER"
|
|
483
514
|
return 1
|
|
484
515
|
fi
|
|
485
|
-
if ! _checkout_out=$(git -C repo checkout -B "${BRANCH}" "refs/remotes/origin/pr-checkout" 2>&1); then
|
|
516
|
+
if ! _checkout_out=$(git -C repo checkout -f -B "${BRANCH}" "refs/remotes/origin/pr-checkout" 2>&1); then
|
|
486
517
|
echo "[pool] ERROR: post-assignment checkout of ${CHECKOUT_REF} failed: ${_checkout_out}" >&2
|
|
487
518
|
printf '%s' "post-assignment checkout of ${CHECKOUT_REF} failed" > "$GIT_FAILED_MARKER"
|
|
488
519
|
return 1
|