diff --git a/java/scripts/codegen/java.ts b/java/scripts/codegen/java.ts index 89fea8930..ba0ed3df1 100644 --- a/java/scripts/codegen/java.ts +++ b/java/scripts/codegen/java.ts @@ -148,36 +148,61 @@ function toEnumConstant(value: string): string { // ── Schema path resolution ─────────────────────────────────────────────────── -async function getSessionEventsSchemaPath(): Promise { - const candidates = [ - path.join(REPO_ROOT, "scripts/codegen/node_modules/@github/copilot/schemas/session-events.schema.json"), - path.join(REPO_ROOT, "nodejs/node_modules/@github/copilot/schemas/session-events.schema.json"), +/** + * Resolve a JSON schema shipped by the `@github/copilot` CLI package. + * + * The CLI package layout changed in 1.0.64-1: the umbrella `@github/copilot` + * package became a thin loader and its bundled assets (including the JSON + * schemas) moved into the platform-specific packages installed as optional + * dependencies, e.g. `@github/copilot-linux-x64` or `@github/copilot-win32-x64`. + * + * We search both the Java codegen install (`scripts/codegen/node_modules`) and + * the Node SDK install (`nodejs/node_modules`), checking the umbrella package + * first (older versions) and then whichever platform package is present. + */ +async function resolveCopilotSchemaPath(fileName: string): Promise { + const nodeModulesDirs = [ + path.join(REPO_ROOT, "scripts/codegen/node_modules"), + path.join(REPO_ROOT, "nodejs/node_modules"), ]; - for (const p of candidates) { + + const candidates: string[] = []; + for (const nodeModulesDir of nodeModulesDirs) { + candidates.push(path.join(nodeModulesDir, "@github/copilot/schemas", fileName)); + const githubScopeDir = path.join(nodeModulesDir, "@github"); try { - await fs.access(p); - return p; - } catch { - // try next + for (const entry of await fs.readdir(githubScopeDir)) { + if (entry.startsWith("copilot-")) { + candidates.push(path.join(githubScopeDir, entry, "schemas", fileName)); + } + } + } catch (err) { + const code = (err as NodeJS.ErrnoException).code; + if (code !== "ENOENT" && code !== "ENOTDIR") { + throw err; + } + // @github scope directory may not exist; try the next location. } } - throw new Error("session-events.schema.json not found. Run 'npm ci' in scripts/codegen first."); -} -async function getApiSchemaPath(): Promise { - const candidates = [ - path.join(REPO_ROOT, "scripts/codegen/node_modules/@github/copilot/schemas/api.schema.json"), - path.join(REPO_ROOT, "nodejs/node_modules/@github/copilot/schemas/api.schema.json"), - ]; - for (const p of candidates) { + for (const candidate of candidates) { try { - await fs.access(p); - return p; + await fs.access(candidate); + return candidate; } catch { - // try next + // Try the next candidate. } } - throw new Error("api.schema.json not found. Run 'npm ci' in scripts/codegen first."); + + throw new Error(`${fileName} not found. Run 'npm ci' in java/scripts/codegen or java/nodejs first.`); +} + +async function getSessionEventsSchemaPath(): Promise { + return resolveCopilotSchemaPath("session-events.schema.json"); +} + +async function getApiSchemaPath(): Promise { + return resolveCopilotSchemaPath("api.schema.json"); } // ── File writing ───────────────────────────────────────────────────────────── diff --git a/scripts/codegen/utils.ts b/scripts/codegen/utils.ts index 3917dad44..b9bfb9730 100644 --- a/scripts/codegen/utils.ts +++ b/scripts/codegen/utils.ts @@ -45,23 +45,59 @@ export type SchemaWithSharedDefinitions = T }; // ── Schema paths ──────────────────────────────────────────────────────────── -export async function getSessionEventsSchemaPath(): Promise { - const schemaPath = path.join( - REPO_ROOT, - "nodejs/node_modules/@github/copilot/schemas/session-events.schema.json" +const SDK_NODE_MODULES = path.join(REPO_ROOT, "nodejs/node_modules"); + +/** + * Resolve a JSON schema shipped by the `@github/copilot` CLI package. + * + * The CLI package layout changed in 1.0.64-1: the umbrella `@github/copilot` + * package became a thin loader and its bundled assets (including the JSON + * schemas) moved into the platform-specific packages installed as optional + * dependencies, e.g. `@github/copilot-linux-x64` or `@github/copilot-win32-x64`. + * + * To support both layouts we look in the umbrella package first (older + * versions) and then in whichever platform package was installed for the + * current host. + */ +async function resolveCopilotSchemaPath(nodeModulesDir: string, fileName: string): Promise { + const candidates = [path.join(nodeModulesDir, "@github/copilot/schemas", fileName)]; + + const githubScopeDir = path.join(nodeModulesDir, "@github"); + try { + for (const entry of await fs.readdir(githubScopeDir)) { + if (entry.startsWith("copilot-")) { + candidates.push(path.join(githubScopeDir, entry, "schemas", fileName)); + } + } + } catch (err) { + const code = (err as NodeJS.ErrnoException).code; + if (code !== "ENOENT" && code !== "ENOTDIR") { + throw err; + } + // @github scope directory may not exist yet; fall through to the error below. + } + + for (const candidate of candidates) { + try { + await fs.access(candidate); + return candidate; + } catch { + // Try the next candidate. + } + } + + throw new Error( + `${fileName} not found under ${githubScopeDir}. Run 'npm ci' in nodejs/ first.` ); - await fs.access(schemaPath); - return schemaPath; +} + +export async function getSessionEventsSchemaPath(): Promise { + return resolveCopilotSchemaPath(SDK_NODE_MODULES, "session-events.schema.json"); } export async function getApiSchemaPath(cliArg?: string): Promise { if (cliArg) return cliArg; - const schemaPath = path.join( - REPO_ROOT, - "nodejs/node_modules/@github/copilot/schemas/api.schema.json" - ); - await fs.access(schemaPath); - return schemaPath; + return resolveCopilotSchemaPath(SDK_NODE_MODULES, "api.schema.json"); } // ── Brand casing normalization ──────────────────────────────────────────────