fix: preserve module URLs in the headless server bundle (#26450)

This commit is contained in:
Brennan Benson
2026-10-11 14:34:38 -07:00
committed by GitHub
parent 8e01e6d9f3
commit fc1686efd6
5 changed files with 143 additions and 3 deletions
+17
View File
@@ -9,6 +9,7 @@ import {
externalNativeAddons,
ORCAD_EXTERNAL_MODULES,
ORCAD_CHILD_ENTRY_POINTS,
ORCAD_COMMONJS_MODULE_OPTIONS,
orcadChildOutputFilename
} from './orcad-entry-build.mjs'
import { createRequire } from 'node:module'
@@ -52,6 +53,8 @@ import { computeOrcadFullVersion } from './orcad-artifact-version.mjs'
import { NODE_RUNTIME_ASSETS, NODE_RUNTIME_PIN } from '../../src/shared/node-runtime-pin.ts'
import { findSlotProblems, readManifest } from './orcad-prebuild-slot-contents.mjs'
import { orcadAgentBrowserNativeName } from '../../src/shared/orcad-agent-browser-name.ts'
import { runProcessSync } from '@orca/process-host'
import { describeProcessFailure } from './process-failure-message.mjs'
const ROOT = join(import.meta.dirname, '..', '..')
const OUT_DIR = process.env.ORCAD_OUT_DIR
@@ -99,6 +102,7 @@ async function stageParcelWatcher(target) {
const wrapperSource = requireFromWatcher.resolve('@parcel/watcher/wrapper.js')
mkdirSync(WATCHER_MODULE_DIR, { recursive: true })
await build({
...ORCAD_COMMONJS_MODULE_OPTIONS,
stdin: {
contents:
`const {createWrapper}=require(${JSON.stringify(wrapperSource)});` +
@@ -197,6 +201,7 @@ cpSync(join(ROOT, 'resources', 'native-chat-visuals'), join(OUT_DIR, 'native-cha
* their runtime resolvers look for them. */
function buildForkedChild(entryPoint, outfile) {
return build({
...ORCAD_COMMONJS_MODULE_OPTIONS,
entryPoints: [entryPoint],
bundle: true,
platform: 'node',
@@ -209,6 +214,7 @@ function buildForkedChild(entryPoint, outfile) {
minify: true,
sourcemap: false,
define: {
...ORCAD_COMMONJS_MODULE_OPTIONS.define,
'process.env.NODE_ENV': '"production"'
},
logLevel: 'error'
@@ -312,6 +318,17 @@ if (graphErrors.length > 0) {
)
process.exitCode = 1
}
const providerSmoke = runProcessSync({
program: process.execPath,
args: [SERVER_OUT_FILE, '--orcad-structured-provider-load-check'],
timeoutMs: 60_000
})
if (providerSmoke.code !== 0 || providerSmoke.timedOut || providerSmoke.outputTruncated) {
console.error(
`[build-orcad] the packaged structured provider failed to load.\n${describeProcessFailure(providerSmoke)}`
)
process.exitCode = 1
}
// Why require + parseArgs and not a real daemon: requiring the bundle evaluates every
// top-level import, and calling its exported argv parser proves the entry's own code is
// there rather than a graph that merely resolved. Booting one would need a socket, a
+19 -2
View File
@@ -25,6 +25,15 @@ export function orcadChildOutputFilename(entryPoint) {
export const ORCAD_EXTERNAL_MODULES = ['electron', 'node-pty', '@parcel/watcher', 'fsevents']
// Bundled ESM resolves from the deployed bundle, like CommonJS __filename.
export const ORCAD_COMMONJS_MODULE_OPTIONS = {
banner: {
js: 'var __orcadModuleUrl = require("node:url").pathToFileURL(__filename).href;'
},
define: { 'import.meta.url': '__orcadModuleUrl' },
logOverride: { 'empty-import-meta': 'error' }
}
// Native binaries are staged separately from every JavaScript entry.
export const externalNativeAddons = {
name: 'external-native-addons',
@@ -45,6 +54,7 @@ const jsoncParserEsm = {
export function buildOrcadEntry(outfile) {
return build({
...ORCAD_COMMONJS_MODULE_OPTIONS,
entryPoints: [join(root, ORCAD_ENTRY_POINT)],
bundle: true,
platform: 'node',
@@ -56,7 +66,10 @@ export function buildOrcadEntry(outfile) {
metafile: true,
minify: true,
sourcemap: false,
define: { 'process.env.NODE_ENV': '"production"' },
define: {
...ORCAD_COMMONJS_MODULE_OPTIONS.define,
'process.env.NODE_ENV': '"production"'
},
logLevel: 'error'
})
}
@@ -81,6 +94,7 @@ export function buildOrcadLauncher(outfile) {
.update(readFileSync(join(dirname(outfile), ORCAD_SERVER_ENTRY_FILENAME)))
.digest('hex')
return build({
...ORCAD_COMMONJS_MODULE_OPTIONS,
entryPoints: [join(root, ORCAD_LAUNCHER_ENTRY_POINT)],
bundle: true,
platform: 'node',
@@ -90,7 +104,10 @@ export function buildOrcadLauncher(outfile) {
metafile: true,
minify: true,
sourcemap: false,
define: { ORCAD_SERVER_SHA256: JSON.stringify(serverSha256) },
define: {
...ORCAD_COMMONJS_MODULE_OPTIONS.define,
ORCAD_SERVER_SHA256: JSON.stringify(serverSha256)
},
logLevel: 'error'
})
}
+79
View File
@@ -0,0 +1,79 @@
import { build } from 'esbuild'
import { mkdirSync, mkdtempSync, renameSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterAll, beforeAll, expect, it } from 'vitest'
import { buildOrcadEntry, ORCAD_COMMONJS_MODULE_OPTIONS } from './orcad-entry-build.mjs'
import { runProcessSync } from '@orca/process-host'
const directory = mkdtempSync(join(tmpdir(), 'orca-structured-provider-build-'))
const bundle = join(directory, 'orcad-server.js')
beforeAll(async () => {
await buildOrcadEntry(bundle)
}, 60_000)
afterAll(() => {
rmSync(directory, { recursive: true, force: true })
})
it('loads the structured Claude SDK through the packaged server under plain Node', () => {
const loaded = runProcessSync({
program: process.execPath,
args: [bundle, '--orcad-structured-provider-load-check'],
cwd: directory,
env: { ORCA_BACKGROUND_LAUNCH: '1' },
timeoutMs: 30_000
})
expect(loaded.code, loaded.stderr.slice(-2_000)).toBe(0)
}, 40_000)
it('resolves bundle-relative requires after deployment to a path with URL-special characters', async () => {
const buildDirectory = join(directory, 'build')
mkdirSync(buildDirectory)
const outfile = join(buildDirectory, 'entry.cjs')
await build({
...ORCAD_COMMONJS_MODULE_OPTIONS,
stdin: {
contents: `import { createRequire } from 'node:module';
console.log(createRequire(import.meta.url)('./asset.cjs'));`,
resolveDir: directory
},
bundle: true,
platform: 'node',
target: 'node18',
format: 'cjs',
outfile,
minify: true,
logLevel: 'error'
})
writeFileSync(join(buildDirectory, 'asset.cjs'), 'module.exports = "deployed asset"')
const deployedDirectory = join(directory, 'deployed # 100%')
renameSync(buildDirectory, deployedDirectory)
const loaded = runProcessSync({
program: process.execPath,
args: [join(deployedDirectory, 'entry.cjs')],
cwd: directory,
env: { ORCA_BACKGROUND_LAUNCH: '1' },
timeoutMs: 30_000
})
expect(loaded.code, loaded.stderr).toBe(0)
expect(loaded.stdout.trim()).toBe('deployed asset')
})
it.each(['import.meta', 'import.meta.dirname', 'import.meta.filename', 'import.meta.resolve'])(
'rejects unsupported %s instead of silently replacing it with an empty object',
async (expression) => {
await expect(
build({
...ORCAD_COMMONJS_MODULE_OPTIONS,
stdin: { contents: `console.log(${expression})`, resolveDir: directory },
bundle: true,
platform: 'node',
format: 'cjs',
write: false,
logLevel: 'silent'
})
).rejects.toThrow('"import.meta" is not available')
}
)
+6 -1
View File
@@ -42,7 +42,12 @@ function failStartup(error: unknown): void {
}
// One-shot launch and stop commands must skip server startup and preflights.
if (process.argv[2] === WINDOWS_BREAKAWAY_LAUNCH_FLAG) {
if (process.argv[2] === '--orcad-structured-provider-load-check') {
void import('./orcad-structured-provider-smoke')
.then(({ smokeLoadOrcadStructuredProviders }) => smokeLoadOrcadStructuredProviders())
.then(() => process.exit(0))
.catch(failStartup)
} else if (process.argv[2] === WINDOWS_BREAKAWAY_LAUNCH_FLAG) {
runWindowsBreakawayLaunchIfRequested(ORCAD_WINDOWS_BREAKAWAY_CONTRACT, process.argv)
} else if (
process.argv[2] === ORCAD_COMPLETE_MANAGED_STOP_FLAG ||
@@ -0,0 +1,22 @@
import { openClaudeStreamJsonConnection } from '../claude/claude-stream-json-connection'
export async function smokeLoadOrcadStructuredProviders(): Promise<void> {
const loaded = new Error('structured provider module loaded')
try {
await openClaudeStreamJsonConnection(
{ pathToClaudeCodeExecutable: 'unused', cwd: process.cwd(), options: {} },
{},
undefined,
() => {
// The production connection loads the SDK before calling the query implementation.
throw loaded
}
)
} catch (error) {
if (error === loaded) {
return
}
throw error
}
throw new Error('structured provider load check did not reach the SDK boundary')
}