@zhuixin/cli 0.2.3 → 0.2.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/index.js +12 -6
- package/dist/index.js.map +1 -1
- package/dist/scripts/bootstrap-tools.ps1 +4 -24
- package/dist/scripts/build-ipatool.ps1 +33 -0
- package/dist/scripts/ipatool-overlay/keyring_windows.go +132 -0
- package/dist/scripts/ipatool-overlay/keyring_windows_test.go +85 -0
- package/dist/skills/apptrace/SKILL.md +25 -13
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -31,7 +31,7 @@ var import_metadata = require("@zhuixin/metadata");
|
|
|
31
31
|
var import_shared = require("@zhuixin/shared");
|
|
32
32
|
|
|
33
33
|
// package.json
|
|
34
|
-
var version = "0.2.
|
|
34
|
+
var version = "0.2.5";
|
|
35
35
|
|
|
36
36
|
// src/mcp-config.ts
|
|
37
37
|
var import_node_fs = __toESM(require("fs"));
|
|
@@ -265,7 +265,7 @@ function createEngineLauncher(workspaceRoot) {
|
|
|
265
265
|
return launcher;
|
|
266
266
|
}
|
|
267
267
|
async function installTools(config, json = false) {
|
|
268
|
-
if ((0, import_metadata.
|
|
268
|
+
if (await (0, import_metadata.setupToolsReady)(config)) {
|
|
269
269
|
const managedLauncher = import_node_path2.default.join(config.workspaceRoot, "tools", "bin", process.platform === "win32" ? "rea.cmd" : "rea");
|
|
270
270
|
if (import_node_path2.default.resolve(config.analysisEnginePath) === managedLauncher) {
|
|
271
271
|
createEngineLauncher(config.workspaceRoot);
|
|
@@ -293,8 +293,7 @@ async function installTools(config, json = false) {
|
|
|
293
293
|
// PowerShell 7's inherited module paths can hide Windows PowerShell 5.1 modules.
|
|
294
294
|
PSModulePath: import_node_path2.default.join(process.env.SystemRoot || "C:\\Windows", "System32", "WindowsPowerShell", "v1.0", "Modules"),
|
|
295
295
|
APPTRACE_WORKSPACE_ROOT: config.workspaceRoot,
|
|
296
|
-
APPTRACE_TOOLS_DIR: import_node_path2.default.join(config.workspaceRoot, "tools")
|
|
297
|
-
...process.env.APPTRACE_IPATOOL_SHA256 ? { APPTRACE_IPATOOL_SHA256: process.env.APPTRACE_IPATOOL_SHA256 } : {}
|
|
296
|
+
APPTRACE_TOOLS_DIR: import_node_path2.default.join(config.workspaceRoot, "tools")
|
|
298
297
|
}, (pid) => (0, import_metadata.trackInstallationChild)(config, pid));
|
|
299
298
|
if (code2 !== 0) {
|
|
300
299
|
throw new import_metadata.CoreFailure(
|
|
@@ -434,6 +433,7 @@ async function authLogin() {
|
|
|
434
433
|
`ipatool not found (${config.ipatoolPath}). Run apptrace setup --install first, then apptrace auth login.`
|
|
435
434
|
);
|
|
436
435
|
}
|
|
436
|
+
await (0, import_metadata.requireBackgroundCredentials)(config);
|
|
437
437
|
const code = await interactive(ipatool, ["auth", "login"]);
|
|
438
438
|
if (code !== 0) {
|
|
439
439
|
throw new import_metadata.CoreFailure(
|
|
@@ -441,12 +441,14 @@ async function authLogin() {
|
|
|
441
441
|
`ipatool login failed with exit code ${code ?? "unknown"}`
|
|
442
442
|
);
|
|
443
443
|
}
|
|
444
|
-
await authStatus();
|
|
444
|
+
await authStatus(true);
|
|
445
|
+
await (0, import_metadata.recoverAnalysisTasks)(config, __filename);
|
|
445
446
|
}
|
|
446
|
-
async function authStatus() {
|
|
447
|
+
async function authStatus(requireAuthenticated = false) {
|
|
447
448
|
await withCore(async (core) => {
|
|
448
449
|
const status = await core.handle(import_shared.CHANNELS.accountStatus, { force: true });
|
|
449
450
|
printJson(status);
|
|
451
|
+
if (requireAuthenticated && !status.authenticated) throw new import_metadata.CoreFailure(status.errorCode ?? "BACKGROUND_AUTH_FAILED", status.message ?? "Background credential verification failed after sign-in.");
|
|
450
452
|
});
|
|
451
453
|
}
|
|
452
454
|
async function addTarget(source) {
|
|
@@ -633,6 +635,10 @@ async function main() {
|
|
|
633
635
|
usage();
|
|
634
636
|
return;
|
|
635
637
|
}
|
|
638
|
+
if (command === "analysis-worker") {
|
|
639
|
+
if (!subcommand || !/^[0-9a-f-]{36}$/.test(subcommand)) throw new import_metadata.CoreFailure("INVALID_INPUT", "Invalid analysis task ID.");
|
|
640
|
+
return (0, import_metadata.runAnalysisWorker)((0, import_metadata.loadConfig)(), subcommand, __filename);
|
|
641
|
+
}
|
|
636
642
|
if (command === "setup-worker") {
|
|
637
643
|
if (!subcommand || !/^[0-9a-f-]{36}$/.test(subcommand)) throw new import_metadata.CoreFailure("INVALID_INPUT", "Invalid install operation id.");
|
|
638
644
|
const config = (0, import_metadata.loadConfig)();
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../package.json","../src/mcp-config.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { spawn } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport {\n AppAnalysisService,\n CoreFailure,\n CoreServer,\n installEngineArtifact,\n VersionComparisonService,\n loadConfig,\n resolveTool,\n OnboardingService,\n runInstallation,\n missingSetupTools,\n trackInstallationChild,\n type AnalysisGoal,\n type AnalyzeAppResult\n} from \"@zhuixin/metadata\";\nimport {\n CHANNELS,\n type CapabilityDescriptor,\n type HealthInfo,\n type JobRecord,\n type TargetRecord\n} from \"@zhuixin/shared\";\n\nimport { version } from \"../package.json\";\nimport { writeMcpConfig } from \"./mcp-config.js\";\n\nconst GOALS: AnalysisGoal[] = [\n \"tech_stack\",\n \"feature_map\",\n \"network\",\n \"native_risk\",\n \"report\"\n];\n\nfunction usage(): void {\n console.log(`AppTrace ${version}\n\nUsage:\n apptrace setup [--json] [--install] [--write-config <path>] [--restart-client]\n apptrace doctor [--json]\n apptrace auth login\n apptrace auth status\n apptrace auth open\n apptrace setup --write-config <path> --install-skill\n apptrace targets add <app-store-url-or-id>\n apptrace targets list\n apptrace analyze [app-store-url-or-id] [--target <target-id>] [--goals ...] [--force]\n apptrace report <target-id>\n apptrace jobs get <job-id>\n apptrace search <target-id> [query] [--type route,module] [--limit 50]\n apptrace native search <target-id> <query> [--kind procedures|strings]\n apptrace native decompile <target-id> <function-name>\n apptrace evidence <target-id>\n apptrace compare <target-id> <left-artifact-id> <right-artifact-id> [--force]\n apptrace capabilities [target-id]\n apptrace engine install <engine-file> [--sha256 <digest>]\n apptrace mcp\n\nExamples:\n apptrace analyze https://apps.apple.com/cn/app/id1616124777\n apptrace analyze 1616124777 --goals tech_stack,feature_map,native_risk,report\n`);\n}\n\nfunction option(args: string[], name: string): string | undefined {\n const index = args.indexOf(name);\n if (index >= 0 && args[index + 1]) return args[index + 1];\n const prefix = `${name}=`;\n const inline = args.find((arg) => arg.startsWith(prefix));\n return inline ? inline.slice(prefix.length) : undefined;\n}\n\nfunction hasFlag(args: string[], name: string): boolean {\n return args.includes(name);\n}\n\nfunction positional(args: string[]): string | undefined {\n return args.find((arg) => !arg.startsWith(\"--\"));\n}\n\nfunction parseGoals(value: string | undefined): AnalysisGoal[] {\n if (!value) return [\"tech_stack\", \"feature_map\", \"report\"];\n const goals = value\n .split(\",\")\n .map((item) => item.trim())\n .filter(Boolean);\n const invalid = goals.filter(\n (goal): goal is string => !GOALS.includes(goal as AnalysisGoal)\n );\n if (invalid.length > 0) {\n throw new CoreFailure(\n \"INVALID_INPUT\",\n `Unknown analysis goal: ${invalid.join(\", \")}. Available: ${GOALS.join(\", \")}`\n );\n }\n return goals as AnalysisGoal[];\n}\n\nfunction printJson(value: unknown): void {\n process.stdout.write(`${JSON.stringify(value, null, 2)}\\n`);\n}\n\nfunction printError(error: unknown): void {\n if (error instanceof CoreFailure) {\n console.error(`${error.code}: ${error.message}`);\n if (error.code === \"AUTH_REQUIRED\" || error.code === \"KEYCHAIN_LOCKED\") {\n console.error(\"Next: apptrace auth login\");\n } else if (error.code === \"LICENSE_REQUIRED\") {\n console.error(\"Next: get or purchase the app in the App Store, then retry.\");\n }\n return;\n }\n console.error(error instanceof Error ? error.stack ?? error.message : String(error));\n}\n\nfunction withCore<T>(operation: (core: CoreServer) => Promise<T>): Promise<T> {\n const core = new CoreServer({ config: loadConfig(), emit: () => {} });\n return operation(core).finally(() => core.close());\n}\n\nfunction interactive(\n command: string,\n args: string[],\n env?: NodeJS.ProcessEnv,\n onSpawn?: (pid: number) => void\n): Promise<number | null> {\n return new Promise((resolve, reject) => {\n const isWindowsCommand =\n process.platform === \"win32\" &&\n [\".cmd\", \".bat\"].includes(path.extname(command).toLowerCase());\n const child = isWindowsCommand\n ? spawn(\n process.env.ComSpec || \"cmd.exe\",\n [\"/d\", \"/s\", \"/c\", `\"${command}\" ${args.join(\" \")}`],\n {\n stdio: \"inherit\",\n windowsVerbatimArguments: true,\n env: env ? { ...process.env, ...env } : process.env\n }\n )\n : spawn(command, args, {\n stdio: \"inherit\",\n env: env ? { ...process.env, ...env } : process.env\n });\n if (child.pid && onSpawn) {\n try { onSpawn(child.pid); }\n catch (error) { child.kill(); reject(error); return; }\n }\n child.on(\"error\", reject);\n child.on(\"exit\", (code) => resolve(code));\n });\n}\n\nasync function doctor(json: boolean): Promise<void> {\n const config = loadConfig();\n await withCore(async (core) => {\n const health = (await core.handle(CHANNELS.health, {})) as HealthInfo;\n const capabilities = (await core.handle(\n CHANNELS.capabilitiesList,\n {}\n )) as CapabilityDescriptor[];\n const result = { config, health, capabilities };\n if (json) {\n printJson(result);\n return;\n }\n console.log(`workspace: ${health.workspaceRoot}`);\n console.log(`host: ${health.hostPlatform}`);\n console.log(`store: ${health.store}`);\n console.log(\"tools:\");\n for (const tool of health.tools ?? []) {\n console.log(\n ` ${tool.available ? \"OK \" : \"MISS\"} ${tool.label}: ${tool.path}`\n );\n }\n console.log(\"capabilities:\");\n for (const capability of capabilities) {\n console.log(\n ` ${capability.available ? \"OK \" : \"NO \"} ${capability.id}${\n capability.reason ? ` — ${capability.reason}` : \"\"\n }`\n );\n }\n });\n}\n\nfunction createEngineLauncher(workspaceRoot: string): string {\n const reaScript = require.resolve(\"rea-agents/scripts/rea.mjs\");\n const tools = path.join(workspaceRoot, \"tools\");\n const javaHome = path.join(tools, \"jdk\");\n const ghidraHome = path.join(tools, \"ghidra_12.1.4_PUBLIC\");\n const ghidraUserHome = path.join(workspaceRoot, \".ghidra-home\");\n fs.mkdirSync(path.join(tools, \"bin\"), { recursive: true, mode: 0o700 });\n fs.mkdirSync(ghidraUserHome, { recursive: true, mode: 0o700 });\n const launcher = path.join(\n tools,\n \"bin\",\n process.platform === \"win32\" ? \"rea.cmd\" : \"rea\"\n );\n if (process.platform === \"win32\") {\n fs.writeFileSync(\n launcher,\n [\n \"@echo off\",\n `set \"JAVA_HOME=${javaHome}\"`,\n `set \"GHIDRA_INSTALL_DIR=${ghidraHome}\"`,\n `set \"GHIDRA_USER_HOME=${ghidraUserHome}\"`,\n 'set \"PATH=%JAVA_HOME%\\\\bin;%PATH%\"',\n `\"${process.execPath}\" \"${reaScript}\" %*`,\n \"\"\n ].join(\"\\r\\n\"),\n { mode: 0o700 }\n );\n } else {\n fs.writeFileSync(\n launcher,\n [\n \"#!/usr/bin/env bash\",\n \"set -euo pipefail\",\n `export JAVA_HOME=${JSON.stringify(javaHome)}`,\n `export GHIDRA_INSTALL_DIR=${JSON.stringify(ghidraHome)}`,\n `export GHIDRA_USER_HOME=${JSON.stringify(ghidraUserHome)}`,\n 'export PATH=\"$JAVA_HOME/bin:$PATH\"',\n 'export JAVA_TOOL_OPTIONS=\"${JAVA_TOOL_OPTIONS:-} -Duser.home=$GHIDRA_USER_HOME\"',\n `exec ${JSON.stringify(process.execPath)} ${JSON.stringify(reaScript)} \"$@\"`,\n \"\"\n ].join(\"\\n\"),\n { mode: 0o700 }\n );\n }\n return launcher;\n}\n\nasync function installTools(\n config: ReturnType<typeof loadConfig>,\n json = false\n): Promise<void> {\n if (missingSetupTools(config).length === 0) {\n const managedLauncher = path.join(config.workspaceRoot, \"tools\", \"bin\", process.platform === \"win32\" ? \"rea.cmd\" : \"rea\");\n if (path.resolve(config.analysisEnginePath) === managedLauncher) {\n createEngineLauncher(config.workspaceRoot);\n }\n process.stderr.write(\"Tools already available; download skipped.\\n\");\n return;\n }\n if (process.platform === \"win32\" && process.arch === \"x64\") {\n const script = path.resolve(__dirname, \"scripts/bootstrap-tools.ps1\");\n if (!fs.existsSync(script)) {\n throw new CoreFailure(\"TOOL_INSTALLER_MISSING\", `Installer script not found: ${script}`);\n }\n process.stderr.write(\n \"Installing JDK, Ghidra and ipatool for Windows x64. This can take several minutes.\\n\"\n );\n const code = await interactive(\"powershell.exe\", [\n \"-NoProfile\",\n \"-ExecutionPolicy\",\n \"Bypass\",\n \"-File\",\n script,\n \"-WorkspaceRoot\",\n config.workspaceRoot\n ], {\n // PowerShell 7's inherited module paths can hide Windows PowerShell 5.1 modules.\n PSModulePath: path.join(process.env.SystemRoot || \"C:\\\\Windows\", \"System32\", \"WindowsPowerShell\", \"v1.0\", \"Modules\"),\n APPTRACE_WORKSPACE_ROOT: config.workspaceRoot,\n APPTRACE_TOOLS_DIR: path.join(config.workspaceRoot, \"tools\"),\n ...(process.env.APPTRACE_IPATOOL_SHA256\n ? { APPTRACE_IPATOOL_SHA256: process.env.APPTRACE_IPATOOL_SHA256 }\n : {})\n }, (pid) => trackInstallationChild(config, pid));\n if (code !== 0) {\n throw new CoreFailure(\n \"TOOL_INSTALL_FAILED\",\n `Tool installation failed with exit code ${code ?? \"unknown\"}`\n );\n }\n const launcher = createEngineLauncher(config.workspaceRoot);\n process.stderr.write(`Analysis engine launcher: ${launcher}\\n`);\n return;\n }\n if (process.platform !== \"darwin\" || process.arch !== \"arm64\") {\n throw new CoreFailure(\n \"TOOL_INSTALL_UNSUPPORTED\",\n \"The automatic installer supports macOS arm64 and Windows x64. On other platforms set APPTRACE_ENGINE_PATH and APPTRACE_IPATOOL_PATH.\"\n );\n }\n const script = path.resolve(__dirname, \"scripts/bootstrap-tools.sh\");\n if (!fs.existsSync(script)) {\n throw new CoreFailure(\"TOOL_INSTALLER_MISSING\", `Installer script not found: ${script}`);\n }\n process.stderr.write(\n \"Installing JDK, Ghidra and ipatool. This can take several minutes.\\n\"\n );\n const code = await interactive(\"/bin/bash\", [script], {\n APPTRACE_WORKSPACE_ROOT: config.workspaceRoot,\n APPTRACE_TOOLS_DIR: path.join(config.workspaceRoot, \"tools\")\n }, (pid) => trackInstallationChild(config, pid));\n if (code !== 0) {\n throw new CoreFailure(\n \"TOOL_INSTALL_FAILED\",\n `Tool installation failed with exit code ${code ?? \"unknown\"}`\n );\n }\n const launcher = createEngineLauncher(config.workspaceRoot);\n process.stderr.write(`Analysis engine launcher: ${launcher}\\n`);\n}\n\nasync function setup(json: boolean, args: string[]): Promise<void> {\n const configPath = option(args, \"--write-config\");\n if (hasFlag(args, \"--install-skill\") && !configPath) throw new CoreFailure(\"CONFIG_REQUIRED\", \"--install-skill requires --write-config.\");\n let config = loadConfig();\n if (hasFlag(args, \"--install\")) {\n await runInstallation(config, () => installTools(config, json));\n config = loadConfig();\n }\n await doctor(json);\n if (json && !configPath) return;\n const serverEntry = resolveMcpEntry();\n const env: Record<string, string> = {\n APPTRACE_WORKSPACE_ROOT: config.workspaceRoot,\n APPTRACE_CLI_PATH: __filename\n };\n const ipatool = resolveTool(config.ipatoolPath);\n if (ipatool) env.APPTRACE_IPATOOL_PATH = ipatool;\n if (fs.existsSync(config.enginePath)) {\n env.APPTRACE_ENGINE_PATH = config.enginePath;\n }\n // Only pin the analysis engine when it really exists, otherwise the engine\n // inherits a path that can never work instead of resolving its own default.\n if (fs.existsSync(config.analysisEnginePath)) {\n env.APPTRACE_ANALYSIS_ENGINE_PATH = config.analysisEnginePath;\n }\n if (configPath) {\n const absolute = path.resolve(configPath);\n writeMcpConfig(absolute, { command: process.execPath, args: [serverEntry], env });\n if (hasFlag(args, \"--install-skill\")) {\n const destination = path.join(path.dirname(absolute), \"skills\", \"apptrace\", \"SKILL.md\");\n fs.mkdirSync(path.dirname(destination), { recursive: true });\n if (fs.existsSync(destination)) fs.copyFileSync(destination, `${destination}.${Date.now()}.bak`);\n fs.copyFileSync(path.join(__dirname, \"skills\", \"apptrace\", \"SKILL.md\"), destination);\n }\n if (json) {\n process.stderr.write(`MCP config written: ${absolute}\\n`);\n return;\n }\n console.log(`MCP config written: ${absolute}`);\n }\n const escape = (value: string) => value.replace(/\\\\/g, \"\\\\\\\\\");\n const envDisplay = Object.entries(env)\n .map(([key, value]) => `${key} = \"${escape(value)}\"`)\n .join(\", \");\n console.log(`\nMCP configuration:\n\n[mcp_servers.apptrace]\ncommand = \"${escape(process.execPath)}\"\nargs = [\"${escape(serverEntry)}\"]\nenv = { ${envDisplay} }\n\nRestart the agent client to apply. Add --restart-client to let AppTrace restart it.\n`);\n if (configPath && hasFlag(args, \"--restart-client\")) {\n const client = clientForConfig(path.resolve(configPath));\n if (!client.app) {\n throw new CoreFailure(\n \"RESTART_UNSUPPORTED\",\n `${client.name} has no desktop process to restart; quit and reopen it yourself.`\n );\n }\n await restartClient(client.app);\n console.log(`Restarted ${client.name}.`);\n }\n}\n\nfunction clientForConfig(configPath: string): { name: string; app?: string } {\n const normalized = configPath.replace(/\\\\/g, \"/\").toLowerCase();\n if (normalized.includes(\"claude_desktop_config.json\")) {\n return { name: \"Claude Desktop\", app: \"Claude\" };\n }\n if (normalized.includes(\"/.codex/\")) return { name: \"Codex\", app: \"Codex\" };\n if (normalized.includes(\"/.cursor/\")) return { name: \"Cursor\", app: \"Cursor\" };\n if (normalized.endsWith(\".mcp.json\")) return { name: \"Claude Code\" };\n return { name: \"Agent client\" };\n}\n\nasync function restartClient(app: string): Promise<void> {\n if (process.platform !== \"darwin\") {\n throw new CoreFailure(\n \"RESTART_UNSUPPORTED\",\n \"Automatic client restart is only supported on macOS; quit and reopen the client yourself.\"\n );\n }\n await interactive(\"/usr/bin/osascript\", [\"-e\", `quit app \"${app}\"`]);\n await new Promise((resolve) => setTimeout(resolve, 1200));\n await interactive(\"/usr/bin/open\", [\"-a\", app]);\n}\n\n/**\n * Global installs nest workspace packages under the CLI package\n * (node_modules/@zhuixin/cli/node_modules/@zhuixin/mcp), so a fixed\n * ../../mcp path only works inside the monorepo.\n */\nfunction resolveMcpEntry(): string {\n try {\n return require.resolve(\"@zhuixin/mcp\");\n } catch {\n return path.resolve(__dirname, \"../../mcp/dist/index.js\");\n }\n}\n\nasync function authLogin(): Promise<void> {\n const config = loadConfig();\n const ipatool = resolveTool(config.ipatoolPath);\n if (!ipatool) {\n throw new CoreFailure(\n \"TOOL_NOT_INSTALLED\",\n `ipatool not found (${config.ipatoolPath}). Run apptrace setup --install first, then apptrace auth login.`\n );\n }\n const code = await interactive(ipatool, [\"auth\", \"login\"]);\n if (code !== 0) {\n throw new CoreFailure(\n \"AUTH_FAILED\",\n `ipatool login failed with exit code ${code ?? \"unknown\"}`\n );\n }\n await authStatus();\n}\n\nasync function authStatus(): Promise<void> {\n await withCore(async (core) => {\n const status = await core.handle(CHANNELS.accountStatus, { force: true });\n printJson(status);\n });\n}\n\nasync function addTarget(source: string): Promise<void> {\n await withCore(async (core) => {\n const resolved = (await core.handle(CHANNELS.targetsResolve, {\n input: source\n })) as { resolutionId: string };\n const target = (await core.handle(CHANNELS.targetsCreate, {\n resolutionId: resolved.resolutionId\n })) as TargetRecord;\n printJson(target);\n });\n}\n\nasync function listTargets(): Promise<void> {\n await withCore(async (core) => {\n printJson(await core.handle(CHANNELS.targetsList, {}));\n });\n}\n\nasync function analyze(source: string | undefined, args: string[]): Promise<void> {\n const goals = parseGoals(option(args, \"--goals\"));\n const json = hasFlag(args, \"--json\");\n const force = hasFlag(args, \"--force\");\n const targetId = option(args, \"--target\");\n if (!source && !targetId) {\n throw new CoreFailure(\"SOURCE_REQUIRED\", \"Missing app link, ID, or --target.\");\n }\n await withCore(async (core) => {\n const service = new AppAnalysisService(core);\n const result = await service.analyzeApp({\n source: targetId ? undefined : source,\n targetId,\n goals,\n force,\n onProgress: (event) => {\n if (!json) {\n process.stderr.write(\n `[${event.step}] ${event.status}${\n event.message ? ` — ${event.message}` : \"\"\n }\\n`\n );\n }\n }\n });\n if (json) {\n printJson(result);\n } else {\n printAnalysis(result);\n }\n });\n}\n\nfunction printAnalysis(result: AnalyzeAppResult): void {\n console.log(`\\n${result.targetName}`);\n console.log(`Target: ${result.targetId}`);\n console.log(`Artifact: ${result.artifact.sha256}`);\n console.log(\n `Technology: ${result.technology?.hints.join(\" / \") || \"unknown\"}`\n );\n console.log(`Routes: ${result.routes?.count ?? 0}`);\n console.log(`Modules: ${result.modules?.count ?? 0}`);\n console.log(`Network endpoints: ${result.networkEndpoints?.count ?? 0}`);\n if (result.native) {\n console.log(\n `Native: ${result.native.procedureCount ?? \"?\"} procedures, ${\n result.native.stringCount ?? \"?\"\n } strings`\n );\n if (result.native.warning) console.log(`Warning: ${result.native.warning}`);\n }\n if (result.reportPath) console.log(`Report: ${result.reportPath}`);\n for (const evidencePath of result.evidencePaths) {\n console.log(`Evidence: ${evidencePath}`);\n }\n if (result.limitations.length > 0) {\n console.log(\"Limitations:\");\n for (const limitation of result.limitations.slice(0, 6)) {\n console.log(` - ${limitation}`);\n }\n }\n}\n\nasync function report(targetId: string): Promise<void> {\n await withCore(async (core) => {\n const service = new AppAnalysisService(core);\n printJson(await service.runCapability(targetId, \"generate:report\"));\n });\n}\n\nasync function getJob(jobId: string): Promise<void> {\n await withCore(async (core) => {\n const result = (await core.handle(CHANNELS.jobsGet, { jobId })) as {\n job: JobRecord;\n events: unknown[];\n };\n printJson({ ...result, events: result.events.slice(-50) });\n });\n}\n\nasync function searchFindings(\n targetId: string,\n query: string | undefined,\n args: string[]\n): Promise<void> {\n const types = option(args, \"--type\")\n ?.split(\",\")\n .map((value) => value.trim())\n .filter(Boolean);\n const limit = Number(option(args, \"--limit\") ?? 50);\n await withCore(async (core) => {\n printJson(\n await core.handle(CHANNELS.findingsSearch, {\n targetId,\n query,\n types,\n limit: Number.isFinite(limit) ? limit : 50\n })\n );\n });\n}\n\nasync function nativeCommand(\n action: string | undefined,\n args: string[]\n): Promise<void> {\n const targetId = args[0];\n const value = args[1];\n if (!targetId || !value) {\n throw new CoreFailure(\n \"INVALID_INPUT\",\n \"Usage: apptrace native <search|decompile> <target-id> <query-or-function>\"\n );\n }\n await withCore(async (core) => {\n const service = new AppAnalysisService(core);\n if (action === \"search\") {\n printJson(\n await service.runCapability(targetId, \"native:search\", {\n query: value,\n kind: option(args, \"--kind\") ?? \"procedures\",\n mode: option(args, \"--mode\") ?? \"literal\"\n })\n );\n return;\n }\n if (action === \"decompile\") {\n printJson(\n await service.runCapability(targetId, \"native:decompile\", {\n functionName: value\n })\n );\n return;\n }\n throw new CoreFailure(\n \"INVALID_INPUT\",\n \"Native commands support only search or decompile.\"\n );\n });\n}\n\nasync function evidence(targetId: string): Promise<void> {\n await withCore(async (core) => {\n printJson(\n await core.handle(CHANNELS.evidenceList, {\n targetId\n })\n );\n });\n}\n\nasync function installEngine(file: string, args: string[]): Promise<void> {\n const config = loadConfig();\n const expected = option(args, \"--sha256\");\n const installed = installEngineArtifact(config.workspaceRoot, file, expected);\n printJson(installed);\n}\n\nasync function compare(\n targetId: string | undefined,\n leftArtifactId: string | undefined,\n rightArtifactId: string | undefined,\n args: string[]\n): Promise<void> {\n if (!targetId || !leftArtifactId || !rightArtifactId) {\n throw new CoreFailure(\n \"INVALID_INPUT\",\n \"Usage: apptrace compare <target-id> <left-artifact-id> <right-artifact-id>\"\n );\n }\n await withCore(async (core) => {\n printJson(\n await new VersionComparisonService(core).compare(\n targetId,\n leftArtifactId,\n rightArtifactId,\n { force: hasFlag(args, \"--force\") }\n )\n );\n });\n}\n\nasync function capabilities(targetId?: string): Promise<void> {\n await withCore(async (core) => {\n printJson(\n await core.handle(CHANNELS.capabilitiesList, {\n ...(targetId ? { targetId } : {})\n })\n );\n });\n}\n\nasync function main(): Promise<void> {\n const [command, subcommand, ...args] = process.argv.slice(2);\n if (!command || [\"help\", \"-h\", \"--help\"].includes(command)) {\n usage();\n return;\n }\n if (command === \"setup-worker\") {\n if (!subcommand || !/^[0-9a-f-]{36}$/.test(subcommand)) throw new CoreFailure(\"INVALID_INPUT\", \"Invalid install operation id.\");\n const config = loadConfig();\n return runInstallation(config, () => installTools(config), subcommand);\n }\n if (command === \"setup\") {\n const setupArgs =\n subcommand?.startsWith(\"--\") && subcommand\n ? [subcommand, ...args]\n : args;\n return setup(hasFlag(setupArgs, \"--json\"), setupArgs);\n }\n if (command === \"doctor\") {\n const doctorArgs =\n subcommand?.startsWith(\"--\") && subcommand\n ? [subcommand, ...args]\n : args;\n return doctor(hasFlag(doctorArgs, \"--json\"));\n }\n if (command === \"auth\") {\n if (subcommand === \"open\") {\n printJson(await new OnboardingService(loadConfig(), __filename).openLogin());\n return;\n }\n if (subcommand === \"login\") return authLogin();\n if (subcommand === \"status\") return authStatus();\n }\n if (command === \"targets\") {\n if (subcommand === \"add\") {\n const source = positional(args);\n if (!source) throw new CoreFailure(\"SOURCE_REQUIRED\", \"Missing app link or ID.\");\n return addTarget(source);\n }\n if (subcommand === \"list\") return listTargets();\n }\n if (command === \"analyze\") {\n const analyzeArgs =\n subcommand?.startsWith(\"--\") && subcommand\n ? [subcommand, ...args]\n : args;\n const source =\n subcommand && !subcommand.startsWith(\"--\")\n ? subcommand\n : positional(analyzeArgs);\n return analyze(source, analyzeArgs);\n }\n if (command === \"report\") {\n if (!subcommand) throw new CoreFailure(\"TARGET_REQUIRED\", \"Missing target ID.\");\n return report(subcommand);\n }\n if (command === \"jobs\" && subcommand === \"get\") {\n const jobId = positional(args);\n if (!jobId) throw new CoreFailure(\"JOB_REQUIRED\", \"Missing job ID.\");\n return getJob(jobId);\n }\n if (command === \"search\") {\n if (!subcommand) throw new CoreFailure(\"TARGET_REQUIRED\", \"Missing target ID.\");\n return searchFindings(subcommand, args[0], args);\n }\n if (command === \"native\") return nativeCommand(subcommand, args);\n if (command === \"evidence\") {\n if (!subcommand) throw new CoreFailure(\"TARGET_REQUIRED\", \"Missing target ID.\");\n return evidence(subcommand);\n }\n if (command === \"engine\" && subcommand === \"install\") {\n const file = args[0];\n if (!file) throw new CoreFailure(\"ENGINE_REQUIRED\", \"Missing engine file path.\");\n return installEngine(file, args);\n }\n if (command === \"compare\") {\n if (!subcommand) throw new CoreFailure(\"TARGET_REQUIRED\", \"Missing target ID.\");\n return compare(subcommand, args[0], args[1], args);\n }\n if (command === \"capabilities\") return capabilities(subcommand);\n if (command === \"mcp\") {\n process.env.APPTRACE_CLI_PATH = __filename;\n const { startMcpServer } = await import(\"@zhuixin/mcp\");\n await startMcpServer(loadConfig());\n return;\n }\n usage();\n throw new CoreFailure(\"UNKNOWN_COMMAND\", `Unknown command: ${command}`);\n}\n\nvoid main().catch((error) => {\n printError(error);\n process.exitCode = 1;\n});\n","{\n \"name\": \"@zhuixin/cli\",\n \"version\": \"0.2.3\",\n \"license\": \"Apache-2.0\",\n \"description\": \"AppTrace CLI for evidence-backed application analysis\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/zhuixin-labs/app-evidence.git\",\n \"directory\": \"packages/cli\"\n },\n \"homepage\": \"https://github.com/zhuixin-labs/app-evidence\",\n \"bugs\": {\"url\": \"https://github.com/zhuixin-labs/app-evidence/issues\"},\n \"keywords\": [\"mcp\", \"ai-agents\", \"app-analysis\", \"static-analysis\", \"ios\", \"ipa\"],\n \"publishConfig\": {\"access\": \"public\"},\n \"engines\": {\"node\": \">=22.19\"},\n \"main\": \"dist/index.js\",\n \"bin\": {\n \"apptrace\": \"dist/index.js\"\n },\n \"files\": [\"dist\"],\n \"scripts\": {\n \"build\": \"tsup\",\n \"typecheck\": \"tsc --noEmit -p tsconfig.json\"\n },\n \"dependencies\": {\n \"@zhuixin/metadata\": \"workspace:*\",\n \"@zhuixin/mcp\": \"workspace:*\",\n \"@zhuixin/shared\": \"workspace:*\",\n \"rea-agents\": \"6.4.0\",\n \"smol-toml\": \"^1.9.0\"\n },\n \"devDependencies\": {\n \"tsup\": \"^8.5.0\"\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { parse, stringify } from \"smol-toml\";\n\ninterface ServerConfig {\n command: string;\n args: string[];\n env: Record<string, string>;\n}\n\nfunction object(value: unknown): Record<string, any> {\n if (value === undefined) return {};\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new Error(\"MCP configuration must contain objects/tables.\");\n }\n return value as Record<string, any>;\n}\n\nexport function writeMcpConfig(filename: string, server: ServerConfig): void {\n const extension = path.extname(filename).toLowerCase();\n if (![\".json\", \".toml\"].includes(extension)) {\n throw new Error(\"--write-config accepts only .json or .toml files.\");\n }\n const original = fs.existsSync(filename) ? fs.readFileSync(filename, \"utf8\") : undefined;\n const config = object(original?.trim()\n ? extension === \".json\" ? JSON.parse(original) : parse(original)\n : {});\n const key = extension === \".json\" ? \"mcpServers\" : \"mcp_servers\";\n const servers = object(config[key]);\n const previous = object(servers.apptrace);\n const environment = { ...object(previous.env), ...server.env };\n for (const [key, placeholder] of [[\"APPTRACE_IPATOOL_PATH\", \"ipatool\"], [\"APPTRACE_ANALYSIS_ENGINE_PATH\", \"rea\"]]) {\n if (!server.env[key] && environment[key] === placeholder) delete environment[key];\n }\n servers.apptrace = { ...previous, ...server, env: environment };\n config[key] = servers;\n const output = extension === \".json\" ? JSON.stringify(config, null, 2) + \"\\n\" : stringify(config);\n fs.mkdirSync(path.dirname(filename), { recursive: true, mode: 0o700 });\n // Keep the original text (including TOML comments) recoverable before serialization.\n if (original !== undefined) {\n fs.writeFileSync(`${filename}.${randomUUID()}.bak`, original, { mode: 0o600, flag: \"wx\" });\n }\n const temporary = `${filename}.${randomUUID()}.tmp`;\n try {\n fs.writeFileSync(temporary, output, { mode: 0o600, flag: \"wx\" });\n fs.renameSync(temporary, filename);\n } finally {\n if (fs.existsSync(temporary)) fs.unlinkSync(temporary);\n }\n}\n"],"mappings":"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","names":["import_node_fs","import_node_path","path","fs","key","path","capabilities","fs","script","code","launcher"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../package.json","../src/mcp-config.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { spawn } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport {\n AppAnalysisService,\n CoreFailure,\n CoreServer,\n installEngineArtifact,\n VersionComparisonService,\n loadConfig,\n resolveTool,\n OnboardingService,\n runInstallation,\n setupToolsReady,\n trackInstallationChild,\n runAnalysisWorker,\n recoverAnalysisTasks,\n requireBackgroundCredentials,\n type AnalysisGoal,\n type AnalyzeAppResult\n} from \"@zhuixin/metadata\";\nimport {\n CHANNELS,\n type AccountStatus,\n type CapabilityDescriptor,\n type HealthInfo,\n type JobRecord,\n type TargetRecord\n} from \"@zhuixin/shared\";\n\nimport { version } from \"../package.json\";\nimport { writeMcpConfig } from \"./mcp-config.js\";\n\nconst GOALS: AnalysisGoal[] = [\n \"tech_stack\",\n \"feature_map\",\n \"network\",\n \"native_risk\",\n \"report\"\n];\n\nfunction usage(): void {\n console.log(`AppTrace ${version}\n\nUsage:\n apptrace setup [--json] [--install] [--write-config <path>] [--restart-client]\n apptrace doctor [--json]\n apptrace auth login\n apptrace auth status\n apptrace auth open\n apptrace setup --write-config <path> --install-skill\n apptrace targets add <app-store-url-or-id>\n apptrace targets list\n apptrace analyze [app-store-url-or-id] [--target <target-id>] [--goals ...] [--force]\n apptrace report <target-id>\n apptrace jobs get <job-id>\n apptrace search <target-id> [query] [--type route,module] [--limit 50]\n apptrace native search <target-id> <query> [--kind procedures|strings]\n apptrace native decompile <target-id> <function-name>\n apptrace evidence <target-id>\n apptrace compare <target-id> <left-artifact-id> <right-artifact-id> [--force]\n apptrace capabilities [target-id]\n apptrace engine install <engine-file> [--sha256 <digest>]\n apptrace mcp\n\nExamples:\n apptrace analyze https://apps.apple.com/cn/app/id1616124777\n apptrace analyze 1616124777 --goals tech_stack,feature_map,native_risk,report\n`);\n}\n\nfunction option(args: string[], name: string): string | undefined {\n const index = args.indexOf(name);\n if (index >= 0 && args[index + 1]) return args[index + 1];\n const prefix = `${name}=`;\n const inline = args.find((arg) => arg.startsWith(prefix));\n return inline ? inline.slice(prefix.length) : undefined;\n}\n\nfunction hasFlag(args: string[], name: string): boolean {\n return args.includes(name);\n}\n\nfunction positional(args: string[]): string | undefined {\n return args.find((arg) => !arg.startsWith(\"--\"));\n}\n\nfunction parseGoals(value: string | undefined): AnalysisGoal[] {\n if (!value) return [\"tech_stack\", \"feature_map\", \"report\"];\n const goals = value\n .split(\",\")\n .map((item) => item.trim())\n .filter(Boolean);\n const invalid = goals.filter(\n (goal): goal is string => !GOALS.includes(goal as AnalysisGoal)\n );\n if (invalid.length > 0) {\n throw new CoreFailure(\n \"INVALID_INPUT\",\n `Unknown analysis goal: ${invalid.join(\", \")}. Available: ${GOALS.join(\", \")}`\n );\n }\n return goals as AnalysisGoal[];\n}\n\nfunction printJson(value: unknown): void {\n process.stdout.write(`${JSON.stringify(value, null, 2)}\\n`);\n}\n\nfunction printError(error: unknown): void {\n if (error instanceof CoreFailure) {\n console.error(`${error.code}: ${error.message}`);\n if (error.code === \"AUTH_REQUIRED\" || error.code === \"KEYCHAIN_LOCKED\") {\n console.error(\"Next: apptrace auth login\");\n } else if (error.code === \"LICENSE_REQUIRED\") {\n console.error(\"Next: get or purchase the app in the App Store, then retry.\");\n }\n return;\n }\n console.error(error instanceof Error ? error.stack ?? error.message : String(error));\n}\n\nfunction withCore<T>(operation: (core: CoreServer) => Promise<T>): Promise<T> {\n const core = new CoreServer({ config: loadConfig(), emit: () => {} });\n return operation(core).finally(() => core.close());\n}\n\nfunction interactive(\n command: string,\n args: string[],\n env?: NodeJS.ProcessEnv,\n onSpawn?: (pid: number) => void\n): Promise<number | null> {\n return new Promise((resolve, reject) => {\n const isWindowsCommand =\n process.platform === \"win32\" &&\n [\".cmd\", \".bat\"].includes(path.extname(command).toLowerCase());\n const child = isWindowsCommand\n ? spawn(\n process.env.ComSpec || \"cmd.exe\",\n [\"/d\", \"/s\", \"/c\", `\"${command}\" ${args.join(\" \")}`],\n {\n stdio: \"inherit\",\n windowsVerbatimArguments: true,\n env: env ? { ...process.env, ...env } : process.env\n }\n )\n : spawn(command, args, {\n stdio: \"inherit\",\n env: env ? { ...process.env, ...env } : process.env\n });\n if (child.pid && onSpawn) {\n try { onSpawn(child.pid); }\n catch (error) { child.kill(); reject(error); return; }\n }\n child.on(\"error\", reject);\n child.on(\"exit\", (code) => resolve(code));\n });\n}\n\nasync function doctor(json: boolean): Promise<void> {\n const config = loadConfig();\n await withCore(async (core) => {\n const health = (await core.handle(CHANNELS.health, {})) as HealthInfo;\n const capabilities = (await core.handle(\n CHANNELS.capabilitiesList,\n {}\n )) as CapabilityDescriptor[];\n const result = { config, health, capabilities };\n if (json) {\n printJson(result);\n return;\n }\n console.log(`workspace: ${health.workspaceRoot}`);\n console.log(`host: ${health.hostPlatform}`);\n console.log(`store: ${health.store}`);\n console.log(\"tools:\");\n for (const tool of health.tools ?? []) {\n console.log(\n ` ${tool.available ? \"OK \" : \"MISS\"} ${tool.label}: ${tool.path}`\n );\n }\n console.log(\"capabilities:\");\n for (const capability of capabilities) {\n console.log(\n ` ${capability.available ? \"OK \" : \"NO \"} ${capability.id}${\n capability.reason ? ` — ${capability.reason}` : \"\"\n }`\n );\n }\n });\n}\n\nfunction createEngineLauncher(workspaceRoot: string): string {\n const reaScript = require.resolve(\"rea-agents/scripts/rea.mjs\");\n const tools = path.join(workspaceRoot, \"tools\");\n const javaHome = path.join(tools, \"jdk\");\n const ghidraHome = path.join(tools, \"ghidra_12.1.4_PUBLIC\");\n const ghidraUserHome = path.join(workspaceRoot, \".ghidra-home\");\n fs.mkdirSync(path.join(tools, \"bin\"), { recursive: true, mode: 0o700 });\n fs.mkdirSync(ghidraUserHome, { recursive: true, mode: 0o700 });\n const launcher = path.join(\n tools,\n \"bin\",\n process.platform === \"win32\" ? \"rea.cmd\" : \"rea\"\n );\n if (process.platform === \"win32\") {\n fs.writeFileSync(\n launcher,\n [\n \"@echo off\",\n `set \"JAVA_HOME=${javaHome}\"`,\n `set \"GHIDRA_INSTALL_DIR=${ghidraHome}\"`,\n `set \"GHIDRA_USER_HOME=${ghidraUserHome}\"`,\n 'set \"PATH=%JAVA_HOME%\\\\bin;%PATH%\"',\n `\"${process.execPath}\" \"${reaScript}\" %*`,\n \"\"\n ].join(\"\\r\\n\"),\n { mode: 0o700 }\n );\n } else {\n fs.writeFileSync(\n launcher,\n [\n \"#!/usr/bin/env bash\",\n \"set -euo pipefail\",\n `export JAVA_HOME=${JSON.stringify(javaHome)}`,\n `export GHIDRA_INSTALL_DIR=${JSON.stringify(ghidraHome)}`,\n `export GHIDRA_USER_HOME=${JSON.stringify(ghidraUserHome)}`,\n 'export PATH=\"$JAVA_HOME/bin:$PATH\"',\n 'export JAVA_TOOL_OPTIONS=\"${JAVA_TOOL_OPTIONS:-} -Duser.home=$GHIDRA_USER_HOME\"',\n `exec ${JSON.stringify(process.execPath)} ${JSON.stringify(reaScript)} \"$@\"`,\n \"\"\n ].join(\"\\n\"),\n { mode: 0o700 }\n );\n }\n return launcher;\n}\n\nasync function installTools(\n config: ReturnType<typeof loadConfig>,\n json = false\n): Promise<void> {\n if (await setupToolsReady(config)) {\n const managedLauncher = path.join(config.workspaceRoot, \"tools\", \"bin\", process.platform === \"win32\" ? \"rea.cmd\" : \"rea\");\n if (path.resolve(config.analysisEnginePath) === managedLauncher) {\n createEngineLauncher(config.workspaceRoot);\n }\n process.stderr.write(\"Tools already available; download skipped.\\n\");\n return;\n }\n if (process.platform === \"win32\" && process.arch === \"x64\") {\n const script = path.resolve(__dirname, \"scripts/bootstrap-tools.ps1\");\n if (!fs.existsSync(script)) {\n throw new CoreFailure(\"TOOL_INSTALLER_MISSING\", `Installer script not found: ${script}`);\n }\n process.stderr.write(\n \"Installing JDK, Ghidra and ipatool for Windows x64. This can take several minutes.\\n\"\n );\n const code = await interactive(\"powershell.exe\", [\n \"-NoProfile\",\n \"-ExecutionPolicy\",\n \"Bypass\",\n \"-File\",\n script,\n \"-WorkspaceRoot\",\n config.workspaceRoot\n ], {\n // PowerShell 7's inherited module paths can hide Windows PowerShell 5.1 modules.\n PSModulePath: path.join(process.env.SystemRoot || \"C:\\\\Windows\", \"System32\", \"WindowsPowerShell\", \"v1.0\", \"Modules\"),\n APPTRACE_WORKSPACE_ROOT: config.workspaceRoot,\n APPTRACE_TOOLS_DIR: path.join(config.workspaceRoot, \"tools\")\n }, (pid) => trackInstallationChild(config, pid));\n if (code !== 0) {\n throw new CoreFailure(\n \"TOOL_INSTALL_FAILED\",\n `Tool installation failed with exit code ${code ?? \"unknown\"}`\n );\n }\n const launcher = createEngineLauncher(config.workspaceRoot);\n process.stderr.write(`Analysis engine launcher: ${launcher}\\n`);\n return;\n }\n if (process.platform !== \"darwin\" || process.arch !== \"arm64\") {\n throw new CoreFailure(\n \"TOOL_INSTALL_UNSUPPORTED\",\n \"The automatic installer supports macOS arm64 and Windows x64. On other platforms set APPTRACE_ENGINE_PATH and APPTRACE_IPATOOL_PATH.\"\n );\n }\n const script = path.resolve(__dirname, \"scripts/bootstrap-tools.sh\");\n if (!fs.existsSync(script)) {\n throw new CoreFailure(\"TOOL_INSTALLER_MISSING\", `Installer script not found: ${script}`);\n }\n process.stderr.write(\n \"Installing JDK, Ghidra and ipatool. This can take several minutes.\\n\"\n );\n const code = await interactive(\"/bin/bash\", [script], {\n APPTRACE_WORKSPACE_ROOT: config.workspaceRoot,\n APPTRACE_TOOLS_DIR: path.join(config.workspaceRoot, \"tools\")\n }, (pid) => trackInstallationChild(config, pid));\n if (code !== 0) {\n throw new CoreFailure(\n \"TOOL_INSTALL_FAILED\",\n `Tool installation failed with exit code ${code ?? \"unknown\"}`\n );\n }\n const launcher = createEngineLauncher(config.workspaceRoot);\n process.stderr.write(`Analysis engine launcher: ${launcher}\\n`);\n}\n\nasync function setup(json: boolean, args: string[]): Promise<void> {\n const configPath = option(args, \"--write-config\");\n if (hasFlag(args, \"--install-skill\") && !configPath) throw new CoreFailure(\"CONFIG_REQUIRED\", \"--install-skill requires --write-config.\");\n let config = loadConfig();\n if (hasFlag(args, \"--install\")) {\n await runInstallation(config, () => installTools(config, json));\n config = loadConfig();\n }\n await doctor(json);\n if (json && !configPath) return;\n const serverEntry = resolveMcpEntry();\n const env: Record<string, string> = {\n APPTRACE_WORKSPACE_ROOT: config.workspaceRoot,\n APPTRACE_CLI_PATH: __filename\n };\n const ipatool = resolveTool(config.ipatoolPath);\n if (ipatool) env.APPTRACE_IPATOOL_PATH = ipatool;\n if (fs.existsSync(config.enginePath)) {\n env.APPTRACE_ENGINE_PATH = config.enginePath;\n }\n // Only pin the analysis engine when it really exists, otherwise the engine\n // inherits a path that can never work instead of resolving its own default.\n if (fs.existsSync(config.analysisEnginePath)) {\n env.APPTRACE_ANALYSIS_ENGINE_PATH = config.analysisEnginePath;\n }\n if (configPath) {\n const absolute = path.resolve(configPath);\n writeMcpConfig(absolute, { command: process.execPath, args: [serverEntry], env });\n if (hasFlag(args, \"--install-skill\")) {\n const destination = path.join(path.dirname(absolute), \"skills\", \"apptrace\", \"SKILL.md\");\n fs.mkdirSync(path.dirname(destination), { recursive: true });\n if (fs.existsSync(destination)) fs.copyFileSync(destination, `${destination}.${Date.now()}.bak`);\n fs.copyFileSync(path.join(__dirname, \"skills\", \"apptrace\", \"SKILL.md\"), destination);\n }\n if (json) {\n process.stderr.write(`MCP config written: ${absolute}\\n`);\n return;\n }\n console.log(`MCP config written: ${absolute}`);\n }\n const escape = (value: string) => value.replace(/\\\\/g, \"\\\\\\\\\");\n const envDisplay = Object.entries(env)\n .map(([key, value]) => `${key} = \"${escape(value)}\"`)\n .join(\", \");\n console.log(`\nMCP configuration:\n\n[mcp_servers.apptrace]\ncommand = \"${escape(process.execPath)}\"\nargs = [\"${escape(serverEntry)}\"]\nenv = { ${envDisplay} }\n\nRestart the agent client to apply. Add --restart-client to let AppTrace restart it.\n`);\n if (configPath && hasFlag(args, \"--restart-client\")) {\n const client = clientForConfig(path.resolve(configPath));\n if (!client.app) {\n throw new CoreFailure(\n \"RESTART_UNSUPPORTED\",\n `${client.name} has no desktop process to restart; quit and reopen it yourself.`\n );\n }\n await restartClient(client.app);\n console.log(`Restarted ${client.name}.`);\n }\n}\n\nfunction clientForConfig(configPath: string): { name: string; app?: string } {\n const normalized = configPath.replace(/\\\\/g, \"/\").toLowerCase();\n if (normalized.includes(\"claude_desktop_config.json\")) {\n return { name: \"Claude Desktop\", app: \"Claude\" };\n }\n if (normalized.includes(\"/.codex/\")) return { name: \"Codex\", app: \"Codex\" };\n if (normalized.includes(\"/.cursor/\")) return { name: \"Cursor\", app: \"Cursor\" };\n if (normalized.endsWith(\".mcp.json\")) return { name: \"Claude Code\" };\n return { name: \"Agent client\" };\n}\n\nasync function restartClient(app: string): Promise<void> {\n if (process.platform !== \"darwin\") {\n throw new CoreFailure(\n \"RESTART_UNSUPPORTED\",\n \"Automatic client restart is only supported on macOS; quit and reopen the client yourself.\"\n );\n }\n await interactive(\"/usr/bin/osascript\", [\"-e\", `quit app \"${app}\"`]);\n await new Promise((resolve) => setTimeout(resolve, 1200));\n await interactive(\"/usr/bin/open\", [\"-a\", app]);\n}\n\n/**\n * Global installs nest workspace packages under the CLI package\n * (node_modules/@zhuixin/cli/node_modules/@zhuixin/mcp), so a fixed\n * ../../mcp path only works inside the monorepo.\n */\nfunction resolveMcpEntry(): string {\n try {\n return require.resolve(\"@zhuixin/mcp\");\n } catch {\n return path.resolve(__dirname, \"../../mcp/dist/index.js\");\n }\n}\n\nasync function authLogin(): Promise<void> {\n const config = loadConfig();\n const ipatool = resolveTool(config.ipatoolPath);\n if (!ipatool) {\n throw new CoreFailure(\n \"TOOL_NOT_INSTALLED\",\n `ipatool not found (${config.ipatoolPath}). Run apptrace setup --install first, then apptrace auth login.`\n );\n }\n await requireBackgroundCredentials(config);\n const code = await interactive(ipatool, [\"auth\", \"login\"]);\n if (code !== 0) {\n throw new CoreFailure(\n \"AUTH_FAILED\",\n `ipatool login failed with exit code ${code ?? \"unknown\"}`\n );\n }\n await authStatus(true);\n await recoverAnalysisTasks(config, __filename);\n}\n\nasync function authStatus(requireAuthenticated = false): Promise<void> {\n await withCore(async (core) => {\n const status = await core.handle(CHANNELS.accountStatus, { force: true }) as AccountStatus;\n printJson(status);\n if (requireAuthenticated && !status.authenticated) throw new CoreFailure(status.errorCode ?? \"BACKGROUND_AUTH_FAILED\", status.message ?? \"Background credential verification failed after sign-in.\");\n });\n}\n\nasync function addTarget(source: string): Promise<void> {\n await withCore(async (core) => {\n const resolved = (await core.handle(CHANNELS.targetsResolve, {\n input: source\n })) as { resolutionId: string };\n const target = (await core.handle(CHANNELS.targetsCreate, {\n resolutionId: resolved.resolutionId\n })) as TargetRecord;\n printJson(target);\n });\n}\n\nasync function listTargets(): Promise<void> {\n await withCore(async (core) => {\n printJson(await core.handle(CHANNELS.targetsList, {}));\n });\n}\n\nasync function analyze(source: string | undefined, args: string[]): Promise<void> {\n const goals = parseGoals(option(args, \"--goals\"));\n const json = hasFlag(args, \"--json\");\n const force = hasFlag(args, \"--force\");\n const targetId = option(args, \"--target\");\n if (!source && !targetId) {\n throw new CoreFailure(\"SOURCE_REQUIRED\", \"Missing app link, ID, or --target.\");\n }\n await withCore(async (core) => {\n const service = new AppAnalysisService(core);\n const result = await service.analyzeApp({\n source: targetId ? undefined : source,\n targetId,\n goals,\n force,\n onProgress: (event) => {\n if (!json) {\n process.stderr.write(\n `[${event.step}] ${event.status}${\n event.message ? ` — ${event.message}` : \"\"\n }\\n`\n );\n }\n }\n });\n if (json) {\n printJson(result);\n } else {\n printAnalysis(result);\n }\n });\n}\n\nfunction printAnalysis(result: AnalyzeAppResult): void {\n console.log(`\\n${result.targetName}`);\n console.log(`Target: ${result.targetId}`);\n console.log(`Artifact: ${result.artifact.sha256}`);\n console.log(\n `Technology: ${result.technology?.hints.join(\" / \") || \"unknown\"}`\n );\n console.log(`Routes: ${result.routes?.count ?? 0}`);\n console.log(`Modules: ${result.modules?.count ?? 0}`);\n console.log(`Network endpoints: ${result.networkEndpoints?.count ?? 0}`);\n if (result.native) {\n console.log(\n `Native: ${result.native.procedureCount ?? \"?\"} procedures, ${\n result.native.stringCount ?? \"?\"\n } strings`\n );\n if (result.native.warning) console.log(`Warning: ${result.native.warning}`);\n }\n if (result.reportPath) console.log(`Report: ${result.reportPath}`);\n for (const evidencePath of result.evidencePaths) {\n console.log(`Evidence: ${evidencePath}`);\n }\n if (result.limitations.length > 0) {\n console.log(\"Limitations:\");\n for (const limitation of result.limitations.slice(0, 6)) {\n console.log(` - ${limitation}`);\n }\n }\n}\n\nasync function report(targetId: string): Promise<void> {\n await withCore(async (core) => {\n const service = new AppAnalysisService(core);\n printJson(await service.runCapability(targetId, \"generate:report\"));\n });\n}\n\nasync function getJob(jobId: string): Promise<void> {\n await withCore(async (core) => {\n const result = (await core.handle(CHANNELS.jobsGet, { jobId })) as {\n job: JobRecord;\n events: unknown[];\n };\n printJson({ ...result, events: result.events.slice(-50) });\n });\n}\n\nasync function searchFindings(\n targetId: string,\n query: string | undefined,\n args: string[]\n): Promise<void> {\n const types = option(args, \"--type\")\n ?.split(\",\")\n .map((value) => value.trim())\n .filter(Boolean);\n const limit = Number(option(args, \"--limit\") ?? 50);\n await withCore(async (core) => {\n printJson(\n await core.handle(CHANNELS.findingsSearch, {\n targetId,\n query,\n types,\n limit: Number.isFinite(limit) ? limit : 50\n })\n );\n });\n}\n\nasync function nativeCommand(\n action: string | undefined,\n args: string[]\n): Promise<void> {\n const targetId = args[0];\n const value = args[1];\n if (!targetId || !value) {\n throw new CoreFailure(\n \"INVALID_INPUT\",\n \"Usage: apptrace native <search|decompile> <target-id> <query-or-function>\"\n );\n }\n await withCore(async (core) => {\n const service = new AppAnalysisService(core);\n if (action === \"search\") {\n printJson(\n await service.runCapability(targetId, \"native:search\", {\n query: value,\n kind: option(args, \"--kind\") ?? \"procedures\",\n mode: option(args, \"--mode\") ?? \"literal\"\n })\n );\n return;\n }\n if (action === \"decompile\") {\n printJson(\n await service.runCapability(targetId, \"native:decompile\", {\n functionName: value\n })\n );\n return;\n }\n throw new CoreFailure(\n \"INVALID_INPUT\",\n \"Native commands support only search or decompile.\"\n );\n });\n}\n\nasync function evidence(targetId: string): Promise<void> {\n await withCore(async (core) => {\n printJson(\n await core.handle(CHANNELS.evidenceList, {\n targetId\n })\n );\n });\n}\n\nasync function installEngine(file: string, args: string[]): Promise<void> {\n const config = loadConfig();\n const expected = option(args, \"--sha256\");\n const installed = installEngineArtifact(config.workspaceRoot, file, expected);\n printJson(installed);\n}\n\nasync function compare(\n targetId: string | undefined,\n leftArtifactId: string | undefined,\n rightArtifactId: string | undefined,\n args: string[]\n): Promise<void> {\n if (!targetId || !leftArtifactId || !rightArtifactId) {\n throw new CoreFailure(\n \"INVALID_INPUT\",\n \"Usage: apptrace compare <target-id> <left-artifact-id> <right-artifact-id>\"\n );\n }\n await withCore(async (core) => {\n printJson(\n await new VersionComparisonService(core).compare(\n targetId,\n leftArtifactId,\n rightArtifactId,\n { force: hasFlag(args, \"--force\") }\n )\n );\n });\n}\n\nasync function capabilities(targetId?: string): Promise<void> {\n await withCore(async (core) => {\n printJson(\n await core.handle(CHANNELS.capabilitiesList, {\n ...(targetId ? { targetId } : {})\n })\n );\n });\n}\n\nasync function main(): Promise<void> {\n const [command, subcommand, ...args] = process.argv.slice(2);\n if (!command || [\"help\", \"-h\", \"--help\"].includes(command)) {\n usage();\n return;\n }\n if (command === \"analysis-worker\") {\n if (!subcommand || !/^[0-9a-f-]{36}$/.test(subcommand)) throw new CoreFailure(\"INVALID_INPUT\", \"Invalid analysis task ID.\");\n return runAnalysisWorker(loadConfig(), subcommand, __filename);\n }\n if (command === \"setup-worker\") {\n if (!subcommand || !/^[0-9a-f-]{36}$/.test(subcommand)) throw new CoreFailure(\"INVALID_INPUT\", \"Invalid install operation id.\");\n const config = loadConfig();\n return runInstallation(config, () => installTools(config), subcommand);\n }\n if (command === \"setup\") {\n const setupArgs =\n subcommand?.startsWith(\"--\") && subcommand\n ? [subcommand, ...args]\n : args;\n return setup(hasFlag(setupArgs, \"--json\"), setupArgs);\n }\n if (command === \"doctor\") {\n const doctorArgs =\n subcommand?.startsWith(\"--\") && subcommand\n ? [subcommand, ...args]\n : args;\n return doctor(hasFlag(doctorArgs, \"--json\"));\n }\n if (command === \"auth\") {\n if (subcommand === \"open\") {\n printJson(await new OnboardingService(loadConfig(), __filename).openLogin());\n return;\n }\n if (subcommand === \"login\") return authLogin();\n if (subcommand === \"status\") return authStatus();\n }\n if (command === \"targets\") {\n if (subcommand === \"add\") {\n const source = positional(args);\n if (!source) throw new CoreFailure(\"SOURCE_REQUIRED\", \"Missing app link or ID.\");\n return addTarget(source);\n }\n if (subcommand === \"list\") return listTargets();\n }\n if (command === \"analyze\") {\n const analyzeArgs =\n subcommand?.startsWith(\"--\") && subcommand\n ? [subcommand, ...args]\n : args;\n const source =\n subcommand && !subcommand.startsWith(\"--\")\n ? subcommand\n : positional(analyzeArgs);\n return analyze(source, analyzeArgs);\n }\n if (command === \"report\") {\n if (!subcommand) throw new CoreFailure(\"TARGET_REQUIRED\", \"Missing target ID.\");\n return report(subcommand);\n }\n if (command === \"jobs\" && subcommand === \"get\") {\n const jobId = positional(args);\n if (!jobId) throw new CoreFailure(\"JOB_REQUIRED\", \"Missing job ID.\");\n return getJob(jobId);\n }\n if (command === \"search\") {\n if (!subcommand) throw new CoreFailure(\"TARGET_REQUIRED\", \"Missing target ID.\");\n return searchFindings(subcommand, args[0], args);\n }\n if (command === \"native\") return nativeCommand(subcommand, args);\n if (command === \"evidence\") {\n if (!subcommand) throw new CoreFailure(\"TARGET_REQUIRED\", \"Missing target ID.\");\n return evidence(subcommand);\n }\n if (command === \"engine\" && subcommand === \"install\") {\n const file = args[0];\n if (!file) throw new CoreFailure(\"ENGINE_REQUIRED\", \"Missing engine file path.\");\n return installEngine(file, args);\n }\n if (command === \"compare\") {\n if (!subcommand) throw new CoreFailure(\"TARGET_REQUIRED\", \"Missing target ID.\");\n return compare(subcommand, args[0], args[1], args);\n }\n if (command === \"capabilities\") return capabilities(subcommand);\n if (command === \"mcp\") {\n process.env.APPTRACE_CLI_PATH = __filename;\n const { startMcpServer } = await import(\"@zhuixin/mcp\");\n await startMcpServer(loadConfig());\n return;\n }\n usage();\n throw new CoreFailure(\"UNKNOWN_COMMAND\", `Unknown command: ${command}`);\n}\n\nvoid main().catch((error) => {\n printError(error);\n process.exitCode = 1;\n});\n","{\n \"name\": \"@zhuixin/cli\",\n \"version\": \"0.2.5\",\n \"license\": \"Apache-2.0\",\n \"description\": \"AppTrace CLI for evidence-backed application analysis\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/zhuixin-labs/app-evidence.git\",\n \"directory\": \"packages/cli\"\n },\n \"homepage\": \"https://github.com/zhuixin-labs/app-evidence\",\n \"bugs\": {\"url\": \"https://github.com/zhuixin-labs/app-evidence/issues\"},\n \"keywords\": [\"mcp\", \"ai-agents\", \"app-analysis\", \"static-analysis\", \"ios\", \"ipa\"],\n \"publishConfig\": {\"access\": \"public\"},\n \"engines\": {\"node\": \">=22.19\"},\n \"main\": \"dist/index.js\",\n \"bin\": {\n \"apptrace\": \"dist/index.js\"\n },\n \"files\": [\"dist\"],\n \"scripts\": {\n \"build\": \"tsup\",\n \"typecheck\": \"tsc --noEmit -p tsconfig.json\"\n },\n \"dependencies\": {\n \"@zhuixin/metadata\": \"workspace:*\",\n \"@zhuixin/mcp\": \"workspace:*\",\n \"@zhuixin/shared\": \"workspace:*\",\n \"rea-agents\": \"6.4.0\",\n \"smol-toml\": \"^1.9.0\"\n },\n \"devDependencies\": {\n \"tsup\": \"^8.5.0\"\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { parse, stringify } from \"smol-toml\";\n\ninterface ServerConfig {\n command: string;\n args: string[];\n env: Record<string, string>;\n}\n\nfunction object(value: unknown): Record<string, any> {\n if (value === undefined) return {};\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new Error(\"MCP configuration must contain objects/tables.\");\n }\n return value as Record<string, any>;\n}\n\nexport function writeMcpConfig(filename: string, server: ServerConfig): void {\n const extension = path.extname(filename).toLowerCase();\n if (![\".json\", \".toml\"].includes(extension)) {\n throw new Error(\"--write-config accepts only .json or .toml files.\");\n }\n const original = fs.existsSync(filename) ? fs.readFileSync(filename, \"utf8\") : undefined;\n const config = object(original?.trim()\n ? extension === \".json\" ? JSON.parse(original) : parse(original)\n : {});\n const key = extension === \".json\" ? \"mcpServers\" : \"mcp_servers\";\n const servers = object(config[key]);\n const previous = object(servers.apptrace);\n const environment = { ...object(previous.env), ...server.env };\n for (const [key, placeholder] of [[\"APPTRACE_IPATOOL_PATH\", \"ipatool\"], [\"APPTRACE_ANALYSIS_ENGINE_PATH\", \"rea\"]]) {\n if (!server.env[key] && environment[key] === placeholder) delete environment[key];\n }\n servers.apptrace = { ...previous, ...server, env: environment };\n config[key] = servers;\n const output = extension === \".json\" ? JSON.stringify(config, null, 2) + \"\\n\" : stringify(config);\n fs.mkdirSync(path.dirname(filename), { recursive: true, mode: 0o700 });\n // Keep the original text (including TOML comments) recoverable before serialization.\n if (original !== undefined) {\n fs.writeFileSync(`${filename}.${randomUUID()}.bak`, original, { mode: 0o600, flag: \"wx\" });\n }\n const temporary = `${filename}.${randomUUID()}.tmp`;\n try {\n fs.writeFileSync(temporary, output, { mode: 0o600, flag: \"wx\" });\n fs.renameSync(temporary, filename);\n } finally {\n if (fs.existsSync(temporary)) fs.unlinkSync(temporary);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AACA,gCAAsB;AACtB,IAAAA,kBAAe;AACf,IAAAC,oBAAiB;AACjB,sBAiBO;AACP,oBAOO;;;AC3BL,cAAW;;;ACFb,qBAAe;AACf,uBAAiB;AACjB,yBAA2B;AAC3B,uBAAiC;AAQjC,SAAS,OAAO,OAAqC;AACnD,MAAI,UAAU,OAAW,QAAO,CAAC;AACjC,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;AAC/D,UAAM,IAAI,MAAM,gDAAgD;AAAA,EAClE;AACA,SAAO;AACT;AAEO,SAAS,eAAe,UAAkB,QAA4B;AAC3E,QAAM,YAAY,iBAAAC,QAAK,QAAQ,QAAQ,EAAE,YAAY;AACrD,MAAI,CAAC,CAAC,SAAS,OAAO,EAAE,SAAS,SAAS,GAAG;AAC3C,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AACA,QAAM,WAAW,eAAAC,QAAG,WAAW,QAAQ,IAAI,eAAAA,QAAG,aAAa,UAAU,MAAM,IAAI;AAC/E,QAAM,SAAS,OAAO,UAAU,KAAK,IACjC,cAAc,UAAU,KAAK,MAAM,QAAQ,QAAI,wBAAM,QAAQ,IAC7D,CAAC,CAAC;AACN,QAAM,MAAM,cAAc,UAAU,eAAe;AACnD,QAAM,UAAU,OAAO,OAAO,GAAG,CAAC;AAClC,QAAM,WAAW,OAAO,QAAQ,QAAQ;AACxC,QAAM,cAAc,EAAE,GAAG,OAAO,SAAS,GAAG,GAAG,GAAG,OAAO,IAAI;AAC7D,aAAW,CAACC,MAAK,WAAW,KAAK,CAAC,CAAC,yBAAyB,SAAS,GAAG,CAAC,iCAAiC,KAAK,CAAC,GAAG;AACjH,QAAI,CAAC,OAAO,IAAIA,IAAG,KAAK,YAAYA,IAAG,MAAM,YAAa,QAAO,YAAYA,IAAG;AAAA,EAClF;AACA,UAAQ,WAAW,EAAE,GAAG,UAAU,GAAG,QAAQ,KAAK,YAAY;AAC9D,SAAO,GAAG,IAAI;AACd,QAAM,SAAS,cAAc,UAAU,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,WAAO,4BAAU,MAAM;AAChG,iBAAAD,QAAG,UAAU,iBAAAD,QAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAErE,MAAI,aAAa,QAAW;AAC1B,mBAAAC,QAAG,cAAc,GAAG,QAAQ,QAAI,+BAAW,CAAC,QAAQ,UAAU,EAAE,MAAM,KAAO,MAAM,KAAK,CAAC;AAAA,EAC3F;AACA,QAAM,YAAY,GAAG,QAAQ,QAAI,+BAAW,CAAC;AAC7C,MAAI;AACF,mBAAAA,QAAG,cAAc,WAAW,QAAQ,EAAE,MAAM,KAAO,MAAM,KAAK,CAAC;AAC/D,mBAAAA,QAAG,WAAW,WAAW,QAAQ;AAAA,EACnC,UAAE;AACA,QAAI,eAAAA,QAAG,WAAW,SAAS,EAAG,gBAAAA,QAAG,WAAW,SAAS;AAAA,EACvD;AACF;;;AFhBA,IAAM,QAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,QAAc;AACrB,UAAQ,IAAI,YAAY,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CA0BhC;AACD;AAEA,SAAS,OAAO,MAAgB,MAAkC;AAChE,QAAM,QAAQ,KAAK,QAAQ,IAAI;AAC/B,MAAI,SAAS,KAAK,KAAK,QAAQ,CAAC,EAAG,QAAO,KAAK,QAAQ,CAAC;AACxD,QAAM,SAAS,GAAG,IAAI;AACtB,QAAM,SAAS,KAAK,KAAK,CAAC,QAAQ,IAAI,WAAW,MAAM,CAAC;AACxD,SAAO,SAAS,OAAO,MAAM,OAAO,MAAM,IAAI;AAChD;AAEA,SAAS,QAAQ,MAAgB,MAAuB;AACtD,SAAO,KAAK,SAAS,IAAI;AAC3B;AAEA,SAAS,WAAW,MAAoC;AACtD,SAAO,KAAK,KAAK,CAAC,QAAQ,CAAC,IAAI,WAAW,IAAI,CAAC;AACjD;AAEA,SAAS,WAAW,OAA2C;AAC7D,MAAI,CAAC,MAAO,QAAO,CAAC,cAAc,eAAe,QAAQ;AACzD,QAAM,QAAQ,MACX,MAAM,GAAG,EACT,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EACzB,OAAO,OAAO;AACjB,QAAM,UAAU,MAAM;AAAA,IACpB,CAAC,SAAyB,CAAC,MAAM,SAAS,IAAoB;AAAA,EAChE;AACA,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,IAAI;AAAA,MACR;AAAA,MACA,0BAA0B,QAAQ,KAAK,IAAI,CAAC,gBAAgB,MAAM,KAAK,IAAI,CAAC;AAAA,IAC9E;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,UAAU,OAAsB;AACvC,UAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,CAAI;AAC5D;AAEA,SAAS,WAAW,OAAsB;AACxC,MAAI,iBAAiB,6BAAa;AAChC,YAAQ,MAAM,GAAG,MAAM,IAAI,KAAK,MAAM,OAAO,EAAE;AAC/C,QAAI,MAAM,SAAS,mBAAmB,MAAM,SAAS,mBAAmB;AACtE,cAAQ,MAAM,2BAA2B;AAAA,IAC3C,WAAW,MAAM,SAAS,oBAAoB;AAC5C,cAAQ,MAAM,6DAA6D;AAAA,IAC7E;AACA;AAAA,EACF;AACA,UAAQ,MAAM,iBAAiB,QAAQ,MAAM,SAAS,MAAM,UAAU,OAAO,KAAK,CAAC;AACrF;AAEA,SAAS,SAAY,WAAyD;AAC5E,QAAM,OAAO,IAAI,2BAAW,EAAE,YAAQ,4BAAW,GAAG,MAAM,MAAM;AAAA,EAAC,EAAE,CAAC;AACpE,SAAO,UAAU,IAAI,EAAE,QAAQ,MAAM,KAAK,MAAM,CAAC;AACnD;AAEA,SAAS,YACP,SACA,MACA,KACA,SACwB;AACxB,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,mBACJ,QAAQ,aAAa,WACrB,CAAC,QAAQ,MAAM,EAAE,SAAS,kBAAAE,QAAK,QAAQ,OAAO,EAAE,YAAY,CAAC;AAC/D,UAAM,QAAQ,uBACV;AAAA,MACE,QAAQ,IAAI,WAAW;AAAA,MACvB,CAAC,MAAM,MAAM,MAAM,IAAI,OAAO,KAAK,KAAK,KAAK,GAAG,CAAC,EAAE;AAAA,MACnD;AAAA,QACE,OAAO;AAAA,QACP,0BAA0B;AAAA,QAC1B,KAAK,MAAM,EAAE,GAAG,QAAQ,KAAK,GAAG,IAAI,IAAI,QAAQ;AAAA,MAClD;AAAA,IACF,QACA,iCAAM,SAAS,MAAM;AAAA,MACnB,OAAO;AAAA,MACP,KAAK,MAAM,EAAE,GAAG,QAAQ,KAAK,GAAG,IAAI,IAAI,QAAQ;AAAA,IAClD,CAAC;AACL,QAAI,MAAM,OAAO,SAAS;AACxB,UAAI;AAAE,gBAAQ,MAAM,GAAG;AAAA,MAAG,SACnB,OAAO;AAAE,cAAM,KAAK;AAAG,eAAO,KAAK;AAAG;AAAA,MAAQ;AAAA,IACvD;AACA,UAAM,GAAG,SAAS,MAAM;AACxB,UAAM,GAAG,QAAQ,CAAC,SAAS,QAAQ,IAAI,CAAC;AAAA,EAC1C,CAAC;AACH;AAEA,eAAe,OAAO,MAA8B;AAClD,QAAM,aAAS,4BAAW;AAC1B,QAAM,SAAS,OAAO,SAAS;AAC7B,UAAM,SAAU,MAAM,KAAK,OAAO,uBAAS,QAAQ,CAAC,CAAC;AACrD,UAAMC,gBAAgB,MAAM,KAAK;AAAA,MAC/B,uBAAS;AAAA,MACT,CAAC;AAAA,IACH;AACA,UAAM,SAAS,EAAE,QAAQ,QAAQ,cAAAA,cAAa;AAC9C,QAAI,MAAM;AACR,gBAAU,MAAM;AAChB;AAAA,IACF;AACA,YAAQ,IAAI,cAAc,OAAO,aAAa,EAAE;AAChD,YAAQ,IAAI,cAAc,OAAO,YAAY,EAAE;AAC/C,YAAQ,IAAI,cAAc,OAAO,KAAK,EAAE;AACxC,YAAQ,IAAI,QAAQ;AACpB,eAAW,QAAQ,OAAO,SAAS,CAAC,GAAG;AACrC,cAAQ;AAAA,QACN,KAAK,KAAK,YAAY,SAAS,MAAM,IAAI,KAAK,KAAK,KAAK,KAAK,IAAI;AAAA,MACnE;AAAA,IACF;AACA,YAAQ,IAAI,eAAe;AAC3B,eAAW,cAAcA,eAAc;AACrC,cAAQ;AAAA,QACN,KAAK,WAAW,YAAY,SAAS,MAAM,IAAI,WAAW,EAAE,GAC1D,WAAW,SAAS,WAAM,WAAW,MAAM,KAAK,EAClD;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,SAAS,qBAAqB,eAA+B;AAC3D,QAAM,YAAY,gBAAgB,4BAA4B;AAC9D,QAAM,QAAQ,kBAAAD,QAAK,KAAK,eAAe,OAAO;AAC9C,QAAM,WAAW,kBAAAA,QAAK,KAAK,OAAO,KAAK;AACvC,QAAM,aAAa,kBAAAA,QAAK,KAAK,OAAO,sBAAsB;AAC1D,QAAM,iBAAiB,kBAAAA,QAAK,KAAK,eAAe,cAAc;AAC9D,kBAAAE,QAAG,UAAU,kBAAAF,QAAK,KAAK,OAAO,KAAK,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACtE,kBAAAE,QAAG,UAAU,gBAAgB,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC7D,QAAM,WAAW,kBAAAF,QAAK;AAAA,IACpB;AAAA,IACA;AAAA,IACA,QAAQ,aAAa,UAAU,YAAY;AAAA,EAC7C;AACA,MAAI,QAAQ,aAAa,SAAS;AAChC,oBAAAE,QAAG;AAAA,MACD;AAAA,MACA;AAAA,QACE;AAAA,QACA,kBAAkB,QAAQ;AAAA,QAC1B,2BAA2B,UAAU;AAAA,QACrC,yBAAyB,cAAc;AAAA,QACvC;AAAA,QACA,IAAI,QAAQ,QAAQ,MAAM,SAAS;AAAA,QACnC;AAAA,MACF,EAAE,KAAK,MAAM;AAAA,MACb,EAAE,MAAM,IAAM;AAAA,IAChB;AAAA,EACF,OAAO;AACL,oBAAAA,QAAG;AAAA,MACD;AAAA,MACA;AAAA,QACE;AAAA,QACA;AAAA,QACA,oBAAoB,KAAK,UAAU,QAAQ,CAAC;AAAA,QAC5C,6BAA6B,KAAK,UAAU,UAAU,CAAC;AAAA,QACvD,2BAA2B,KAAK,UAAU,cAAc,CAAC;AAAA,QACzD;AAAA,QACA;AAAA,QACA,QAAQ,KAAK,UAAU,QAAQ,QAAQ,CAAC,IAAI,KAAK,UAAU,SAAS,CAAC;AAAA,QACrE;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,MACX,EAAE,MAAM,IAAM;AAAA,IAChB;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAe,aACb,QACA,OAAO,OACQ;AACf,MAAI,UAAM,iCAAgB,MAAM,GAAG;AACjC,UAAM,kBAAkB,kBAAAF,QAAK,KAAK,OAAO,eAAe,SAAS,OAAO,QAAQ,aAAa,UAAU,YAAY,KAAK;AACxH,QAAI,kBAAAA,QAAK,QAAQ,OAAO,kBAAkB,MAAM,iBAAiB;AAC/D,2BAAqB,OAAO,aAAa;AAAA,IAC3C;AACA,YAAQ,OAAO,MAAM,8CAA8C;AACnE;AAAA,EACF;AACA,MAAI,QAAQ,aAAa,WAAW,QAAQ,SAAS,OAAO;AAC1D,UAAMG,UAAS,kBAAAH,QAAK,QAAQ,WAAW,6BAA6B;AACpE,QAAI,CAAC,gBAAAE,QAAG,WAAWC,OAAM,GAAG;AAC1B,YAAM,IAAI,4BAAY,0BAA0B,+BAA+BA,OAAM,EAAE;AAAA,IACzF;AACA,YAAQ,OAAO;AAAA,MACb;AAAA,IACF;AACA,UAAMC,QAAO,MAAM,YAAY,kBAAkB;AAAA,MAC/C;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACAD;AAAA,MACA;AAAA,MACA,OAAO;AAAA,IACT,GAAG;AAAA;AAAA,MAED,cAAc,kBAAAH,QAAK,KAAK,QAAQ,IAAI,cAAc,eAAe,YAAY,qBAAqB,QAAQ,SAAS;AAAA,MACnH,yBAAyB,OAAO;AAAA,MAChC,oBAAoB,kBAAAA,QAAK,KAAK,OAAO,eAAe,OAAO;AAAA,IAC7D,GAAG,CAAC,YAAQ,wCAAuB,QAAQ,GAAG,CAAC;AAC/C,QAAII,UAAS,GAAG;AACd,YAAM,IAAI;AAAA,QACR;AAAA,QACA,2CAA2CA,SAAQ,SAAS;AAAA,MAC9D;AAAA,IACF;AACA,UAAMC,YAAW,qBAAqB,OAAO,aAAa;AAC1D,YAAQ,OAAO,MAAM,6BAA6BA,SAAQ;AAAA,CAAI;AAC9D;AAAA,EACF;AACA,MAAI,QAAQ,aAAa,YAAY,QAAQ,SAAS,SAAS;AAC7D,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,QAAM,SAAS,kBAAAL,QAAK,QAAQ,WAAW,4BAA4B;AACnE,MAAI,CAAC,gBAAAE,QAAG,WAAW,MAAM,GAAG;AAC1B,UAAM,IAAI,4BAAY,0BAA0B,+BAA+B,MAAM,EAAE;AAAA,EACzF;AACA,UAAQ,OAAO;AAAA,IACb;AAAA,EACF;AACA,QAAM,OAAO,MAAM,YAAY,aAAa,CAAC,MAAM,GAAG;AAAA,IACpD,yBAAyB,OAAO;AAAA,IAChC,oBAAoB,kBAAAF,QAAK,KAAK,OAAO,eAAe,OAAO;AAAA,EAC7D,GAAG,CAAC,YAAQ,wCAAuB,QAAQ,GAAG,CAAC;AAC/C,MAAI,SAAS,GAAG;AACd,UAAM,IAAI;AAAA,MACR;AAAA,MACA,2CAA2C,QAAQ,SAAS;AAAA,IAC9D;AAAA,EACF;AACA,QAAM,WAAW,qBAAqB,OAAO,aAAa;AAC1D,UAAQ,OAAO,MAAM,6BAA6B,QAAQ;AAAA,CAAI;AAChE;AAEA,eAAe,MAAM,MAAe,MAA+B;AACjE,QAAM,aAAa,OAAO,MAAM,gBAAgB;AAChD,MAAI,QAAQ,MAAM,iBAAiB,KAAK,CAAC,WAAY,OAAM,IAAI,4BAAY,mBAAmB,0CAA0C;AACxI,MAAI,aAAS,4BAAW;AACxB,MAAI,QAAQ,MAAM,WAAW,GAAG;AAC9B,cAAM,iCAAgB,QAAQ,MAAM,aAAa,QAAQ,IAAI,CAAC;AAC9D,iBAAS,4BAAW;AAAA,EACtB;AACA,QAAM,OAAO,IAAI;AACjB,MAAI,QAAQ,CAAC,WAAY;AACzB,QAAM,cAAc,gBAAgB;AACpC,QAAM,MAA8B;AAAA,IAClC,yBAAyB,OAAO;AAAA,IAChC,mBAAmB;AAAA,EACrB;AACA,QAAM,cAAU,6BAAY,OAAO,WAAW;AAC9C,MAAI,QAAS,KAAI,wBAAwB;AACzC,MAAI,gBAAAE,QAAG,WAAW,OAAO,UAAU,GAAG;AACpC,QAAI,uBAAuB,OAAO;AAAA,EACpC;AAGA,MAAI,gBAAAA,QAAG,WAAW,OAAO,kBAAkB,GAAG;AAC5C,QAAI,gCAAgC,OAAO;AAAA,EAC7C;AACA,MAAI,YAAY;AACd,UAAM,WAAW,kBAAAF,QAAK,QAAQ,UAAU;AACxC,mBAAe,UAAU,EAAE,SAAS,QAAQ,UAAU,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC;AAChF,QAAI,QAAQ,MAAM,iBAAiB,GAAG;AACpC,YAAM,cAAc,kBAAAA,QAAK,KAAK,kBAAAA,QAAK,QAAQ,QAAQ,GAAG,UAAU,YAAY,UAAU;AACtF,sBAAAE,QAAG,UAAU,kBAAAF,QAAK,QAAQ,WAAW,GAAG,EAAE,WAAW,KAAK,CAAC;AAC3D,UAAI,gBAAAE,QAAG,WAAW,WAAW,EAAG,iBAAAA,QAAG,aAAa,aAAa,GAAG,WAAW,IAAI,KAAK,IAAI,CAAC,MAAM;AAC/F,sBAAAA,QAAG,aAAa,kBAAAF,QAAK,KAAK,WAAW,UAAU,YAAY,UAAU,GAAG,WAAW;AAAA,IACrF;AACA,QAAI,MAAM;AACR,cAAQ,OAAO,MAAM,uBAAuB,QAAQ;AAAA,CAAI;AACxD;AAAA,IACF;AACA,YAAQ,IAAI,uBAAuB,QAAQ,EAAE;AAAA,EAC/C;AACA,QAAM,SAAS,CAAC,UAAkB,MAAM,QAAQ,OAAO,MAAM;AAC7D,QAAM,aAAa,OAAO,QAAQ,GAAG,EAClC,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,GAAG,OAAO,OAAO,KAAK,CAAC,GAAG,EACnD,KAAK,IAAI;AACZ,UAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,aAID,OAAO,QAAQ,QAAQ,CAAC;AAAA,WAC1B,OAAO,WAAW,CAAC;AAAA,UACpB,UAAU;AAAA;AAAA;AAAA,CAGnB;AACC,MAAI,cAAc,QAAQ,MAAM,kBAAkB,GAAG;AACnD,UAAM,SAAS,gBAAgB,kBAAAA,QAAK,QAAQ,UAAU,CAAC;AACvD,QAAI,CAAC,OAAO,KAAK;AACf,YAAM,IAAI;AAAA,QACR;AAAA,QACA,GAAG,OAAO,IAAI;AAAA,MAChB;AAAA,IACF;AACA,UAAM,cAAc,OAAO,GAAG;AAC9B,YAAQ,IAAI,aAAa,OAAO,IAAI,GAAG;AAAA,EACzC;AACF;AAEA,SAAS,gBAAgB,YAAoD;AAC3E,QAAM,aAAa,WAAW,QAAQ,OAAO,GAAG,EAAE,YAAY;AAC9D,MAAI,WAAW,SAAS,4BAA4B,GAAG;AACrD,WAAO,EAAE,MAAM,kBAAkB,KAAK,SAAS;AAAA,EACjD;AACA,MAAI,WAAW,SAAS,UAAU,EAAG,QAAO,EAAE,MAAM,SAAS,KAAK,QAAQ;AAC1E,MAAI,WAAW,SAAS,WAAW,EAAG,QAAO,EAAE,MAAM,UAAU,KAAK,SAAS;AAC7E,MAAI,WAAW,SAAS,WAAW,EAAG,QAAO,EAAE,MAAM,cAAc;AACnE,SAAO,EAAE,MAAM,eAAe;AAChC;AAEA,eAAe,cAAc,KAA4B;AACvD,MAAI,QAAQ,aAAa,UAAU;AACjC,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,QAAM,YAAY,sBAAsB,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC;AACnE,QAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,IAAI,CAAC;AACxD,QAAM,YAAY,iBAAiB,CAAC,MAAM,GAAG,CAAC;AAChD;AAOA,SAAS,kBAA0B;AACjC,MAAI;AACF,WAAO,gBAAgB,cAAc;AAAA,EACvC,QAAQ;AACN,WAAO,kBAAAA,QAAK,QAAQ,WAAW,yBAAyB;AAAA,EAC1D;AACF;AAEA,eAAe,YAA2B;AACxC,QAAM,aAAS,4BAAW;AAC1B,QAAM,cAAU,6BAAY,OAAO,WAAW;AAC9C,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,MACA,sBAAsB,OAAO,WAAW;AAAA,IAC1C;AAAA,EACF;AACA,YAAM,8CAA6B,MAAM;AACzC,QAAM,OAAO,MAAM,YAAY,SAAS,CAAC,QAAQ,OAAO,CAAC;AACzD,MAAI,SAAS,GAAG;AACd,UAAM,IAAI;AAAA,MACR;AAAA,MACA,uCAAuC,QAAQ,SAAS;AAAA,IAC1D;AAAA,EACF;AACA,QAAM,WAAW,IAAI;AACrB,YAAM,sCAAqB,QAAQ,UAAU;AAC/C;AAEA,eAAe,WAAW,uBAAuB,OAAsB;AACrE,QAAM,SAAS,OAAO,SAAS;AAC7B,UAAM,SAAS,MAAM,KAAK,OAAO,uBAAS,eAAe,EAAE,OAAO,KAAK,CAAC;AACxE,cAAU,MAAM;AAChB,QAAI,wBAAwB,CAAC,OAAO,cAAe,OAAM,IAAI,4BAAY,OAAO,aAAa,0BAA0B,OAAO,WAAW,0DAA0D;AAAA,EACrM,CAAC;AACH;AAEA,eAAe,UAAU,QAA+B;AACtD,QAAM,SAAS,OAAO,SAAS;AAC7B,UAAM,WAAY,MAAM,KAAK,OAAO,uBAAS,gBAAgB;AAAA,MAC3D,OAAO;AAAA,IACT,CAAC;AACD,UAAM,SAAU,MAAM,KAAK,OAAO,uBAAS,eAAe;AAAA,MACxD,cAAc,SAAS;AAAA,IACzB,CAAC;AACD,cAAU,MAAM;AAAA,EAClB,CAAC;AACH;AAEA,eAAe,cAA6B;AAC1C,QAAM,SAAS,OAAO,SAAS;AAC7B,cAAU,MAAM,KAAK,OAAO,uBAAS,aAAa,CAAC,CAAC,CAAC;AAAA,EACvD,CAAC;AACH;AAEA,eAAe,QAAQ,QAA4B,MAA+B;AAChF,QAAM,QAAQ,WAAW,OAAO,MAAM,SAAS,CAAC;AAChD,QAAM,OAAO,QAAQ,MAAM,QAAQ;AACnC,QAAM,QAAQ,QAAQ,MAAM,SAAS;AACrC,QAAM,WAAW,OAAO,MAAM,UAAU;AACxC,MAAI,CAAC,UAAU,CAAC,UAAU;AACxB,UAAM,IAAI,4BAAY,mBAAmB,oCAAoC;AAAA,EAC/E;AACA,QAAM,SAAS,OAAO,SAAS;AAC7B,UAAM,UAAU,IAAI,mCAAmB,IAAI;AAC3C,UAAM,SAAS,MAAM,QAAQ,WAAW;AAAA,MACtC,QAAQ,WAAW,SAAY;AAAA,MAC/B;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,CAAC,UAAU;AACrB,YAAI,CAAC,MAAM;AACT,kBAAQ,OAAO;AAAA,YACb,IAAI,MAAM,IAAI,KAAK,MAAM,MAAM,GAC7B,MAAM,UAAU,WAAM,MAAM,OAAO,KAAK,EAC1C;AAAA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AACD,QAAI,MAAM;AACR,gBAAU,MAAM;AAAA,IAClB,OAAO;AACL,oBAAc,MAAM;AAAA,IACtB;AAAA,EACF,CAAC;AACH;AAEA,SAAS,cAAc,QAAgC;AACrD,UAAQ,IAAI;AAAA,EAAK,OAAO,UAAU,EAAE;AACpC,UAAQ,IAAI,WAAW,OAAO,QAAQ,EAAE;AACxC,UAAQ,IAAI,aAAa,OAAO,SAAS,MAAM,EAAE;AACjD,UAAQ;AAAA,IACN,eAAe,OAAO,YAAY,MAAM,KAAK,KAAK,KAAK,SAAS;AAAA,EAClE;AACA,UAAQ,IAAI,WAAW,OAAO,QAAQ,SAAS,CAAC,EAAE;AAClD,UAAQ,IAAI,YAAY,OAAO,SAAS,SAAS,CAAC,EAAE;AACpD,UAAQ,IAAI,sBAAsB,OAAO,kBAAkB,SAAS,CAAC,EAAE;AACvE,MAAI,OAAO,QAAQ;AACjB,YAAQ;AAAA,MACN,WAAW,OAAO,OAAO,kBAAkB,GAAG,gBAC5C,OAAO,OAAO,eAAe,GAC/B;AAAA,IACF;AACA,QAAI,OAAO,OAAO,QAAS,SAAQ,IAAI,YAAY,OAAO,OAAO,OAAO,EAAE;AAAA,EAC5E;AACA,MAAI,OAAO,WAAY,SAAQ,IAAI,WAAW,OAAO,UAAU,EAAE;AACjE,aAAW,gBAAgB,OAAO,eAAe;AAC/C,YAAQ,IAAI,aAAa,YAAY,EAAE;AAAA,EACzC;AACA,MAAI,OAAO,YAAY,SAAS,GAAG;AACjC,YAAQ,IAAI,cAAc;AAC1B,eAAW,cAAc,OAAO,YAAY,MAAM,GAAG,CAAC,GAAG;AACvD,cAAQ,IAAI,OAAO,UAAU,EAAE;AAAA,IACjC;AAAA,EACF;AACF;AAEA,eAAe,OAAO,UAAiC;AACrD,QAAM,SAAS,OAAO,SAAS;AAC7B,UAAM,UAAU,IAAI,mCAAmB,IAAI;AAC3C,cAAU,MAAM,QAAQ,cAAc,UAAU,iBAAiB,CAAC;AAAA,EACpE,CAAC;AACH;AAEA,eAAe,OAAO,OAA8B;AAClD,QAAM,SAAS,OAAO,SAAS;AAC7B,UAAM,SAAU,MAAM,KAAK,OAAO,uBAAS,SAAS,EAAE,MAAM,CAAC;AAI7D,cAAU,EAAE,GAAG,QAAQ,QAAQ,OAAO,OAAO,MAAM,GAAG,EAAE,CAAC;AAAA,EAC3D,CAAC;AACH;AAEA,eAAe,eACb,UACA,OACA,MACe;AACf,QAAM,QAAQ,OAAO,MAAM,QAAQ,GAC/B,MAAM,GAAG,EACV,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAC3B,OAAO,OAAO;AACjB,QAAM,QAAQ,OAAO,OAAO,MAAM,SAAS,KAAK,EAAE;AAClD,QAAM,SAAS,OAAO,SAAS;AAC7B;AAAA,MACE,MAAM,KAAK,OAAO,uBAAS,gBAAgB;AAAA,QACzC;AAAA,QACA;AAAA,QACA;AAAA,QACA,OAAO,OAAO,SAAS,KAAK,IAAI,QAAQ;AAAA,MAC1C,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AACH;AAEA,eAAe,cACb,QACA,MACe;AACf,QAAM,WAAW,KAAK,CAAC;AACvB,QAAM,QAAQ,KAAK,CAAC;AACpB,MAAI,CAAC,YAAY,CAAC,OAAO;AACvB,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,QAAM,SAAS,OAAO,SAAS;AAC7B,UAAM,UAAU,IAAI,mCAAmB,IAAI;AAC3C,QAAI,WAAW,UAAU;AACvB;AAAA,QACE,MAAM,QAAQ,cAAc,UAAU,iBAAiB;AAAA,UACrD,OAAO;AAAA,UACP,MAAM,OAAO,MAAM,QAAQ,KAAK;AAAA,UAChC,MAAM,OAAO,MAAM,QAAQ,KAAK;AAAA,QAClC,CAAC;AAAA,MACH;AACA;AAAA,IACF;AACA,QAAI,WAAW,aAAa;AAC1B;AAAA,QACE,MAAM,QAAQ,cAAc,UAAU,oBAAoB;AAAA,UACxD,cAAc;AAAA,QAChB,CAAC;AAAA,MACH;AACA;AAAA,IACF;AACA,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,eAAe,SAAS,UAAiC;AACvD,QAAM,SAAS,OAAO,SAAS;AAC7B;AAAA,MACE,MAAM,KAAK,OAAO,uBAAS,cAAc;AAAA,QACvC;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AACH;AAEA,eAAe,cAAc,MAAc,MAA+B;AACxE,QAAM,aAAS,4BAAW;AAC1B,QAAM,WAAW,OAAO,MAAM,UAAU;AACxC,QAAM,gBAAY,uCAAsB,OAAO,eAAe,MAAM,QAAQ;AAC5E,YAAU,SAAS;AACrB;AAEA,eAAe,QACb,UACA,gBACA,iBACA,MACe;AACf,MAAI,CAAC,YAAY,CAAC,kBAAkB,CAAC,iBAAiB;AACpD,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,QAAM,SAAS,OAAO,SAAS;AAC7B;AAAA,MACE,MAAM,IAAI,yCAAyB,IAAI,EAAE;AAAA,QACvC;AAAA,QACA;AAAA,QACA;AAAA,QACA,EAAE,OAAO,QAAQ,MAAM,SAAS,EAAE;AAAA,MACpC;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,eAAe,aAAa,UAAkC;AAC5D,QAAM,SAAS,OAAO,SAAS;AAC7B;AAAA,MACE,MAAM,KAAK,OAAO,uBAAS,kBAAkB;AAAA,QAC3C,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,MACjC,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AACH;AAEA,eAAe,OAAsB;AACnC,QAAM,CAAC,SAAS,YAAY,GAAG,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC;AAC3D,MAAI,CAAC,WAAW,CAAC,QAAQ,MAAM,QAAQ,EAAE,SAAS,OAAO,GAAG;AAC1D,UAAM;AACN;AAAA,EACF;AACA,MAAI,YAAY,mBAAmB;AACjC,QAAI,CAAC,cAAc,CAAC,kBAAkB,KAAK,UAAU,EAAG,OAAM,IAAI,4BAAY,iBAAiB,2BAA2B;AAC1H,eAAO,uCAAkB,4BAAW,GAAG,YAAY,UAAU;AAAA,EAC/D;AACA,MAAI,YAAY,gBAAgB;AAC9B,QAAI,CAAC,cAAc,CAAC,kBAAkB,KAAK,UAAU,EAAG,OAAM,IAAI,4BAAY,iBAAiB,+BAA+B;AAC9H,UAAM,aAAS,4BAAW;AAC1B,eAAO,iCAAgB,QAAQ,MAAM,aAAa,MAAM,GAAG,UAAU;AAAA,EACvE;AACA,MAAI,YAAY,SAAS;AACvB,UAAM,YACJ,YAAY,WAAW,IAAI,KAAK,aAC5B,CAAC,YAAY,GAAG,IAAI,IACpB;AACN,WAAO,MAAM,QAAQ,WAAW,QAAQ,GAAG,SAAS;AAAA,EACtD;AACA,MAAI,YAAY,UAAU;AACxB,UAAM,aACJ,YAAY,WAAW,IAAI,KAAK,aAC5B,CAAC,YAAY,GAAG,IAAI,IACpB;AACN,WAAO,OAAO,QAAQ,YAAY,QAAQ,CAAC;AAAA,EAC7C;AACA,MAAI,YAAY,QAAQ;AACtB,QAAI,eAAe,QAAQ;AACzB,gBAAU,MAAM,IAAI,sCAAkB,4BAAW,GAAG,UAAU,EAAE,UAAU,CAAC;AAC3E;AAAA,IACF;AACA,QAAI,eAAe,QAAS,QAAO,UAAU;AAC7C,QAAI,eAAe,SAAU,QAAO,WAAW;AAAA,EACjD;AACA,MAAI,YAAY,WAAW;AACzB,QAAI,eAAe,OAAO;AACxB,YAAM,SAAS,WAAW,IAAI;AAC9B,UAAI,CAAC,OAAQ,OAAM,IAAI,4BAAY,mBAAmB,yBAAyB;AAC/E,aAAO,UAAU,MAAM;AAAA,IACzB;AACA,QAAI,eAAe,OAAQ,QAAO,YAAY;AAAA,EAChD;AACA,MAAI,YAAY,WAAW;AACzB,UAAM,cACJ,YAAY,WAAW,IAAI,KAAK,aAC5B,CAAC,YAAY,GAAG,IAAI,IACpB;AACN,UAAM,SACJ,cAAc,CAAC,WAAW,WAAW,IAAI,IACrC,aACA,WAAW,WAAW;AAC5B,WAAO,QAAQ,QAAQ,WAAW;AAAA,EACpC;AACA,MAAI,YAAY,UAAU;AACxB,QAAI,CAAC,WAAY,OAAM,IAAI,4BAAY,mBAAmB,oBAAoB;AAC9E,WAAO,OAAO,UAAU;AAAA,EAC1B;AACA,MAAI,YAAY,UAAU,eAAe,OAAO;AAC9C,UAAM,QAAQ,WAAW,IAAI;AAC7B,QAAI,CAAC,MAAO,OAAM,IAAI,4BAAY,gBAAgB,iBAAiB;AACnE,WAAO,OAAO,KAAK;AAAA,EACrB;AACA,MAAI,YAAY,UAAU;AACxB,QAAI,CAAC,WAAY,OAAM,IAAI,4BAAY,mBAAmB,oBAAoB;AAC9E,WAAO,eAAe,YAAY,KAAK,CAAC,GAAG,IAAI;AAAA,EACjD;AACA,MAAI,YAAY,SAAU,QAAO,cAAc,YAAY,IAAI;AAC/D,MAAI,YAAY,YAAY;AAC1B,QAAI,CAAC,WAAY,OAAM,IAAI,4BAAY,mBAAmB,oBAAoB;AAC9E,WAAO,SAAS,UAAU;AAAA,EAC5B;AACA,MAAI,YAAY,YAAY,eAAe,WAAW;AACpD,UAAM,OAAO,KAAK,CAAC;AACnB,QAAI,CAAC,KAAM,OAAM,IAAI,4BAAY,mBAAmB,2BAA2B;AAC/E,WAAO,cAAc,MAAM,IAAI;AAAA,EACjC;AACA,MAAI,YAAY,WAAW;AACzB,QAAI,CAAC,WAAY,OAAM,IAAI,4BAAY,mBAAmB,oBAAoB;AAC9E,WAAO,QAAQ,YAAY,KAAK,CAAC,GAAG,KAAK,CAAC,GAAG,IAAI;AAAA,EACnD;AACA,MAAI,YAAY,eAAgB,QAAO,aAAa,UAAU;AAC9D,MAAI,YAAY,OAAO;AACrB,YAAQ,IAAI,oBAAoB;AAChC,UAAM,EAAE,eAAe,IAAI,MAAM,OAAO,cAAc;AACtD,UAAM,mBAAe,4BAAW,CAAC;AACjC;AAAA,EACF;AACA,QAAM;AACN,QAAM,IAAI,4BAAY,mBAAmB,oBAAoB,OAAO,EAAE;AACxE;AAEA,KAAK,KAAK,EAAE,MAAM,CAAC,UAAU;AAC3B,aAAW,KAAK;AAChB,UAAQ,WAAW;AACrB,CAAC;","names":["import_node_fs","import_node_path","path","fs","key","path","capabilities","fs","script","code","launcher"]}
|
|
@@ -86,31 +86,11 @@ if (-not (Test-Path (Join-Path $JdkHome "bin\java.exe"))) {
|
|
|
86
86
|
Remove-ToolDirectory $TempJdk
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
# ipatool 2.7.0 Windows
|
|
90
|
-
$
|
|
91
|
-
$
|
|
92
|
-
Download $IpatoolUrl $IpatoolZip
|
|
93
|
-
if ($env:APPTRACE_IPATOOL_SHA256) {
|
|
94
|
-
Assert-Sha256 $IpatoolZip $env:APPTRACE_IPATOOL_SHA256
|
|
95
|
-
} else {
|
|
96
|
-
Assert-Sha256 $IpatoolZip "b729608716908be287c2e6972f9e8cd05f879810ae5d182fc856c9e57cca0dd5"
|
|
97
|
-
}
|
|
98
|
-
$IpatoolHome = Join-Path $Tools "ipatool"
|
|
99
|
-
if (-not (Test-Path (Join-Path $IpatoolHome "bin\ipatool.exe"))) {
|
|
100
|
-
$TempIpatool = Join-Path $Tools "ipatool.tmp"
|
|
101
|
-
Remove-ToolDirectory $TempIpatool
|
|
102
|
-
New-Item -ItemType Directory -Force -Path $TempIpatool | Out-Null
|
|
103
|
-
& tar.exe -xzf $IpatoolZip -C $TempIpatool
|
|
104
|
-
if ($LASTEXITCODE -ne 0) { throw "Failed to extract ipatool archive." }
|
|
105
|
-
$Exe = Get-Item -LiteralPath (Join-Path $TempIpatool "bin\ipatool-2.7.0-windows-amd64.exe") -ErrorAction SilentlyContinue
|
|
106
|
-
if (-not $Exe) { throw "ipatool.exe not found in archive." }
|
|
107
|
-
Remove-ToolDirectory $IpatoolHome
|
|
108
|
-
New-Item -ItemType Directory -Force -Path (Join-Path $IpatoolHome "bin") | Out-Null
|
|
109
|
-
Copy-Item -LiteralPath $Exe.FullName -Destination (Join-Path $IpatoolHome "bin\ipatool.exe") -Force
|
|
110
|
-
Remove-ToolDirectory $TempIpatool
|
|
111
|
-
}
|
|
89
|
+
# ipatool 2.7.0 with user-scoped Windows DPAPI credentials.
|
|
90
|
+
& (Join-Path $PSScriptRoot 'build-ipatool.ps1') -ToolsRoot $Tools
|
|
91
|
+
if ($LASTEXITCODE -ne 0) { throw 'Windows credential adapter installation failed' }
|
|
112
92
|
|
|
113
93
|
Write-Host "Ghidra: $GhidraHome"
|
|
114
94
|
Write-Host "JDK: $JdkHome"
|
|
115
|
-
Write-Host "ipatool: $(Join-Path $
|
|
95
|
+
Write-Host "ipatool: $(Join-Path $Tools 'ipatool\bin\ipatool-dpapi-v1.exe')"
|
|
116
96
|
Write-Host "Windows tool bootstrap complete."
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
param([Parameter(Mandatory=$true)][string]$ToolsRoot)
|
|
2
|
+
$ErrorActionPreference = 'Stop'
|
|
3
|
+
$ToolsRoot = [IO.Path]::GetFullPath($ToolsRoot)
|
|
4
|
+
$Cache = Join-Path $ToolsRoot 'downloads'
|
|
5
|
+
New-Item -ItemType Directory -Force -Path $Cache | Out-Null
|
|
6
|
+
function Fetch([string]$Url, [string]$File, [string]$Hash) {
|
|
7
|
+
if (-not (Test-Path -LiteralPath $File)) { Invoke-WebRequest -Uri $Url -OutFile $File }
|
|
8
|
+
if ((Get-FileHash -LiteralPath $File -Algorithm SHA256).Hash.ToLower() -ne $Hash) { throw "Checksum mismatch: $File" }
|
|
9
|
+
}
|
|
10
|
+
$GoZip = Join-Path $Cache 'go1.27.3.windows-amd64.zip'
|
|
11
|
+
Fetch 'https://go.dev/dl/go1.27.3.windows-amd64.zip' $GoZip 'f26081f438319757f8a1b46d7d0b2c5ec7583d4038e485e0aa725cfde7885fff'
|
|
12
|
+
$GoRoot = Join-Path $ToolsRoot 'go-sdk-1.27.3'
|
|
13
|
+
if (-not (Test-Path (Join-Path $GoRoot 'go/bin/go.exe'))) { Expand-Archive -LiteralPath $GoZip -DestinationPath $GoRoot -Force }
|
|
14
|
+
$SourceZip = Join-Path $Cache 'ipatool-4484b55.zip'
|
|
15
|
+
Fetch 'https://codeload.github.com/majd/ipatool/zip/4484b55d366e177ae6c89758c61588712c572fad' $SourceZip 'b9d497f70e4756720bef8081059c4a40fa3f9719ea8d001cc2bb76a2de5eb00d'
|
|
16
|
+
$SourceRoot = Join-Path $ToolsRoot 'ipatool-source-dpapi-v1'
|
|
17
|
+
Expand-Archive -LiteralPath $SourceZip -DestinationPath $SourceRoot -Force
|
|
18
|
+
$Source = Join-Path $SourceRoot 'ipatool-4484b55d366e177ae6c89758c61588712c572fad'
|
|
19
|
+
$DefaultKeyring = Join-Path $Source 'cmd/keyring_default.go'
|
|
20
|
+
[IO.File]::WriteAllText($DefaultKeyring, ([IO.File]::ReadAllText($DefaultKeyring).Replace('//go:build !ios', '//go:build !ios && !windows')))
|
|
21
|
+
Copy-Item -Path (Join-Path $PSScriptRoot 'ipatool-overlay/*.go') -Destination (Join-Path $Source 'cmd') -Force
|
|
22
|
+
$Go = Join-Path $GoRoot 'go/bin/go.exe'
|
|
23
|
+
$Destination = Join-Path $ToolsRoot 'ipatool/bin'
|
|
24
|
+
New-Item -ItemType Directory -Force -Path $Destination | Out-Null
|
|
25
|
+
Push-Location $Source
|
|
26
|
+
try {
|
|
27
|
+
& $Go test ./cmd -run '^TestDPAPI' -count=1
|
|
28
|
+
if ($LASTEXITCODE -ne 0) { throw 'DPAPI credential verification failed' }
|
|
29
|
+
& $Go build -trimpath -ldflags '-s -w -X github.com/majd/ipatool/v2/cmd.version=2.7.0-apptrace-dpapi-v1' -o (Join-Path $Destination 'ipatool-dpapi-v1.exe') .
|
|
30
|
+
if ($LASTEXITCODE -ne 0) { throw 'ipatool build failed' }
|
|
31
|
+
Copy-Item -LiteralPath (Join-Path $Source 'LICENSE') -Destination (Join-Path $Destination 'IPATOOL-LICENSE') -Force
|
|
32
|
+
} finally { Pop-Location }
|
|
33
|
+
Write-Host 'ipatool Windows DPAPI adapter built and verified.'
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
//go:build windows
|
|
2
|
+
|
|
3
|
+
package cmd
|
|
4
|
+
|
|
5
|
+
import (
|
|
6
|
+
"crypto/sha256"
|
|
7
|
+
"errors"
|
|
8
|
+
"fmt"
|
|
9
|
+
"io"
|
|
10
|
+
"os"
|
|
11
|
+
"path/filepath"
|
|
12
|
+
"runtime"
|
|
13
|
+
"syscall"
|
|
14
|
+
"time"
|
|
15
|
+
"unsafe"
|
|
16
|
+
|
|
17
|
+
"github.com/byteness/keyring"
|
|
18
|
+
"github.com/majd/ipatool/v2/pkg/keychain"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
// The OS user protects these bytes across processes. Never fall back to the
|
|
22
|
+
// legacy passphrase file backend: that would recreate the login/read split.
|
|
23
|
+
type dpapiKeyring struct{ directory string }
|
|
24
|
+
type dataBlob struct {
|
|
25
|
+
Size uint32
|
|
26
|
+
Data *byte
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
var crypt32 = syscall.NewLazyDLL("crypt32.dll")
|
|
30
|
+
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
|
31
|
+
|
|
32
|
+
func openKeyring(config keyring.Config) (keychain.Keyring, error) {
|
|
33
|
+
directory := filepath.Join(config.FileDir, "apptrace-dpapi-v1")
|
|
34
|
+
if err := os.MkdirAll(directory, 0700); err != nil {
|
|
35
|
+
return nil, err
|
|
36
|
+
}
|
|
37
|
+
return &dpapiKeyring{directory: directory}, nil
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
func protectData(input []byte, decrypt bool) ([]byte, error) {
|
|
41
|
+
var source, output dataBlob
|
|
42
|
+
source.Size = uint32(len(input))
|
|
43
|
+
if len(input) > 0 {
|
|
44
|
+
source.Data = &input[0]
|
|
45
|
+
}
|
|
46
|
+
name := "CryptProtectData"
|
|
47
|
+
if decrypt {
|
|
48
|
+
name = "CryptUnprotectData"
|
|
49
|
+
}
|
|
50
|
+
// CRYPTPROTECT_UI_FORBIDDEN, deliberately without LOCAL_MACHINE.
|
|
51
|
+
ok, _, err := crypt32.NewProc(name).Call(uintptr(unsafe.Pointer(&source)), 0, 0, 0, 0, 1, uintptr(unsafe.Pointer(&output)))
|
|
52
|
+
runtime.KeepAlive(input)
|
|
53
|
+
if ok == 0 {
|
|
54
|
+
return nil, fmt.Errorf("APPTRACE_CREDENTIAL_ACCESS_FAILED: %w", err)
|
|
55
|
+
}
|
|
56
|
+
defer kernel32.NewProc("LocalFree").Call(uintptr(unsafe.Pointer(output.Data)))
|
|
57
|
+
return append([]byte(nil), unsafe.Slice(output.Data, int(output.Size))...), nil
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
func (k *dpapiKeyring) filename(key string) string {
|
|
61
|
+
return filepath.Join(k.directory, fmt.Sprintf("%x.bin", sha256.Sum256([]byte(key))))
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
func (k *dpapiKeyring) Get(key string) (keyring.Item, error) {
|
|
65
|
+
filename, err := syscall.UTF16PtrFromString(k.filename(key))
|
|
66
|
+
if err != nil {
|
|
67
|
+
return keyring.Item{}, err
|
|
68
|
+
}
|
|
69
|
+
// Permit atomic replacement while another process reads the old version.
|
|
70
|
+
handle, err := syscall.CreateFile(filename, syscall.GENERIC_READ, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|4, nil, syscall.OPEN_EXISTING, syscall.FILE_ATTRIBUTE_NORMAL, 0)
|
|
71
|
+
if errors.Is(err, syscall.ERROR_FILE_NOT_FOUND) {
|
|
72
|
+
return keyring.Item{}, keyring.ErrKeyNotFound
|
|
73
|
+
}
|
|
74
|
+
if err != nil {
|
|
75
|
+
return keyring.Item{}, fmt.Errorf("APPTRACE_CREDENTIAL_ACCESS_FAILED: %w", err)
|
|
76
|
+
}
|
|
77
|
+
file := os.NewFile(uintptr(handle), k.filename(key))
|
|
78
|
+
defer file.Close()
|
|
79
|
+
data, err := io.ReadAll(file)
|
|
80
|
+
if err != nil {
|
|
81
|
+
return keyring.Item{}, fmt.Errorf("APPTRACE_CREDENTIAL_ACCESS_FAILED: %w", err)
|
|
82
|
+
}
|
|
83
|
+
plain, err := protectData(data, true)
|
|
84
|
+
return keyring.Item{Key: key, Data: plain}, err
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
func (k *dpapiKeyring) Set(item keyring.Item) error {
|
|
88
|
+
if _, err := k.Get(item.Key); err != nil && !errors.Is(err, keyring.ErrKeyNotFound) {
|
|
89
|
+
return err
|
|
90
|
+
}
|
|
91
|
+
data, err := protectData(item.Data, false)
|
|
92
|
+
if err != nil {
|
|
93
|
+
return err
|
|
94
|
+
}
|
|
95
|
+
file, err := os.CreateTemp(k.directory, "credential-*.tmp")
|
|
96
|
+
if err != nil {
|
|
97
|
+
return err
|
|
98
|
+
}
|
|
99
|
+
defer os.Remove(file.Name())
|
|
100
|
+
if _, err = file.Write(data); err != nil {
|
|
101
|
+
file.Close()
|
|
102
|
+
return err
|
|
103
|
+
}
|
|
104
|
+
if err = file.Sync(); err != nil {
|
|
105
|
+
file.Close()
|
|
106
|
+
return err
|
|
107
|
+
}
|
|
108
|
+
if err = file.Close(); err != nil {
|
|
109
|
+
return err
|
|
110
|
+
}
|
|
111
|
+
// Windows may transiently deny replacement while closing a shared reader.
|
|
112
|
+
// Keep the old complete ciphertext until replacement succeeds.
|
|
113
|
+
for attempt := 0; attempt < 30; attempt++ {
|
|
114
|
+
err = os.Rename(file.Name(), k.filename(item.Key))
|
|
115
|
+
if err == nil {
|
|
116
|
+
return nil
|
|
117
|
+
}
|
|
118
|
+
if !errors.Is(err, syscall.ERROR_ACCESS_DENIED) && !errors.Is(err, syscall.Errno(32)) {
|
|
119
|
+
return err
|
|
120
|
+
}
|
|
121
|
+
time.Sleep(10 * time.Millisecond)
|
|
122
|
+
}
|
|
123
|
+
return err
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
func (k *dpapiKeyring) Remove(key string) error {
|
|
127
|
+
err := os.Remove(k.filename(key))
|
|
128
|
+
if errors.Is(err, os.ErrNotExist) {
|
|
129
|
+
return keyring.ErrKeyNotFound
|
|
130
|
+
}
|
|
131
|
+
return err
|
|
132
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
//go:build windows
|
|
2
|
+
|
|
3
|
+
package cmd
|
|
4
|
+
|
|
5
|
+
import (
|
|
6
|
+
"bytes"
|
|
7
|
+
"github.com/byteness/keyring"
|
|
8
|
+
"os"
|
|
9
|
+
"os/exec"
|
|
10
|
+
"sync"
|
|
11
|
+
"testing"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
func TestDPAPICrossProcess(t *testing.T) {
|
|
15
|
+
if dir := os.Getenv("APPTRACE_DPAPI_TEST_DIR"); dir != "" {
|
|
16
|
+
store := &dpapiKeyring{directory: dir}
|
|
17
|
+
item, err := store.Get("test-only")
|
|
18
|
+
if err != nil || !bytes.Equal(item.Data, bytes.Repeat([]byte("test-only-secret"), 1000)) {
|
|
19
|
+
t.Fatal("cross-process read failed", err)
|
|
20
|
+
}
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
store := &dpapiKeyring{directory: t.TempDir()}
|
|
24
|
+
secret := bytes.Repeat([]byte("test-only-secret"), 1000)
|
|
25
|
+
if err := store.Set(keyring.Item{Key: "test-only", Data: secret}); err != nil {
|
|
26
|
+
t.Fatal(err)
|
|
27
|
+
}
|
|
28
|
+
ciphertext, _ := os.ReadFile(store.filename("test-only"))
|
|
29
|
+
if bytes.Contains(ciphertext, []byte("test-only-secret")) {
|
|
30
|
+
t.Fatal("plaintext on disk")
|
|
31
|
+
}
|
|
32
|
+
child := exec.Command(os.Args[0], "-test.run=^TestDPAPICrossProcess$")
|
|
33
|
+
child.Env = append(os.Environ(), "APPTRACE_DPAPI_TEST_DIR="+store.directory)
|
|
34
|
+
if output, err := child.CombinedOutput(); err != nil {
|
|
35
|
+
t.Fatalf("child failed: %s %v", output, err)
|
|
36
|
+
}
|
|
37
|
+
if err := store.Set(keyring.Item{Key: "test-only", Data: []byte("updated")}); err != nil {
|
|
38
|
+
t.Fatal("replace failed", err)
|
|
39
|
+
}
|
|
40
|
+
if err := store.Remove("test-only"); err != nil {
|
|
41
|
+
t.Fatal(err)
|
|
42
|
+
}
|
|
43
|
+
if _, err := store.Get("test-only"); err != keyring.ErrKeyNotFound {
|
|
44
|
+
t.Fatal("missing credential classification", err)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
func TestDPAPIRejectsCorruption(t *testing.T) {
|
|
49
|
+
store := &dpapiKeyring{directory: t.TempDir()}
|
|
50
|
+
if err := os.WriteFile(store.filename("corrupt"), []byte("invalid cipher"), 0600); err != nil {
|
|
51
|
+
t.Fatal(err)
|
|
52
|
+
}
|
|
53
|
+
if _, err := store.Get("corrupt"); err == nil {
|
|
54
|
+
t.Fatal("corruption accepted")
|
|
55
|
+
}
|
|
56
|
+
if err := store.Set(keyring.Item{Key: "corrupt", Data: []byte("replacement")}); err == nil {
|
|
57
|
+
t.Fatal("corrupt credential overwritten")
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
func TestDPAPIConcurrentReaders(t *testing.T) {
|
|
62
|
+
store := &dpapiKeyring{directory: t.TempDir()}
|
|
63
|
+
if err := store.Set(keyring.Item{Key: "concurrent", Data: []byte("valid")}); err != nil {
|
|
64
|
+
t.Fatal(err)
|
|
65
|
+
}
|
|
66
|
+
var readers sync.WaitGroup
|
|
67
|
+
for i := 0; i < 4; i++ {
|
|
68
|
+
readers.Add(1)
|
|
69
|
+
go func() {
|
|
70
|
+
defer readers.Done()
|
|
71
|
+
for j := 0; j < 30; j++ {
|
|
72
|
+
item, err := store.Get("concurrent")
|
|
73
|
+
if err != nil || string(item.Data) != "valid" {
|
|
74
|
+
t.Error("partial credential read", err)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}()
|
|
78
|
+
}
|
|
79
|
+
for j := 0; j < 30; j++ {
|
|
80
|
+
if err := store.Set(keyring.Item{Key: "concurrent", Data: []byte("valid")}); err != nil {
|
|
81
|
+
t.Error(err)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
readers.Wait()
|
|
85
|
+
}
|
|
@@ -24,14 +24,15 @@ When the user provides an App Store URL or App ID and asks what the app uses,
|
|
|
24
24
|
what it contains, or for a report:
|
|
25
25
|
|
|
26
26
|
1. Call `analyze_app` with the source and the smallest useful set of goals.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
`
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
Retain its `runId` from the first response, including `queued`, `preparing`
|
|
28
|
+
and `awaiting_auth`. A persistent local worker installs missing tools, opens
|
|
29
|
+
Windows sign-in once and resumes the original task after background account
|
|
30
|
+
verification. Poll `get_analysis_run(run_id=runId, wait_seconds=20)` until
|
|
31
|
+
completed or failed. Do not end the turn just because login is pending and
|
|
32
|
+
do not ask the user to confirm completion or resend `analyze_app`.
|
|
33
|
+
Report meaningful stage changes; credentials belong only in the local window.
|
|
34
|
+
If `stage=login_cancelled`, explain that the task is saved and wait for an
|
|
35
|
+
explicit retry; do not reopen the window automatically.
|
|
35
36
|
2. Use `get_analysis_run` to wait for or retrieve the completed result.
|
|
36
37
|
3. Prefer goals `tech_stack`, `feature_map`, and `report` for a first pass.
|
|
37
38
|
4. Add `network` when the user asks about APIs or data flow.
|
|
@@ -45,15 +46,26 @@ what it contains, or for a report:
|
|
|
45
46
|
If a tool returns `AUTH_REQUIRED` or `KEYCHAIN_LOCKED`:
|
|
46
47
|
|
|
47
48
|
1. Call `open_login`. Tell the user to enter credentials only in the local window.
|
|
48
|
-
2.
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
2. For an existing analysis task, poll `get_analysis_run` with its saved ID;
|
|
50
|
+
the worker resumes automatically. For standalone setup, poll
|
|
51
|
+
`get_setup_status(wait_seconds=20, require_account=true)`. Do not open repeated
|
|
52
|
+
windows or ask the user to repeat their goal.
|
|
51
53
|
3. Never ask for, capture, screenshot, log, or relay passwords, verification
|
|
52
54
|
codes, keychain passphrases, cookies, or tokens. Only the user handles them.
|
|
53
55
|
4. If a login attempt is cancelled or fails, explain the state and wait for the
|
|
54
56
|
user to request another attempt. Do not repeatedly reopen a dismissed window.
|
|
55
|
-
5. On hosts without automatic login windows, give the
|
|
56
|
-
`apptrace auth login` and
|
|
57
|
+
5. On hosts without automatic login windows, give the fallback command
|
|
58
|
+
`apptrace auth login` and keep querying the saved task. Confirmation in chat
|
|
59
|
+
is not a substitute for background account verification.
|
|
60
|
+
|
|
61
|
+
Windows requires the AppTrace DPAPI adapter. `IPATOOL_UPDATE_REQUIRED` means the
|
|
62
|
+
managed tool needs an update or a custom tool path points to an old version.
|
|
63
|
+
An update preserves the old file keyring but requires one local sign-in into
|
|
64
|
+
the new Windows-user-protected store. `CREDENTIAL_ACCESS_FAILED` means the store
|
|
65
|
+
cannot be read/decrypted; do not overwrite it or treat that as logged out.
|
|
66
|
+
Use `cancel_analysis_run` only when the user requests cancellation. Waiting
|
|
67
|
+
tasks survive MCP disconnection; after 30 idle minutes a worker stops waiting
|
|
68
|
+
and retains the task, resumed by the next MCP connection or explicit login.
|
|
57
69
|
|
|
58
70
|
For `failed`, distinguish account-check failure from installation failure.
|
|
59
71
|
Do not infer that the user is logged out from a timeout or unknown response.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhuixin/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "AppTrace CLI for evidence-backed application analysis",
|
|
6
6
|
"repository": {
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"rea-agents": "6.4.0",
|
|
38
38
|
"smol-toml": "^1.9.0",
|
|
39
|
-
"@zhuixin/metadata": "0.2.
|
|
40
|
-
"@zhuixin/
|
|
41
|
-
"@zhuixin/
|
|
39
|
+
"@zhuixin/metadata": "0.2.5",
|
|
40
|
+
"@zhuixin/shared": "0.2.5",
|
|
41
|
+
"@zhuixin/mcp": "0.2.5"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"tsup": "^8.5.0"
|