From 5a2f0eb6785b59d8b455ed5cb1d9773351ff895a Mon Sep 17 00:00:00 2001 From: brother7 <7brother7@gmail.com> Date: Wed, 2 Sep 2026 22:46:58 +0800 Subject: [PATCH] fix(packages): load bundled Pi runtime for local installs --- ...260902-local-skill-install-fix-6b3e91a4.md | 125 ++++++++++++++++++ .../coding-packages/device-package-manager.ts | 58 ++++++-- electron/coding-runtime/pi/extension-host.ts | 10 +- .../pi/extensions/makelore-runtime.ts | 12 +- scripts/lib/pi-product-artifact.mjs | 33 +++++ tests/unit/device-package-manager.test.ts | 18 ++- tests/unit/pi-extension-bundle.test.ts | 81 ++++++++++++ tests/unit/pi-managed-worker-opener.test.ts | 2 +- tests/unit/pi-product-artifact.test.ts | 36 +++++ 9 files changed, 356 insertions(+), 19 deletions(-) create mode 100644 .project-docs/30-worklog/tasks/20260902-local-skill-install-fix-6b3e91a4.md diff --git a/.project-docs/30-worklog/tasks/20260902-local-skill-install-fix-6b3e91a4.md b/.project-docs/30-worklog/tasks/20260902-local-skill-install-fix-6b3e91a4.md new file mode 100644 index 0000000..f28f0f0 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260902-local-skill-install-fix-6b3e91a4.md @@ -0,0 +1,125 @@ +# Task: Fix packaged local Skill installation + +## Identity + +- Task ID: 20260902-local-skill-install-fix-6b3e91a4 +- Mode: Feature +- Branch: codex/20260902-local-skill-install-fix-6b3e91a4-local-skill-install-fix +- Worktree: D:\Datas\OthersProjects\makelore-local-skill-install-fix-6b3e91a4 +- Base commit: 28e1690038d61b9333a2f6ca77f0187ea4273676 +- Owner: codex-root +- Status: In Progress + +## Scope + +- Fix packaged conversation-driven Device Package installation after the bundled + Pi CLI succeeds but the Main-owned package inspection path fails to load its + production dependency closure. +- Add a red/green packaged-runtime regression for the lazy package-manager + boundary. +- Preserve closed Device Package errors across the Pi extension bridge so + supported-source dependency failures and unsupported source syntax no longer + collapse to the same generic `Bridge request failed` response. +- Rebuild and verify the Windows `app.asar`/product artifact, then execute the + real packaged npm and Git prepare paths without committing a package. + +## Intent And Constraints + +- Keep the accepted Device Packages product model unchanged: installation is + initiated only through conversation tools; preview and a distinct later + confirmation precede commit; packages are local Main-owned immutable + generations; lifecycle scripts stay disabled; only future/idle parent workers + receive resources and child workers remain empty. +- Work only in the isolated feature worktree from exact client `main` + `28e1690038d61b9333a2f6ca77f0187ea4273676`. Preserve the three unowned task + records in the client root and do not reset, stash, clean, adopt, or modify + that worktree. +- Do not add a global Pi CLI requirement, visible installer, Account Library, + Marketplace Release/Admission, hosted Web Search, Provider, server, billing, + deploy, publish, push, or PR behavior. +- Avoid leaking local paths, stacks, credentials, or raw dependency diagnostics + to Pi/Renderer. Only closed Device Package codes and bounded public messages + may cross the bridge; internal errors may be logged in Main. +- The prior diagnosis is accepted evidence: installed bundled Pi npm/Git + resolution succeeds, while importing the packaged + `@earendil-works/pi-coding-agent` graph fails because + `partial-json@0.1.7` is absent from `app.asar`. + +## Concurrent Task Gate + +- `check_project_docs` passed for the client root. +- `task_context start` created the exact isolated worktree/branch/base recorded + above; `status --json` confirmed matching task, owner, mode, branch, worktree, + and base. +- The client root remains on `main` with exactly three pre-existing untracked + task records; none was copied as a change or adopted. +- The only semantically adjacent registered peer is the older native Web Search + client coordinator. Its task record owns hosted/model-tool composition, not + Device Package manager loading, bridge error projection, or packaging closure; + no semantic or file-ownership conflict is present. +- Gate result: Passed. + +## Project Context Loaded + +Read the project memory entry, active record, positioning/current state, +decision index, ADR-006, system/module/data-flow architecture, business rules, +success criteria, evidence/reflection/commitment/stale indexes, the completed +Model Tools and Device Packages integration record, the relevant peer record, +and the prior diagnosis record. + +Relevant understanding: + +- Electron Main owns package bytes, inspection, durable generations, Pi resource + materialization, and packaged dependency authority; Renderer/Pi receive only + closed projections. +- The current integrated frontier deliberately lazy-loads the ESM-only Pi package + manager for remote installs. Workspace tests passed, but packaged signed-in + installation remained an explicit acceptance gate and the installed artifact + now proves that its ESM dependency closure is incomplete. +- ADR-006 explicitly requires installed-package regressions to be exercised + against final `app.asar` and packaged runtime roots. +- Likely owned modules are `electron/coding-packages/**`, the Pi extension bridge, + artifact verification scripts/tests, and the root production dependency lock. +- Planning Gate result: Passed. + +## Outcome + +- Replaced the packaged Device Package inspection dependency on the incomplete + `app.asar` Pi graph with the already-distributed physical + `resources/pi-runtime/dist/index.js` authority used by the bundled Pi CLI. +- Preserved closed `local_package_*` failures through Main's worker bridge and + the generated Pi extension while keeping unknown failures generic. +- Added an artifact gate that rejects a Main-reachable bare + `@earendil-works/pi-coding-agent` dynamic import and proves that the packaged + physical runtime exposes `DefaultPackageManager` and + `SettingsManager.inMemory`. +- Bumped the generated MakeLore Pi extension generation so new workers cannot + reuse stale bridge code. +- Windows product packaging and real packaged-source acceptance remain in + progress. + +## Verification + +- Installed-artifact RED: importing the installed `app.asar` Pi graph failed + with `ERR_MODULE_NOT_FOUND` for `partial-json`; importing its physical + `resources/pi-runtime/dist/index.js` succeeded with both required exports. +- TDD RED: the new Device Package manager regression attempted the unavailable + app Pi graph; the real worker bridge reduced a closed failure to + `Bridge request failed`; the artifact gate accepted the stale bare import. +- Focused and adjacent unit tests: 8 files, 69 passed. +- Full unit suite: 222 files, 1815 passed, 2 skipped; pressure test 1 passed. + The first run saw two Windows `spawn EBUSY` failures while Electron was first + downloaded; that file then passed 3/3 alone and the complete rerun passed. +- `pnpm typecheck`: passed. +- Scoped ESLint for every changed TypeScript/JavaScript source and test: passed. +- `pnpm run build:vite`: passed; generated Main contains the physical runtime + loader and no bare dynamic import of `@earendil-works/pi-coding-agent`. +- `git diff --check`: passed. + +## Follow-ups + +- None recorded. + +## Promotion Candidates + +- None recorded. diff --git a/electron/coding-packages/device-package-manager.ts b/electron/coding-packages/device-package-manager.ts index 477bcec..4e92596 100644 --- a/electron/coding-packages/device-package-manager.ts +++ b/electron/coding-packages/device-package-manager.ts @@ -1,6 +1,7 @@ import { execFile } from 'node:child_process'; import { randomUUID } from 'node:crypto'; import { cp, mkdir, readFile, rename, rm, stat } from 'node:fs/promises'; +import { createRequire } from 'node:module'; import path from 'node:path'; import { atomicWriteJson } from '../coding-projects/atomic-json'; import { @@ -53,6 +54,17 @@ export interface DevicePackageInstallInput { export type DevicePackageInstallRunner = (input: DevicePackageInstallInput) => Promise; +interface PiPackageManagerModule { + DefaultPackageManager: new (input: { + cwd: string; + agentDir: string; + settingsManager: unknown; + }) => { getInstalledPath(source: string, scope: 'user'): string | undefined }; + SettingsManager: { + inMemory(user: Record, project: { projectTrusted: boolean }): unknown; + }; +} + export interface DevicePackageManagerOptions { rootDir: string; executablePath?: string; @@ -208,6 +220,23 @@ async function gitHead(directory: string): Promise { }); } +async function loadBundledPiPackageManager(cliPath: string): Promise { + const entryPath = path.join(path.dirname(path.resolve(cliPath)), 'index.js'); + try { + const loaded = createRequire(entryPath)(entryPath) as Partial; + if (typeof loaded.DefaultPackageManager !== 'function' + || typeof loaded.SettingsManager?.inMemory !== 'function') { + throw new Error('Pi package manager exports are unavailable'); + } + return loaded as PiPackageManagerModule; + } catch { + throw new DevicePackageError( + 'local_package_dependency_failed', + 'Bundled Pi package manager is unavailable', + ); + } +} + export class DevicePackageManager { private readonly rootDir: string; private readonly packagesDir: string; @@ -270,18 +299,23 @@ export class DevicePackageManager { } const agentDir = path.join(planRoot, 'pi-agent'); const cwd = path.join(planRoot, 'project'); - await this.runInstall({ - source: source.spec, - executablePath, - cliPath, - ...(this.options.npmCliPath ? { npmCliPath: this.options.npmCliPath } : {}), - agentDir, - cwd, - env: installEnvironment(agentDir), - }); - const { DefaultPackageManager, SettingsManager } = await import( - '@earendil-works/pi-coding-agent' - ); + try { + await this.runInstall({ + source: source.spec, + executablePath, + cliPath, + ...(this.options.npmCliPath ? { npmCliPath: this.options.npmCliPath } : {}), + agentDir, + cwd, + env: installEnvironment(agentDir), + }); + } catch { + throw new DevicePackageError( + 'local_package_dependency_failed', + 'Pi could not install this package source', + ); + } + const { DefaultPackageManager, SettingsManager } = await loadBundledPiPackageManager(cliPath); const manager = new DefaultPackageManager({ cwd, agentDir, diff --git a/electron/coding-runtime/pi/extension-host.ts b/electron/coding-runtime/pi/extension-host.ts index 2a9550f..6133ef1 100644 --- a/electron/coding-runtime/pi/extension-host.ts +++ b/electron/coding-runtime/pi/extension-host.ts @@ -17,6 +17,7 @@ import type { PiProductTools } from './product-tools'; import type { CodingPluginToolDefinition } from '../../../shared/coding-plugins'; import type { PiSkillEntry } from './resource-loader'; import type { EffectivePluginSnapshot } from '../../coding-plugins/effective-resolver'; +import { DevicePackageError } from '../../coding-packages/device-package-manager'; const MAX_REQUEST_BYTES = 64 * 1024; const PRODUCT_TOOL_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9._:-]{0,63}$/u; @@ -483,7 +484,14 @@ export class PiManagedExtensionHost { response.removeListener('close', cancel); record.waiters.delete(value.resourceId); } - } catch { + } catch (error) { + if (!response.writableEnded && error instanceof DevicePackageError) { + this.respond(response, 400, { + code: error.code, + error: error.message.slice(0, 512), + }); + return; + } if (!response.writableEnded) this.respond(response, 400, { error: 'Bridge request failed' }); } } diff --git a/electron/coding-runtime/pi/extensions/makelore-runtime.ts b/electron/coding-runtime/pi/extensions/makelore-runtime.ts index 25718df..270f026 100644 --- a/electron/coding-runtime/pi/extensions/makelore-runtime.ts +++ b/electron/coding-runtime/pi/extensions/makelore-runtime.ts @@ -1,7 +1,7 @@ import path from 'node:path'; import { atomicWriteText } from '../../../coding-projects/atomic-json'; -export const MAKELORE_PI_EXTENSION_VERSION = 4; +export const MAKELORE_PI_EXTENSION_VERSION = 5; export const MAKELORE_PI_EXTENSION_FILENAME = `makelore-runtime-v${MAKELORE_PI_EXTENSION_VERSION}.mjs`; const BUNDLE_SOURCE = String.raw` @@ -93,7 +93,15 @@ export function createMakeloreRuntime(runtimeDefaults = {}) { signal, }); const result = await response.json().catch(() => ({})); - if (!response.ok) throw new Error(result.error || 'Makelore runtime bridge rejected the request'); + if (!response.ok) { + const message = typeof result.error === 'string' && result.error + ? result.error + : 'Makelore runtime bridge rejected the request'; + const code = typeof result.code === 'string' && /^local_package_[a-z_]+$/.test(result.code) + ? result.code + : ''; + throw new Error(code ? code + ': ' + message : message); + } return result; } diff --git a/scripts/lib/pi-product-artifact.mjs b/scripts/lib/pi-product-artifact.mjs index 70729e3..ba85fab 100644 --- a/scripts/lib/pi-product-artifact.mjs +++ b/scripts/lib/pi-product-artifact.mjs @@ -471,6 +471,28 @@ export async function verifyPackagedMarketplaceClientArtifact(appAsar) { return verifyMarketplaceClientArtifact(reachableContents, verifiedTrustSource); } +export async function verifyPackagedDevicePackageRuntime(appAsar, runtimePlatform) { + const { reachable } = await readPackagedApplicationGraph(appAsar); + const staleImports = reachable + .filter(({ source }) => /\bimport\(\s*["']@earendil-works\/pi-coding-agent["']\s*\)/u.test(source)) + .map(({ entry }) => entry); + if (staleImports.length > 0) { + throw new Error( + `Packaged Device Package manager still imports the incomplete app graph: ${staleImports.join(', ')}`, + ); + } + if (runtimePlatform.devicePackageManager?.defaultPackageManager !== 'function' + || runtimePlatform.devicePackageManager?.settingsManager !== 'function') { + throw new Error('Packaged Pi runtime cannot load the Device Package manager'); + } + return { + authority: 'bundled-pi-runtime', + appAsarRootImport: false, + exports: runtimePlatform.devicePackageManager, + result: 'pass', + }; +} + async function filesContainingNeedles(root, needles) { const matches = []; const visit = async (path) => { @@ -496,12 +518,21 @@ async function inspectProductRuntime(executable, resourcesDirectory) { const resources = process.env.MAKELORE_PI_PRODUCT_RESOURCES; const appRequire = createRequire(path.join(resources, 'app.asar', 'package.json')); const packagedPackage = appRequire('./package.json'); + const piRuntime = require(path.join(resources, 'pi-runtime', 'dist', 'index.js')); + if (typeof piRuntime.DefaultPackageManager !== 'function' + || typeof piRuntime.SettingsManager?.inMemory !== 'function') { + throw new Error('Bundled Pi package manager exports are unavailable'); + } process.stdout.write(JSON.stringify({ platform: process.platform, arch: process.arch, node: process.versions.node, electron: process.versions.electron, packagedPackage, + devicePackageManager: { + defaultPackageManager: typeof piRuntime.DefaultPackageManager, + settingsManager: typeof piRuntime.SettingsManager.inMemory, + }, })); `; const { stdout } = await runCommand(executable, ['-e', script], { @@ -744,6 +775,7 @@ export async function verifyPiProductArtifact({ projectRoot, executable }) { appAsarContents, }); const marketplace = await verifyPackagedMarketplaceClientArtifact(appAsar); + const devicePackages = await verifyPackagedDevicePackageRuntime(appAsar, runtimePlatform); const actualSkills = bundledPluginResources.coreResources.skills; const missingExtensionMarkers = EXTENSION_CONTRACT_MARKERS.filter( (marker) => !appAsarContents.includes(Buffer.from(marker)), @@ -797,6 +829,7 @@ export async function verifyPiProductArtifact({ projectRoot, executable }) { packagedClosure, bundledPlugins: bundledPluginResources, marketplace, + devicePackages, extension: { contractMarkers: EXTENSION_CONTRACT_MARKERS, packaged: true, diff --git a/tests/unit/device-package-manager.test.ts b/tests/unit/device-package-manager.test.ts index 61475fa..de370e0 100644 --- a/tests/unit/device-package-manager.test.ts +++ b/tests/unit/device-package-manager.test.ts @@ -118,12 +118,24 @@ describe('DevicePackageManager', () => { expect(preview.warnings.join(' ')).toContain('网络'); }); - it('runs remote Pi installation with lifecycle scripts disabled and reports declared scripts', async () => { + it('uses the bundled Pi runtime for remote installation when the app Pi graph is unavailable', async () => { const root = await temporaryRoot('remote'); + const runtimeRoot = path.join(root, 'pi-runtime'); + const cliPath = path.join(runtimeRoot, 'dist', 'cli.js'); + await writeJson(path.join(runtimeRoot, 'package.json'), { type: 'module' }); + await mkdir(path.dirname(cliPath), { recursive: true }); + await writeFile(path.join(path.dirname(cliPath), 'index.js'), [ + "import path from 'node:path';", + 'export class SettingsManager { static inMemory() { return {}; } }', + 'export class DefaultPackageManager {', + ' constructor(options) { this.agentDir = options.agentDir; }', + " getInstalledPath() { return path.join(this.agentDir, 'npm', 'node_modules', 'fixture-installed'); }", + '}', + ].join('\n'), 'utf8'); const calls: DevicePackageInstallInput[] = []; const runInstall = vi.fn(async (input: DevicePackageInstallInput) => { calls.push(input); - const packageRoot = path.join(input.agentDir, 'npm', 'node_modules', 'pi-web-search'); + const packageRoot = path.join(input.agentDir, 'npm', 'node_modules', 'fixture-installed'); await writeJson(path.join(packageRoot, 'package.json'), { name: 'pi-web-search', version: '2.4.0', @@ -135,7 +147,7 @@ describe('DevicePackageManager', () => { const manager = new DevicePackageManager({ rootDir: path.join(root, 'store'), executablePath: 'Makelore.exe', - cliPath: 'pi-cli.js', + cliPath, npmCliPath: 'npm-cli.js', runInstall, now: () => NOW, diff --git a/tests/unit/pi-extension-bundle.test.ts b/tests/unit/pi-extension-bundle.test.ts index 8a8655d..2daa493 100644 --- a/tests/unit/pi-extension-bundle.test.ts +++ b/tests/unit/pi-extension-bundle.test.ts @@ -11,6 +11,14 @@ import { PiProcessBudget } from '../../electron/coding-runtime/pi/worker-pool'; import type { AgentBrowserModule } from '../../electron/agent-browser'; import { CodingAttachmentStore } from '../../electron/coding-projects/attachment-store'; import { PiProductTools } from '../../electron/coding-runtime/pi/product-tools'; +import { + DevicePackageError, + type DevicePackageManager, +} from '../../electron/coding-packages/device-package-manager'; +import { + DEVICE_PACKAGE_TOOL_DEFINITIONS, + DevicePackageTools, +} from '../../electron/coding-packages/device-package-tools'; import { CodingCapabilityRegistryImpl } from '../../electron/coding-plugins/registry'; import type { PluginPolicyClientState } from '../../electron/services/plugin-policy-client'; import { @@ -48,6 +56,79 @@ afterEach(async () => { }); describe('Makelore Pi extension bundle', () => { + it('preserves a closed Device Package failure through the real worker bridge', async () => { + const root = await mkdtemp(path.join(tmpdir(), 'makelore-pi-device-package-error-')); + roots.push(root); + const manager = { + async prepare() { + throw new DevicePackageError( + 'local_package_dependency_failed', + 'Bundled Pi package manager is unavailable', + ); + }, + } as unknown as DevicePackageManager; + const host = new PiManagedExtensionHost(); + host.configureProductTools(new PiProductTools({ + browser: {} as AgentBrowserModule, + attachments: new CodingAttachmentStore(path.join(root, 'attachments')), + bundledSkillsDir: path.resolve('resources/coding-skills'), + devicePackageTools: new DevicePackageTools(manager), + })); + hosts.push(host); + const prepareTool = DEVICE_PACKAGE_TOOL_DEFINITIONS.find( + ({ name }) => name === 'local_package_prepare', + ); + if (!prepareTool) throw new Error('Device Package prepare definition missing'); + const worker = await host.registerWorker({ + conversationId: 'device-package-conversation', + generation: 1, + projectId: 'project-a', + projectPath: root, + extensionsDir: root, + tools: [prepareTool], + }); + await host.bindRun('device-package-conversation', 1, 'device-package-run'); + const previous = { + bridge: process.env.MAKELORE_PI_BRIDGE_URL, + token: process.env.MAKELORE_PI_WORKER_TOKEN, + context: process.env.MAKELORE_PI_CONTEXT_FILE, + role: process.env.MAKELORE_PI_WORKER_ROLE, + }; + Object.assign(process.env, worker.env); + try { + const module = await import( + /* @vite-ignore */ `${pathToFileURL(worker.extensionPath).href}?device-package=${Date.now()}` + ) as { + default(factory: { + registerTool(tool: ExtensionTool): void; + on(event: string, handler: ExtensionHandler): void; + }): void | Promise; + }; + const tools = new Map(); + await module.default({ + registerTool: (tool) => tools.set(tool.name, tool), + on: () => undefined, + }); + + await expect(tools.get('local_package_prepare')?.execute?.( + 'prepare-a', + { source: 'npm:pi-web-search' }, + new AbortController().signal, + )).rejects.toThrow( + 'local_package_dependency_failed: Bundled Pi package manager is unavailable', + ); + } finally { + for (const [key, value] of Object.entries(previous)) { + const environmentKey = key === 'bridge' ? 'MAKELORE_PI_BRIDGE_URL' + : key === 'token' ? 'MAKELORE_PI_WORKER_TOKEN' + : key === 'context' ? 'MAKELORE_PI_CONTEXT_FILE' + : 'MAKELORE_PI_WORKER_ROLE'; + if (value === undefined) delete process.env[environmentKey]; + else process.env[environmentKey] = value; + } + } + }); + it('hydrates persisted Pi identity through reconnect and event replay into the capability registry', async () => { const root = await mkdtemp(path.join(tmpdir(), 'makelore-pi-persisted-identity-')); roots.push(root); diff --git a/tests/unit/pi-managed-worker-opener.test.ts b/tests/unit/pi-managed-worker-opener.test.ts index 6bcadd5..1b3e6f8 100644 --- a/tests/unit/pi-managed-worker-opener.test.ts +++ b/tests/unit/pi-managed-worker-opener.test.ts @@ -289,7 +289,7 @@ describe('managed Pi worker opener', () => { expect(argv).toContain('grilling'); expect(argv).toContain('--session-id'); expect(argv).toContain('--extension'); - expect(argv).toContain('makelore-runtime-v4.mjs'); + expect(argv).toContain('makelore-runtime-v5.mjs'); expect(options.additionalArgs?.filter((argument) => argument === '--extension')).toHaveLength(2); expect(options.additionalArgs).toEqual(expect.arrayContaining([ '--skill', deviceSkillPath, '--extension', deviceExtensionPath, diff --git a/tests/unit/pi-product-artifact.test.ts b/tests/unit/pi-product-artifact.test.ts index 827e149..8a1eb5e 100644 --- a/tests/unit/pi-product-artifact.test.ts +++ b/tests/unit/pi-product-artifact.test.ts @@ -16,6 +16,7 @@ import { verifyBundledCodingPluginResources, defaultProductExecutable, readPackagedMarketplaceTrustSource, + verifyPackagedDevicePackageRuntime, verifyPackagedMarketplaceClientArtifact, validatePiArtifactMetadata, verifyMarketplaceClientArtifact, @@ -326,6 +327,41 @@ describe('final Pi product artifact verification', () => { await expect(readPackagedMarketplaceTrustSource(appAsar)).rejects.toThrow('trust source'); }); + it('rejects the incomplete app Pi graph and requires the bundled runtime package manager', async () => { + const root = await mkdtemp(path.join(tmpdir(), 'makelore-device-package-runtime-asar-')); + roots.push(root); + const source = path.join(root, 'source'); + await mkdir(path.join(source, 'dist-electron'), { recursive: true }); + await writeFile(path.join(source, 'package.json'), JSON.stringify({ main: 'dist-electron/main.js' })); + await writeFile( + path.join(source, 'dist-electron', 'main.js'), + 'export async function prepare() { return await import("@earendil-works/pi-coding-agent"); }', + ); + const appAsar = path.join(root, 'app.asar'); + await createAsarFixture(source, appAsar); + const runtimePlatform = { + devicePackageManager: { defaultPackageManager: 'function', settingsManager: 'function' }, + }; + + await expect(verifyPackagedDevicePackageRuntime(appAsar, runtimePlatform)) + .rejects.toThrow('incomplete app graph'); + + await writeFile( + path.join(source, 'dist-electron', 'main.js'), + 'export const devicePackageManagerAuthority = "bundled-pi-runtime";', + ); + const cleanAsar = path.join(root, 'clean.asar'); + await createAsarFixture(source, cleanAsar); + await expect(verifyPackagedDevicePackageRuntime(cleanAsar, runtimePlatform)).resolves.toEqual({ + authority: 'bundled-pi-runtime', + appAsarRootImport: false, + exports: runtimePlatform.devicePackageManager, + result: 'pass', + }); + await expect(verifyPackagedDevicePackageRuntime(cleanAsar, { devicePackageManager: null })) + .rejects.toThrow('cannot load the Device Package manager'); + }); + it('binds Marketplace route, Renderer, and effective markers to the package main graph', async () => { const root = await mkdtemp(path.join(tmpdir(), 'makelore-marketplace-contract-asar-')); roots.push(root);