project_apple_application_graph accepted only IPA inventories. It now also projects macOS .app directories and ZIP or DMG inventories that contain applications. It reports application roots, platforms, and every nested bundle with its path-convention role, layout, parent, Info.plist path, executable candidates, and signing paths: - roles: app extensions, XPC services (including inside frameworks), frameworks, login items, system and driver extensions, plug-ins, resource bundles, and helper or nested applications; - privileged helpers in Contents/Library/LaunchServices and launchd plists in Contents/Library/LaunchAgents or LaunchDaemons; - direct files of Contents/Helpers; - symlinks by path. Their targets are not inventoried, so versioned frameworks are described through their real Versions/<version>/ directories. AppleDouble sidecars (._* and __MACOSX/) are excluded from roles. A DMG whose contents could not be inventoried is reported as partial. Other inventory formats remain input errors. The projection still reads no plist or CMS semantics; it points callers at inspect_plist. The result schema is additive. projection_id now covers the new fields, so IPA projection IDs differ from earlier versions. verify:macos-bundle compiles a source-owned app with clang (CLT only). It packs it as a directory, a ditto ZIP, and an APFS DMG, and checks identical anatomy through the CLI with stdio MCP parity. It runs in the macOS CI job. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
71 lines
2.5 KiB
JavaScript
71 lines
2.5 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { execFile } from "node:child_process";
|
|
import { promisify } from "node:util";
|
|
import { fileURLToPath } from "node:url";
|
|
import { Client } from "@modelcontextprotocol/client";
|
|
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
|
|
|
const environment = () => ({
|
|
PATH: process.env.PATH ?? "/usr/bin:/bin",
|
|
REA_LOG_LEVEL: "silent",
|
|
REA_ANALYSIS_PROVIDER: "auto",
|
|
// Artifact workflows have no deep-provider prerequisite or GUI authority.
|
|
HOPPER_LAUNCHER_PATH: "/rea-unconfigured-deep-provider/hopper",
|
|
});
|
|
|
|
/** Execute the compiled CLI with production providers and no test adapters. */
|
|
export async function artifactCli(command, target, arguments_ = []) {
|
|
return (await artifactCliEvidence(command, target, arguments_))
|
|
.normalized_result;
|
|
}
|
|
|
|
/** Execute the compiled CLI and return its complete Evidence record. */
|
|
export async function artifactCliEvidence(command, target, arguments_ = []) {
|
|
const { stdout } = await promisify(execFile)(
|
|
process.execPath,
|
|
[
|
|
fileURLToPath(new URL("../rea.mjs", import.meta.url)),
|
|
command,
|
|
target,
|
|
...arguments_,
|
|
"--json",
|
|
],
|
|
{ env: environment(), timeout: 60000, maxBuffer: 16 * 1024 * 1024 },
|
|
);
|
|
const evidence = JSON.parse(stdout);
|
|
assert.equal(evidence.error, undefined, stdout);
|
|
assert.ok(evidence.normalized_result, "CLI omitted normalized result");
|
|
return evidence;
|
|
}
|
|
|
|
/** Run a real stdio MCP subprocess, owning and closing its transport. */
|
|
export async function withArtifactMcp(target, verify) {
|
|
const transport = new StdioClientTransport({
|
|
command: process.execPath,
|
|
args: [fileURLToPath(new URL("../rea.mjs", import.meta.url)), "mcp"],
|
|
env: environment(),
|
|
stderr: "pipe",
|
|
});
|
|
const client = new Client({ name: "artifact-real-e2e", version: "1" });
|
|
try {
|
|
await client.connect(transport);
|
|
const opened = await client.callTool({
|
|
name: "open_binary",
|
|
arguments: { path: target },
|
|
});
|
|
assert.notEqual(opened.isError, true, JSON.stringify(opened));
|
|
await verify(client);
|
|
} finally {
|
|
await client.close();
|
|
await transport.close();
|
|
}
|
|
}
|
|
|
|
/** Require a successful structured MCP tool result rather than its text rendering. */
|
|
export async function artifactMcpResult(client, name, arguments_ = {}) {
|
|
const result = await client.callTool({ name, arguments: arguments_ });
|
|
assert.notEqual(result.isError, true, JSON.stringify(result));
|
|
assert.ok(result.structuredContent?.result, "MCP omitted structured result");
|
|
return result.structuredContent.result;
|
|
}
|