@rallycry/conveyor-agent 10.13.13 → 10.13.14
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-TB5SQIGX.js → chunk-J4UH5TOO.js} +192 -433
- package/dist/chunk-J4UH5TOO.js.map +1 -0
- package/dist/{chunk-YRC57EYG.js → chunk-LZM5OKAC.js} +2 -68
- package/dist/chunk-LZM5OKAC.js.map +1 -0
- package/dist/{chunk-2K6RRTQC.js → chunk-RSIW2UCR.js} +4 -180
- package/dist/chunk-RSIW2UCR.js.map +1 -0
- package/dist/{chunk-IE7EOKMD.js → chunk-VKZ5W2VO.js} +1 -1
- package/dist/chunk-VKZ5W2VO.js.map +1 -0
- package/dist/cli.js +27 -34
- package/dist/cli.js.map +1 -1
- package/dist/{client-ICIWKSK2.js → client-VQIBGPE6.js} +3 -3
- package/dist/index.d.ts +27 -76
- package/dist/index.js +5 -17
- package/dist/index.js.map +1 -1
- package/dist/{protocol-QJRHTTMQ.js → protocol-IHRTO5C4.js} +2 -2
- package/dist/{server-UGE67KIZ.js → server-TVIXWCAI.js} +7 -72
- package/dist/server-TVIXWCAI.js.map +1 -0
- package/package.json +1 -1
- package/runtime/entrypoint.sh +24 -98
- package/dist/chunk-2K6RRTQC.js.map +0 -1
- package/dist/chunk-IE7EOKMD.js.map +0 -1
- package/dist/chunk-TB5SQIGX.js.map +0 -1
- package/dist/chunk-YRC57EYG.js.map +0 -1
- package/dist/server-UGE67KIZ.js.map +0 -1
- /package/dist/{client-ICIWKSK2.js.map → client-VQIBGPE6.js.map} +0 -0
- /package/dist/{protocol-QJRHTTMQ.js.map → protocol-IHRTO5C4.js.map} +0 -0
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
WorkbenchError,
|
|
5
5
|
getWorkbenchClient,
|
|
6
6
|
resetWorkbenchClient
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-LZM5OKAC.js";
|
|
8
8
|
import "./chunk-4VUQ2NPF.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-VKZ5W2VO.js";
|
|
10
10
|
export {
|
|
11
11
|
RemoteProcessHandle,
|
|
12
12
|
WorkbenchClient,
|
|
@@ -14,4 +14,4 @@ export {
|
|
|
14
14
|
getWorkbenchClient,
|
|
15
15
|
resetWorkbenchClient
|
|
16
16
|
};
|
|
17
|
-
//# sourceMappingURL=client-
|
|
17
|
+
//# sourceMappingURL=client-VQIBGPE6.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _project_shared from '@project/shared';
|
|
2
|
-
import { RunnerMode, AgentSessionServiceMethods, AgentMode, PtyChatEventPayload, WorkspaceDiscoveredPort, AgentQuestion, AgentRunnerStatus
|
|
2
|
+
import { RunnerMode, AgentSessionServiceMethods, AgentMode, PtyChatEventPayload, WorkspaceDiscoveredPort, AgentQuestion, AgentRunnerStatus } from '@project/shared';
|
|
3
3
|
export * from '@project/shared';
|
|
4
4
|
import { ChildProcess } from 'node:child_process';
|
|
5
5
|
|
|
@@ -88,7 +88,6 @@ declare class AgentConnection {
|
|
|
88
88
|
private modeChangeCallback;
|
|
89
89
|
private apiKeyUpdateCallback;
|
|
90
90
|
private pullBranchCallback;
|
|
91
|
-
private finalizeSnapshotCallback;
|
|
92
91
|
private runStartCommandCallback;
|
|
93
92
|
private earlyPullBranches;
|
|
94
93
|
private spawnReviewCallback;
|
|
@@ -164,7 +163,6 @@ declare class AgentConnection {
|
|
|
164
163
|
* sessionId is OUR (builder) session — the SEC-9 guard runs on it.
|
|
165
164
|
*/
|
|
166
165
|
reportSessionSpawnFailure(spawnedSessionId: string, error?: string): void;
|
|
167
|
-
onFinalizeSnapshot(callback: () => void): void;
|
|
168
166
|
onRunStartCommand(callback: () => void): void;
|
|
169
167
|
/**
|
|
170
168
|
* Forward a raw chunk of terminal output to the S2 relay (fire-and-forget).
|
|
@@ -437,6 +435,12 @@ interface SessionRunnerPortDiscovery {
|
|
|
437
435
|
start(): Promise<void>;
|
|
438
436
|
stop(): void;
|
|
439
437
|
}
|
|
438
|
+
/** Duck-typed handle onto the boot supervisor — just enough surface for the
|
|
439
|
+
* runner to release the app's start command once the core loop is live.
|
|
440
|
+
* Avoids importing the concrete WorkspaceCommandSupervisor class here. */
|
|
441
|
+
interface SessionRunnerWorkspaceCommands {
|
|
442
|
+
notifyLoopReady(): void;
|
|
443
|
+
}
|
|
440
444
|
interface SessionRunnerDependencies {
|
|
441
445
|
portDiscovery?: SessionRunnerPortDiscovery;
|
|
442
446
|
}
|
|
@@ -474,10 +478,20 @@ declare class SessionRunner {
|
|
|
474
478
|
private readonly portDiscovery;
|
|
475
479
|
/** Main event-loop lag measurement, shared with the heartbeat worker. */
|
|
476
480
|
private readonly loopLag;
|
|
481
|
+
/** Boot supervisor handle, set post-construction once it's known (it is
|
|
482
|
+
* started alongside connect(), before the runner itself exists in some
|
|
483
|
+
* call sites) — notified when the core loop goes live so it can release
|
|
484
|
+
* the app's start command. Optional: never set in tests/paths that don't
|
|
485
|
+
* wire a supervisor. */
|
|
486
|
+
private workspaceCommands;
|
|
477
487
|
constructor(config: SessionRunnerConfig, callbacks: SessionRunnerCallbacks, deps?: SessionRunnerDependencies);
|
|
478
488
|
get state(): AgentRunnerStatus;
|
|
479
489
|
get sessionId(): string;
|
|
480
490
|
get isStopped(): boolean;
|
|
491
|
+
/** Wire the boot supervisor handle post-construction — cli.ts constructs it
|
|
492
|
+
* via startWorkspaceCommandsAfterConnect() (which needs connect() to already
|
|
493
|
+
* be callable), so it can't be a constructor dependency. */
|
|
494
|
+
setWorkspaceCommands(supervisor: SessionRunnerWorkspaceCommands | null): void;
|
|
481
495
|
/**
|
|
482
496
|
* Establish the API connection, wire callbacks, and join the session room.
|
|
483
497
|
* Call this before run() when you need to send events (e.g. setup/start
|
|
@@ -558,6 +572,15 @@ declare class SessionRunner {
|
|
|
558
572
|
private onPassiveTuiActivity;
|
|
559
573
|
/** Run queryBridge.executePassive, swallowing abort errors from stop/softStop. */
|
|
560
574
|
private executePassive;
|
|
575
|
+
/** Shared token-refresh closure for the git-flush call sites (periodic
|
|
576
|
+
* backstop + per-turn flush). Never throws — returns undefined so the
|
|
577
|
+
* caller falls back to its existing remote credentials. */
|
|
578
|
+
private refreshGithubTokenForFlush;
|
|
579
|
+
/** Push uncommitted work to the conveyor-wip ref after a turn. Git-only (no
|
|
580
|
+
* GCS). Primary-branch only — cheap enough to run every turn. Shares the
|
|
581
|
+
* in-flight guard with the periodic backstop so a turn-end flush and a
|
|
582
|
+
* timer tick never overlap. Best-effort, never throws. */
|
|
583
|
+
private flushWipNow;
|
|
561
584
|
/** Periodic best-effort WIP commit + push during normal agent execution.
|
|
562
585
|
* Covers ungraceful pod termination (OOMKilled, node crash/eviction) where
|
|
563
586
|
* the preStop hook + SIGTERM flush don't get a chance to run. No-ops on a
|
|
@@ -570,9 +593,6 @@ declare class SessionRunner {
|
|
|
570
593
|
* `selectBestKey` rotation honest. Best-effort — never throws, no-op when the
|
|
571
594
|
* pod has no OAuth token (e.g. API-key projects). */
|
|
572
595
|
private sampleAndReportKeyUsage;
|
|
573
|
-
/** PUT the WorkPreservation snapshot tar to the bundle's capability URL
|
|
574
|
-
* when this pod is v3 (CONVEYOR_SNAPSHOT_UPLOAD_URL set). Never throws. */
|
|
575
|
-
private uploadGcsSnapshotIfConfigured;
|
|
576
596
|
/** Best-effort WIP commit + push on shutdown so in-flight work isn't lost
|
|
577
597
|
* when a claudespace pod is killed. Must be called BEFORE stop() so the
|
|
578
598
|
* connection is still alive for token refresh. Never throws. */
|
|
@@ -582,14 +602,6 @@ declare class SessionRunner {
|
|
|
582
602
|
private buildFullContext;
|
|
583
603
|
private createQueryBridge;
|
|
584
604
|
private wireConnectionCallbacks;
|
|
585
|
-
/** Eager finalize snapshot triggered by the reconciler's sleep signal
|
|
586
|
-
* (session:finalizeSnapshot). Runs a full workspace capture and uploads it
|
|
587
|
-
* NOW so the sleep confirms on this snapshot instead of waiting for the
|
|
588
|
-
* ~2min periodic flush. Sidecar DB state is intentionally NOT captured — it
|
|
589
|
-
* is no longer durable across sleep/wake (#2623); only the agent's
|
|
590
|
-
* /workspace rides the snapshot. Best-effort: on failure the
|
|
591
|
-
* periodic/shutdown path stays the fallback. No-op on non-v3 pods. */
|
|
592
|
-
private finalizeSnapshotNow;
|
|
593
605
|
/** Proactively refresh the GitHub token before the 1-hour expiry. */
|
|
594
606
|
private refreshGithubToken;
|
|
595
607
|
/** Re-fetch task context to pick up a newly created branch and check it out. */
|
|
@@ -659,67 +671,6 @@ declare function detachWorktreeBranch(projectDir: string, branch: string): Promi
|
|
|
659
671
|
*/
|
|
660
672
|
declare function removeWorktree(projectDir: string, taskId: string): Promise<void>;
|
|
661
673
|
|
|
662
|
-
interface CaptureContext {
|
|
663
|
-
cwd: string;
|
|
664
|
-
branch: string;
|
|
665
|
-
snapshotUploadUrl?: string;
|
|
666
|
-
refreshToken?: () => Promise<string | undefined>;
|
|
667
|
-
wipMessage?: string;
|
|
668
|
-
/** Force the GCS PUT even when the tar is empty (sizeBytes === 0). Used by the
|
|
669
|
-
* sleep FINALIZE path so `snapshotAt` is stamped and the reconciler's
|
|
670
|
-
* teardown confirm lands immediately — an empty workspace is a valid (empty)
|
|
671
|
-
* snapshot; on wake the restore's manifest check falls back to a clean
|
|
672
|
-
* checkout. The periodic path leaves this false so a racing empty capture
|
|
673
|
-
* never clobbers a good snapshot. */
|
|
674
|
-
forceUpload?: boolean;
|
|
675
|
-
}
|
|
676
|
-
interface SnapshotArtifact {
|
|
677
|
-
fileCount: number;
|
|
678
|
-
deletionCount: number;
|
|
679
|
-
sizeBytes: number;
|
|
680
|
-
gcsUploaded: boolean;
|
|
681
|
-
wipPushed: boolean;
|
|
682
|
-
}
|
|
683
|
-
interface RestoreResult {
|
|
684
|
-
source: "gcs" | "wip" | "clean";
|
|
685
|
-
fileCount?: number;
|
|
686
|
-
}
|
|
687
|
-
interface GcsUploadResult {
|
|
688
|
-
uploaded: boolean;
|
|
689
|
-
fileCount: number;
|
|
690
|
-
deletionCount: number;
|
|
691
|
-
sizeBytes: number;
|
|
692
|
-
}
|
|
693
|
-
/**
|
|
694
|
-
* The GCS sink on its own: assemble the snapshot tar (the single capture
|
|
695
|
-
* path) and PUT it to the capability URL. Exposed so a runner whose git-tier
|
|
696
|
-
* flush is driven elsewhere (SessionRunner's periodic flushAllPendingWork)
|
|
697
|
-
* can add the GCS sink to the SAME trigger instead of running a second,
|
|
698
|
-
* racing snapshotter. Calls are serialized: a later capture always PUTs
|
|
699
|
-
* after any earlier in-flight one has settled.
|
|
700
|
-
*/
|
|
701
|
-
declare function uploadSnapshotToGcs(cwd: string, uploadUrl: string | undefined, opts?: {
|
|
702
|
-
forceUpload?: boolean;
|
|
703
|
-
}): Promise<GcsUploadResult>;
|
|
704
|
-
declare function captureSnapshot(ctx: CaptureContext): Promise<SnapshotArtifact>;
|
|
705
|
-
declare function startPeriodic(intervalMs: number, ctx: CaptureContext): void;
|
|
706
|
-
declare function stop(): void;
|
|
707
|
-
declare function finalizeForSleep(ctx: CaptureContext): Promise<SnapshotArtifact>;
|
|
708
|
-
/**
|
|
709
|
-
* Restore precedence at boot: GCS snapshot (tracked+untracked+deletions),
|
|
710
|
-
* else the conveyor-wip ref, else a clean checkout. Accepts the narrow slice
|
|
711
|
-
* of the bundle it reads so v3 pods can call it from env-derived values
|
|
712
|
-
* (CONVEYOR_SNAPSHOT_URL + the task branch) as well as from a full bundle.
|
|
713
|
-
*
|
|
714
|
-
* GCS is only authoritative when the archive's manifest HEAD matches the
|
|
715
|
-
* current clone (checked BEFORE any tree mutation) — a stale or invalid
|
|
716
|
-
* snapshot leaves the tree untouched and falls through to the wip ref, whose
|
|
717
|
-
* own parent-HEAD guard handles staleness.
|
|
718
|
-
*/
|
|
719
|
-
declare function restoreOnBoot(bundle: Pick<BootstrapBundle, "snapshotUrl"> & {
|
|
720
|
-
gitPlan: Pick<BootstrapBundle["gitPlan"], "branch">;
|
|
721
|
-
}, cwd: string): Promise<RestoreResult>;
|
|
722
|
-
|
|
723
674
|
interface ConveyorConfig {
|
|
724
675
|
setupCommand?: string;
|
|
725
676
|
startCommand?: string;
|
|
@@ -742,4 +693,4 @@ declare function runStartCommand(cmd: string, cwd: string, onOutput: (stream: "s
|
|
|
742
693
|
/** Fetch full git history if the repo was cloned with --depth=1. */
|
|
743
694
|
declare function unshallowRepo(workspaceDir: string): void;
|
|
744
695
|
|
|
745
|
-
export { AgentConnection, type AgentConnectionConfig, type AgentRunnerCallbacks, type AgentRunnerConfig, type ApiKeyUpdateData, type
|
|
696
|
+
export { AgentConnection, type AgentConnectionConfig, type AgentRunnerCallbacks, type AgentRunnerConfig, type ApiKeyUpdateData, type ConveyorConfig, type IncomingMessage, type LifecycleCallbacks, type LifecycleConfig, type ModeAction, type ModeTaskContext, SessionRunner, type SessionRunnerCallbacks, type SessionRunnerConfig, type SetModeData, detachWorktreeBranch, ensureWorktree, flushPendingChanges, getCurrentBranch, hasUncommittedChanges, hasUnpushedCommits, loadConveyorConfig, loadForwardPorts, pushToOrigin, removeWorktree, runAuthTokenCommand, runSetupCommand, runStartCommand, stageAndCommit, unshallowRepo, updateRemoteToken };
|
package/dist/index.js
CHANGED
|
@@ -2,8 +2,6 @@ import {
|
|
|
2
2
|
AgentConnection,
|
|
3
3
|
GIT_TIMEOUT_MS,
|
|
4
4
|
SessionRunner,
|
|
5
|
-
captureSnapshot,
|
|
6
|
-
finalizeForSleep,
|
|
7
5
|
flushPendingChanges,
|
|
8
6
|
getCurrentBranch,
|
|
9
7
|
hasUncommittedChanges,
|
|
@@ -11,19 +9,15 @@ import {
|
|
|
11
9
|
loadConveyorConfig,
|
|
12
10
|
loadForwardPorts,
|
|
13
11
|
pushToOrigin,
|
|
14
|
-
restoreOnBoot,
|
|
15
12
|
stageAndCommit,
|
|
16
|
-
startPeriodic,
|
|
17
|
-
stop,
|
|
18
13
|
unshallowRepo,
|
|
19
14
|
updateRemoteToken,
|
|
20
|
-
uploadSnapshotToGcs,
|
|
21
15
|
workspacePathExists
|
|
22
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-J4UH5TOO.js";
|
|
23
17
|
import "./chunk-7TQO4ZF4.js";
|
|
24
18
|
import {
|
|
25
19
|
getWorkbenchClient
|
|
26
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-LZM5OKAC.js";
|
|
27
21
|
import {
|
|
28
22
|
workbenchEnabled
|
|
29
23
|
} from "./chunk-4VUQ2NPF.js";
|
|
@@ -31,8 +25,8 @@ import {
|
|
|
31
25
|
runAuthTokenCommand,
|
|
32
26
|
runSetupCommand,
|
|
33
27
|
runStartCommand
|
|
34
|
-
} from "./chunk-
|
|
35
|
-
import "./chunk-
|
|
28
|
+
} from "./chunk-RSIW2UCR.js";
|
|
29
|
+
import "./chunk-VKZ5W2VO.js";
|
|
36
30
|
|
|
37
31
|
// src/runner/worktree.ts
|
|
38
32
|
import { execFile } from "child_process";
|
|
@@ -102,10 +96,8 @@ async function removeWorktree(projectDir, taskId) {
|
|
|
102
96
|
export {
|
|
103
97
|
AgentConnection,
|
|
104
98
|
SessionRunner,
|
|
105
|
-
captureSnapshot,
|
|
106
99
|
detachWorktreeBranch,
|
|
107
100
|
ensureWorktree,
|
|
108
|
-
finalizeForSleep,
|
|
109
101
|
flushPendingChanges,
|
|
110
102
|
getCurrentBranch,
|
|
111
103
|
hasUncommittedChanges,
|
|
@@ -114,15 +106,11 @@ export {
|
|
|
114
106
|
loadForwardPorts,
|
|
115
107
|
pushToOrigin,
|
|
116
108
|
removeWorktree,
|
|
117
|
-
restoreOnBoot,
|
|
118
109
|
runAuthTokenCommand,
|
|
119
110
|
runSetupCommand,
|
|
120
111
|
runStartCommand,
|
|
121
112
|
stageAndCommit,
|
|
122
|
-
startPeriodic as startWorkPreservation,
|
|
123
|
-
stop as stopWorkPreservation,
|
|
124
113
|
unshallowRepo,
|
|
125
|
-
updateRemoteToken
|
|
126
|
-
uploadSnapshotToGcs
|
|
114
|
+
updateRemoteToken
|
|
127
115
|
};
|
|
128
116
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runner/worktree.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\nimport { join } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { GIT_TIMEOUT_MS, hasUncommittedChanges } from \"./git-utils.js\";\nimport { getWorkbenchClient } from \"../workbench/client.js\";\nimport { workbenchEnabled } from \"../workbench/mode.js\";\nimport { workspacePathExists } from \"../workbench/fs.js\";\n\n// Async git (execFile, no shell) — synchronous child processes freeze the\n// agent's event loop and starve the socket heartbeat; see git-utils.ts.\n// Split-mode pods route over the launcher (the repo lives in the workbench).\nconst execFileAsync = promisify(execFile);\n\nasync function git(cwd: string, args: string[]): Promise<string> {\n if (workbenchEnabled()) {\n const { stdout } = await getWorkbenchClient().execFile(\"git\", args, {\n cwd,\n timeout: GIT_TIMEOUT_MS,\n });\n return stdout;\n }\n const { stdout } = await execFileAsync(\"git\", args, { cwd, timeout: GIT_TIMEOUT_MS });\n return stdout.toString();\n}\n\nconst WORKTREE_DIR = \".worktrees\";\n\nexport async function ensureWorktree(\n projectDir: string,\n taskId: string,\n branch?: string,\n): Promise<string> {\n if (projectDir.includes(`/${WORKTREE_DIR}/`)) {\n return projectDir;\n }\n\n const worktreePath = join(projectDir, WORKTREE_DIR, taskId);\n\n if (await workspacePathExists(worktreePath)) {\n if (branch) {\n if (await hasUncommittedChanges(worktreePath)) {\n return worktreePath;\n }\n try {\n await git(worktreePath, [\"checkout\", \"--detach\", `origin/${branch}`]);\n } catch {\n /* branch doesn't exist on remote yet */\n }\n }\n return worktreePath;\n }\n\n const ref = branch ? `origin/${branch}` : \"HEAD\";\n await git(projectDir, [\"worktree\", \"add\", \"--detach\", worktreePath, ref]);\n\n return worktreePath;\n}\n\n/**\n * Detach any task worktree that has `branch` checked out, releasing the branch lock\n * so the main project directory can check it out. Only touches worktrees inside\n * `.worktrees/` — never the main project directory.\n */\nexport async function detachWorktreeBranch(projectDir: string, branch: string): Promise<void> {\n try {\n const output = await git(projectDir, [\"worktree\", \"list\", \"--porcelain\"]);\n const entries = output.split(\"\\n\\n\");\n for (const entry of entries) {\n const lines = entry.trim().split(\"\\n\");\n const worktreeLine = lines.find((l) => l.startsWith(\"worktree \"));\n const branchLine = lines.find((l) => l.startsWith(\"branch \"));\n if (!worktreeLine || branchLine !== `branch refs/heads/${branch}`) continue;\n\n const worktreePath = worktreeLine.replace(\"worktree \", \"\");\n if (!worktreePath.includes(`/${WORKTREE_DIR}/`)) continue;\n\n try {\n await git(worktreePath, [\"checkout\", \"--detach\"]);\n } catch {\n /* best effort */\n }\n }\n } catch {\n /* best effort */\n }\n}\n\n/**\n * Force-remove is intentional: this runs after task completion or explicit stop.\n * Any uncommitted changes at this point are scratch artifacts, not valuable work.\n */\nexport async function removeWorktree(projectDir: string, taskId: string): Promise<void> {\n const worktreePath = join(projectDir, WORKTREE_DIR, taskId);\n if (!(await workspacePathExists(worktreePath))) return;\n try {\n await git(projectDir, [\"worktree\", \"remove\", worktreePath, \"--force\"]);\n } catch {\n /* best effort */\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/runner/worktree.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\nimport { join } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { GIT_TIMEOUT_MS, hasUncommittedChanges } from \"./git-utils.js\";\nimport { getWorkbenchClient } from \"../workbench/client.js\";\nimport { workbenchEnabled } from \"../workbench/mode.js\";\nimport { workspacePathExists } from \"../workbench/fs.js\";\n\n// Async git (execFile, no shell) — synchronous child processes freeze the\n// agent's event loop and starve the socket heartbeat; see git-utils.ts.\n// Split-mode pods route over the launcher (the repo lives in the workbench).\nconst execFileAsync = promisify(execFile);\n\nasync function git(cwd: string, args: string[]): Promise<string> {\n if (workbenchEnabled()) {\n const { stdout } = await getWorkbenchClient().execFile(\"git\", args, {\n cwd,\n timeout: GIT_TIMEOUT_MS,\n });\n return stdout;\n }\n const { stdout } = await execFileAsync(\"git\", args, { cwd, timeout: GIT_TIMEOUT_MS });\n return stdout.toString();\n}\n\nconst WORKTREE_DIR = \".worktrees\";\n\nexport async function ensureWorktree(\n projectDir: string,\n taskId: string,\n branch?: string,\n): Promise<string> {\n if (projectDir.includes(`/${WORKTREE_DIR}/`)) {\n return projectDir;\n }\n\n const worktreePath = join(projectDir, WORKTREE_DIR, taskId);\n\n if (await workspacePathExists(worktreePath)) {\n if (branch) {\n if (await hasUncommittedChanges(worktreePath)) {\n return worktreePath;\n }\n try {\n await git(worktreePath, [\"checkout\", \"--detach\", `origin/${branch}`]);\n } catch {\n /* branch doesn't exist on remote yet */\n }\n }\n return worktreePath;\n }\n\n const ref = branch ? `origin/${branch}` : \"HEAD\";\n await git(projectDir, [\"worktree\", \"add\", \"--detach\", worktreePath, ref]);\n\n return worktreePath;\n}\n\n/**\n * Detach any task worktree that has `branch` checked out, releasing the branch lock\n * so the main project directory can check it out. Only touches worktrees inside\n * `.worktrees/` — never the main project directory.\n */\nexport async function detachWorktreeBranch(projectDir: string, branch: string): Promise<void> {\n try {\n const output = await git(projectDir, [\"worktree\", \"list\", \"--porcelain\"]);\n const entries = output.split(\"\\n\\n\");\n for (const entry of entries) {\n const lines = entry.trim().split(\"\\n\");\n const worktreeLine = lines.find((l) => l.startsWith(\"worktree \"));\n const branchLine = lines.find((l) => l.startsWith(\"branch \"));\n if (!worktreeLine || branchLine !== `branch refs/heads/${branch}`) continue;\n\n const worktreePath = worktreeLine.replace(\"worktree \", \"\");\n if (!worktreePath.includes(`/${WORKTREE_DIR}/`)) continue;\n\n try {\n await git(worktreePath, [\"checkout\", \"--detach\"]);\n } catch {\n /* best effort */\n }\n }\n } catch {\n /* best effort */\n }\n}\n\n/**\n * Force-remove is intentional: this runs after task completion or explicit stop.\n * Any uncommitted changes at this point are scratch artifacts, not valuable work.\n */\nexport async function removeWorktree(projectDir: string, taskId: string): Promise<void> {\n const worktreePath = join(projectDir, WORKTREE_DIR, taskId);\n if (!(await workspacePathExists(worktreePath))) return;\n try {\n await git(projectDir, [\"worktree\", \"remove\", worktreePath, \"--force\"]);\n } catch {\n /* best effort */\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,gBAAgB;AACzB,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAS1B,IAAM,gBAAgB,UAAU,QAAQ;AAExC,eAAe,IAAI,KAAa,MAAiC;AAC/D,MAAI,iBAAiB,GAAG;AACtB,UAAM,EAAE,QAAAA,QAAO,IAAI,MAAM,mBAAmB,EAAE,SAAS,OAAO,MAAM;AAAA,MAClE;AAAA,MACA,SAAS;AAAA,IACX,CAAC;AACD,WAAOA;AAAA,EACT;AACA,QAAM,EAAE,OAAO,IAAI,MAAM,cAAc,OAAO,MAAM,EAAE,KAAK,SAAS,eAAe,CAAC;AACpF,SAAO,OAAO,SAAS;AACzB;AAEA,IAAM,eAAe;AAErB,eAAsB,eACpB,YACA,QACA,QACiB;AACjB,MAAI,WAAW,SAAS,IAAI,YAAY,GAAG,GAAG;AAC5C,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,KAAK,YAAY,cAAc,MAAM;AAE1D,MAAI,MAAM,oBAAoB,YAAY,GAAG;AAC3C,QAAI,QAAQ;AACV,UAAI,MAAM,sBAAsB,YAAY,GAAG;AAC7C,eAAO;AAAA,MACT;AACA,UAAI;AACF,cAAM,IAAI,cAAc,CAAC,YAAY,YAAY,UAAU,MAAM,EAAE,CAAC;AAAA,MACtE,QAAQ;AAAA,MAER;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,QAAM,MAAM,SAAS,UAAU,MAAM,KAAK;AAC1C,QAAM,IAAI,YAAY,CAAC,YAAY,OAAO,YAAY,cAAc,GAAG,CAAC;AAExE,SAAO;AACT;AAOA,eAAsB,qBAAqB,YAAoB,QAA+B;AAC5F,MAAI;AACF,UAAM,SAAS,MAAM,IAAI,YAAY,CAAC,YAAY,QAAQ,aAAa,CAAC;AACxE,UAAM,UAAU,OAAO,MAAM,MAAM;AACnC,eAAW,SAAS,SAAS;AAC3B,YAAM,QAAQ,MAAM,KAAK,EAAE,MAAM,IAAI;AACrC,YAAM,eAAe,MAAM,KAAK,CAAC,MAAM,EAAE,WAAW,WAAW,CAAC;AAChE,YAAM,aAAa,MAAM,KAAK,CAAC,MAAM,EAAE,WAAW,SAAS,CAAC;AAC5D,UAAI,CAAC,gBAAgB,eAAe,qBAAqB,MAAM,GAAI;AAEnE,YAAM,eAAe,aAAa,QAAQ,aAAa,EAAE;AACzD,UAAI,CAAC,aAAa,SAAS,IAAI,YAAY,GAAG,EAAG;AAEjD,UAAI;AACF,cAAM,IAAI,cAAc,CAAC,YAAY,UAAU,CAAC;AAAA,MAClD,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AACF;AAMA,eAAsB,eAAe,YAAoB,QAA+B;AACtF,QAAM,eAAe,KAAK,YAAY,cAAc,MAAM;AAC1D,MAAI,CAAE,MAAM,oBAAoB,YAAY,EAAI;AAChD,MAAI;AACF,UAAM,IAAI,YAAY,CAAC,YAAY,UAAU,cAAc,SAAS,CAAC;AAAA,EACvE,QAAQ;AAAA,EAER;AACF;","names":["stdout"]}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
parseLine,
|
|
5
5
|
timingSafeTokenEqual,
|
|
6
6
|
writeFrame
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-VKZ5W2VO.js";
|
|
8
8
|
export {
|
|
9
9
|
DEFAULT_WORKBENCH_PORT,
|
|
10
10
|
FrameReader,
|
|
@@ -12,4 +12,4 @@ export {
|
|
|
12
12
|
timingSafeTokenEqual,
|
|
13
13
|
writeFrame
|
|
14
14
|
};
|
|
15
|
-
//# sourceMappingURL=protocol-
|
|
15
|
+
//# sourceMappingURL=protocol-IHRTO5C4.js.map
|
|
@@ -1,23 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
|
-
buildSnapshotTar,
|
|
3
|
-
extractSnapshotTar,
|
|
4
2
|
loadPtySpawn,
|
|
5
3
|
terminateProcessGroup
|
|
6
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RSIW2UCR.js";
|
|
7
5
|
import {
|
|
8
6
|
FrameReader,
|
|
9
7
|
timingSafeTokenEqual,
|
|
10
8
|
writeFrame
|
|
11
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-VKZ5W2VO.js";
|
|
12
10
|
|
|
13
11
|
// src/workbench/server.ts
|
|
14
12
|
import { spawn } from "child_process";
|
|
15
|
-
import {
|
|
16
|
-
import { mkdtemp, readdir, readFile, rm, stat } from "fs/promises";
|
|
13
|
+
import { readdir, readFile, stat } from "fs/promises";
|
|
17
14
|
import { createServer } from "net";
|
|
18
|
-
|
|
19
|
-
import { join } from "path";
|
|
20
|
-
var TAR_CHUNK_BYTES = 64 * 1024;
|
|
15
|
+
var READ_CHUNK_BYTES = 64 * 1024;
|
|
21
16
|
function errCode(err) {
|
|
22
17
|
return typeof err?.code === "string" ? err.code : void 0;
|
|
23
18
|
}
|
|
@@ -90,12 +85,6 @@ function dispatch(socket, req, sink, opts) {
|
|
|
90
85
|
case "pty":
|
|
91
86
|
void runPty(socket, req, sink);
|
|
92
87
|
return;
|
|
93
|
-
case "snapshotBuild":
|
|
94
|
-
void runSnapshotBuild(socket, req);
|
|
95
|
-
return;
|
|
96
|
-
case "snapshotExtract":
|
|
97
|
-
void runSnapshotExtract(socket, req, sink);
|
|
98
|
-
return;
|
|
99
88
|
case "readFile":
|
|
100
89
|
void runReadFile(socket, req);
|
|
101
90
|
return;
|
|
@@ -203,67 +192,13 @@ async function runPty(socket, req, sink) {
|
|
|
203
192
|
}
|
|
204
193
|
});
|
|
205
194
|
}
|
|
206
|
-
async function runSnapshotBuild(socket, req) {
|
|
207
|
-
try {
|
|
208
|
-
const result = await buildSnapshotTar(req.cwd);
|
|
209
|
-
try {
|
|
210
|
-
writeFrame(socket, {
|
|
211
|
-
t: "meta",
|
|
212
|
-
sizeBytes: result.sizeBytes,
|
|
213
|
-
fileCount: result.fileCount,
|
|
214
|
-
deletionCount: result.deletionCount,
|
|
215
|
-
capturedAt: result.capturedAt
|
|
216
|
-
});
|
|
217
|
-
await new Promise((resolve, reject) => {
|
|
218
|
-
const stream = createReadStream(result.tarPath, { highWaterMark: TAR_CHUNK_BYTES });
|
|
219
|
-
stream.on("data", (chunk) => {
|
|
220
|
-
writeFrame(socket, { t: "data", d: Buffer.from(chunk).toString("base64") });
|
|
221
|
-
});
|
|
222
|
-
stream.on("end", () => resolve());
|
|
223
|
-
stream.on("error", reject);
|
|
224
|
-
socket.on("close", () => stream.destroy());
|
|
225
|
-
});
|
|
226
|
-
writeFrame(socket, { t: "end" });
|
|
227
|
-
} finally {
|
|
228
|
-
await result.cleanup();
|
|
229
|
-
}
|
|
230
|
-
} catch (err) {
|
|
231
|
-
sendError(socket, err);
|
|
232
|
-
}
|
|
233
|
-
socket.end();
|
|
234
|
-
}
|
|
235
|
-
async function runSnapshotExtract(socket, req, sink) {
|
|
236
|
-
const staging = await mkdtemp(join(tmpdir(), "conveyor-workbench-extract-"));
|
|
237
|
-
const tarPath = join(staging, "snapshot.tar.gz");
|
|
238
|
-
const out = createWriteStream(tarPath);
|
|
239
|
-
try {
|
|
240
|
-
await new Promise((resolve, reject) => {
|
|
241
|
-
sink.setHandler((frame) => {
|
|
242
|
-
if (frame.t === "data" && typeof frame.d === "string") {
|
|
243
|
-
out.write(Buffer.from(frame.d, "base64"));
|
|
244
|
-
} else if (frame.t === "end") {
|
|
245
|
-
out.end(() => resolve());
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
|
-
out.on("error", reject);
|
|
249
|
-
socket.on("close", () => reject(new Error("connection closed before end frame")));
|
|
250
|
-
});
|
|
251
|
-
const value = await extractSnapshotTar(req.cwd, tarPath);
|
|
252
|
-
writeFrame(socket, { t: "result", value });
|
|
253
|
-
} catch (err) {
|
|
254
|
-
sendError(socket, err);
|
|
255
|
-
} finally {
|
|
256
|
-
await rm(staging, { recursive: true, force: true }).catch(() => void 0);
|
|
257
|
-
socket.end();
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
195
|
async function runReadFile(socket, req) {
|
|
261
196
|
try {
|
|
262
197
|
const content = await readFile(req.path);
|
|
263
|
-
for (let offset = 0; offset < content.length; offset +=
|
|
198
|
+
for (let offset = 0; offset < content.length; offset += READ_CHUNK_BYTES) {
|
|
264
199
|
writeFrame(socket, {
|
|
265
200
|
t: "data",
|
|
266
|
-
d: content.subarray(offset, offset +
|
|
201
|
+
d: content.subarray(offset, offset + READ_CHUNK_BYTES).toString("base64")
|
|
267
202
|
});
|
|
268
203
|
}
|
|
269
204
|
writeFrame(socket, { t: "end" });
|
|
@@ -310,4 +245,4 @@ async function runReaddir(socket, req) {
|
|
|
310
245
|
export {
|
|
311
246
|
startWorkbenchServer
|
|
312
247
|
};
|
|
313
|
-
//# sourceMappingURL=server-
|
|
248
|
+
//# sourceMappingURL=server-TVIXWCAI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/workbench/server.ts"],"sourcesContent":["/**\n * The workbench launcher — PID 1 of the workbench container in split-mode\n * pods (CONVEYOR_MODE=workbench). A token-authed, loopback-only exec daemon:\n * the agent container drives every workspace-touching operation through it\n * (shell commands, the claude PTY, git, file reads) while staying insulated\n * from workload OOMs in its own container cgroup.\n *\n * One connection = one operation. A dropped connection kills the operation's\n * process (group) — the agent dying, restarting, or timing out must never\n * leave orphans accumulating against the workbench memory budget.\n */\n\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport { readdir, readFile, stat } from \"node:fs/promises\";\nimport { createServer, type Socket } from \"node:net\";\nimport { terminateProcessGroup } from \"../setup/commands.js\";\nimport { loadPtySpawn, type PtyProcess } from \"../harness/pty/pty-support.js\";\nimport {\n FrameReader,\n timingSafeTokenEqual,\n writeFrame,\n type WorkbenchFrame,\n type WorkbenchRequest,\n} from \"./protocol.js\";\n\nconst READ_CHUNK_BYTES = 64 * 1024;\n\nexport interface WorkbenchServerOptions {\n port: number;\n token: string;\n version: string;\n}\n\nexport interface WorkbenchServerHandle {\n port: number;\n close(): Promise<void>;\n}\n\nfunction errCode(err: unknown): string | undefined {\n return typeof (err as { code?: unknown })?.code === \"string\"\n ? (err as { code: string }).code\n : undefined;\n}\n\nfunction sendError(socket: Socket, err: unknown): void {\n const frame: WorkbenchFrame = {\n t: \"error\",\n message: err instanceof Error ? err.message : String(err),\n ...(errCode(err) ? { code: errCode(err) } : {}),\n };\n writeFrame(socket, frame);\n}\n\n/** Merge request env over the daemon's own (mirrors the local spawn paths,\n * which inherit process.env and let callers override). */\nfunction mergedEnv(overrides?: Record<string, string>): NodeJS.ProcessEnv {\n return overrides ? { ...process.env, ...overrides } : { ...process.env };\n}\n\n/**\n * Post-dispatch inbound frames for the connection's operation. Handlers\n * assign `onNext`; frames arriving before assignment are queued so an input\n * racing the (async) pty spawn is never dropped.\n */\nclass FrameSink {\n private pending: WorkbenchFrame[] = [];\n private handler: ((frame: WorkbenchFrame) => void) | null = null;\n\n push(frame: WorkbenchFrame): void {\n if (this.handler) this.handler(frame);\n else this.pending.push(frame);\n }\n\n setHandler(handler: (frame: WorkbenchFrame) => void): void {\n this.handler = handler;\n for (const frame of this.pending.splice(0)) handler(frame);\n }\n}\n\nexport async function startWorkbenchServer(\n opts: WorkbenchServerOptions,\n): Promise<WorkbenchServerHandle> {\n const server = createServer((socket) => handleConnection(socket, opts));\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(opts.port, \"127.0.0.1\", () => resolve());\n });\n const address = server.address();\n const port = typeof address === \"object\" && address ? address.port : opts.port;\n return {\n port,\n close: () =>\n new Promise<void>((resolve) => {\n server.close(() => resolve());\n }),\n };\n}\n\nfunction handleConnection(socket: Socket, opts: WorkbenchServerOptions): void {\n socket.on(\"error\", () => undefined);\n const sink = new FrameSink();\n let dispatched = false;\n const reader = new FrameReader((frame) => {\n if (dispatched) {\n sink.push(frame as unknown as WorkbenchFrame);\n return;\n }\n dispatched = true;\n dispatch(socket, frame as unknown as WorkbenchRequest, sink, opts);\n });\n socket.on(\"data\", (chunk: Buffer) => reader.push(chunk));\n}\n\nfunction dispatch(\n socket: Socket,\n req: WorkbenchRequest,\n sink: FrameSink,\n opts: WorkbenchServerOptions,\n): void {\n if (!req || typeof req !== \"object\" || !timingSafeTokenEqual(req.token ?? \"\", opts.token)) {\n writeFrame(socket, { t: \"error\", message: \"unauthorized\", code: \"unauthorized\" });\n socket.destroy();\n return;\n }\n switch (req.op) {\n case \"ping\":\n writeFrame(socket, { t: \"pong\", version: opts.version });\n socket.end();\n return;\n case \"exec\":\n runExec(socket, req, sink);\n return;\n case \"pty\":\n void runPty(socket, req, sink);\n return;\n case \"readFile\":\n void runReadFile(socket, req);\n return;\n case \"stat\":\n void runStat(socket, req);\n return;\n case \"readdir\":\n void runReaddir(socket, req);\n return;\n default:\n writeFrame(socket, { t: \"error\", message: \"unknown op\", code: \"bad_request\" });\n socket.end();\n }\n}\n\nfunction runExec(\n socket: Socket,\n req: Extract<WorkbenchRequest, { op: \"exec\" }>,\n sink: FrameSink,\n): void {\n let child: ChildProcess;\n try {\n // Argv form (git and friends) spawns directly with no shell; shell form\n // (setup/start commands) mirrors the local `spawn(\"sh\", [\"-c\", cmd])`.\n const [argvFile, ...argvRest] = req.argv ?? [];\n child = argvFile\n ? spawn(argvFile, argvRest, {\n cwd: req.cwd,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n detached: true,\n env: mergedEnv(req.env),\n })\n : spawn(\"sh\", [\"-c\", req.command ?? \"\"], {\n cwd: req.cwd,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n detached: true,\n env: mergedEnv(req.env),\n });\n } catch (err) {\n sendError(socket, err);\n socket.end();\n return;\n }\n\n let settled = false;\n sink.setHandler((frame) => {\n if (frame.t === \"signal\" && frame.mode === \"term-group\" && !settled) {\n void terminateProcessGroup(child);\n }\n });\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n writeFrame(socket, { t: \"out\", s: \"stdout\", d: chunk.toString(\"base64\") });\n });\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n writeFrame(socket, { t: \"out\", s: \"stderr\", d: chunk.toString(\"base64\") });\n });\n child.on(\"close\", (code, signal) => {\n settled = true;\n writeFrame(socket, { t: \"exit\", code, signal });\n socket.end();\n });\n child.on(\"error\", (err) => {\n if (settled) return;\n settled = true;\n sendError(socket, err);\n socket.end();\n });\n // Agent gone (crash, timeout-abort, container death) → no orphans.\n socket.on(\"close\", () => {\n if (!settled) void terminateProcessGroup(child);\n });\n}\n\nasync function runPty(\n socket: Socket,\n req: Extract<WorkbenchRequest, { op: \"pty\" }>,\n sink: FrameSink,\n): Promise<void> {\n let pty: PtyProcess;\n try {\n const ptySpawn = await loadPtySpawn();\n pty = ptySpawn(req.file, req.args, {\n name: \"xterm-color\",\n cols: req.cols,\n rows: req.rows,\n cwd: req.cwd,\n env: req.env,\n });\n } catch (err) {\n sendError(socket, err);\n socket.end();\n return;\n }\n\n let exited = false;\n sink.setHandler((frame) => {\n try {\n if (frame.t === \"input\" && typeof frame.d === \"string\") {\n pty.write(Buffer.from(frame.d, \"base64\").toString(\"utf8\"));\n } else if (\n frame.t === \"resize\" &&\n typeof frame.cols === \"number\" &&\n typeof frame.rows === \"number\"\n ) {\n pty.resize(frame.cols, frame.rows);\n } else if (frame.t === \"kill\") {\n pty.kill(typeof frame.sig === \"string\" ? frame.sig : undefined);\n }\n } catch {\n /* pty already dead — exit handler owns teardown */\n }\n });\n pty.onData((data) => {\n writeFrame(socket, { t: \"data\", d: Buffer.from(data, \"utf8\").toString(\"base64\") });\n });\n pty.onExit((event) => {\n exited = true;\n writeFrame(socket, { t: \"exit\", code: event.exitCode, signal: null });\n socket.end();\n });\n socket.on(\"close\", () => {\n if (!exited) {\n try {\n pty.kill();\n } catch {\n /* already dead */\n }\n }\n });\n}\n\nasync function runReadFile(\n socket: Socket,\n req: Extract<WorkbenchRequest, { op: \"readFile\" }>,\n): Promise<void> {\n try {\n const content = await readFile(req.path);\n for (let offset = 0; offset < content.length; offset += READ_CHUNK_BYTES) {\n writeFrame(socket, {\n t: \"data\",\n d: content.subarray(offset, offset + READ_CHUNK_BYTES).toString(\"base64\"),\n });\n }\n writeFrame(socket, { t: \"end\" });\n } catch (err) {\n sendError(socket, err);\n }\n socket.end();\n}\n\nasync function runStat(\n socket: Socket,\n req: Extract<WorkbenchRequest, { op: \"stat\" }>,\n): Promise<void> {\n try {\n const s = await stat(req.path);\n writeFrame(socket, {\n t: \"stat\",\n exists: true,\n isFile: s.isFile(),\n isDirectory: s.isDirectory(),\n size: s.size,\n mtimeMs: s.mtimeMs,\n });\n } catch (err) {\n if (errCode(err) === \"ENOENT\") {\n writeFrame(socket, {\n t: \"stat\",\n exists: false,\n isFile: false,\n isDirectory: false,\n size: 0,\n mtimeMs: 0,\n });\n } else {\n sendError(socket, err);\n }\n }\n socket.end();\n}\n\nasync function runReaddir(\n socket: Socket,\n req: Extract<WorkbenchRequest, { op: \"readdir\" }>,\n): Promise<void> {\n try {\n writeFrame(socket, { t: \"entries\", names: await readdir(req.path) });\n } catch (err) {\n sendError(socket, err);\n }\n socket.end();\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rallycry/conveyor-agent",
|
|
3
|
-
"version": "10.13.
|
|
3
|
+
"version": "10.13.14",
|
|
4
4
|
"description": "Conveyor Agent Runner v10 - PTY harness for the task chat (SDK harness for audit/project-chat). Agent-as-User architecture with BaseService patterns. Works locally too.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
package/runtime/entrypoint.sh
CHANGED
|
@@ -334,13 +334,6 @@ boot_mark "env injected — entering user-home wiring"
|
|
|
334
334
|
|
|
335
335
|
export ANTHROPIC_API_KEY=$(echo "${BOOTSTRAP_JSON}" | jq -r '.anthropicKey // empty')
|
|
336
336
|
export CLOUDSDK_AUTH_ACCESS_TOKEN=$(echo "${BOOTSTRAP_JSON}" | jq -r '.gcpToken // empty')
|
|
337
|
-
# WorkPreservation GCS tier (spec §Durability & snapshots): the upload URL is
|
|
338
|
-
# long-lived (used for the pod's whole lifetime of periodic captures + sleep
|
|
339
|
-
# finalize); the download URL is present only when a snapshot exists and is
|
|
340
|
-
# consumed once by restoreOnBoot.
|
|
341
|
-
CONVEYOR_SNAPSHOT_UPLOAD_URL=$(echo "${BOOTSTRAP_JSON}" | jq -r '.snapshotUploadUrl // empty')
|
|
342
|
-
CONVEYOR_SNAPSHOT_URL=$(echo "${BOOTSTRAP_JSON}" | jq -r '.snapshotUrl // empty')
|
|
343
|
-
export CONVEYOR_SNAPSHOT_UPLOAD_URL CONVEYOR_SNAPSHOT_URL
|
|
344
337
|
unset BOOTSTRAP_JSON
|
|
345
338
|
# POD_BOOTSTRAP_TOKEN is deliberately NOT unset: it is the credential-refresh
|
|
346
339
|
# key. The agent re-polls GET /api/v3/pods/bootstrap with it to swap in a
|
|
@@ -688,96 +681,29 @@ reset_tracked_repo_changes_before_assignment_checkout() {
|
|
|
688
681
|
}
|
|
689
682
|
|
|
690
683
|
# Shared by both the pod-image branch and the repo-present-non-pod-image branch
|
|
691
|
-
# of prepare_workspace_git: refresh the remote token
|
|
692
|
-
#
|
|
693
|
-
#
|
|
694
|
-
#
|
|
695
|
-
#
|
|
696
|
-
#
|
|
684
|
+
# of prepare_workspace_git: refresh the remote token and warm origin/<base>.
|
|
685
|
+
# The agent (ensureOnTaskBranch, Tasks 4-5) now owns the task-branch
|
|
686
|
+
# fetch/checkout/create-from-base and the merge of base into the task branch —
|
|
687
|
+
# this function no longer touches BRANCH or CHECKOUT_REF at all. On success the
|
|
688
|
+
# repo is left on a clean tree with an up-to-date origin/<base> remote-tracking
|
|
689
|
+
# ref. On any failure it writes GIT_FAILED_MARKER and returns 1 — callers must
|
|
690
|
+
# not fall through to `: > "$GIT_READY_MARKER"` in that case.
|
|
697
691
|
sync_task_branch_to_repo() {
|
|
698
692
|
# Guarded so a set-url failure writes the failed marker instead of aborting
|
|
699
693
|
# the backgrounded subshell (which would leave the agent waiting forever).
|
|
700
694
|
if ! git -C repo remote set-url origin "https://x-access-token:${CONVEYOR_GITHUB_TOKEN}@github.com/${REPO_OWNER}/${REPO_NAME}.git" 2>/dev/null; then
|
|
701
|
-
echo "[pool] ERROR: remote set-url failed
|
|
702
|
-
printf '%s' "
|
|
695
|
+
echo "[pool] ERROR: remote set-url failed" >&2
|
|
696
|
+
printf '%s' "remote set-url failed" > "$GIT_FAILED_MARKER"
|
|
703
697
|
return 1
|
|
704
698
|
fi
|
|
705
|
-
#
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
if ! _fetch_dev_out=$(git -C repo fetch origin "+refs/heads/${DEV_BRANCH}:refs/remotes/origin/${DEV_BRANCH}" 2>&1); then
|
|
709
|
-
echo "[pool] WARN: fetch origin/${DEV_BRANCH} failed: ${_fetch_dev_out}" >&2
|
|
699
|
+
# Warm origin/<base> so the agent's checkout/fetch is a fast-forward.
|
|
700
|
+
if ! _fetch_out=$(git -C repo fetch origin "+refs/heads/${BASE_BRANCH}:refs/remotes/origin/${BASE_BRANCH}" 2>&1); then
|
|
701
|
+
echo "[pool] WARN: fetch origin/${BASE_BRANCH} failed: ${_fetch_out}" >&2
|
|
710
702
|
fi
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
if [ -n "${CHECKOUT_REF}" ]; then
|
|
717
|
-
echo "[pool] Fetching checkout ref ${CHECKOUT_REF} for review branch ${BRANCH}..."
|
|
718
|
-
if ! _fetch_checkout_out=$(git -C repo fetch origin "+${CHECKOUT_REF}:refs/remotes/origin/pr-checkout" 2>&1); then
|
|
719
|
-
echo "[pool] ERROR: Repo fetch failed for checkout ref '${CHECKOUT_REF}': ${_fetch_checkout_out}" >&2
|
|
720
|
-
printf '%s' "fetch checkout ref ${CHECKOUT_REF} failed" > "$GIT_FAILED_MARKER"
|
|
721
|
-
return 1
|
|
722
|
-
fi
|
|
723
|
-
if ! _checkout_out=$(git -C repo checkout -f -B "${BRANCH}" "refs/remotes/origin/pr-checkout" 2>&1); then
|
|
724
|
-
echo "[pool] ERROR: Repo checkout of ${CHECKOUT_REF} failed: ${_checkout_out}" >&2
|
|
725
|
-
printf '%s' "checkout of ${CHECKOUT_REF} failed" > "$GIT_FAILED_MARKER"
|
|
726
|
-
return 1
|
|
727
|
-
fi
|
|
728
|
-
else
|
|
729
|
-
# Fetch the task branch into a local ref explicitly so we can check it out.
|
|
730
|
-
# If the branch doesn't exist on origin, create it from the base branch.
|
|
731
|
-
# Note: if BRANCH equals DEV_BRANCH (deferred-branch case), the fetch above
|
|
732
|
-
# already populated the ref and this is a no-op.
|
|
733
|
-
if ! _fetch_out=$(git -C repo fetch origin "+refs/heads/${BRANCH}:refs/remotes/origin/${BRANCH}" 2>&1); then
|
|
734
|
-
# Check if the error is specifically due to the remote ref not existing
|
|
735
|
-
if echo "${_fetch_out}" | grep -q "couldn't find remote ref"; then
|
|
736
|
-
echo "[pool] Branch missing on origin — creating from base '${DEV_BRANCH}'"
|
|
737
|
-
# Create the branch locally from the base branch
|
|
738
|
-
if ! _create_out=$(git -C repo checkout -f -B "${BRANCH}" "origin/${DEV_BRANCH}" 2>&1); then
|
|
739
|
-
echo "[pool] ERROR: Failed to create branch '${BRANCH}' from '${DEV_BRANCH}': ${_create_out}" >&2
|
|
740
|
-
printf '%s' "create branch ${BRANCH} from ${DEV_BRANCH} failed" > "$GIT_FAILED_MARKER"
|
|
741
|
-
return 1
|
|
742
|
-
fi
|
|
743
|
-
# Push the new branch to origin with -u to set up tracking
|
|
744
|
-
if ! _push_out=$(git -C repo push -u origin "${BRANCH}" 2>&1); then
|
|
745
|
-
echo "[pool] ERROR: Failed to push new branch '${BRANCH}': ${_push_out}" >&2
|
|
746
|
-
printf '%s' "push new branch ${BRANCH} failed" > "$GIT_FAILED_MARKER"
|
|
747
|
-
return 1
|
|
748
|
-
fi
|
|
749
|
-
echo "[pool] Created and pushed new branch '${BRANCH}' from '${DEV_BRANCH}'"
|
|
750
|
-
else
|
|
751
|
-
# Other git error (auth, network, etc.) — fail loud as before
|
|
752
|
-
echo "[pool] ERROR: Repo fetch failed for branch '${BRANCH}': ${_fetch_out}" >&2
|
|
753
|
-
printf '%s' "fetch branch ${BRANCH} failed" > "$GIT_FAILED_MARKER"
|
|
754
|
-
return 1
|
|
755
|
-
fi
|
|
756
|
-
fi
|
|
757
|
-
# Use `checkout -B` to create or reset the local branch tracking origin.
|
|
758
|
-
# This leaves HEAD attached to a real branch (not detached) so `git status`,
|
|
759
|
-
# `git push`, and any tool that keys off branch state work correctly.
|
|
760
|
-
if ! _checkout_out=$(git -C repo checkout -f -B "${BRANCH}" "origin/${BRANCH}" 2>&1); then
|
|
761
|
-
echo "[pool] ERROR: Repo checkout of ${BRANCH} failed: ${_checkout_out}" >&2
|
|
762
|
-
printf '%s' "checkout of ${BRANCH} failed" > "$GIT_FAILED_MARKER"
|
|
763
|
-
return 1
|
|
764
|
-
fi
|
|
765
|
-
fi
|
|
766
|
-
|
|
767
|
-
# Merge latest dev into the task branch so the agent starts on up-to-date
|
|
768
|
-
# base code. Skip if the branch already contains origin/dev (fast-path)
|
|
769
|
-
# or if BRANCH is the dev branch itself.
|
|
770
|
-
if [ "${BRANCH}" != "${DEV_BRANCH}" ] && git -C repo rev-parse "origin/${DEV_BRANCH}" >/dev/null 2>&1; then
|
|
771
|
-
if git -C repo merge-base --is-ancestor "origin/${DEV_BRANCH}" HEAD; then
|
|
772
|
-
echo "[pool] Branch already up-to-date with origin/${DEV_BRANCH}"
|
|
773
|
-
elif ! _merge_out=$(git -C repo merge "origin/${DEV_BRANCH}" --no-edit 2>&1); then
|
|
774
|
-
echo "[pool] WARN: merge origin/${DEV_BRANCH} failed, aborting: ${_merge_out}" >&2
|
|
775
|
-
git -C repo merge --abort 2>/dev/null || true
|
|
776
|
-
else
|
|
777
|
-
echo "[pool] Merged origin/${DEV_BRANCH} into ${BRANCH}"
|
|
778
|
-
fi
|
|
779
|
-
fi
|
|
780
|
-
echo "[pool] Repo updated to ${BRANCH}@$(git -C repo rev-parse --short HEAD)"
|
|
703
|
+
# Leave the tree clean; the agent (ensureOnTaskBranch) does the authoritative
|
|
704
|
+
# checkout + conveyor-wip restore. No task-branch checkout, no dev merge here.
|
|
705
|
+
reset_tracked_repo_changes_before_assignment_checkout || return 1
|
|
706
|
+
echo "[pool] Repo remote ready; agent will checkout ${BRANCH}"
|
|
781
707
|
return 0
|
|
782
708
|
}
|
|
783
709
|
|
|
@@ -803,11 +729,10 @@ prepare_workspace_git() {
|
|
|
803
729
|
mark_git_ready
|
|
804
730
|
elif [ -d "repo/.git" ] && [ -n "${CONVEYOR_GITHUB_TOKEN}" ]; then
|
|
805
731
|
# Non-image but repo already present (e.g. a baked image running without
|
|
806
|
-
# CONVEYOR_POD_IMAGE set).
|
|
807
|
-
#
|
|
808
|
-
#
|
|
809
|
-
# branch
|
|
810
|
-
# branch so this path ends up in the same checked-out-on-task-branch state.
|
|
732
|
+
# CONVEYOR_POD_IMAGE set). Run the exact same remote-refresh + base-warm
|
|
733
|
+
# flow as the pod-image branch so this path ends up in the same
|
|
734
|
+
# clean-tree, remote-ready state; the agent (ensureOnTaskBranch) does the
|
|
735
|
+
# actual task-branch checkout regardless of which branch got it here.
|
|
811
736
|
echo "[pool] Repo present (non-pod-image) — updating repo to latest (branch=${BRANCH})..."
|
|
812
737
|
if ! sync_task_branch_to_repo; then
|
|
813
738
|
return 1
|
|
@@ -840,9 +765,10 @@ prepare_workspace_git() {
|
|
|
840
765
|
# branch. A naive `clone --branch <task>` dies with "Remote branch not
|
|
841
766
|
# found" when the task branch has never been pushed to origin, which
|
|
842
767
|
# crash-loops the pod on every fresh task. sync_task_branch_to_repo below
|
|
843
|
-
#
|
|
844
|
-
#
|
|
845
|
-
#
|
|
768
|
+
# only refreshes the remote and warms origin/<base> from this clone; the
|
|
769
|
+
# agent (ensureOnTaskBranch) resolves/creates ${BRANCH} from it. Full
|
|
770
|
+
# depth (no --depth 1) so the agent's base-merge never hits "refusing to
|
|
771
|
+
# merge unrelated histories".
|
|
846
772
|
if ! _clone_out=$(git clone --single-branch --branch "${BASE_BRANCH}" \
|
|
847
773
|
"https://x-access-token:${CONVEYOR_GITHUB_TOKEN}@github.com/${REPO_OWNER}/${REPO_NAME}.git" \
|
|
848
774
|
repo 2>&1); then
|