Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions scripts/check-package-bin.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,22 @@ try {
if (packedPaths.has('scripts/fetch-adapters.js')) {
fail('packed tarball contains the retired adapter fetch lifecycle');
}

// Every command the core manifest advertises must be in the tarball. A
// manifest entry whose module was left behind is discoverable but dies with
// ADAPTER_LOAD on first use — see #247, where web/fetch-browser was the only
// escalation path FETCH_BLOCKED knew how to recommend.
const manifest = JSON.parse(fs.readFileSync(path.join(ROOT, 'cli-manifest.json'), 'utf8'));
for (const entry of manifest) {
if (!entry.modulePath) continue;
const packedModule = `dist/src/clis/${entry.modulePath}`;
if (!packedPaths.has(packedModule)) {
fail(`packed tarball is missing manifest module for ${entry.site}/${entry.name}: ${packedModule}`);
}
}
if (manifest.length > 0 && !packedPaths.has('dist/src/cli-manifest.json')) {
fail('packed tarball is missing the staged core manifest: dist/src/cli-manifest.json');
}
for (const [name, target] of binEntries) {
if (!packedPaths.has(String(target))) {
fail(`packed tarball is missing bin "${name}" target: ${target}`);
Expand Down
13 changes: 12 additions & 1 deletion scripts/copy-yaml.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
* Copy YAML support files to dist/.
* (Adapters are JS-first and no longer need yaml copying.)
*/
const { copyFileSync, mkdirSync, existsSync } = require('fs');
const { copyFileSync, cpSync, mkdirSync, existsSync } = require('fs');
const { sep } = require('path');

// Copy external CLI registry to dist/
const extSrc = 'src/external-clis.yaml';
Expand All @@ -14,3 +15,13 @@ if (existsSync(extSrc)) {
const playwrightClient = 'src/browser/run/generated/playwright-client.js';
mkdirSync('dist/src/browser/run/generated', { recursive: true });
copyFileSync(playwrightClient, 'dist/src/browser/run/generated/playwright-client.js');

// Stage the builtin adapter tree next to the compiled output. package.json
// `files` ships dist/src/ but not the repo-root clis/, so without this the
// core manifest points at modules the published package does not contain.
if (existsSync('clis')) {
cpSync('clis', 'dist/src/clis', {
recursive: true,
filter: (src) => !src.split(sep).includes('test'),
});
}
9 changes: 8 additions & 1 deletion src/build-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import * as path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { getErrorMessage } from './errors.js';
import { fullName, getRegistry, type CliCommand } from './registry.js';
import { findPackageRoot } from './package-paths.js';
import { findPackageRoot, getCliManifestPath } from './package-paths.js';
import type { ManifestEntry } from './manifest-types.js';
import { isRecord } from './utils.js';
import {
Expand Down Expand Up @@ -450,6 +450,13 @@ async function main(): Promise<void> {

fs.mkdirSync(path.dirname(OUTPUT), { recursive: true });
fs.writeFileSync(OUTPUT, artifacts.manifestJson);
// The published package resolves the builtin tree to dist/src/clis/, and the
// manifest lookup is always clisDir/../cli-manifest.json — keep the staged
// copy in step so an installed CLI does not fall back to a filesystem scan.
const stagedClis = path.join(PACKAGE_ROOT, 'dist', 'src', 'clis');
if (fs.existsSync(stagedClis)) {
fs.writeFileSync(getCliManifestPath(stagedClis), artifacts.manifestJson);
}
fs.writeFileSync(HOSTED_CONTRACT_OUTPUT, artifacts.hostedContractJson);
console.error(`✅ Manifest compiled: ${entries.length} entries → ${OUTPUT}`);
console.error(`✅ Hosted contract compiled: ${packageMetadata.name}@${packageMetadata.version} → ${HOSTED_CONTRACT_OUTPUT}`);
Expand Down
10 changes: 7 additions & 3 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,13 @@ import { CONFIG_DIR_NAME } from './brand.js';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// The empty core manifest remains next to the retired clis/ location so older
// user-local adapter manifests keep the same lookup contract.
const BUILTIN_CLIS = path.join(findPackageRoot(__filename), 'clis');
// The core manifest sits next to the builtin clis/ tree so user-local adapter
// manifests keep the same lookup contract. In a repo checkout that tree is
// clis/ at the package root; the published package cannot ship adapter source
// at the root, so the build stages a copy next to the compiled output and this
// resolves to dist/src/clis/ there.
const REPO_BUILTIN_CLIS = path.join(findPackageRoot(__filename), 'clis');
const BUILTIN_CLIS = fs.existsSync(REPO_BUILTIN_CLIS) ? REPO_BUILTIN_CLIS : path.join(__dirname, 'clis');
const USER_CLIS = path.join(os.homedir(), CONFIG_DIR_NAME, 'clis');
const USER_PLUGINS = path.join(os.homedir(), CONFIG_DIR_NAME, 'plugins');

Expand Down