Skip to content

Commit 630f4f4

Browse files
V48 Gate 3 (implementation-only): deposit host selection — InlineHost (dev) / VercelSandboxHost (prod)
Closes the prod-host loose end: resolveDepositPipelineHost now selects the Host implementation. selectDepositHostKind (pure, tested) honors an explicit BITCODE_PIPELINE_HOST, else routes a Vercel serverless runtime (no git/FS) to the sandbox host and a local/dev runtime to the in-process InlineHost. The sandbox path constructs VercelSandboxHost via loadVercelSandboxFactory() (the @vercel/sandbox SDK) with Vercel auth (OIDC-preferred, else access token). resolveDepositPipelineHost is now async; the route awaits it. Both implement the same primitive, so provisioning is identical downstream: the host clones the full checkout, readWorkspaceSources materializes the verbatim source into the inventory, and measurement reads it — on either host. selectDepositHostKind unit tests (explicit override, Vercel→sandbox, local→inline); provisioning + route suites 8/8; tsc clean. Prod deposit now rides the sandbox host (its infra — @vercel/sandbox + VERCEL_* creds + git in the sandbox image — is deployment config). Known follow-on: the sandbox workspace reads files per-call (readFileToBuffer); batch via tar for large repos. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 33a6408 commit 630f4f4

3 files changed

Lines changed: 58 additions & 6 deletions

File tree

uapi/app/api/deposit/synthesize-options/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ export async function POST(request: Request) {
207207
// in-process; the Vercel Sandbox host in prod). The static-analysis measurement
208208
// reads the real full source (inventory.sources); bounded samples feed the
209209
// prompts. This is the host's job — the dispatching request does not clone.
210-
const host = resolveDepositPipelineHost();
210+
const host = await resolveDepositPipelineHost();
211211
await emitStatus(
212212
`Provisioning ${repositoryFullName}@${reference} on the ${host.capabilities.hostKind} host…`,
213213
);

uapi/lib/deposit-source-provisioning.ts

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
import {
1818
InlineHost,
19+
VercelSandboxHost,
20+
loadVercelSandboxFactory,
1921
readWorkspaceSources,
2022
type BitcodePipelineHost,
2123
type HostSourceFile,
@@ -58,12 +60,46 @@ function pickSamples(sources: HostSourceFile[]): { path: string; excerpt: string
5860
.map((path) => ({ path, excerpt: (byPath.get(path) || '').slice(0, MAX_SAMPLE_CHARS) }));
5961
}
6062

63+
export type DepositHostKind = 'inline' | 'vercel-sandbox';
64+
65+
/**
66+
* Select the deposit Host kind. Explicit `BITCODE_PIPELINE_HOST` wins; otherwise a
67+
* Vercel serverless runtime (no git binary / ephemeral FS) MUST use the sandbox host,
68+
* while a local/dev runtime (the persistent Node server has git + a filesystem) uses
69+
* the in-process InlineHost. Pure + testable.
70+
*/
71+
export function selectDepositHostKind(env: NodeJS.ProcessEnv = process.env): DepositHostKind {
72+
const explicit = env.BITCODE_PIPELINE_HOST?.trim().toLowerCase();
73+
if (explicit === 'inline' || explicit === 'vercel-sandbox') return explicit;
74+
return env.VERCEL ? 'vercel-sandbox' : 'inline';
75+
}
76+
77+
/** Vercel auth for sandbox creation: prefer OIDC; otherwise pass the access token. */
78+
function vercelSandboxCreateOptions() {
79+
if (process.env.VERCEL_OIDC_TOKEN) return {};
80+
return {
81+
token: process.env.VERCEL_TOKEN,
82+
teamId: process.env.VERCEL_TEAM_ID,
83+
projectId: process.env.VERCEL_PROJECT_ID,
84+
};
85+
}
86+
6187
/**
62-
* Resolve the deposit pipeline Host. In-process (InlineHost) today; the Vercel
63-
* Sandbox host in prod once wired. The deposit run is the harness run, so the host
64-
* provisions the source — the dispatching request does not.
88+
* Resolve the deposit pipeline Host. The deposit run is the harness run, so the Host
89+
* provisions the source — the dispatching request does not. InlineHost runs in-process
90+
* (dev); VercelSandboxHost provisions a durable sandbox (prod) because a serverless
91+
* function cannot clone. Both implement the same primitive, so provisioning is
92+
* identical downstream.
6593
*/
66-
export function resolveDepositPipelineHost(): BitcodePipelineHost {
94+
export async function resolveDepositPipelineHost(): Promise<BitcodePipelineHost> {
95+
if (selectDepositHostKind() === 'vercel-sandbox') {
96+
const sandboxFactory = await loadVercelSandboxFactory();
97+
return new VercelSandboxHost({
98+
sandboxFactory,
99+
runtime: 'node22',
100+
createOptions: vercelSandboxCreateOptions(),
101+
});
102+
}
67103
return new InlineHost();
68104
}
69105

uapi/tests/lib/depositSourceProvisioning.test.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
/**
22
* @jest-environment node
33
*/
4-
import { provisionDepositSourceInventory } from '@/lib/deposit-source-provisioning';
4+
import {
5+
provisionDepositSourceInventory,
6+
selectDepositHostKind,
7+
} from '@/lib/deposit-source-provisioning';
58
import type { BitcodeHostWorkspace, BitcodePipelineHost } from '@bitcode/pipeline-hosts';
69

710
const FILES: Record<string, string> = {
@@ -75,3 +78,16 @@ describe('provisionDepositSourceInventory', () => {
7578
expect(isDisposed()).toBe(true);
7679
});
7780
});
81+
82+
describe('selectDepositHostKind', () => {
83+
it('honors an explicit BITCODE_PIPELINE_HOST', () => {
84+
expect(selectDepositHostKind({ BITCODE_PIPELINE_HOST: 'inline' } as any)).toBe('inline');
85+
expect(selectDepositHostKind({ BITCODE_PIPELINE_HOST: 'vercel-sandbox' } as any)).toBe('vercel-sandbox');
86+
expect(selectDepositHostKind({ BITCODE_PIPELINE_HOST: ' Vercel-Sandbox ' } as any)).toBe('vercel-sandbox');
87+
});
88+
89+
it('uses the sandbox on Vercel (no git/FS) and inline locally', () => {
90+
expect(selectDepositHostKind({ VERCEL: '1' } as any)).toBe('vercel-sandbox');
91+
expect(selectDepositHostKind({} as any)).toBe('inline');
92+
});
93+
});

0 commit comments

Comments
 (0)