@midscene/test 1.12.5-beta-20260908034641.0 → 1.12.5

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/lib/0~458.js CHANGED
@@ -49,7 +49,9 @@ exports.modules = {
49
49
  function detectPackageManager(userAgent) {
50
50
  return userAgent?.startsWith('pnpm/') ? 'pnpm' : 'npm';
51
51
  }
52
- var package_namespaceObject = JSON.parse('{"rE":"1.12.5-beta-20260908034641.0"}');
52
+ var package_namespaceObject = {
53
+ rE: "1.12.5"
54
+ };
53
55
  const createPlatforms = [
54
56
  'web',
55
57
  'android',
@@ -1 +1 @@
1
- {"version":3,"file":"0~458.js","sources":["../../src/cli/create-package-manager.ts","../../src/cli/create-template.ts","../../src/cli/create-command.ts"],"sourcesContent":["export const createPackageManagers = ['npm', 'pnpm'] as const;\nexport type CreatePackageManager = (typeof createPackageManagers)[number];\n\nexport const packageManagerCommands: Record<\n CreatePackageManager,\n { install: string[]; describe: string[]; installChromium: string }\n> = {\n npm: {\n install: ['install', '--workspaces=false'],\n describe: [\n 'exec',\n '--no',\n '--workspaces=false',\n '--',\n 'midscene-test',\n 'nodes',\n ],\n installChromium: 'npm exec -- playwright install chromium',\n },\n pnpm: {\n install: ['install', '--ignore-workspace'],\n describe: ['exec', 'midscene-test', 'nodes'],\n installChromium: 'pnpm exec playwright install chromium',\n },\n};\n\nexport function detectPackageManager(userAgent?: string): CreatePackageManager {\n return userAgent?.startsWith('pnpm/') ? 'pnpm' : 'npm';\n}\n","import { version } from '../../package.json';\nimport {\n type CreatePackageManager,\n packageManagerCommands,\n} from './create-package-manager';\n\nexport const createPlatforms = [\n 'web',\n 'android',\n 'ios',\n 'harmony',\n 'computer',\n] as const;\nexport type CreatePlatform = (typeof createPlatforms)[number];\n\nexport const createPlatformLabels: Record<CreatePlatform, string> = {\n web: 'Web (Playwright)',\n android: 'Android',\n ios: 'iOS',\n harmony: 'HarmonyOS',\n computer: 'Desktop (Computer)',\n};\n\nexport interface NodePackageSpec {\n name: string;\n version: string;\n}\n\n/** Registry packages only: keep the install spec separate from the import name. */\nexport function parseNodePackageSpec(spec: string): NodePackageSpec {\n const match =\n /^(?:(@[a-z0-9][a-z0-9._-]*)\\/)?([a-z0-9][a-z0-9._-]*)(?:@([a-zA-Z0-9^~*>=<|. +_-]+))?$/.exec(\n spec,\n );\n if (!match || (match[3] !== undefined && !match[3].trim())) {\n throw new Error(\n `Invalid Node package \"${spec}\". Use an npm package name with an optional version, such as @acme/test-nodes@1.2.0.`,\n );\n }\n return {\n name: `${match[1] ? `${match[1]}/` : ''}${match[2]}`,\n version: match[3] ?? 'latest',\n };\n}\n\nconst platformImports: Record<CreatePlatform, string> = {\n web: `import { PlaywrightAgent } from '@midscene/web/playwright/agent';\nimport { createPlaywrightNodes } from '@midscene/test/playwright';\nimport { chromium, type Page } from 'playwright';`,\n android: `import { AndroidAgent, agentFromAdbDevice } from '@midscene/android';`,\n ios: `import { IOSAgent, agentFromWebDriverAgent } from '@midscene/ios';`,\n harmony: `import { HarmonyAgent, agentFromHdcDevice } from '@midscene/harmony';`,\n computer: `import { ComputerAgent, agentForComputer } from '@midscene/computer';`,\n};\n\nconst agentClasses: Record<CreatePlatform, string> = {\n web: 'PlaywrightAgent',\n android: 'AndroidAgent',\n ios: 'IOSAgent',\n harmony: 'HarmonyAgent',\n computer: 'ComputerAgent',\n};\n\nconst platformSetup: Record<CreatePlatform, string> = {\n web: ` const browser = await chromium.launch({ headless: env.HEADLESS !== 'false' });\n onTeardown(() => browser.close());\n const page = await browser.newPage();\n const context: ProjectContext = { page };\n onTeardown(async () => { await context.agent?.destroy(); });\n return context;`,\n android: ` const agent = await agentFromAdbDevice(env.ANDROID_DEVICE_ID || undefined);\n onTeardown(() => agent.destroy());\n return { agent };`,\n ios: ` const port = Number(env.WDA_PORT || 8100);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n throw new Error('WDA_PORT must be an integer between 1 and 65535.');\n }\n const agent = await agentFromWebDriverAgent({\n wdaHost: env.WDA_HOST || 'localhost',\n wdaPort: port,\n });\n onTeardown(() => agent.destroy());\n return { agent };`,\n harmony: ` const agent = await agentFromHdcDevice(env.HARMONY_DEVICE_ID || undefined);\n onTeardown(() => agent.destroy());\n return { agent };`,\n computer: ` const agent = await agentForComputer({\n displayId: env.COMPUTER_DISPLAY_ID || undefined,\n });\n onTeardown(() => agent.destroy());\n return { agent };`,\n};\n\nconst platformEnv: Record<CreatePlatform, string> = {\n web: 'HEADLESS=true\\n',\n android:\n '# Optional: choose a device from adb devices.\\nANDROID_DEVICE_ID=\\n',\n ios: 'WDA_HOST=localhost\\nWDA_PORT=8100\\n',\n harmony:\n '# Optional: choose a device from hdc list targets.\\nHARMONY_DEVICE_ID=\\n',\n computer:\n '# Optional: select a display; leave empty for the default display.\\nCOMPUTER_DISPLAY_ID=\\n# Enable Xvfb on headless Linux after installing its dependencies.\\nMIDSCENE_COMPUTER_HEADLESS_LINUX=false\\n',\n};\n\nconst platformInstructions: Record<Exclude<CreatePlatform, 'web'>, string> = {\n android:\n 'Connect an Android device and verify it with `adb devices`. Set ANDROID_DEVICE_ID to select a device.',\n ios: 'Start WebDriverAgent and set WDA_HOST and WDA_PORT for your iOS device.',\n harmony:\n 'Connect a HarmonyOS device and verify it with `hdc list targets`. Set HARMONY_DEVICE_ID to select a device. Set HDC_HOME if hdc is not on PATH.',\n computer:\n 'Prepare the local desktop using the [desktop setup guide](https://midscenejs.com/platforms/desktop.html), including system dependencies and permissions. Set COMPUTER_DISPLAY_ID to select a display. For headless Linux, install Xvfb and enable MIDSCENE_COMPUTER_HEADLESS_LINUX.',\n};\n\nexport function createProjectFiles(\n name: string,\n platform: CreatePlatform,\n packages: readonly NodePackageSpec[],\n packageManager: CreatePackageManager,\n): Record<string, string> {\n const instructions =\n platform === 'web'\n ? `Install Chromium before the first test: \\`${packageManagerCommands[packageManager].installChromium}\\`.`\n : platformInstructions[platform];\n const agentClass = agentClasses[platform];\n const imports = packages\n .map(\n (pkg, index) =>\n `import { createMidsceneTestNodes as createPackageNodes${index} } from ${JSON.stringify(pkg.name)};`,\n )\n .join('\\n');\n const extraNodes = packages\n .map(\n (_, index) =>\n ` ...createPackageNodes${index}<ProjectContext>({ platform: '${platform}', getAgent }),`,\n )\n .join('\\n');\n const config = `import { fileURLToPath } from 'node:url';\nimport { config as loadEnv } from 'dotenv';\nimport type { NodeExecutionContext } from '@midscene/test';\nimport { defineProjectSetup, defineTestProject } from '@midscene/test/config';\nimport { createMidsceneNodes } from '@midscene/test/midscene';\n${platformImports[platform]}\n${imports}\n\nloadEnv({ path: fileURLToPath(new URL('.env', import.meta.url)) });\n\ninterface ProjectContext {\n${platform === 'web' ? ' page: Page;\\n agent?: PlaywrightAgent;' : ` agent: ${agentClass};`}\n}\n\nconst getAgent = ({ context }: NodeExecutionContext<unknown, ProjectContext>) => {\n${platform === 'web' ? ' context.agent ??= new PlaywrightAgent(context.page);' : ''}\n return context.agent;\n};\n\nconst setup = defineProjectSetup<ProjectContext>({\n name: '${platform}',\n platform: '${platform}',\n async setup({ env, onTeardown }) {\n${platformSetup[platform]}\n },\n});\n\nexport default defineTestProject<ProjectContext>({\n projects: [{\n name: '${platform}',\n platform: '${platform}',\n setup,\n files: { include: ['cases/**/*.{yaml,yml}'] },\n }],\n nodes: [\n ...createMidsceneNodes<ProjectContext>({ agentClass: ${agentClass}, getAgent }),\n${platform === 'web' ? ' ...createPlaywrightNodes<ProjectContext>({ getPage: ({ context }) => context.page }),' : ''}\n${extraNodes}\n ],\n});\n`;\n const dependencies: Record<string, string> = {\n '@midscene/test': version,\n [`@midscene/${platform}`]: version,\n '@types/node': '^20.0.0',\n dotenv: '^16.4.5',\n typescript: '^5.8.3',\n ...(platform === 'web' ? { playwright: '^1.45.0' } : {}),\n };\n for (const pkg of packages) {\n if (Object.hasOwn(dependencies, pkg.name)) {\n throw new Error(\n `Node package \"${pkg.name}\" conflicts with a generated project dependency.`,\n );\n }\n dependencies[pkg.name] = pkg.version;\n }\n const env = platformEnv[platform];\n return {\n 'package.json': `${JSON.stringify(\n {\n name,\n private: true,\n type: 'module',\n scripts: {\n test: 'midscene-test',\n nodes: 'midscene-test nodes',\n postinstall: 'midscene-test nodes',\n },\n engines: { node: '^20.19.0 || ^22.12.0 || >=24.0.0' },\n devDependencies: dependencies,\n },\n null,\n 2,\n )}\\n`,\n 'midscene.config.ts': config,\n 'tsconfig.json': `${JSON.stringify(\n {\n compilerOptions: {\n target: 'ES2022',\n module: 'ESNext',\n moduleResolution: 'Bundler',\n strict: true,\n noEmit: true,\n skipLibCheck: true,\n types: ['node'],\n },\n include: ['midscene.config.ts'],\n },\n null,\n 2,\n )}\\n`,\n 'cases/example.yaml':\n platform === 'web'\n ? 'cases:\\n - name: Open example.com\\n steps:\\n - gotoUrl: https://example.com\\n - aiAssert: The page displays the heading Example Domain\\n'\n : platform === 'computer'\n ? 'cases:\\n - name: Inspect the desktop\\n steps:\\n - aiAsk: Describe the current desktop\\n'\n : 'cases:\\n - name: Inspect the home screen\\n steps:\\n - home: {}\\n - aiAsk: Describe the current screen\\n',\n '.env.example': `# Copy this file to .env and configure your model before running tests.\\n# See https://midscenejs.com/model-config.html\\nMIDSCENE_MODEL_BASE_URL=\\nMIDSCENE_MODEL_API_KEY=\\nMIDSCENE_MODEL_NAME=\\nMIDSCENE_MODEL_FAMILY=\\n\\n${env}`,\n '.gitignore': 'node_modules/\\n.env\\nmidscene_run/\\n',\n 'README.md': `# ${name}\\n\\nA Midscene Test project for ${platform}.\\n\\nIf you skipped installation during creation, run \\`${packageManager} ${packageManagerCommands[packageManager].install.join(' ')}\\`. The \\`postinstall\\` script automatically generates \\`midscene-node-reference.md\\` after each dependency installation. If lifecycle scripts are disabled, run \\`${packageManager} run nodes\\` manually.\\n\\nCopy \\`.env.example\\` to \\`.env\\` and fill in your [model configuration](https://midscenejs.com/model-config.html).\\n\\n${instructions}\\n\\nRun tests with \\`${packageManager} test\\`. Read \\`midscene-node-reference.md\\` for the available Nodes and their inputs.\\n\\nAfter changing Node registrations in \\`midscene.config.ts\\`, run \\`${packageManager} run nodes\\` to refresh the reference. This loads the configuration without connecting to a device or running tests. Extension factories must only acquire runtime resources inside Node execution.\\n\\n${packages.length ? `Node packages: ${packages.map((pkg) => `\\`${pkg.name}\\``).join(', ')}. Their \\`createMidsceneTestNodes\\` factories are imported in the configuration.\\n\\n` : ''}Reports are written to \\`midscene_run/report/\\`.\\n`,\n };\n}\n","import { lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { basename, dirname, resolve } from 'node:path';\nimport { confirm, input, select } from '@inquirer/prompts';\nimport { execa } from 'execa';\nimport yargs from 'yargs/yargs';\nimport {\n type CreatePackageManager,\n createPackageManagers,\n detectPackageManager,\n packageManagerCommands,\n} from './create-package-manager';\nimport {\n type CreatePlatform,\n type NodePackageSpec,\n createPlatformLabels,\n createPlatforms,\n createProjectFiles,\n parseNodePackageSpec,\n} from './create-template';\nimport type { TestCliIO } from './test-command';\n\nconst createParser = () =>\n yargs()\n .scriptName('midscene-test create')\n .usage('Usage: $0 [directory] [options]')\n .parserConfiguration({\n 'parse-positional-numbers': false,\n 'greedy-arrays': false,\n 'camel-case-expansion': false,\n 'boolean-negation': false,\n })\n .option('platform', {\n type: 'string',\n choices: createPlatforms,\n requiresArg: true,\n description: 'Platform preset (prompt if omitted)',\n })\n .option('with', {\n type: 'array',\n string: true,\n nargs: 1,\n requiresArg: true,\n description: 'npm Node package, optionally versioned (repeatable)',\n })\n .option('package-manager', {\n type: 'string',\n choices: createPackageManagers,\n requiresArg: true,\n description:\n 'Package manager (prompt if omitted; detect without prompts, falling back to npm)',\n })\n .option('yes', {\n type: 'boolean',\n alias: 'y',\n default: false,\n description:\n 'Disable prompts and install dependencies; directory and platform must be supplied',\n })\n .option('skip-install', {\n type: 'boolean',\n default: false,\n description:\n 'Create files without installing dependencies or generating midscene-node-reference.md',\n })\n .demandCommand(0, 1, '', 'Only one project directory is allowed.')\n .example('$0', 'Choose a directory and platform interactively')\n .example('$0 my-tests --platform web', 'Create a Web project')\n .example(\n '$0 . --platform android --with @acme/test-nodes@1.2.0',\n 'Include a Node package',\n )\n .epilogue(\n 'Optionally installs dependencies with the selected package manager. The generated postinstall script creates midscene-node-reference.md after installation.\\nNode packages must export a synchronous createMidsceneTestNodes(options) factory.\\nExisting files are never overwritten. Setup and tests are not run during creation.',\n )\n .help('help')\n .alias('help', 'h')\n .version(false)\n .strict()\n .exitProcess(false)\n .showHelpOnFail(false)\n .fail((message, error) => {\n throw error ?? new Error(message);\n })\n .wrap(100);\n\nexport interface CreateOptions {\n directory?: string;\n platform?: CreatePlatform;\n packageManager?: CreatePackageManager;\n packages: NodePackageSpec[];\n yes: boolean;\n skipInstall: boolean;\n help: boolean;\n}\n\nexport function parseCreateArgs(\n args: string[],\n onOutput?: (message: string) => void,\n): CreateOptions {\n const values = createParser().parseSync(args, {}, (error, _argv, output) => {\n if (error) throw error;\n if (output) onOutput?.(output);\n });\n const directory = values._[0] === undefined ? undefined : String(values._[0]);\n if (directory !== undefined && !directory.trim()) {\n throw new Error('Project directory must not be empty.');\n }\n const packages = (values.with ?? []).map(parseNodePackageSpec);\n const seen = new Set<string>();\n for (const pkg of packages) {\n if (seen.has(pkg.name)) {\n throw new Error(\n `Node package \"${pkg.name}\" was specified more than once.`,\n );\n }\n seen.add(pkg.name);\n }\n return {\n directory,\n platform: values.platform,\n packageManager: values['package-manager'],\n packages,\n yes: values.yes ?? false,\n skipInstall: values['skip-install'] ?? false,\n help: values.help === true,\n };\n}\n\nexport interface CreateServices {\n cwd: string;\n interactive: boolean;\n userAgent: string | undefined;\n promptDirectory(): Promise<string>;\n selectPlatform(): Promise<CreatePlatform>;\n selectPackageManager(\n defaultValue: CreatePackageManager,\n ): Promise<CreatePackageManager>;\n confirmInstall(): Promise<boolean>;\n runPackageManager(\n packageManager: CreatePackageManager,\n args: string[],\n cwd: string,\n captureOutput?: boolean,\n ): Promise<string>;\n}\n\nconst promptDirectory = () =>\n input({\n message: 'Project directory:',\n default: 'my-tests',\n validate: (value) =>\n value.trim().length > 0 || 'Project directory must not be empty.',\n });\n\nconst selectPlatform = () =>\n select<CreatePlatform>({\n message: 'Select a platform:',\n choices: createPlatforms.map((platform) => ({\n name: createPlatformLabels[platform],\n value: platform,\n })),\n });\n\nconst selectPackageManager: CreateServices['selectPackageManager'] = (\n defaultValue,\n) =>\n select<CreatePackageManager>({\n message: 'Select a package manager:',\n choices: createPackageManagers.map((value) => ({ name: value, value })),\n default: defaultValue,\n });\n\nconst runPackageManager: CreateServices['runPackageManager'] = async (\n packageManager,\n args,\n cwd,\n captureOutput = false,\n) => {\n const result = await execa(packageManager, args, {\n cwd,\n stdin: 'inherit',\n stdout: captureOutput ? 'pipe' : 'inherit',\n stderr: 'inherit',\n stripFinalNewline: false,\n });\n return result.stdout ?? '';\n};\n\nconst statIfPresent = (path: string) => {\n try {\n return lstatSync(path);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw error;\n }\n};\n\n/** Check every destination before writing anything, including dangling links. */\nfunction checkDestinations(root: string, filenames: string[]): void {\n const conflicts: string[] = [];\n for (const filename of filenames) {\n const path = resolve(root, filename);\n if (statIfPresent(path)) conflicts.push(path);\n let parent = dirname(path);\n while (true) {\n const stat = statIfPresent(parent);\n if (stat && !stat.isDirectory()) conflicts.push(parent);\n if (parent === root || parent === dirname(parent)) break;\n parent = dirname(parent);\n }\n }\n if (conflicts.length) {\n throw new Error(\n `Cannot create project; these paths already exist or are not directories:\\n${[...new Set(conflicts)].join('\\n')}\\nChoose another directory or move the conflicting files.`,\n );\n }\n}\n\nexport async function runCreateCommand(\n args: string[],\n io: TestCliIO,\n overrides: Partial<CreateServices> = {},\n): Promise<void> {\n const options = parseCreateArgs(args, (message) => io.log(message));\n if (options.help) return;\n const services: CreateServices = {\n cwd: process.cwd(),\n interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY),\n userAgent: process.env.npm_config_user_agent,\n promptDirectory,\n selectPlatform,\n selectPackageManager,\n confirmInstall: () =>\n confirm({\n message:\n 'Install dependencies and generate midscene-node-reference.md now?',\n default: true,\n }),\n runPackageManager,\n ...overrides,\n };\n if (\n (!options.directory || !options.platform) &&\n (options.yes || !services.interactive)\n ) {\n throw new Error(\n 'Project directory and --platform are required without prompts. Example: midscene-test create my-tests --platform web',\n );\n }\n let directory: string;\n let platform: CreatePlatform;\n let packageManager: CreatePackageManager;\n try {\n directory = options.directory ?? (await services.promptDirectory()).trim();\n platform = options.platform ?? (await services.selectPlatform());\n const defaultPackageManager = detectPackageManager(services.userAgent);\n packageManager =\n options.packageManager ??\n (services.interactive && !options.yes\n ? await services.selectPackageManager(defaultPackageManager)\n : defaultPackageManager);\n } catch (error) {\n if (\n error instanceof Error &&\n ['ExitPromptError', 'AbortPromptError'].includes(error.name)\n ) {\n throw new Error('Project creation cancelled.');\n }\n throw error;\n }\n const root = resolve(services.cwd, directory);\n const name =\n basename(root)\n .toLowerCase()\n .replace(/[^a-z0-9._-]/g, '-')\n .replace(/^[._-]+/, '') || 'midscene-tests';\n const commands = packageManagerCommands[packageManager];\n const files = createProjectFiles(\n name,\n platform,\n options.packages,\n packageManager,\n );\n checkDestinations(root, [\n ...Object.keys(files),\n 'midscene-node-reference.md',\n 'pnpm-lock.yaml',\n 'package-lock.json',\n 'npm-shrinkwrap.json',\n ]);\n for (const [filename, content] of Object.entries(files)) {\n const path = resolve(root, filename);\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, content, { flag: 'wx' });\n }\n io.log(`Created ${platform} project files in ${root}`);\n let install = !options.skipInstall;\n if (install && services.interactive && !options.yes) {\n try {\n install = await services.confirmInstall();\n } catch (error) {\n if (\n error instanceof Error &&\n ['ExitPromptError', 'AbortPromptError'].includes(error.name)\n ) {\n throw new Error(\n `Project creation cancelled. Project files are preserved in ${root}.\\nIn that directory, run ${packageManager} ${commands.install.join(' ')} to install dependencies and generate midscene-node-reference.md.`,\n );\n }\n throw error;\n }\n }\n if (!install) {\n io.log(\n `Project files ready: ${root}\\nNext: run ${packageManager} ${commands.install.join(' ')} in the project directory. The postinstall script will generate midscene-node-reference.md.\\nThen follow README.md to configure your model and run tests.`,\n );\n return;\n }\n io.log(`Installing dependencies with ${packageManager}...`);\n try {\n await services.runPackageManager(packageManager, commands.install, root);\n } catch (error) {\n throw new Error(\n `Dependency installation or postinstall Node reference generation failed. Project files are preserved in ${root}.\\nIn that directory, run ${packageManager} ${commands.install.join(' ')}, then ${packageManager} run nodes if lifecycle scripts are disabled.\\nIf postinstall failed, check midscene.config.ts and the Node package factories, then run ${packageManager} run nodes.\\n${error instanceof Error ? error.message : String(error)}`,\n );\n }\n try {\n const referencePath = resolve(root, 'midscene-node-reference.md');\n const generatedByPostinstall = Boolean(statIfPresent(referencePath));\n // Fall back to explicit generation if lifecycle scripts were disabled.\n if (!generatedByPostinstall) {\n io.log('Generating Node reference...');\n await services.runPackageManager(packageManager, commands.describe, root);\n }\n const markdown = readFileSync(referencePath, 'utf8');\n if (!markdown.startsWith('<!-- Generated by `midscene-test nodes`.')) {\n throw new Error('nodes did not generate a valid Node reference.');\n }\n } catch (error) {\n throw new Error(\n `Node reference generation failed. Project files are preserved in ${root}.\\nCheck that each Node package exports createMidsceneTestNodes(options), returns a Node array synchronously, and does not register duplicate names.\\nFix midscene.config.ts, then run ${packageManager} run nodes in the project directory.\\n${error instanceof Error ? error.message : String(error)}`,\n );\n }\n io.log(\n `Project ready: ${root}\\nNode reference: ${resolve(root, 'midscene-node-reference.md')}\\nNext: copy .env.example to .env and configure your model.${platform === 'web' ? `\\nInstall Chromium: ${commands.installChromium}` : platform === 'computer' ? '\\nPrepare desktop dependencies and permissions as described in README.md.' : '\\nConfigure your device connection in .env.'}\\nRun tests from the project directory: ${packageManager} test`,\n );\n}\n"],"names":["createPackageManagers","packageManagerCommands","detectPackageManager","userAgent","createPlatforms","createPlatformLabels","parseNodePackageSpec","spec","match","undefined","Error","platformImports","agentClasses","platformSetup","platformEnv","platformInstructions","createProjectFiles","name","platform","packages","packageManager","instructions","agentClass","imports","pkg","index","JSON","extraNodes","_","config","dependencies","version","Object","env","createParser","yargs","message","error","parseCreateArgs","args","onOutput","values","_argv","output","directory","String","seen","Set","promptDirectory","input","value","selectPlatform","select","selectPackageManager","defaultValue","runPackageManager","cwd","captureOutput","result","execa","statIfPresent","path","lstatSync","checkDestinations","root","filenames","conflicts","filename","resolve","parent","dirname","stat","runCreateCommand","io","overrides","options","services","process","Boolean","confirm","defaultPackageManager","basename","commands","files","content","mkdirSync","writeFileSync","install","referencePath","generatedByPostinstall","markdown","readFileSync"],"mappings":";;;;;;;;;;;;;;;QAAO,MAAMA,wBAAwB;YAAC;YAAO;SAAO;QAG7C,MAAMC,yBAGT;YACF,KAAK;gBACH,SAAS;oBAAC;oBAAW;iBAAqB;gBAC1C,UAAU;oBACR;oBACA;oBACA;oBACA;oBACA;oBACA;iBACD;gBACD,iBAAiB;YACnB;YACA,MAAM;gBACJ,SAAS;oBAAC;oBAAW;iBAAqB;gBAC1C,UAAU;oBAAC;oBAAQ;oBAAiB;iBAAQ;gBAC5C,iBAAiB;YACnB;QACF;QAEO,SAASC,qBAAqBC,SAAkB;YACrD,OAAOA,WAAW,WAAW,WAAW,SAAS;QACnD;;QCtBO,MAAMC,kBAAkB;YAC7B;YACA;YACA;YACA;YACA;SACD;QAGM,MAAMC,uBAAuD;YAClE,KAAK;YACL,SAAS;YACT,KAAK;YACL,SAAS;YACT,UAAU;QACZ;QAQO,SAASC,qBAAqBC,IAAY;YAC/C,MAAMC,QACJ,yFAAyF,IAAI,CAC3FD;YAEJ,IAAI,CAACC,SAAUA,AAAaC,WAAbD,KAAK,CAAC,EAAE,IAAkB,CAACA,KAAK,CAAC,EAAE,CAAC,IAAI,IACrD,MAAM,IAAIE,MACR,CAAC,sBAAsB,EAAEH,KAAK,oFAAoF,CAAC;YAGvH,OAAO;gBACL,MAAM,GAAGC,KAAK,CAAC,EAAE,GAAG,GAAGA,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAKA,KAAK,CAAC,EAAE,EAAE;gBACpD,SAASA,KAAK,CAAC,EAAE,IAAI;YACvB;QACF;QAEA,MAAMG,kBAAkD;YACtD,KAAK,CAAC;;iDAEyC,CAAC;YAChD,SAAS;YACT,KAAK;YACL,SAAS;YACT,UAAU;QACZ;QAEA,MAAMC,eAA+C;YACnD,KAAK;YACL,SAAS;YACT,KAAK;YACL,SAAS;YACT,UAAU;QACZ;QAEA,MAAMC,gBAAgD;YACpD,KAAK,CAAC;;;;;mBAKW,CAAC;YAClB,SAAS,CAAC;;qBAES,CAAC;YACpB,KAAK,CAAC;;;;;;;;;qBASa,CAAC;YACpB,SAAS,CAAC;;qBAES,CAAC;YACpB,UAAU,CAAC;;;;qBAIQ,CAAC;QACtB;QAEA,MAAMC,cAA8C;YAClD,KAAK;YACL,SACE;YACF,KAAK;YACL,SACE;YACF,UACE;QACJ;QAEA,MAAMC,uBAAuE;YAC3E,SACE;YACF,KAAK;YACL,SACE;YACF,UACE;QACJ;QAEO,SAASC,mBACdC,IAAY,EACZC,QAAwB,EACxBC,QAAoC,EACpCC,cAAoC;YAEpC,MAAMC,eACJH,AAAa,UAAbA,WACI,CAAC,0CAA0C,EAAEjB,sBAAsB,CAACmB,eAAe,CAAC,eAAe,CAAC,GAAG,CAAC,GACxGL,oBAAoB,CAACG,SAAS;YACpC,MAAMI,aAAaV,YAAY,CAACM,SAAS;YACzC,MAAMK,UAAUJ,SACb,GAAG,CACF,CAACK,KAAKC,QACJ,CAAC,sDAAsD,EAAEA,MAAM,QAAQ,EAAEC,KAAK,SAAS,CAACF,IAAI,IAAI,EAAE,CAAC,CAAC,EAEvG,IAAI,CAAC;YACR,MAAMG,aAAaR,SAChB,GAAG,CACF,CAACS,GAAGH,QACF,CAAC,yBAAyB,EAAEA,MAAM,8BAA8B,EAAEP,SAAS,eAAe,CAAC,EAE9F,IAAI,CAAC;YACR,MAAMW,SAAS,CAAC;;;;;AAKlB,EAAElB,eAAe,CAACO,SAAS,CAAC;AAC5B,EAAEK,QAAQ;;;;;AAKV,EAAEL,AAAa,UAAbA,WAAqB,8CAA8C,CAAC,SAAS,EAAEI,WAAW,CAAC,CAAC,CAAC;;;;AAI/F,EAAEJ,AAAa,UAAbA,WAAqB,2DAA2D,GAAG;;;;;SAK5E,EAAEA,SAAS;aACP,EAAEA,SAAS;;AAExB,EAAEL,aAAa,CAACK,SAAS,CAAC;;;;;;WAMf,EAAEA,SAAS;eACP,EAAEA,SAAS;;;;;yDAK+B,EAAEI,WAAW;AACtE,EAAEJ,AAAa,UAAbA,WAAqB,8FAA8F,GAAG;AACxH,EAAES,WAAW;;;AAGb,CAAC;YACC,MAAMG,eAAuC;gBAC3C,kBAAkBC,wBAAAA,EAAOA;gBACzB,CAAC,CAAC,UAAU,EAAEb,UAAU,CAAC,EAAEa,wBAAAA,EAAOA;gBAClC,eAAe;gBACf,QAAQ;gBACR,YAAY;gBACZ,GAAIb,AAAa,UAAbA,WAAqB;oBAAE,YAAY;gBAAU,IAAI,CAAC,CAAC;YACzD;YACA,KAAK,MAAMM,OAAOL,SAAU;gBAC1B,IAAIa,OAAO,MAAM,CAACF,cAAcN,IAAI,IAAI,GACtC,MAAM,IAAId,MACR,CAAC,cAAc,EAAEc,IAAI,IAAI,CAAC,gDAAgD,CAAC;gBAG/EM,YAAY,CAACN,IAAI,IAAI,CAAC,GAAGA,IAAI,OAAO;YACtC;YACA,MAAMS,MAAMnB,WAAW,CAACI,SAAS;YACjC,OAAO;gBACL,gBAAgB,GAAGQ,KAAK,SAAS,CAC/B;oBACET;oBACA,SAAS;oBACT,MAAM;oBACN,SAAS;wBACP,MAAM;wBACN,OAAO;wBACP,aAAa;oBACf;oBACA,SAAS;wBAAE,MAAM;oBAAmC;oBACpD,iBAAiBa;gBACnB,GACA,MACA,GACA,EAAE,CAAC;gBACL,sBAAsBD;gBACtB,iBAAiB,GAAGH,KAAK,SAAS,CAChC;oBACE,iBAAiB;wBACf,QAAQ;wBACR,QAAQ;wBACR,kBAAkB;wBAClB,QAAQ;wBACR,QAAQ;wBACR,cAAc;wBACd,OAAO;4BAAC;yBAAO;oBACjB;oBACA,SAAS;wBAAC;qBAAqB;gBACjC,GACA,MACA,GACA,EAAE,CAAC;gBACL,sBACER,AAAa,UAAbA,WACI,2JACAA,AAAa,eAAbA,WACE,qGACA;gBACR,gBAAgB,CAAC,4NAA4N,EAAEe,KAAK;gBACpP,cAAc;gBACd,aAAa,CAAC,EAAE,EAAEhB,KAAK,gCAAgC,EAAEC,SAAS,wDAAwD,EAAEE,eAAe,CAAC,EAAEnB,sBAAsB,CAACmB,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,mKAAmK,EAAEA,eAAe,iJAAiJ,EAAEC,aAAa,qBAAqB,EAAED,eAAe,6JAA6J,EAAEA,eAAe,uMAAuM,EAAED,SAAS,MAAM,GAAG,CAAC,eAAe,EAAEA,SAAS,GAAG,CAAC,CAACK,MAAQ,CAAC,EAAE,EAAEA,IAAI,IAAI,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,MAAM,oFAAoF,CAAC,GAAG,GAAG,kDAAkD,CAAC;YAChqC;QACF;QC1NA,MAAMU,eAAe,IACnBC,kBACG,UAAU,CAAC,wBACX,KAAK,CAAC,mCACN,mBAAmB,CAAC;gBACnB,4BAA4B;gBAC5B,iBAAiB;gBACjB,wBAAwB;gBACxB,oBAAoB;YACtB,GACC,MAAM,CAAC,YAAY;gBAClB,MAAM;gBACN,SAAS/B;gBACT,aAAa;gBACb,aAAa;YACf,GACC,MAAM,CAAC,QAAQ;gBACd,MAAM;gBACN,QAAQ;gBACR,OAAO;gBACP,aAAa;gBACb,aAAa;YACf,GACC,MAAM,CAAC,mBAAmB;gBACzB,MAAM;gBACN,SAASJ;gBACT,aAAa;gBACb,aACE;YACJ,GACC,MAAM,CAAC,OAAO;gBACb,MAAM;gBACN,OAAO;gBACP,SAAS;gBACT,aACE;YACJ,GACC,MAAM,CAAC,gBAAgB;gBACtB,MAAM;gBACN,SAAS;gBACT,aACE;YACJ,GACC,aAAa,CAAC,GAAG,GAAG,IAAI,0CACxB,OAAO,CAAC,MAAM,iDACd,OAAO,CAAC,8BAA8B,wBACtC,OAAO,CACN,yDACA,0BAED,QAAQ,CACP,sUAED,IAAI,CAAC,QACL,KAAK,CAAC,QAAQ,KACd,OAAO,CAAC,OACR,MAAM,GACN,WAAW,CAAC,OACZ,cAAc,CAAC,OACf,IAAI,CAAC,CAACoC,SAASC;gBACd,MAAMA,SAAS,IAAI3B,MAAM0B;YAC3B,GACC,IAAI,CAAC;QAYH,SAASE,gBACdC,IAAc,EACdC,QAAoC;YAEpC,MAAMC,SAASP,eAAe,SAAS,CAACK,MAAM,CAAC,GAAG,CAACF,OAAOK,OAAOC;gBAC/D,IAAIN,OAAO,MAAMA;gBACjB,IAAIM,QAAQH,WAAWG;YACzB;YACA,MAAMC,YAAYH,AAAgBhC,WAAhBgC,OAAO,CAAC,CAAC,EAAE,GAAiBhC,SAAYoC,OAAOJ,OAAO,CAAC,CAAC,EAAE;YAC5E,IAAIG,AAAcnC,WAAdmC,aAA2B,CAACA,UAAU,IAAI,IAC5C,MAAM,IAAIlC,MAAM;YAElB,MAAMS,WAAYsB,AAAAA,CAAAA,OAAO,IAAI,IAAI,EAAC,EAAG,GAAG,CAACnC;YACzC,MAAMwC,OAAO,IAAIC;YACjB,KAAK,MAAMvB,OAAOL,SAAU;gBAC1B,IAAI2B,KAAK,GAAG,CAACtB,IAAI,IAAI,GACnB,MAAM,IAAId,MACR,CAAC,cAAc,EAAEc,IAAI,IAAI,CAAC,+BAA+B,CAAC;gBAG9DsB,KAAK,GAAG,CAACtB,IAAI,IAAI;YACnB;YACA,OAAO;gBACLoB;gBACA,UAAUH,OAAO,QAAQ;gBACzB,gBAAgBA,MAAM,CAAC,kBAAkB;gBACzCtB;gBACA,KAAKsB,OAAO,GAAG,IAAI;gBACnB,aAAaA,MAAM,CAAC,eAAe,IAAI;gBACvC,MAAMA,AAAgB,SAAhBA,OAAO,IAAI;YACnB;QACF;QAoBA,MAAMO,kBAAkB,IACtBC,AAAAA,IAAAA,SAAAA,KAAAA,AAAAA,EAAM;gBACJ,SAAS;gBACT,SAAS;gBACT,UAAU,CAACC,QACTA,MAAM,IAAI,GAAG,MAAM,GAAG,KAAK;YAC/B;QAEF,MAAMC,iBAAiB,IACrBC,AAAAA,IAAAA,SAAAA,MAAAA,AAAAA,EAAuB;gBACrB,SAAS;gBACT,SAAShD,gBAAgB,GAAG,CAAC,CAACc,WAAc;wBAC1C,MAAMb,oBAAoB,CAACa,SAAS;wBACpC,OAAOA;oBACT;YACF;QAEF,MAAMmC,uBAA+D,CACnEC,eAEAF,AAAAA,IAAAA,SAAAA,MAAAA,AAAAA,EAA6B;gBAC3B,SAAS;gBACT,SAASpD,sBAAsB,GAAG,CAAC,CAACkD,QAAW;wBAAE,MAAMA;wBAAOA;oBAAM;gBACpE,SAASI;YACX;QAEF,MAAMC,oBAAyD,OAC7DnC,gBACAmB,MACAiB,KACAC,gBAAgB,KAAK;YAErB,MAAMC,SAAS,MAAMC,AAAAA,IAAAA,gBAAAA,KAAAA,AAAAA,EAAMvC,gBAAgBmB,MAAM;gBAC/CiB;gBACA,OAAO;gBACP,QAAQC,gBAAgB,SAAS;gBACjC,QAAQ;gBACR,mBAAmB;YACrB;YACA,OAAOC,OAAO,MAAM,IAAI;QAC1B;QAEA,MAAME,gBAAgB,CAACC;YACrB,IAAI;gBACF,OAAOC,AAAAA,IAAAA,kBAAAA,SAAAA,AAAAA,EAAUD;YACnB,EAAE,OAAOxB,OAAO;gBACd,IAAKA,AAAyC,aAAzCA,MAAgC,IAAI,EAAe;gBACxD,MAAMA;YACR;QACF;QAGA,SAAS0B,kBAAkBC,IAAY,EAAEC,SAAmB;YAC1D,MAAMC,YAAsB,EAAE;YAC9B,KAAK,MAAMC,YAAYF,UAAW;gBAChC,MAAMJ,OAAOO,AAAAA,IAAAA,oBAAAA,OAAAA,AAAAA,EAAQJ,MAAMG;gBAC3B,IAAIP,cAAcC,OAAOK,UAAU,IAAI,CAACL;gBACxC,IAAIQ,SAASC,AAAAA,IAAAA,oBAAAA,OAAAA,AAAAA,EAAQT;gBACrB,MAAO,KAAM;oBACX,MAAMU,OAAOX,cAAcS;oBAC3B,IAAIE,QAAQ,CAACA,KAAK,WAAW,IAAIL,UAAU,IAAI,CAACG;oBAChD,IAAIA,WAAWL,QAAQK,WAAWC,AAAAA,IAAAA,oBAAAA,OAAAA,AAAAA,EAAQD,SAAS;oBACnDA,SAASC,AAAAA,IAAAA,oBAAAA,OAAAA,AAAAA,EAAQD;gBACnB;YACF;YACA,IAAIH,UAAU,MAAM,EAClB,MAAM,IAAIxD,MACR,CAAC,0EAA0E,EAAE;mBAAI,IAAIqC,IAAImB;aAAW,CAAC,IAAI,CAAC,MAAM,yDAAyD,CAAC;QAGhL;QAEO,eAAeM,iBACpBjC,IAAc,EACdkC,EAAa,EACbC,YAAqC,CAAC,CAAC;YAEvC,MAAMC,UAAUrC,gBAAgBC,MAAM,CAACH,UAAYqC,GAAG,GAAG,CAACrC;YAC1D,IAAIuC,QAAQ,IAAI,EAAE;YAClB,MAAMC,WAA2B;gBAC/B,KAAKC,QAAQ,GAAG;gBAChB,aAAaC,QAAQD,QAAQ,KAAK,CAAC,KAAK,IAAIA,QAAQ,MAAM,CAAC,KAAK;gBAChE,WAAWA,QAAQ,GAAG,CAAC,qBAAqB;gBAC5C7B;gBACAG;gBACAE;gBACA,gBAAgB,IACd0B,AAAAA,IAAAA,SAAAA,OAAAA,AAAAA,EAAQ;wBACN,SACE;wBACF,SAAS;oBACX;gBACFxB;gBACA,GAAGmB,SAAS;YACd;YACA,IACG,EAACC,QAAQ,SAAS,IAAI,CAACA,QAAQ,QAAO,KACtCA,CAAAA,QAAQ,GAAG,IAAI,CAACC,SAAS,WAAU,GAEpC,MAAM,IAAIlE,MACR;YAGJ,IAAIkC;YACJ,IAAI1B;YACJ,IAAIE;YACJ,IAAI;gBACFwB,YAAY+B,QAAQ,SAAS,IAAK,OAAMC,SAAS,eAAe,EAAC,EAAG,IAAI;gBACxE1D,WAAWyD,QAAQ,QAAQ,IAAK,MAAMC,SAAS,cAAc;gBAC7D,MAAMI,wBAAwB9E,qBAAqB0E,SAAS,SAAS;gBACrExD,iBACEuD,QAAQ,cAAc,IACrBC,CAAAA,SAAS,WAAW,IAAI,CAACD,QAAQ,GAAG,GACjC,MAAMC,SAAS,oBAAoB,CAACI,yBACpCA,qBAAoB;YAC5B,EAAE,OAAO3C,OAAO;gBACd,IACEA,iBAAiB3B,SACjB;oBAAC;oBAAmB;iBAAmB,CAAC,QAAQ,CAAC2B,MAAM,IAAI,GAE3D,MAAM,IAAI3B,MAAM;gBAElB,MAAM2B;YACR;YACA,MAAM2B,OAAOI,AAAAA,IAAAA,oBAAAA,OAAAA,AAAAA,EAAQQ,SAAS,GAAG,EAAEhC;YACnC,MAAM3B,OACJgE,AAAAA,IAAAA,oBAAAA,QAAAA,AAAAA,EAASjB,MACN,WAAW,GACX,OAAO,CAAC,iBAAiB,KACzB,OAAO,CAAC,WAAW,OAAO;YAC/B,MAAMkB,WAAWjF,sBAAsB,CAACmB,eAAe;YACvD,MAAM+D,QAAQnE,mBACZC,MACAC,UACAyD,QAAQ,QAAQ,EAChBvD;YAEF2C,kBAAkBC,MAAM;mBACnBhC,OAAO,IAAI,CAACmD;gBACf;gBACA;gBACA;gBACA;aACD;YACD,KAAK,MAAM,CAAChB,UAAUiB,QAAQ,IAAIpD,OAAO,OAAO,CAACmD,OAAQ;gBACvD,MAAMtB,OAAOO,AAAAA,IAAAA,oBAAAA,OAAAA,AAAAA,EAAQJ,MAAMG;gBAC3BkB,IAAAA,kBAAAA,SAAAA,AAAAA,EAAUf,AAAAA,IAAAA,oBAAAA,OAAAA,AAAAA,EAAQT,OAAO;oBAAE,WAAW;gBAAK;gBAC3CyB,IAAAA,kBAAAA,aAAAA,AAAAA,EAAczB,MAAMuB,SAAS;oBAAE,MAAM;gBAAK;YAC5C;YACAX,GAAG,GAAG,CAAC,CAAC,QAAQ,EAAEvD,SAAS,kBAAkB,EAAE8C,MAAM;YACrD,IAAIuB,UAAU,CAACZ,QAAQ,WAAW;YAClC,IAAIY,WAAWX,SAAS,WAAW,IAAI,CAACD,QAAQ,GAAG,EACjD,IAAI;gBACFY,UAAU,MAAMX,SAAS,cAAc;YACzC,EAAE,OAAOvC,OAAO;gBACd,IACEA,iBAAiB3B,SACjB;oBAAC;oBAAmB;iBAAmB,CAAC,QAAQ,CAAC2B,MAAM,IAAI,GAE3D,MAAM,IAAI3B,MACR,CAAC,2DAA2D,EAAEsD,KAAK,0BAA0B,EAAE5C,eAAe,CAAC,EAAE8D,SAAS,OAAO,CAAC,IAAI,CAAC,KAAK,iEAAiE,CAAC;gBAGlN,MAAM7C;YACR;YAEF,IAAI,CAACkD,SAAS,YACZd,GAAG,GAAG,CACJ,CAAC,qBAAqB,EAAET,KAAK,YAAY,EAAE5C,eAAe,CAAC,EAAE8D,SAAS,OAAO,CAAC,IAAI,CAAC,KAAK,yJAAyJ,CAAC;YAItPT,GAAG,GAAG,CAAC,CAAC,6BAA6B,EAAErD,eAAe,GAAG,CAAC;YAC1D,IAAI;gBACF,MAAMwD,SAAS,iBAAiB,CAACxD,gBAAgB8D,SAAS,OAAO,EAAElB;YACrE,EAAE,OAAO3B,OAAO;gBACd,MAAM,IAAI3B,MACR,CAAC,wGAAwG,EAAEsD,KAAK,0BAA0B,EAAE5C,eAAe,CAAC,EAAE8D,SAAS,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,EAAE9D,eAAe,wIAAwI,EAAEA,eAAe,aAAa,EAAEiB,iBAAiB3B,QAAQ2B,MAAM,OAAO,GAAGQ,OAAOR,QAAQ;YAErb;YACA,IAAI;gBACF,MAAMmD,gBAAgBpB,AAAAA,IAAAA,oBAAAA,OAAAA,AAAAA,EAAQJ,MAAM;gBACpC,MAAMyB,yBAAyBX,QAAQlB,cAAc4B;gBAErD,IAAI,CAACC,wBAAwB;oBAC3BhB,GAAG,GAAG,CAAC;oBACP,MAAMG,SAAS,iBAAiB,CAACxD,gBAAgB8D,SAAS,QAAQ,EAAElB;gBACtE;gBACA,MAAM0B,WAAWC,AAAAA,IAAAA,kBAAAA,YAAAA,AAAAA,EAAaH,eAAe;gBAC7C,IAAI,CAACE,SAAS,UAAU,CAAC,6CACvB,MAAM,IAAIhF,MAAM;YAEpB,EAAE,OAAO2B,OAAO;gBACd,MAAM,IAAI3B,MACR,CAAC,iEAAiE,EAAEsD,KAAK,uLAAuL,EAAE5C,eAAe,sCAAsC,EAAEiB,iBAAiB3B,QAAQ2B,MAAM,OAAO,GAAGQ,OAAOR,QAAQ;YAErX;YACAoC,GAAG,GAAG,CACJ,CAAC,eAAe,EAAET,KAAK,kBAAkB,EAAEI,AAAAA,IAAAA,oBAAAA,OAAAA,AAAAA,EAAQJ,MAAM,8BAA8B,2DAA2D,EAAE9C,AAAa,UAAbA,WAAqB,CAAC,oBAAoB,EAAEgE,SAAS,eAAe,EAAE,GAAGhE,AAAa,eAAbA,WAA0B,8EAA8E,8CAA8C,wCAAwC,EAAEE,eAAe,KAAK,CAAC;QAEtb"}
1
+ {"version":3,"file":"0~458.js","sources":["../../src/cli/create-package-manager.ts","../../src/cli/create-template.ts","../../src/cli/create-command.ts"],"sourcesContent":["export const createPackageManagers = ['npm', 'pnpm'] as const;\nexport type CreatePackageManager = (typeof createPackageManagers)[number];\n\nexport const packageManagerCommands: Record<\n CreatePackageManager,\n { install: string[]; describe: string[]; installChromium: string }\n> = {\n npm: {\n install: ['install', '--workspaces=false'],\n describe: [\n 'exec',\n '--no',\n '--workspaces=false',\n '--',\n 'midscene-test',\n 'nodes',\n ],\n installChromium: 'npm exec -- playwright install chromium',\n },\n pnpm: {\n install: ['install', '--ignore-workspace'],\n describe: ['exec', 'midscene-test', 'nodes'],\n installChromium: 'pnpm exec playwright install chromium',\n },\n};\n\nexport function detectPackageManager(userAgent?: string): CreatePackageManager {\n return userAgent?.startsWith('pnpm/') ? 'pnpm' : 'npm';\n}\n","import { version } from '../../package.json';\nimport {\n type CreatePackageManager,\n packageManagerCommands,\n} from './create-package-manager';\n\nexport const createPlatforms = [\n 'web',\n 'android',\n 'ios',\n 'harmony',\n 'computer',\n] as const;\nexport type CreatePlatform = (typeof createPlatforms)[number];\n\nexport const createPlatformLabels: Record<CreatePlatform, string> = {\n web: 'Web (Playwright)',\n android: 'Android',\n ios: 'iOS',\n harmony: 'HarmonyOS',\n computer: 'Desktop (Computer)',\n};\n\nexport interface NodePackageSpec {\n name: string;\n version: string;\n}\n\n/** Registry packages only: keep the install spec separate from the import name. */\nexport function parseNodePackageSpec(spec: string): NodePackageSpec {\n const match =\n /^(?:(@[a-z0-9][a-z0-9._-]*)\\/)?([a-z0-9][a-z0-9._-]*)(?:@([a-zA-Z0-9^~*>=<|. +_-]+))?$/.exec(\n spec,\n );\n if (!match || (match[3] !== undefined && !match[3].trim())) {\n throw new Error(\n `Invalid Node package \"${spec}\". Use an npm package name with an optional version, such as @acme/test-nodes@1.2.0.`,\n );\n }\n return {\n name: `${match[1] ? `${match[1]}/` : ''}${match[2]}`,\n version: match[3] ?? 'latest',\n };\n}\n\nconst platformImports: Record<CreatePlatform, string> = {\n web: `import { PlaywrightAgent } from '@midscene/web/playwright/agent';\nimport { createPlaywrightNodes } from '@midscene/test/playwright';\nimport { chromium, type Page } from 'playwright';`,\n android: `import { AndroidAgent, agentFromAdbDevice } from '@midscene/android';`,\n ios: `import { IOSAgent, agentFromWebDriverAgent } from '@midscene/ios';`,\n harmony: `import { HarmonyAgent, agentFromHdcDevice } from '@midscene/harmony';`,\n computer: `import { ComputerAgent, agentForComputer } from '@midscene/computer';`,\n};\n\nconst agentClasses: Record<CreatePlatform, string> = {\n web: 'PlaywrightAgent',\n android: 'AndroidAgent',\n ios: 'IOSAgent',\n harmony: 'HarmonyAgent',\n computer: 'ComputerAgent',\n};\n\nconst platformSetup: Record<CreatePlatform, string> = {\n web: ` const browser = await chromium.launch({ headless: env.HEADLESS !== 'false' });\n onTeardown(() => browser.close());\n const page = await browser.newPage();\n const context: ProjectContext = { page };\n onTeardown(async () => { await context.agent?.destroy(); });\n return context;`,\n android: ` const agent = await agentFromAdbDevice(env.ANDROID_DEVICE_ID || undefined);\n onTeardown(() => agent.destroy());\n return { agent };`,\n ios: ` const port = Number(env.WDA_PORT || 8100);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n throw new Error('WDA_PORT must be an integer between 1 and 65535.');\n }\n const agent = await agentFromWebDriverAgent({\n wdaHost: env.WDA_HOST || 'localhost',\n wdaPort: port,\n });\n onTeardown(() => agent.destroy());\n return { agent };`,\n harmony: ` const agent = await agentFromHdcDevice(env.HARMONY_DEVICE_ID || undefined);\n onTeardown(() => agent.destroy());\n return { agent };`,\n computer: ` const agent = await agentForComputer({\n displayId: env.COMPUTER_DISPLAY_ID || undefined,\n });\n onTeardown(() => agent.destroy());\n return { agent };`,\n};\n\nconst platformEnv: Record<CreatePlatform, string> = {\n web: 'HEADLESS=true\\n',\n android:\n '# Optional: choose a device from adb devices.\\nANDROID_DEVICE_ID=\\n',\n ios: 'WDA_HOST=localhost\\nWDA_PORT=8100\\n',\n harmony:\n '# Optional: choose a device from hdc list targets.\\nHARMONY_DEVICE_ID=\\n',\n computer:\n '# Optional: select a display; leave empty for the default display.\\nCOMPUTER_DISPLAY_ID=\\n# Enable Xvfb on headless Linux after installing its dependencies.\\nMIDSCENE_COMPUTER_HEADLESS_LINUX=false\\n',\n};\n\nconst platformInstructions: Record<Exclude<CreatePlatform, 'web'>, string> = {\n android:\n 'Connect an Android device and verify it with `adb devices`. Set ANDROID_DEVICE_ID to select a device.',\n ios: 'Start WebDriverAgent and set WDA_HOST and WDA_PORT for your iOS device.',\n harmony:\n 'Connect a HarmonyOS device and verify it with `hdc list targets`. Set HARMONY_DEVICE_ID to select a device. Set HDC_HOME if hdc is not on PATH.',\n computer:\n 'Prepare the local desktop using the [desktop setup guide](https://midscenejs.com/platforms/desktop.html), including system dependencies and permissions. Set COMPUTER_DISPLAY_ID to select a display. For headless Linux, install Xvfb and enable MIDSCENE_COMPUTER_HEADLESS_LINUX.',\n};\n\nexport function createProjectFiles(\n name: string,\n platform: CreatePlatform,\n packages: readonly NodePackageSpec[],\n packageManager: CreatePackageManager,\n): Record<string, string> {\n const instructions =\n platform === 'web'\n ? `Install Chromium before the first test: \\`${packageManagerCommands[packageManager].installChromium}\\`.`\n : platformInstructions[platform];\n const agentClass = agentClasses[platform];\n const imports = packages\n .map(\n (pkg, index) =>\n `import { createMidsceneTestNodes as createPackageNodes${index} } from ${JSON.stringify(pkg.name)};`,\n )\n .join('\\n');\n const extraNodes = packages\n .map(\n (_, index) =>\n ` ...createPackageNodes${index}<ProjectContext>({ platform: '${platform}', getAgent }),`,\n )\n .join('\\n');\n const config = `import { fileURLToPath } from 'node:url';\nimport { config as loadEnv } from 'dotenv';\nimport type { NodeExecutionContext } from '@midscene/test';\nimport { defineProjectSetup, defineTestProject } from '@midscene/test/config';\nimport { createMidsceneNodes } from '@midscene/test/midscene';\n${platformImports[platform]}\n${imports}\n\nloadEnv({ path: fileURLToPath(new URL('.env', import.meta.url)) });\n\ninterface ProjectContext {\n${platform === 'web' ? ' page: Page;\\n agent?: PlaywrightAgent;' : ` agent: ${agentClass};`}\n}\n\nconst getAgent = ({ context }: NodeExecutionContext<unknown, ProjectContext>) => {\n${platform === 'web' ? ' context.agent ??= new PlaywrightAgent(context.page);' : ''}\n return context.agent;\n};\n\nconst setup = defineProjectSetup<ProjectContext>({\n name: '${platform}',\n platform: '${platform}',\n async setup({ env, onTeardown }) {\n${platformSetup[platform]}\n },\n});\n\nexport default defineTestProject<ProjectContext>({\n projects: [{\n name: '${platform}',\n platform: '${platform}',\n setup,\n files: { include: ['cases/**/*.{yaml,yml}'] },\n }],\n nodes: [\n ...createMidsceneNodes<ProjectContext>({ agentClass: ${agentClass}, getAgent }),\n${platform === 'web' ? ' ...createPlaywrightNodes<ProjectContext>({ getPage: ({ context }) => context.page }),' : ''}\n${extraNodes}\n ],\n});\n`;\n const dependencies: Record<string, string> = {\n '@midscene/test': version,\n [`@midscene/${platform}`]: version,\n '@types/node': '^20.0.0',\n dotenv: '^16.4.5',\n typescript: '^5.8.3',\n ...(platform === 'web' ? { playwright: '^1.45.0' } : {}),\n };\n for (const pkg of packages) {\n if (Object.hasOwn(dependencies, pkg.name)) {\n throw new Error(\n `Node package \"${pkg.name}\" conflicts with a generated project dependency.`,\n );\n }\n dependencies[pkg.name] = pkg.version;\n }\n const env = platformEnv[platform];\n return {\n 'package.json': `${JSON.stringify(\n {\n name,\n private: true,\n type: 'module',\n scripts: {\n test: 'midscene-test',\n nodes: 'midscene-test nodes',\n postinstall: 'midscene-test nodes',\n },\n engines: { node: '^20.19.0 || ^22.12.0 || >=24.0.0' },\n devDependencies: dependencies,\n },\n null,\n 2,\n )}\\n`,\n 'midscene.config.ts': config,\n 'tsconfig.json': `${JSON.stringify(\n {\n compilerOptions: {\n target: 'ES2022',\n module: 'ESNext',\n moduleResolution: 'Bundler',\n strict: true,\n noEmit: true,\n skipLibCheck: true,\n types: ['node'],\n },\n include: ['midscene.config.ts'],\n },\n null,\n 2,\n )}\\n`,\n 'cases/example.yaml':\n platform === 'web'\n ? 'cases:\\n - name: Open example.com\\n steps:\\n - gotoUrl: https://example.com\\n - aiAssert: The page displays the heading Example Domain\\n'\n : platform === 'computer'\n ? 'cases:\\n - name: Inspect the desktop\\n steps:\\n - aiAsk: Describe the current desktop\\n'\n : 'cases:\\n - name: Inspect the home screen\\n steps:\\n - home: {}\\n - aiAsk: Describe the current screen\\n',\n '.env.example': `# Copy this file to .env and configure your model before running tests.\\n# See https://midscenejs.com/model-config.html\\nMIDSCENE_MODEL_BASE_URL=\\nMIDSCENE_MODEL_API_KEY=\\nMIDSCENE_MODEL_NAME=\\nMIDSCENE_MODEL_FAMILY=\\n\\n${env}`,\n '.gitignore': 'node_modules/\\n.env\\nmidscene_run/\\n',\n 'README.md': `# ${name}\\n\\nA Midscene Test project for ${platform}.\\n\\nIf you skipped installation during creation, run \\`${packageManager} ${packageManagerCommands[packageManager].install.join(' ')}\\`. The \\`postinstall\\` script automatically generates \\`midscene-node-reference.md\\` after each dependency installation. If lifecycle scripts are disabled, run \\`${packageManager} run nodes\\` manually.\\n\\nCopy \\`.env.example\\` to \\`.env\\` and fill in your [model configuration](https://midscenejs.com/model-config.html).\\n\\n${instructions}\\n\\nRun tests with \\`${packageManager} test\\`. Read \\`midscene-node-reference.md\\` for the available Nodes and their inputs.\\n\\nAfter changing Node registrations in \\`midscene.config.ts\\`, run \\`${packageManager} run nodes\\` to refresh the reference. This loads the configuration without connecting to a device or running tests. Extension factories must only acquire runtime resources inside Node execution.\\n\\n${packages.length ? `Node packages: ${packages.map((pkg) => `\\`${pkg.name}\\``).join(', ')}. Their \\`createMidsceneTestNodes\\` factories are imported in the configuration.\\n\\n` : ''}Reports are written to \\`midscene_run/report/\\`.\\n`,\n };\n}\n","import { lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { basename, dirname, resolve } from 'node:path';\nimport { confirm, input, select } from '@inquirer/prompts';\nimport { execa } from 'execa';\nimport yargs from 'yargs/yargs';\nimport {\n type CreatePackageManager,\n createPackageManagers,\n detectPackageManager,\n packageManagerCommands,\n} from './create-package-manager';\nimport {\n type CreatePlatform,\n type NodePackageSpec,\n createPlatformLabels,\n createPlatforms,\n createProjectFiles,\n parseNodePackageSpec,\n} from './create-template';\nimport type { TestCliIO } from './test-command';\n\nconst createParser = () =>\n yargs()\n .scriptName('midscene-test create')\n .usage('Usage: $0 [directory] [options]')\n .parserConfiguration({\n 'parse-positional-numbers': false,\n 'greedy-arrays': false,\n 'camel-case-expansion': false,\n 'boolean-negation': false,\n })\n .option('platform', {\n type: 'string',\n choices: createPlatforms,\n requiresArg: true,\n description: 'Platform preset (prompt if omitted)',\n })\n .option('with', {\n type: 'array',\n string: true,\n nargs: 1,\n requiresArg: true,\n description: 'npm Node package, optionally versioned (repeatable)',\n })\n .option('package-manager', {\n type: 'string',\n choices: createPackageManagers,\n requiresArg: true,\n description:\n 'Package manager (prompt if omitted; detect without prompts, falling back to npm)',\n })\n .option('yes', {\n type: 'boolean',\n alias: 'y',\n default: false,\n description:\n 'Disable prompts and install dependencies; directory and platform must be supplied',\n })\n .option('skip-install', {\n type: 'boolean',\n default: false,\n description:\n 'Create files without installing dependencies or generating midscene-node-reference.md',\n })\n .demandCommand(0, 1, '', 'Only one project directory is allowed.')\n .example('$0', 'Choose a directory and platform interactively')\n .example('$0 my-tests --platform web', 'Create a Web project')\n .example(\n '$0 . --platform android --with @acme/test-nodes@1.2.0',\n 'Include a Node package',\n )\n .epilogue(\n 'Optionally installs dependencies with the selected package manager. The generated postinstall script creates midscene-node-reference.md after installation.\\nNode packages must export a synchronous createMidsceneTestNodes(options) factory.\\nExisting files are never overwritten. Setup and tests are not run during creation.',\n )\n .help('help')\n .alias('help', 'h')\n .version(false)\n .strict()\n .exitProcess(false)\n .showHelpOnFail(false)\n .fail((message, error) => {\n throw error ?? new Error(message);\n })\n .wrap(100);\n\nexport interface CreateOptions {\n directory?: string;\n platform?: CreatePlatform;\n packageManager?: CreatePackageManager;\n packages: NodePackageSpec[];\n yes: boolean;\n skipInstall: boolean;\n help: boolean;\n}\n\nexport function parseCreateArgs(\n args: string[],\n onOutput?: (message: string) => void,\n): CreateOptions {\n const values = createParser().parseSync(args, {}, (error, _argv, output) => {\n if (error) throw error;\n if (output) onOutput?.(output);\n });\n const directory = values._[0] === undefined ? undefined : String(values._[0]);\n if (directory !== undefined && !directory.trim()) {\n throw new Error('Project directory must not be empty.');\n }\n const packages = (values.with ?? []).map(parseNodePackageSpec);\n const seen = new Set<string>();\n for (const pkg of packages) {\n if (seen.has(pkg.name)) {\n throw new Error(\n `Node package \"${pkg.name}\" was specified more than once.`,\n );\n }\n seen.add(pkg.name);\n }\n return {\n directory,\n platform: values.platform,\n packageManager: values['package-manager'],\n packages,\n yes: values.yes ?? false,\n skipInstall: values['skip-install'] ?? false,\n help: values.help === true,\n };\n}\n\nexport interface CreateServices {\n cwd: string;\n interactive: boolean;\n userAgent: string | undefined;\n promptDirectory(): Promise<string>;\n selectPlatform(): Promise<CreatePlatform>;\n selectPackageManager(\n defaultValue: CreatePackageManager,\n ): Promise<CreatePackageManager>;\n confirmInstall(): Promise<boolean>;\n runPackageManager(\n packageManager: CreatePackageManager,\n args: string[],\n cwd: string,\n captureOutput?: boolean,\n ): Promise<string>;\n}\n\nconst promptDirectory = () =>\n input({\n message: 'Project directory:',\n default: 'my-tests',\n validate: (value) =>\n value.trim().length > 0 || 'Project directory must not be empty.',\n });\n\nconst selectPlatform = () =>\n select<CreatePlatform>({\n message: 'Select a platform:',\n choices: createPlatforms.map((platform) => ({\n name: createPlatformLabels[platform],\n value: platform,\n })),\n });\n\nconst selectPackageManager: CreateServices['selectPackageManager'] = (\n defaultValue,\n) =>\n select<CreatePackageManager>({\n message: 'Select a package manager:',\n choices: createPackageManagers.map((value) => ({ name: value, value })),\n default: defaultValue,\n });\n\nconst runPackageManager: CreateServices['runPackageManager'] = async (\n packageManager,\n args,\n cwd,\n captureOutput = false,\n) => {\n const result = await execa(packageManager, args, {\n cwd,\n stdin: 'inherit',\n stdout: captureOutput ? 'pipe' : 'inherit',\n stderr: 'inherit',\n stripFinalNewline: false,\n });\n return result.stdout ?? '';\n};\n\nconst statIfPresent = (path: string) => {\n try {\n return lstatSync(path);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw error;\n }\n};\n\n/** Check every destination before writing anything, including dangling links. */\nfunction checkDestinations(root: string, filenames: string[]): void {\n const conflicts: string[] = [];\n for (const filename of filenames) {\n const path = resolve(root, filename);\n if (statIfPresent(path)) conflicts.push(path);\n let parent = dirname(path);\n while (true) {\n const stat = statIfPresent(parent);\n if (stat && !stat.isDirectory()) conflicts.push(parent);\n if (parent === root || parent === dirname(parent)) break;\n parent = dirname(parent);\n }\n }\n if (conflicts.length) {\n throw new Error(\n `Cannot create project; these paths already exist or are not directories:\\n${[...new Set(conflicts)].join('\\n')}\\nChoose another directory or move the conflicting files.`,\n );\n }\n}\n\nexport async function runCreateCommand(\n args: string[],\n io: TestCliIO,\n overrides: Partial<CreateServices> = {},\n): Promise<void> {\n const options = parseCreateArgs(args, (message) => io.log(message));\n if (options.help) return;\n const services: CreateServices = {\n cwd: process.cwd(),\n interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY),\n userAgent: process.env.npm_config_user_agent,\n promptDirectory,\n selectPlatform,\n selectPackageManager,\n confirmInstall: () =>\n confirm({\n message:\n 'Install dependencies and generate midscene-node-reference.md now?',\n default: true,\n }),\n runPackageManager,\n ...overrides,\n };\n if (\n (!options.directory || !options.platform) &&\n (options.yes || !services.interactive)\n ) {\n throw new Error(\n 'Project directory and --platform are required without prompts. Example: midscene-test create my-tests --platform web',\n );\n }\n let directory: string;\n let platform: CreatePlatform;\n let packageManager: CreatePackageManager;\n try {\n directory = options.directory ?? (await services.promptDirectory()).trim();\n platform = options.platform ?? (await services.selectPlatform());\n const defaultPackageManager = detectPackageManager(services.userAgent);\n packageManager =\n options.packageManager ??\n (services.interactive && !options.yes\n ? await services.selectPackageManager(defaultPackageManager)\n : defaultPackageManager);\n } catch (error) {\n if (\n error instanceof Error &&\n ['ExitPromptError', 'AbortPromptError'].includes(error.name)\n ) {\n throw new Error('Project creation cancelled.');\n }\n throw error;\n }\n const root = resolve(services.cwd, directory);\n const name =\n basename(root)\n .toLowerCase()\n .replace(/[^a-z0-9._-]/g, '-')\n .replace(/^[._-]+/, '') || 'midscene-tests';\n const commands = packageManagerCommands[packageManager];\n const files = createProjectFiles(\n name,\n platform,\n options.packages,\n packageManager,\n );\n checkDestinations(root, [\n ...Object.keys(files),\n 'midscene-node-reference.md',\n 'pnpm-lock.yaml',\n 'package-lock.json',\n 'npm-shrinkwrap.json',\n ]);\n for (const [filename, content] of Object.entries(files)) {\n const path = resolve(root, filename);\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, content, { flag: 'wx' });\n }\n io.log(`Created ${platform} project files in ${root}`);\n let install = !options.skipInstall;\n if (install && services.interactive && !options.yes) {\n try {\n install = await services.confirmInstall();\n } catch (error) {\n if (\n error instanceof Error &&\n ['ExitPromptError', 'AbortPromptError'].includes(error.name)\n ) {\n throw new Error(\n `Project creation cancelled. Project files are preserved in ${root}.\\nIn that directory, run ${packageManager} ${commands.install.join(' ')} to install dependencies and generate midscene-node-reference.md.`,\n );\n }\n throw error;\n }\n }\n if (!install) {\n io.log(\n `Project files ready: ${root}\\nNext: run ${packageManager} ${commands.install.join(' ')} in the project directory. The postinstall script will generate midscene-node-reference.md.\\nThen follow README.md to configure your model and run tests.`,\n );\n return;\n }\n io.log(`Installing dependencies with ${packageManager}...`);\n try {\n await services.runPackageManager(packageManager, commands.install, root);\n } catch (error) {\n throw new Error(\n `Dependency installation or postinstall Node reference generation failed. Project files are preserved in ${root}.\\nIn that directory, run ${packageManager} ${commands.install.join(' ')}, then ${packageManager} run nodes if lifecycle scripts are disabled.\\nIf postinstall failed, check midscene.config.ts and the Node package factories, then run ${packageManager} run nodes.\\n${error instanceof Error ? error.message : String(error)}`,\n );\n }\n try {\n const referencePath = resolve(root, 'midscene-node-reference.md');\n const generatedByPostinstall = Boolean(statIfPresent(referencePath));\n // Fall back to explicit generation if lifecycle scripts were disabled.\n if (!generatedByPostinstall) {\n io.log('Generating Node reference...');\n await services.runPackageManager(packageManager, commands.describe, root);\n }\n const markdown = readFileSync(referencePath, 'utf8');\n if (!markdown.startsWith('<!-- Generated by `midscene-test nodes`.')) {\n throw new Error('nodes did not generate a valid Node reference.');\n }\n } catch (error) {\n throw new Error(\n `Node reference generation failed. Project files are preserved in ${root}.\\nCheck that each Node package exports createMidsceneTestNodes(options), returns a Node array synchronously, and does not register duplicate names.\\nFix midscene.config.ts, then run ${packageManager} run nodes in the project directory.\\n${error instanceof Error ? error.message : String(error)}`,\n );\n }\n io.log(\n `Project ready: ${root}\\nNode reference: ${resolve(root, 'midscene-node-reference.md')}\\nNext: copy .env.example to .env and configure your model.${platform === 'web' ? `\\nInstall Chromium: ${commands.installChromium}` : platform === 'computer' ? '\\nPrepare desktop dependencies and permissions as described in README.md.' : '\\nConfigure your device connection in .env.'}\\nRun tests from the project directory: ${packageManager} test`,\n );\n}\n"],"names":["createPackageManagers","packageManagerCommands","detectPackageManager","userAgent","createPlatforms","createPlatformLabels","parseNodePackageSpec","spec","match","undefined","Error","platformImports","agentClasses","platformSetup","platformEnv","platformInstructions","createProjectFiles","name","platform","packages","packageManager","instructions","agentClass","imports","pkg","index","JSON","extraNodes","_","config","dependencies","version","Object","env","createParser","yargs","message","error","parseCreateArgs","args","onOutput","values","_argv","output","directory","String","seen","Set","promptDirectory","input","value","selectPlatform","select","selectPackageManager","defaultValue","runPackageManager","cwd","captureOutput","result","execa","statIfPresent","path","lstatSync","checkDestinations","root","filenames","conflicts","filename","resolve","parent","dirname","stat","runCreateCommand","io","overrides","options","services","process","Boolean","confirm","defaultPackageManager","basename","commands","files","content","mkdirSync","writeFileSync","install","referencePath","generatedByPostinstall","markdown","readFileSync"],"mappings":"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"}
@@ -434,7 +434,7 @@ const validateNonNegativeInteger = (value, fallback, label)=>{
434
434
  if (!Number.isInteger(value) || value < 0) throw new TypeError(`Midscene config ${label} must be a non-negative integer.`);
435
435
  return value;
436
436
  };
437
- const validateExecutionProjects = (value, defaultSetup, globalNodes)=>{
437
+ const validateExecutionProjects = (value, defaultSetup)=>{
438
438
  if (void 0 === value) {
439
439
  let platform = 'web';
440
440
  if (void 0 !== defaultSetup) {
@@ -460,8 +460,7 @@ const validateExecutionProjects = (value, defaultSetup, globalNodes)=>{
460
460
  exclude: []
461
461
  }),
462
462
  retry: 0,
463
- variables: Object.freeze({}),
464
- nodes: new NodeRegistry(globalNodes.definitions())
463
+ variables: Object.freeze({})
465
464
  })
466
465
  ])
467
466
  };
