import { spawn } from 'node:child_process'; import { readFile, readdir, stat } from 'node:fs/promises'; import { createRequire } from 'node:module'; import { arch as hostArch, platform as hostPlatform } from 'node:os'; import { dirname, isAbsolute, join, relative, resolve, sep, } from 'node:path'; import YAML from 'yaml'; import { PI_RUNTIME_CLI_ENTRY, PI_RUNTIME_MANIFEST, PI_RUNTIME_PACKAGE, PI_RUNTIME_VERSION, } from './pi-runtime-bundle.mjs'; import { inspectPackagedClosure, packagedResourcesDirectory, } from '../probe-pi-packaged-runtime.mjs'; const { extractFile, listPackage } = createRequire(import.meta.url)('@electron/asar'); const PRODUCT_NAME = 'Makelore'; const LINUX_EXECUTABLE_NAME = 'niancode'; const EXTENSION_CONTRACT_MARKERS = Object.freeze([ 'Makelore runtime bridge rejected the request', 'subagent.dispatch', 'makelore.write-lease', 'MAKELORE_PI_BRIDGE_URL', ]); const MARKETPLACE_ARTIFACT_MARKERS = Object.freeze({ packageTrust: Object.freeze([ 'makelore-plugin-release.v1', 'skill_only', 'platform_hosted', 'plugin_signature_invalid', 'signing key is not trusted', ]), hostedRuntime: Object.freeze([ 'makelore.game-resource', '/api/plugins/v1/hosted/game-resource/generations', ]), modelTools: Object.freeze([ 'makelore-model-tool.v1', 'model.web-search', 'forced_search', ]), devicePackages: Object.freeze([ 'makelore-device-package.v1', '/api/coding/device-packages', 'device-parent-workers', ]), mainRoutes: Object.freeze([ '/api/coding/plugin-marketplace', 'plugin-marketplace\\/install\\/', 'plugin-marketplace\\/update\\/', ]), effectiveSnapshot: Object.freeze([ 'effectiveSkillIds', 'pluginReleaseIds', ]), renderer: Object.freeze([ '/api/coding/plugin-marketplace/catalog', '/api/coding/plugin-marketplace/library', '免费获取', '获取、项目启用与伙伴分配彼此独立', '/plugins', ]), }); const FORBIDDEN_PROVIDER_AUTHORITY_MARKERS = Object.freeze([ 'MEOWA_API_KEY', 'MEOWA_API_URL', 'MEOWA_GAME_ASSETS_SHARED_SECRET', '/api/coding/meowa-game-assets', 'https://api.meowa.ai', 'WEB_SEARCH_OPENAI_API_KEY', 'https://api.openai.com/v1/responses', ]); const CODE_OWNED_PLUGIN_SIGNING_KEYS_SOURCE_MARKER = 'makelore.plugin-trust.code-owned.v1'; const PI_AI_PROVIDER_PREFIX = 'pi-runtime/node_modules/@earendil-works/pi-ai/dist/providers/'; const PI_AI_PROVIDER_ASAR_PREFIX = 'app.asar/node_modules/@earendil-works/pi-ai/dist/providers/'; export const BUNDLED_CODING_PLUGIN_RESOURCE_ROOT = 'resources/coding-plugins'; export const CORE_CODING_SKILL_RESOURCE_ROOT = 'resources/coding-skills'; const SDK_ASSET_PATH_PATTERN = /^skills\/[^/]+\/assets\/.+\.(?:js|ts)$/u; async function pathExists(path) { try { await stat(path); return true; } catch { return false; } } async function listResourceFiles(root) { const files = []; const visit = async (directory, prefix = '') => { if (!await pathExists(directory)) return; for (const entry of await readdir(directory, { withFileTypes: true })) { const path = join(directory, entry.name); const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name; if (entry.isDirectory()) await visit(path, relativePath); else if (entry.isFile()) files.push(relativePath); } }; await visit(root); return files.sort(); } function resolveBundledResource(root, baseDirectory, candidate, label) { if (typeof candidate !== 'string' || candidate.trim().length === 0) { throw new Error(`Bundled plugin ${label} is missing`); } const portableCandidate = candidate.replaceAll('\\', '/'); if (portableCandidate.startsWith('/') || /^[A-Za-z]:\//u.test(portableCandidate) || /^[A-Za-z][A-Za-z0-9+.-]*:/u.test(portableCandidate)) { throw new Error(`Bundled plugin ${label} must be relative`); } const resolved = resolve(baseDirectory, candidate); const relativePath = relative(root, resolved); if (!relativePath || relativePath === '..' || relativePath.startsWith(`..${sep}`) || isAbsolute(relativePath)) { throw new Error(`Bundled plugin ${label} escapes its package root`); } return resolved; } async function readJsonResource(path, label) { try { return JSON.parse(await readFile(path, 'utf8')); } catch (error) { throw new Error(`Bundled plugin ${label} is unreadable: ${error.message}`); } } async function assertMirroredResourceTree(sourceRoot, packagedRoot, label) { const [sourceFiles, packagedFiles] = await Promise.all([ listResourceFiles(sourceRoot), listResourceFiles(packagedRoot), ]); if (JSON.stringify(sourceFiles) !== JSON.stringify(packagedFiles)) { throw new Error( `Packaged ${label} files differ: expected ${sourceFiles.join(', ')}, got ${packagedFiles.join(', ')}`, ); } for (const file of sourceFiles) { const [source, packaged] = await Promise.all([ readFile(join(sourceRoot, file)), readFile(join(packagedRoot, file)), ]); if (!source.equals(packaged)) throw new Error(`Packaged ${label} file differs: ${file}`); } return sourceFiles; } function portable(path) { return path.split(sep).join('/'); } function normalizeLockedVersion(value) { return typeof value === 'string' ? value.split('(', 1)[0] : null; } function runCommand(executable, args, options = {}) { return new Promise((resolvePromise, reject) => { const child = spawn(executable, args, { cwd: options.cwd, env: options.env ?? process.env, stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true, }); let stdout = ''; let stderr = ''; child.stdout.on('data', (chunk) => { stdout += chunk.toString(); }); child.stderr.on('data', (chunk) => { stderr += chunk.toString(); }); child.once('error', reject); child.once('exit', (code, signal) => { if (code === 0) resolvePromise({ stdout, stderr }); else reject(new Error( `${executable} exited with code ${code ?? 'null'} signal ${signal ?? 'none'}: ${stderr || stdout}`, )); }); }); } export function defaultProductExecutable(projectRoot, currentPlatform = hostPlatform()) { const root = resolve(projectRoot); if (currentPlatform === 'win32') { return join(root, 'release', 'win-unpacked', `${PRODUCT_NAME}.exe`); } if (currentPlatform === 'darwin') { return join(root, 'release', 'mac', `${PRODUCT_NAME}.app`, 'Contents', 'MacOS', PRODUCT_NAME); } if (currentPlatform === 'linux') { return join(root, 'release', 'linux-unpacked', LINUX_EXECUTABLE_NAME); } throw new Error(`Unsupported product artifact platform: ${currentPlatform}`); } export function assertNodeEngineCompatible(engine, nodeVersion) { const match = /^>=(\d+)\.(\d+)\.(\d+)$/.exec(engine ?? ''); if (!match) throw new Error(`Unsupported Pi Node engine expression: ${engine ?? 'missing'}`); const required = match.slice(1).map(Number); const actualMatch = /^(\d+)\.(\d+)\.(\d+)/.exec(nodeVersion ?? ''); if (!actualMatch) throw new Error(`Invalid packaged Node version: ${nodeVersion ?? 'missing'}`); const actual = actualMatch.slice(1).map(Number); for (let index = 0; index < required.length; index += 1) { if (actual[index] > required[index]) return; if (actual[index] < required[index]) { throw new Error(`Packaged Node ${nodeVersion} does not satisfy Pi engine ${engine}`); } } } export function validatePiArtifactMetadata({ rootPackage, lockfile, packagedPackage, runtimePackage, manifest, runtimePlatform, }) { const rootDependency = rootPackage.dependencies?.[PI_RUNTIME_PACKAGE]; const packagedDependency = packagedPackage.dependencies?.[PI_RUNTIME_PACKAGE]; const lockDependency = lockfile.importers?.['.']?.dependencies?.[PI_RUNTIME_PACKAGE]; const lockedVersion = normalizeLockedVersion(lockDependency?.version); const versions = { expected: PI_RUNTIME_VERSION, rootDependency, packagedDependency, lockSpecifier: lockDependency?.specifier ?? null, lockedVersion, runtimePackage: runtimePackage.version, manifest: manifest.runtime?.version ?? null, }; const mismatched = Object.entries(versions) .filter(([key, value]) => key !== 'expected' && value !== PI_RUNTIME_VERSION); if (mismatched.length > 0) { throw new Error(`Pi artifact versions do not match: ${JSON.stringify(versions)}`); } if (runtimePackage.name !== PI_RUNTIME_PACKAGE) { throw new Error(`Packaged Pi package name is ${runtimePackage.name ?? 'missing'}`); } if (runtimePackage.bin?.pi !== PI_RUNTIME_CLI_ENTRY) { throw new Error(`Packaged Pi CLI entry is ${runtimePackage.bin?.pi ?? 'missing'}`); } if (manifest.runtime?.packageName !== PI_RUNTIME_PACKAGE || manifest.runtime?.cliEntry !== PI_RUNTIME_CLI_ENTRY) { throw new Error(`Packaged Pi manifest identity is invalid: ${JSON.stringify(manifest.runtime)}`); } if (manifest.target?.platform !== runtimePlatform.platform || manifest.target?.arch !== runtimePlatform.arch) { throw new Error( `Packaged Pi target ${manifest.target?.platform ?? 'missing'}-${manifest.target?.arch ?? 'missing'} ` + `does not match executable ${runtimePlatform.platform}-${runtimePlatform.arch}`, ); } assertNodeEngineCompatible(manifest.runtime?.nodeEngine, runtimePlatform.node); return versions; } export function collectAbsoluteManifestValues(value, at = '$', results = []) { if (typeof value === 'string') { if (isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value)) results.push({ at, value }); return results; } if (Array.isArray(value)) { value.forEach((entry, index) => collectAbsoluteManifestValues(entry, `${at}[${index}]`, results)); return results; } if (value && typeof value === 'object') { for (const [key, entry] of Object.entries(value)) { collectAbsoluteManifestValues(entry, `${at}.${key}`, results); } } return results; } export async function collectForbiddenResourcePaths(root, pattern = /opencode/i) { const matches = []; const visit = async (directory) => { if (!await pathExists(directory)) return; for (const entry of await readdir(directory, { withFileTypes: true })) { const path = join(directory, entry.name); if (pattern.test(entry.name)) matches.push(portable(relative(root, path))); if (entry.isDirectory()) await visit(path); } }; await visit(root); return matches.sort(); } export function classifyOpenCodeResourcePaths(paths) { const isUpstreamPiProvider = (path) => path.startsWith(PI_AI_PROVIDER_PREFIX) || path.startsWith(PI_AI_PROVIDER_ASAR_PREFIX); const upstreamPiProvider = paths.filter(isUpstreamPiProvider); const productOwned = paths.filter((path) => !isUpstreamPiProvider(path)); return { productOwned, upstreamPiProvider }; } export function collectForbiddenAsarPaths(appAsar, pattern = /opencode/i) { const paths = listPackage(appAsar, { isPack: false }).map((entry) => ( `app.asar/${entry.replace(/^[\\/]+/, '').replaceAll('\\', '/')}` )); return { entryCount: paths.length, matches: paths.filter((entry) => pattern.test(entry)).sort(), }; } export function verifyMarketplaceClientArtifact(appAsarContents, productionTrustSource) { const missing = Object.entries(MARKETPLACE_ARTIFACT_MARKERS).flatMap(([group, markers]) => ( markers .filter((marker) => !appAsarContents.includes(Buffer.from(marker))) .map((marker) => `${group}:${marker}`) )); if (missing.length > 0) { throw new Error(`Packaged app.asar does not contain Marketplace contract markers: ${missing.join(', ')}`); } const forbidden = FORBIDDEN_PROVIDER_AUTHORITY_MARKERS.filter((marker) => ( appAsarContents.includes(Buffer.from(marker)) )); if (forbidden.length > 0) { throw new Error(`Packaged app.asar still contains provider authority: ${forbidden.join(', ')}`); } const hasEmptyCodeOwnedTrust = /(?:CODE_OWNED_PLUGIN_SIGNING_KEYS\s*=\s*)?Object\.freeze\(\s*\{\}\s*(?:as\s+[^)]*)?\)/u.test(productionTrustSource); if (!hasEmptyCodeOwnedTrust || !productionTrustSource.includes(CODE_OWNED_PLUGIN_SIGNING_KEYS_SOURCE_MARKER) || productionTrustSource.includes('process.env') || productionTrustSource.includes('-----BEGIN PRIVATE KEY-----') || productionTrustSource.includes('-----BEGIN ED25519 PRIVATE KEY-----')) { throw new Error('Marketplace production trust source is not an empty code-owned fail-closed store'); } return { schema2SkillOnly: true, schema2PlatformHosted: true, legacyMeowaClientAuthorityAbsent: true, webSearchProviderAuthorityAbsent: true, productionTrust: 'official-key-absent-fail-closed', libraryInstallAndEffectiveRoutes: true, rendererAssets: true, productionKeyIds: [], privateKeyMaterialInTrustSource: false, markers: MARKETPLACE_ARTIFACT_MARKERS, result: 'pass', }; } /** * Read the package.main-reachable application graph from the packaged app. * Checkout sources and unrelated app.asar files are not product evidence. */ async function readPackagedApplicationGraph(appAsar) { const entries = new Set(listPackage(appAsar, { isPack: false }) .map((entry) => entry.replace(/^[/\\]+/u, '').replaceAll('\\', '/'))); const readArchiveText = (entry) => { if (!entries.has(entry)) throw new Error(`Packaged app.asar entry is missing: ${entry}`); return Buffer.from(extractFile(appAsar, entry.replaceAll('/', sep))).toString('utf8'); }; let packageJson; try { packageJson = JSON.parse(readArchiveText('package.json')); } catch (error) { const reason = error instanceof Error ? error.message : 'unknown error'; throw new Error(`Packaged app.asar package.json is unreadable: ${reason}`); } if (typeof packageJson.main !== 'string' || packageJson.main.trim().length === 0) { throw new Error('Packaged app.asar package.json.main is missing'); } const normalizeAsarPath = (value) => { const result = []; for (const segment of value.replaceAll('\\', '/').split('/')) { if (!segment || segment === '.') continue; if (segment === '..') { if (result.length === 0) return null; result.pop(); } else result.push(segment); } return result.join('/'); }; const resolveEntry = (from, specifier) => { if (!specifier.startsWith('.')) return null; const withoutSuffix = specifier.split(/[?#]/u, 1)[0]; const base = normalizeAsarPath(`${dirname(from).replaceAll('\\', '/')}/${withoutSuffix}`); if (!base) return null; const candidates = [base, `${base}.js`, `${base}.mjs`, `${base}.cjs`, `${base}/index.js`]; return candidates.find((candidate) => entries.has(candidate)) ?? null; }; const main = normalizeAsarPath(packageJson.main); if (!main || !entries.has(main)) throw new Error('Packaged app.asar package.json.main is not present'); const pending = [main]; const reachable = []; const seen = new Set(); while (pending.length > 0) { const entry = pending.shift(); if (!entry || seen.has(entry)) continue; seen.add(entry); let source; try { source = readArchiveText(entry); } catch { continue; } reachable.push({ entry, source }); const specifiers = []; const importPattern = /\b(?:import|export)(?:[^"'`;]*?\bfrom)?\s*['"]([^'"]+)['"]/gu; const dynamicImportPattern = /\bimport\(\s*['"]([^'"]+)['"]\s*\)/gu; const requirePattern = /\brequire\(\s*['"]([^'"]+)['"]\s*\)/gu; const htmlEntryPattern = /['"]([^'"]+\.html(?:[?#][^'"]*)?)['"]/gu; const htmlAssetPattern = /\b(?:src|href)\s*=\s*['"]([^'"]+)['"]/gu; for (const match of source.matchAll(importPattern)) specifiers.push(match[1]); for (const match of source.matchAll(dynamicImportPattern)) specifiers.push(match[1]); for (const match of source.matchAll(requirePattern)) specifiers.push(match[1]); for (const match of source.matchAll(htmlEntryPattern)) specifiers.push(match[1]); if (entry.endsWith('.html')) { for (const match of source.matchAll(htmlAssetPattern)) specifiers.push(match[1]); } for (const specifier of specifiers) { const resolved = resolveEntry(entry, specifier); if (resolved) pending.push(resolved); } } return { packageJson, reachable }; } function findPackagedMarketplaceTrustSource(reachable) { for (const { source } of reachable) { const trustAssignment = source.match( /CODE_OWNED_PLUGIN_SIGNING_KEYS\s*=\s*Object\.freeze\(\s*\{\}\s*(?:as\s+[^)]*)?\)/u, ); if (trustAssignment) return trustAssignment[0]; const compiledTrust = source.match( /(?:const|let|var)\s+[A-Za-z_$][\w$]*\s*=\s*Object\.freeze\(\s*\{\}\s*\)\s*;(?=[\s\S]{0,512}?sourceMarker\s*:\s*["']makelore\.plugin-trust\.code-owned\.v1["'])/u, ); if (compiledTrust) { return `${compiledTrust[0]} sourceMarker: ${CODE_OWNED_PLUGIN_SIGNING_KEYS_SOURCE_MARKER}`; } } throw new Error('Packaged app.asar does not contain the Marketplace trust source'); } /** * Read the Marketplace trust source from the packaged application itself. * Checkout sources are not evidence of what an installed app will trust. */ export async function readPackagedMarketplaceTrustSource(appAsar) { const { reachable } = await readPackagedApplicationGraph(appAsar); return findPackagedMarketplaceTrustSource(reachable); } /** * Verify Marketplace contract markers only in the package.main-reachable app * graph. An arbitrary string elsewhere in app.asar is not evidence that the * installed Main/Renderer contract is present. */ export async function verifyPackagedMarketplaceClientArtifact(appAsar) { const { reachable } = await readPackagedApplicationGraph(appAsar); const reachableContents = Buffer.from(reachable.map(({ source }) => source).join('\n')); const trustSource = findPackagedMarketplaceTrustSource(reachable); if (!reachableContents.includes(Buffer.from(CODE_OWNED_PLUGIN_SIGNING_KEYS_SOURCE_MARKER))) { throw new Error('Packaged app.asar reachable graph does not contain the Marketplace trust provenance marker'); } const verifiedTrustSource = `${trustSource}\n${CODE_OWNED_PLUGIN_SIGNING_KEYS_SOURCE_MARKER}`; 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) => { const details = await stat(path); if (details.isDirectory()) { for (const entry of await readdir(path, { withFileTypes: true })) { await visit(join(path, entry.name)); } return; } const contents = await readFile(path); const found = needles.filter((needle) => needle.length > 0 && contents.includes(needle)); if (found.length > 0) matches.push({ path, needles: found.map((value) => value.toString()) }); }; await visit(root); return matches; } async function inspectProductRuntime(executable, resourcesDirectory) { const script = String.raw` const { createRequire } = require('node:module'); const path = require('node:path'); 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], { env: { ...process.env, ELECTRON_RUN_AS_NODE: '1', MAKELORE_PI_PRODUCT_RESOURCES: resourcesDirectory, }, }); return JSON.parse(stdout.trim()); } async function packagedSkillIds(resourcesDirectory) { const root = join(resourcesDirectory, 'resources', 'coding-skills'); if (!await pathExists(root)) throw new Error(`Packaged coding skills are missing: ${root}`); const entries = await readdir(root, { withFileTypes: true }); const ids = []; for (const entry of entries) { if (!entry.isDirectory()) continue; if (!await pathExists(join(root, entry.name, 'SKILL.md'))) { throw new Error(`Packaged coding skill has no SKILL.md: ${entry.name}`); } ids.push(entry.name); } return ids.sort(); } async function sourceSkillIds(projectRoot) { const root = join(projectRoot, 'resources', 'coding-skills'); const entries = await readdir(root, { withFileTypes: true }); return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort(); } export async function verifyBundledCodingPluginResources({ projectRoot, resourcesDirectory, appAsarContents, }) { const root = resolve(projectRoot); const sourcePluginRoot = join(root, BUNDLED_CODING_PLUGIN_RESOURCE_ROOT); const packagedPluginRoot = join(resourcesDirectory, BUNDLED_CODING_PLUGIN_RESOURCE_ROOT); if (!await pathExists(sourcePluginRoot)) { throw new Error(`Bundled coding plugin resources are missing: ${sourcePluginRoot}`); } if (!await pathExists(packagedPluginRoot)) { throw new Error(`Packaged coding plugin resources are missing: ${packagedPluginRoot}`); } const [sourcePackages, packagedPackages] = await Promise.all([ readdir(sourcePluginRoot, { withFileTypes: true }), readdir(packagedPluginRoot, { withFileTypes: true }), ]); const sourcePackageNames = sourcePackages .filter((entry) => entry.isDirectory()) .map((entry) => entry.name) .sort(); const packagedPackageNames = packagedPackages .filter((entry) => entry.isDirectory()) .map((entry) => entry.name) .sort(); if (JSON.stringify(sourcePackageNames) !== JSON.stringify(packagedPackageNames)) { throw new Error( `Packaged coding plugin packages differ: expected ${sourcePackageNames.join(', ')}, ` + `got ${packagedPackageNames.join(', ')}`, ); } const packages = []; const catalogMarkers = []; for (const packageName of sourcePackageNames) { const sourcePackageRoot = join(sourcePluginRoot, packageName); const packagedPackageRoot = join(packagedPluginRoot, packageName); const packageFiles = await assertMirroredResourceTree( sourcePackageRoot, packagedPackageRoot, `coding plugin ${packageName}`, ); const sourceManifestPath = join(sourcePackageRoot, 'plugin.json'); const packagedManifestPath = join(packagedPackageRoot, 'plugin.json'); const sourceManifest = await readJsonResource(sourceManifestPath, `${packageName} plugin.json`); const packagedManifest = await readJsonResource(packagedManifestPath, `${packageName} plugin.json`); const sourceCapabilityPath = resolveBundledResource( sourcePackageRoot, sourcePackageRoot, sourceManifest.extensions?.['com.makelore']?.capabilityManifest, `${packageName} capability manifest`, ); const packagedCapabilityPath = resolveBundledResource( packagedPackageRoot, packagedPackageRoot, packagedManifest.extensions?.['com.makelore']?.capabilityManifest, `${packageName} capability manifest`, ); const sourceCapability = await readJsonResource(sourceCapabilityPath, `${packageName} capability manifest`); const packagedCapability = await readJsonResource(packagedCapabilityPath, `${packageName} capability manifest`); if (packagedCapability.pluginId !== packagedManifest.name) { throw new Error(`Packaged coding plugin ${packageName} identity does not match its manifest`); } const capabilityRelativePath = portable(relative(packagedPackageRoot, packagedCapabilityPath)); if (!packageFiles.includes(capabilityRelativePath)) { throw new Error(`Packaged coding plugin capability manifest is not in its resource tree: ${packageName}`); } const skills = Array.isArray(packagedCapability.skills) ? packagedCapability.skills : []; if (skills.length === 0) throw new Error(`Packaged coding plugin has no Skill: ${packageName}`); const skillPaths = []; for (const [index, skill] of skills.entries()) { const skillPath = resolveBundledResource( packagedPackageRoot, dirname(packagedCapabilityPath), skill?.entry, `${packageName} Skill ${index}`, ); const relativePath = portable(relative(packagedPackageRoot, skillPath)); if (!packageFiles.includes(relativePath)) { throw new Error(`Packaged coding plugin Skill is missing: ${packageName}/${relativePath}`); } const content = await readFile(skillPath, 'utf8'); if (content.trim().length === 0) throw new Error(`Packaged coding plugin Skill is empty: ${packageName}/${relativePath}`); skillPaths.push({ id: skill?.id, path: relativePath }); } const sdkAssetPaths = packageFiles.filter((file) => SDK_ASSET_PATH_PATTERN.test(file)); if (packagedCapability.schemaVersion === 1 && sdkAssetPaths.length === 0) { throw new Error(`Packaged schema-1 coding plugin has no SDK assets: ${packageName}`); } const adapterId = packagedCapability.schemaVersion === 2 ? packagedCapability.pluginId : packagedCapability.adapterId; const tools = Array.isArray(packagedCapability.tools) ? packagedCapability.tools : []; const toolNames = tools.map((tool) => tool?.name); if (typeof adapterId !== 'string' || adapterId.length === 0 || tools.length === 0 || toolNames.some((name) => typeof name !== 'string' || name.length === 0) || new Set(toolNames).size !== toolNames.length) { throw new Error(`Packaged coding plugin adapter/tool catalog is invalid: ${packageName}`); } catalogMarkers.push(packagedManifest.name, adapterId, ...toolNames); packages.push({ id: packagedManifest.name, version: packagedManifest.version, manifestPath: portable(relative(resourcesDirectory, packagedManifestPath)), capabilityManifestPath: portable(relative(resourcesDirectory, packagedCapabilityPath)), files: packageFiles.map((file) => portable(join(BUNDLED_CODING_PLUGIN_RESOURCE_ROOT, packageName, file))), skills: skillPaths, sdkAssets: sdkAssetPaths.map((file) => portable(join(BUNDLED_CODING_PLUGIN_RESOURCE_ROOT, packageName, file))), adapterId, tools: toolNames, sourceManifest: sourceManifest.name, sourceCapability: sourceCapability.pluginId, }); } const asar = appAsarContents ?? await readFile(join(resourcesDirectory, 'app.asar')); const missingCatalogMarkers = [...new Set(catalogMarkers)].filter( (marker) => !asar.includes(Buffer.from(marker)), ); if (missingCatalogMarkers.length > 0) { throw new Error( `Packaged app.asar does not contain the coding plugin adapter/tool catalog: ${missingCatalogMarkers.join(', ')}`, ); } const sourceCoreRoot = join(root, CORE_CODING_SKILL_RESOURCE_ROOT); const packagedCoreRoot = join(resourcesDirectory, CORE_CODING_SKILL_RESOURCE_ROOT); const coreFiles = await assertMirroredResourceTree(sourceCoreRoot, packagedCoreRoot, 'core coding resources'); const expectedSkills = await sourceSkillIds(root); const actualSkills = await packagedSkillIds(resourcesDirectory); if (JSON.stringify(actualSkills) !== JSON.stringify(expectedSkills)) { throw new Error(`Packaged coding skills differ: expected ${expectedSkills}, got ${actualSkills}`); } return { root: BUNDLED_CODING_PLUGIN_RESOURCE_ROOT, packages, catalog: { adapters: [...new Set(packages.map(({ adapterId }) => adapterId))], tools: [...new Set(packages.flatMap(({ tools }) => tools))], packagedInAsar: true, }, coreResources: { root: CORE_CODING_SKILL_RESOURCE_ROOT, files: coreFiles, skills: actualSkills, }, result: 'pass', }; } export async function verifyPiProductArtifact({ projectRoot, executable }) { const root = resolve(projectRoot); const appExecutable = resolve(executable ?? defaultProductExecutable(root)); if (!await pathExists(appExecutable)) throw new Error(`Product executable is missing: ${appExecutable}`); const resourcesDirectory = packagedResourcesDirectory(appExecutable); const runtimeRoot = join(resourcesDirectory, 'pi-runtime'); const agentServerPath = join(resourcesDirectory, 'resources', 'pi-agent-server.mjs'); const appAsar = join(resourcesDirectory, 'app.asar'); for (const required of [runtimeRoot, agentServerPath, appAsar]) { if (!await pathExists(required)) throw new Error(`Product artifact resource is missing: ${required}`); } const [rootPackageSource, lockfileSource, runtimePackageSource, manifestSource] = await Promise.all([ readFile(join(root, 'package.json'), 'utf8'), readFile(join(root, 'pnpm-lock.yaml'), 'utf8'), readFile(join(runtimeRoot, 'package.json'), 'utf8'), readFile(join(runtimeRoot, PI_RUNTIME_MANIFEST), 'utf8'), ]); const rootPackage = JSON.parse(rootPackageSource); const lockfile = YAML.parse(lockfileSource); const runtimePackage = JSON.parse(runtimePackageSource); const manifest = JSON.parse(manifestSource); const runtimePlatform = await inspectProductRuntime(appExecutable, resourcesDirectory); const versions = validatePiArtifactMetadata({ rootPackage, lockfile, packagedPackage: runtimePlatform.packagedPackage, runtimePackage, manifest, runtimePlatform, }); const absoluteManifestValues = collectAbsoluteManifestValues(manifest); if (absoluteManifestValues.length > 0) { throw new Error(`Pi runtime manifest contains absolute paths: ${JSON.stringify(absoluteManifestValues)}`); } const appAsarContents = await readFile(appAsar); const physicalOpenCodePaths = await collectForbiddenResourcePaths(resourcesDirectory); const asarOpenCodePaths = collectForbiddenAsarPaths(appAsar); const openCodeResourcePaths = classifyOpenCodeResourcePaths([ ...physicalOpenCodePaths, ...asarOpenCodePaths.matches, ]); if (openCodeResourcePaths.productOwned.length > 0) { throw new Error( `Product-owned resources contain OpenCode paths: ${openCodeResourcePaths.productOwned.join(', ')}`, ); } const bundledPluginResources = await verifyBundledCodingPluginResources({ projectRoot: root, resourcesDirectory, 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)), ); if (missingExtensionMarkers.length > 0) { throw new Error( `Packaged app.asar does not contain Pi extension contract markers: ${missingExtensionMarkers.join(', ')}`, ); } const sourceNeedles = [ Buffer.from(root), Buffer.from(portable(root)), ]; const devPathResidue = [ ...await filesContainingNeedles(appAsar, sourceNeedles), ...await filesContainingNeedles(runtimeRoot, sourceNeedles), ]; if (devPathResidue.length > 0) { throw new Error(`Product artifact contains development path residue: ${JSON.stringify(devPathResidue)}`); } const packagedClosure = await inspectPackagedClosure( appExecutable, resourcesDirectory, manifest.runtimeAssets, ); return { schemaVersion: 1, artifact: { executable: appExecutable, resourcesDirectory, appAsar, runtimeRoot, cliPath: join(runtimeRoot, ...PI_RUNTIME_CLI_ENTRY.split('/')), agentServerPath, }, platform: { platform: runtimePlatform.platform, arch: runtimePlatform.arch, electron: runtimePlatform.electron, node: runtimePlatform.node, }, versions, manifest: { target: manifest.target, packageCount: manifest.productionPackages.length, assetCount: manifest.runtimeAssets.length, nodeEngine: manifest.runtime.nodeEngine, }, packagedClosure, bundledPlugins: bundledPluginResources, marketplace, devicePackages, extension: { contractMarkers: EXTENSION_CONTRACT_MARKERS, packaged: true, executionProof: 'smoke:pi:real final-product extension/subagent run', }, skills: actualSkills, openCodeResourcePaths: { ...openCodeResourcePaths, physicalMatches: physicalOpenCodePaths, asar: asarOpenCodePaths, upstreamDecision: openCodeResourcePaths.upstreamPiProvider.length > 0 ? 'retained-required-files-from-exact-pinned-pi-production-package' : 'none', }, developmentPathResidue: [], result: 'pass', }; } export const PI_PRODUCT_ARTIFACT_DEFAULTS = Object.freeze({ platform: hostPlatform(), arch: hostArch(), extensionContractMarkers: EXTENSION_CONTRACT_MARKERS, });