chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:22 +08:00
commit 4c395b06e4
1964 changed files with 333726 additions and 0 deletions
@@ -0,0 +1,574 @@
/**
* Prepare aioncore binary for packaging.
*
* Resolution order:
* 1. GitHub Actions artifact download when AIONUI_BACKEND_RUN_ID is set
* 2. GitHub release download (requires version or defaults to "latest")
* 3. Complete local bundle from AIONUI_BACKEND_LOCAL_BUNDLE_DIR
* 4. Local binary fallback from AIONUI_BACKEND_LOCAL_BINARY
*
* Output: {projectRoot}/resources/bundled-aioncore/{platform}-{arch}/
* - aioncore[.exe]
* - manifest.json
* - managed-resources/...
*
* @module prepare-aioncore
*/
const { execSync, execFileSync } = require('child_process');
const fs = require('fs');
const os = require('os');
const path = require('path');
const GITHUB_OWNER = 'iOfficeAI';
const GITHUB_REPO = 'AionCore';
const ACTIONS_ARTIFACT_TARGETS = {
'darwin-arm64': {
artifactName: 'aioncore-manual-macos-arm64',
manualPlatform: 'macos-arm64',
},
'darwin-x64': {
artifactName: 'aioncore-manual-macos-x64',
manualPlatform: 'macos-x64',
},
'linux-arm64': {
artifactName: 'aioncore-manual-linux-arm64',
manualPlatform: 'linux-arm64',
},
'linux-x64': {
artifactName: 'aioncore-manual-linux-x64',
manualPlatform: 'linux-x64',
},
'win32-arm64': {
artifactName: 'aioncore-manual-windows-arm64',
manualPlatform: 'windows-arm64',
},
'win32-x64': {
artifactName: 'aioncore-manual-windows-x64',
manualPlatform: 'windows-x64',
},
};
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function ensureDirectory(dirPath) {
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
}
function removeDirectorySafe(dirPath) {
fs.rmSync(dirPath, { recursive: true, force: true });
}
function copyFileSafe(sourcePath, targetPath) {
ensureDirectory(path.dirname(targetPath));
fs.copyFileSync(sourcePath, targetPath);
}
function copyDirectorySafe(sourcePath, targetPath) {
ensureDirectory(path.dirname(targetPath));
fs.cpSync(sourcePath, targetPath, { recursive: true, force: true });
}
function ensureExecutableMode(filePath) {
if (process.platform === 'win32') return;
try {
fs.chmodSync(filePath, 0o755);
} catch {}
}
function writeJson(filePath, payload) {
fs.writeFileSync(filePath, JSON.stringify(payload, null, 2) + '\n', 'utf-8');
}
function getBinaryName(platform) {
return platform === 'win32' ? 'aioncore.exe' : 'aioncore';
}
function getActionsTarget(platform, arch) {
return ACTIONS_ARTIFACT_TARGETS[`${platform}-${arch}`] || null;
}
function getActionsArtifactName(platform, arch) {
return getActionsTarget(platform, arch)?.artifactName || null;
}
function getActionsManualPlatform(platform, arch) {
return getActionsTarget(platform, arch)?.manualPlatform || `${platform}-${arch}`;
}
function getActionsArtifactMissingMessage({ runId, platform, arch, expectedArtifactName, availableArtifactNames }) {
const available =
Array.isArray(availableArtifactNames) && availableArtifactNames.length > 0
? availableArtifactNames.join(', ')
: '(none)';
return [
`AionCore run ${runId} does not contain artifact [ ${expectedArtifactName} ] required for [ ${platform}-${arch} ].`,
`Available artifacts: ${available}.`,
`Re-run AionCore Manual Build with platform [ ${getActionsManualPlatform(platform, arch)} ] or all.`,
].join(' ');
}
function prepareManagedResources(binaryPath, targetDir) {
const bundleOut = path.join(targetDir, 'managed-resources');
const dataDir = path.join(targetDir, '.prepare-data');
removeDirectorySafe(bundleOut);
removeDirectorySafe(dataDir);
ensureDirectory(bundleOut);
ensureDirectory(dataDir);
console.log(` Preparing managed resources under ${path.relative(process.cwd(), bundleOut)}`);
execFileSync(binaryPath, ['--data-dir', dataDir, 'prepare-managed-resources', '--bundle-out', bundleOut], {
stdio: 'inherit',
env: {
...process.env,
AIONUI_BUNDLED_MANAGED_RESOURCES: '',
},
});
removeDirectorySafe(dataDir);
return bundleOut;
}
// ---------------------------------------------------------------------------
// Source resolvers
// ---------------------------------------------------------------------------
/**
* Resolve the actual version tag when "latest" is requested.
* Uses GitHub API via `gh` CLI (needs GH_TOKEN in CI) or falls back to
* `curl` with an optional Authorization header (GITHUB_TOKEN / GH_TOKEN).
*/
function resolveLatestTag() {
const token = process.env.GH_TOKEN || process.env.GITHUB_TOKEN || '';
// 1. Try gh CLI (honours GH_TOKEN automatically)
try {
const out = execSync(`gh api repos/${GITHUB_OWNER}/${GITHUB_REPO}/releases/latest --jq .tag_name`, {
encoding: 'utf-8',
timeout: 15000,
}).trim();
if (out) return out;
} catch {
// gh CLI not available or no token — fall back to curl
}
// 2. Curl with optional token to avoid rate-limit 403
try {
const authArgs = token ? ['-H', `Authorization: token ${token}`] : [];
const args = ['-fsSL', ...authArgs, `https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/releases/latest`];
const out = execFileSync('curl', args, { encoding: 'utf-8', timeout: 15000 });
const tag = JSON.parse(out).tag_name;
if (tag) return tag;
} catch {
// network issue or rate-limited
}
return null;
}
/**
* Build the release asset filename for the given platform/arch/tag.
*
* Expected asset naming convention:
* aioncore-v0.1.0-aarch64-apple-darwin.tar.gz
*/
function getAssetName(platform, arch, tag) {
const archMap = { x64: 'x86_64', arm64: 'aarch64' };
const platformMap = {
darwin: 'apple-darwin',
linux: 'unknown-linux-gnu',
win32: 'pc-windows-msvc',
};
const normalizedArch = archMap[arch];
const normalizedPlatform = platformMap[platform];
if (!normalizedArch || !normalizedPlatform) return null;
const ext = platform === 'win32' ? '.zip' : '.tar.gz';
return `aioncore-${tag}-${normalizedArch}-${normalizedPlatform}${ext}`;
}
function getDownloadUrl(assetName, tag) {
return `https://github.com/${GITHUB_OWNER}/${GITHUB_REPO}/releases/download/${tag}/${assetName}`;
}
function downloadFile(url, outputPath) {
console.log(` Downloading aioncore from ${url}`);
if (process.platform === 'win32') {
const ps = `$ProgressPreference='SilentlyContinue'; Invoke-WebRequest -Uri '${url}' -OutFile '${outputPath.replace(/'/g, "''")}'`;
execFileSync('powershell', ['-NoProfile', '-NonInteractive', '-Command', ps], {
timeout: 120000,
});
return;
}
try {
execFileSync('curl', ['-L', '--fail', '--silent', '--show-error', '-o', outputPath, url], { timeout: 120000 });
} catch {
execFileSync('wget', ['-q', '-O', outputPath, url], { timeout: 120000 });
}
}
function extractArchive(archivePath, outputDir, platform) {
ensureDirectory(outputDir);
if (platform === 'win32' || archivePath.endsWith('.zip')) {
if (platform === 'win32') {
const ps = `Expand-Archive -LiteralPath '${archivePath.replace(/'/g, "''")}' -DestinationPath '${outputDir.replace(/'/g, "''")}' -Force`;
execFileSync('powershell', ['-NoProfile', '-NonInteractive', '-Command', ps]);
} else {
execFileSync('unzip', ['-o', archivePath, '-d', outputDir]);
}
} else {
execFileSync('tar', ['-xzf', archivePath, '-C', outputDir]);
}
}
function findBinaryInDir(dir, binaryName) {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isFile() && entry.name === binaryName) return fullPath;
if (entry.isDirectory()) {
const found = findBinaryInDir(fullPath, binaryName);
if (found) return found;
}
}
return null;
}
function findAioncoreArchiveInDir(dir) {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (
entry.isFile() &&
entry.name.startsWith('aioncore-') &&
(entry.name.endsWith('.zip') || entry.name.endsWith('.tar.gz'))
) {
return fullPath;
}
if (entry.isDirectory()) {
const found = findAioncoreArchiveInDir(fullPath);
if (found) return found;
}
}
return null;
}
function getGitHubToken() {
return process.env.GH_TOKEN || process.env.GITHUB_TOKEN || '';
}
function githubApiGetJson(apiPath) {
const token = getGitHubToken();
try {
return JSON.parse(
execFileSync('gh', ['api', apiPath], {
encoding: 'utf-8',
timeout: 15000,
env: {
...process.env,
GH_TOKEN: token || process.env.GH_TOKEN,
},
})
);
} catch {
// gh CLI not available or failed — fall back to curl.
}
const headers = ['-H', 'Accept: application/vnd.github+json'];
if (token) {
headers.push('-H', `Authorization: Bearer ${token}`);
}
const url = `https://api.github.com/${apiPath}`;
const out = execFileSync('curl', ['-fsSL', ...headers, url], {
encoding: 'utf-8',
timeout: 15000,
});
return JSON.parse(out);
}
function downloadFileWithAuth(url, outputPath) {
const token = getGitHubToken();
const headers = ['-H', 'Accept: application/vnd.github+json'];
if (token) {
headers.push('-H', `Authorization: Bearer ${token}`);
}
try {
execFileSync('curl', ['-L', '--fail', '--silent', '--show-error', ...headers, '-o', outputPath, url], {
timeout: 120000,
});
return;
} catch {
// curl may be unavailable in some local environments; try gh before failing.
}
execFileSync('gh', ['api', url, '--output', outputPath], {
timeout: 120000,
env: {
...process.env,
GH_TOKEN: token || process.env.GH_TOKEN,
},
});
}
function listActionsArtifacts(runId) {
const response = githubApiGetJson(
`repos/${GITHUB_OWNER}/${GITHUB_REPO}/actions/runs/${runId}/artifacts?per_page=100`
);
return Array.isArray(response?.artifacts) ? response.artifacts : [];
}
function downloadAndExtractActionsArtifact(platform, arch, runId) {
const expectedArtifactName = getActionsArtifactName(platform, arch);
if (!expectedArtifactName) {
throw new Error(`Unsupported AionCore Actions artifact target: ${platform}-${arch}`);
}
const artifacts = listActionsArtifacts(runId);
const availableArtifactNames = artifacts
.map((artifact) => artifact.name)
.filter(Boolean)
.toSorted();
const artifact = artifacts.find((candidate) => candidate.name === expectedArtifactName);
if (!artifact) {
throw new Error(
getActionsArtifactMissingMessage({
runId,
platform,
arch,
expectedArtifactName,
availableArtifactNames,
})
);
}
const tempDir = path.join(os.tmpdir(), 'aioncore-prepare-actions', runId, `${platform}-${arch}`);
const artifactZipPath = path.join(tempDir, `${expectedArtifactName}.zip`);
const artifactExtractDir = path.join(tempDir, 'artifact');
const binaryExtractDir = path.join(tempDir, 'binary');
removeDirectorySafe(tempDir);
ensureDirectory(tempDir);
const downloadUrl =
artifact.archive_download_url ||
`https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/actions/artifacts/${artifact.id}/zip`;
console.log(` Downloading aioncore from AionCore run ${runId} artifact ${expectedArtifactName}`);
downloadFileWithAuth(downloadUrl, artifactZipPath);
extractArchive(artifactZipPath, artifactExtractDir, platform);
const archivePath = findAioncoreArchiveInDir(artifactExtractDir);
if (!archivePath) {
throw new Error(`AionCore artifact ${expectedArtifactName} from run ${runId} does not contain an aioncore archive`);
}
extractArchive(archivePath, binaryExtractDir, platform);
const binaryName = getBinaryName(platform);
const binaryPath = findBinaryInDir(binaryExtractDir, binaryName);
if (!binaryPath) {
throw new Error(`Binary ${binaryName} not found in AionCore artifact ${expectedArtifactName} from run ${runId}`);
}
return {
binaryPath,
tempDir,
artifactName: expectedArtifactName,
archivePath,
url: downloadUrl,
};
}
function downloadAndExtract(platform, arch, tag) {
const assetName = getAssetName(platform, arch, tag);
if (!assetName) {
throw new Error(`Unsupported aioncore target: ${platform}-${arch}`);
}
const url = getDownloadUrl(assetName, tag);
const tempDir = path.join(os.tmpdir(), 'aioncore-prepare', tag, `${platform}-${arch}`);
const archivePath = path.join(tempDir, assetName);
const extractDir = path.join(tempDir, 'extracted');
removeDirectorySafe(tempDir);
ensureDirectory(tempDir);
downloadFile(url, archivePath);
extractArchive(archivePath, extractDir, platform);
const binaryName = getBinaryName(platform);
const binaryPath = findBinaryInDir(extractDir, binaryName);
if (!binaryPath) {
throw new Error(`Binary ${binaryName} not found in downloaded archive`);
}
return { binaryPath, tempDir, url };
}
// ---------------------------------------------------------------------------
// Main export
// ---------------------------------------------------------------------------
/**
* Prepare aioncore binary for packaging.
*
* @param {object} options - Configuration options
* @param {string} options.projectRoot - Project root directory
* @param {string} options.platform - Target platform (process.platform)
* @param {string} options.arch - Target architecture (process.arch)
* @param {string} options.version - Backend version (default: 'latest')
* @returns {{ prepared: true; dir: string; sourceType: string }}
*/
function prepareAioncore(options) {
const { projectRoot, platform, arch, version = 'latest' } = options;
const runtimeKey = `${platform}-${arch}`;
const actionsRunId = (process.env.AIONUI_BACKEND_RUN_ID || '').trim();
let tag = null;
if (!actionsRunId) {
// Resolve the actual version tag — release asset filenames include the tag.
if (version === 'latest') {
const resolved = resolveLatestTag();
if (!resolved) {
throw new Error('Failed to resolve latest aioncore release tag from GitHub API');
}
tag = resolved;
console.log(`Resolved aioncore "latest" → ${tag}`);
} else {
tag = version.startsWith('v') ? version : `v${version}`;
}
}
const targetDir = path.join(projectRoot, 'resources', 'bundled-aioncore', runtimeKey);
const binaryName = getBinaryName(platform);
const targetBinaryPath = path.join(targetDir, binaryName);
console.log(
`Preparing aioncore for ${runtimeKey} (${actionsRunId ? `actions run: ${actionsRunId}` : `version: ${tag}`})`
);
removeDirectorySafe(targetDir);
ensureDirectory(targetDir);
const localBundleDir = (process.env.AIONUI_BACKEND_LOCAL_BUNDLE_DIR || '').trim();
if (localBundleDir) {
const resolvedLocalBundleDir = path.resolve(localBundleDir);
const localBinaryPath = path.join(resolvedLocalBundleDir, binaryName);
const localManagedResourcesDir = path.join(resolvedLocalBundleDir, 'managed-resources');
if (
fs.existsSync(resolvedLocalBundleDir) &&
fs.statSync(resolvedLocalBundleDir).isDirectory() &&
fs.existsSync(localBinaryPath) &&
fs.existsSync(localManagedResourcesDir)
) {
copyDirectorySafe(resolvedLocalBundleDir, targetDir);
ensureExecutableMode(targetBinaryPath);
const manifest = {
platform,
arch,
version: tag || `actions-run-${actionsRunId}` || 'local-bundle',
generatedAt: new Date().toISOString(),
sourceType: 'local-bundle',
source: { path: resolvedLocalBundleDir },
files: [binaryName, 'managed-resources/'],
};
writeJson(path.join(targetDir, 'manifest.json'), manifest);
console.log(` Using local aioncore bundle: ${resolvedLocalBundleDir}`);
return { prepared: true, dir: targetDir, sourceType: 'local-bundle' };
}
console.warn(` Local aioncore bundle is incomplete or missing: ${resolvedLocalBundleDir}`);
}
let sourcePath = null;
let sourceType = 'none';
let sourceDetail = {};
let tempDir = null;
// 1. Download from GitHub Actions artifacts when manual build run id is provided.
if (actionsRunId) {
const result = downloadAndExtractActionsArtifact(platform, arch, actionsRunId);
sourcePath = result.binaryPath;
tempDir = result.tempDir;
sourceType = 'actions-artifact';
sourceDetail = {
runId: actionsRunId,
artifactName: result.artifactName,
url: result.url,
};
console.log(` Downloaded from GitHub Actions artifact`);
}
// 2. Download from GitHub releases.
if (!sourcePath && tag) {
try {
const result = downloadAndExtract(platform, arch, tag);
sourcePath = result.binaryPath;
tempDir = result.tempDir;
sourceType = 'download';
sourceDetail = { url: result.url };
console.log(` Downloaded from GitHub releases`);
} catch (error) {
console.warn(` Download failed: ${error.message}`);
}
}
// 3. Use an explicitly supplied local cache when network download is unavailable.
if (!sourcePath) {
const localBinary = (process.env.AIONUI_BACKEND_LOCAL_BINARY || '').trim();
if (localBinary) {
const resolvedLocalBinary = path.resolve(localBinary);
if (fs.existsSync(resolvedLocalBinary) && fs.statSync(resolvedLocalBinary).isFile()) {
sourcePath = resolvedLocalBinary;
sourceType = 'local-binary';
sourceDetail = { path: resolvedLocalBinary };
console.log(` Using local aioncore binary: ${resolvedLocalBinary}`);
} else {
console.warn(` Local aioncore binary not found: ${resolvedLocalBinary}`);
}
}
}
// Write result
if (sourcePath) {
copyFileSafe(sourcePath, targetBinaryPath);
ensureExecutableMode(targetBinaryPath);
const bundledManagedResourcesDir = prepareManagedResources(targetBinaryPath, targetDir);
// The release tag is the authoritative version — the aioncore
// binary does not expose a --version flag (it has --app-version which
// takes a value, not a self-report).
const manifest = {
platform,
arch,
version: tag || `actions-run-${actionsRunId}`,
generatedAt: new Date().toISOString(),
sourceType,
source: sourceDetail,
files: [binaryName, 'managed-resources/'],
};
writeJson(path.join(targetDir, 'manifest.json'), manifest);
console.log(
` Bundled aioncore prepared: resources/bundled-aioncore/${runtimeKey}/${binaryName} [source=${sourceType}]`
);
console.log(` Bundled managed resources prepared: ${bundledManagedResourcesDir}`);
if (tempDir) removeDirectorySafe(tempDir);
return { prepared: true, dir: targetDir, sourceType };
}
throw new Error(`aioncore binary not found for ${runtimeKey} (tag: ${tag})`);
}
module.exports = {
getActionsArtifactMissingMessage,
getActionsArtifactName,
prepareAioncore,
};
@@ -0,0 +1,254 @@
const fs = require('fs');
const path = require('path');
function backendBinaryName(platform) {
return platform === 'win32' ? 'aioncore.exe' : 'aioncore';
}
function nodeBinaryName(platform) {
return platform === 'win32' ? 'node.exe' : 'node';
}
function nodeExecutableParts(platform) {
return platform === 'win32' ? [nodeBinaryName(platform)] : ['bin', nodeBinaryName(platform)];
}
function normalize(relativePath) {
return relativePath.split(path.sep).join('/');
}
function bundledPath(runtimeKey, ...parts) {
return normalize(path.join('bundled-aioncore', runtimeKey, ...parts));
}
function requireRelativePath(baseDir, runtimeKey, parts, checked, missing) {
const relativePath = bundledPath(runtimeKey, ...parts);
checked.push(relativePath);
if (!isFile(path.join(baseDir, ...parts))) {
missing.push(relativePath);
}
}
function requireRelativeDirectory(baseDir, runtimeKey, parts, checked, missing) {
const relativePath = bundledPath(runtimeKey, ...parts);
checked.push(relativePath);
const fullPath = path.join(baseDir, ...parts);
if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isDirectory()) {
missing.push(relativePath);
}
}
function readDirectories(root) {
if (!fs.existsSync(root) || !fs.statSync(root).isDirectory()) return [];
return fs
.readdirSync(root, { withFileTypes: true })
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name)
.toSorted();
}
function isFile(filePath) {
return fs.existsSync(filePath) && fs.statSync(filePath).isFile();
}
function requireFile(baseDir, runtimeKey, parts, checked, missing) {
const relativePath = bundledPath(runtimeKey, ...parts);
checked.push(relativePath);
if (!isFile(path.join(baseDir, ...parts))) {
missing.push(relativePath);
}
}
function requireDirectory(baseDir, runtimeKey, parts, checked, missing) {
const relativePath = bundledPath(runtimeKey, ...parts);
checked.push(relativePath);
const fullPath = path.join(baseDir, ...parts);
if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isDirectory()) {
missing.push(relativePath);
}
}
function verifyBundleManifest(baseDir, runtimeKey, electronPlatformName, targetArch, checked, missing) {
const parts = ['manifest.json'];
const relativePath = bundledPath(runtimeKey, ...parts);
const manifestPath = path.join(baseDir, ...parts);
checked.push(relativePath);
if (!isFile(manifestPath)) {
missing.push(relativePath);
return;
}
const manifest = readManifest(manifestPath);
if (!manifest) {
missing.push(`${relativePath}<invalid-json>`);
return;
}
if (manifest.platform !== electronPlatformName) {
missing.push(`${relativePath}<platform:${electronPlatformName}>`);
}
if (manifest.arch !== targetArch) {
missing.push(`${relativePath}<arch:${targetArch}>`);
}
}
function requireManagedNode(baseDir, runtimeKey, platform, checked, missing) {
const nodeRoot = path.join(baseDir, 'managed-resources', 'node');
const versions = readDirectories(nodeRoot);
const executableParts = nodeExecutableParts(platform);
if (versions.length === 0) {
const relativePath = bundledPath(runtimeKey, 'managed-resources', 'node', '*', ...executableParts);
checked.push(relativePath);
missing.push(relativePath);
return;
}
for (const version of versions) {
requireFile(baseDir, runtimeKey, ['managed-resources', 'node', version, ...executableParts], checked, missing);
}
}
const CODEX_VENDOR_TRIPLE_BY_RUNTIME_KEY = {
'win32-arm64': 'aarch64-pc-windows-msvc',
'win32-x64': 'x86_64-pc-windows-msvc',
};
function acpToolPlatformExecutableParts(platform, runtimeKey, toolId) {
if (platform !== 'win32') return null;
if (toolId === 'codex-acp') {
const vendorTriple = CODEX_VENDOR_TRIPLE_BY_RUNTIME_KEY[runtimeKey];
if (!vendorTriple) return null;
return ['node_modules', '@openai', `codex-${runtimeKey}`, 'vendor', vendorTriple, 'bin', 'codex.exe'];
}
if (toolId === 'claude-agent-acp') {
return ['node_modules', '@anthropic-ai', `claude-agent-sdk-${runtimeKey}`, 'claude.exe'];
}
return null;
}
function readManifest(manifestPath) {
try {
return JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
} catch {
return null;
}
}
function requireManagedAcpTool(baseDir, runtimeKey, platform, toolId, checked, missing) {
const toolRoot = path.join(baseDir, 'managed-resources', 'acp', toolId);
const versions = readDirectories(toolRoot);
if (versions.length === 0) {
const relativePath = bundledPath(runtimeKey, 'managed-resources', 'acp', toolId, '*', runtimeKey, 'manifest.json');
checked.push(relativePath);
missing.push(relativePath);
return;
}
for (const version of versions) {
const platformRoot = path.join(toolRoot, version, runtimeKey);
const manifestRelativePath = bundledPath(
runtimeKey,
'managed-resources',
'acp',
toolId,
version,
runtimeKey,
'manifest.json'
);
checked.push(manifestRelativePath);
const manifestPath = path.join(platformRoot, 'manifest.json');
if (!isFile(manifestPath)) {
missing.push(manifestRelativePath);
continue;
}
const manifest = readManifest(manifestPath);
const entrypoint = typeof manifest?.entrypoint === 'string' ? manifest.entrypoint : null;
if (!entrypoint) {
missing.push(bundledPath(runtimeKey, 'managed-resources', 'acp', toolId, version, runtimeKey, '<entrypoint>'));
continue;
}
const entrypointRelativePath = bundledPath(
runtimeKey,
'managed-resources',
'acp',
toolId,
version,
runtimeKey,
entrypoint
);
checked.push(entrypointRelativePath);
if (!isFile(path.join(platformRoot, entrypoint))) {
missing.push(entrypointRelativePath);
}
requireFile(
baseDir,
runtimeKey,
['managed-resources', 'acp', toolId, version, runtimeKey, 'package.json'],
checked,
missing
);
requireFile(
baseDir,
runtimeKey,
['managed-resources', 'acp', toolId, version, runtimeKey, 'package-lock.json'],
checked,
missing
);
requireDirectory(
baseDir,
runtimeKey,
['managed-resources', 'acp', toolId, version, runtimeKey, 'node_modules'],
checked,
missing
);
const platformExecutableParts = acpToolPlatformExecutableParts(platform, runtimeKey, toolId);
if (platformExecutableParts) {
requireFile(
baseDir,
runtimeKey,
['managed-resources', 'acp', toolId, version, runtimeKey, ...platformExecutableParts],
checked,
missing
);
}
}
}
function verifyBundledAioncoreResources({ resourcesDir, electronPlatformName, targetArch }) {
const runtimeKey = `${electronPlatformName}-${targetArch}`;
const baseDir = path.join(resourcesDir, 'bundled-aioncore', runtimeKey);
const checked = [];
const missing = [];
requireRelativePath(baseDir, runtimeKey, [backendBinaryName(electronPlatformName)], checked, missing);
verifyBundleManifest(baseDir, runtimeKey, electronPlatformName, targetArch, checked, missing);
requireRelativeDirectory(baseDir, runtimeKey, ['managed-resources'], checked, missing);
requireManagedNode(baseDir, runtimeKey, electronPlatformName, checked, missing);
requireManagedAcpTool(baseDir, runtimeKey, electronPlatformName, 'codex-acp', checked, missing);
requireManagedAcpTool(baseDir, runtimeKey, electronPlatformName, 'claude-agent-acp', checked, missing);
return { runtimeKey, checked, missing };
}
module.exports = {
verifyBundledAioncoreResources,
};