@@ -476,7 +475,6 @@ const validateExecutionProjects = (value, defaultSetup, globalNodes)=>{
476
475
  'name',
477
476
  'platform',
478
477
  'setup',
479
- 'nodes',
480
478
  'files',
481
479
  'tags',
482
480
  'retry',
@@ -487,17 +485,10 @@ const validateExecutionProjects = (value, defaultSetup, globalNodes)=>{
487
485
  names.add(candidate.name);
488
486
  const platform = validatePlatform(candidate.platform, `${label}.platform`);
489
487
  const files = validateTestFileSelection(candidate.files, `${label}.files`);
490
- if (void 0 !== candidate.nodes && !Array.isArray(candidate.nodes)) throw new TypeError(`Midscene config ${label}.nodes must be an array.`);
491
- const localNodes = new NodeRegistry(candidate.nodes);
492
- const nodes = new NodeRegistry([
493
- ...globalNodes.definitions().filter((node)=>!localNodes.has(node.name)),
494
- ...localNodes.definitions()
495
- ]);
496
488
  return Object.freeze({
497
489
  projectId: projectIdFromIndex(index),
498
490
  name: candidate.name,
499
491
  platform,
500
- nodes,
501
492
  ...files ? {
502
493
  files
503
494
  } : {},
@@ -539,8 +530,7 @@ const validateOutput = (value)=>{
539
530
  });
540
531
  };
541
532
  const validateTestProjectDefinition = (definition)=>{
542
- if (!isRecord(definition)) throw new TypeError('Midscene config must default export an object.');
543
- if (void 0 !== definition.nodes && !Array.isArray(definition.nodes)) throw new TypeError('Midscene config nodes must be an array.');
533
+ if (!isRecord(definition) || !('nodes' in definition) || !Array.isArray(definition.nodes)) throw new TypeError('Midscene config must default export an object with a nodes array.');
544
534
  if ('setupWorkflow' in definition) throw new TypeError('Midscene config setupWorkflow is no longer supported. Use setup or projects[].setup instead.');
545
535
  if ('setupDocument' in definition) throw new TypeError('Midscene config setupDocument is not supported. Use setup or projects[].setup for resources, and beforeAll/afterAll for document lifecycle steps.');
546
536
  if ('root' in definition) throw new TypeError('Midscene config root is not supported. Pass the Test Project directory to midscene-test instead.');
@@ -553,8 +543,8 @@ const validateTestProjectDefinition = (definition)=>{
553
543
  'output',
554
544
  'nodes'
555
545
  ], 'root');
546
+ const resolvedProjects = validateExecutionProjects(definition.projects, definition.setup);
556
547
  const nodes = new NodeRegistry(definition.nodes);
557
- const resolvedProjects = validateExecutionProjects(definition.projects, definition.setup, nodes);
558
548
  return {
559
549
  ...resolvedProjects,
560
550
  test: validateTestOptions(definition.test),
@@ -1764,7 +1754,7 @@ const selectProjects = (projects, names)=>{
1764
1754
  if (unknown) throw new Error(`Unknown Midscene project: ${unknown}`);
1765
1755
  return projects.filter((project)=>requested.has(project.name));
1766
1756
  };
1767
- const prepareProject = (project, projectRoot, runDir)=>{
1757
+ const prepareProject = (project, projectRoot, resolveNode, runDir)=>{
1768
1758
  const fileSelection = project.files ?? DEFAULT_TEST_FILE_SELECTION;
1769
1759
  const files = discoverTestFiles(projectRoot, fileSelection);
1770
1760
  const sources = files.map((absolutePath)=>({
@@ -1786,7 +1776,7 @@ const prepareProject = (project, projectRoot, runDir)=>{
1786
1776
  }
1787
1777
  for (const source of sources)try {
1788
1778
  const collected = collectWorkflowDocument(source, {
1789
- resolveNode: project.nodes.get.bind(project.nodes),
1779
+ resolveNode,
1790
1780
  variables: project.variables,
1791
1781
  env: process.env
1792
1782
  });
@@ -1830,7 +1820,7 @@ async function runTestProject(options = {}) {
1830
1820
  });
1831
1821
  (0, external_node_fs_namespaceObject.mkdirSync)(runDir);
1832
1822
  const selectedProjects = selectProjects(definition.projects, options.projectNames);
1833
- const preparedProjects = selectedProjects.map((project)=>prepareProject(project, projectRoot, runDir));
1823
+ const preparedProjects = selectedProjects.map((project)=>prepareProject(project, projectRoot, definition.resolveNode, runDir));
1834
1824
  const progress = options.onProgress ?? (()=>{});
1835
1825
  const totalDocuments = preparedProjects.reduce((total, prepared)=>total + prepared.documents.length, 0);
1836
1826
  const totalCases = preparedProjects.reduce((total, prepared)=>total + prepared.selectedCaseCount, 0);
@@ -1923,7 +1913,7 @@ async function runTestProject(options = {}) {
1923
1913
  }
1924
1914
  projectProgress(` [document ${documentIndex + 1}/${prepared.documents.length}] ${document.sourcePath}`);
1925
1915
  const execution = await runWorkflowDocument(document, {
1926
- resolveNode: project.nodes.require.bind(project.nodes),
1916
+ resolveNode: definition.nodes.require.bind(definition.nodes),
1927
1917
  project,
1928
1918
  projectContext: runtime.context,
1929
1919
  retry: project.retry,