@zhuixin/cli 0.1.2 → 0.2.1

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 CHANGED
@@ -25,10 +25,57 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
 
26
26
  // src/index.ts
27
27
  var import_node_child_process = require("child_process");
28
- var import_node_fs = __toESM(require("fs"));
29
- var import_node_path = __toESM(require("path"));
28
+ var import_node_fs2 = __toESM(require("fs"));
29
+ var import_node_path2 = __toESM(require("path"));
30
30
  var import_metadata = require("@zhuixin/metadata");
31
31
  var import_shared = require("@zhuixin/shared");
32
+
33
+ // package.json
34
+ var version = "0.2.1";
35
+
36
+ // src/mcp-config.ts
37
+ var import_node_fs = __toESM(require("fs"));
38
+ var import_node_path = __toESM(require("path"));
39
+ var import_node_crypto = require("crypto");
40
+ var import_smol_toml = require("smol-toml");
41
+ function object(value) {
42
+ if (value === void 0) return {};
43
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
44
+ throw new Error("MCP configuration must contain objects/tables.");
45
+ }
46
+ return value;
47
+ }
48
+ function writeMcpConfig(filename, server) {
49
+ const extension = import_node_path.default.extname(filename).toLowerCase();
50
+ if (![".json", ".toml"].includes(extension)) {
51
+ throw new Error("--write-config \u53EA\u652F\u6301 .json \u6216 .toml \u6587\u4EF6\u3002");
52
+ }
53
+ const original = import_node_fs.default.existsSync(filename) ? import_node_fs.default.readFileSync(filename, "utf8") : void 0;
54
+ const config = object(original?.trim() ? extension === ".json" ? JSON.parse(original) : (0, import_smol_toml.parse)(original) : {});
55
+ const key = extension === ".json" ? "mcpServers" : "mcp_servers";
56
+ const servers = object(config[key]);
57
+ const previous = object(servers.apptrace);
58
+ const environment = { ...object(previous.env), ...server.env };
59
+ for (const [key2, placeholder] of [["APPTRACE_IPATOOL_PATH", "ipatool"], ["APPTRACE_ANALYSIS_ENGINE_PATH", "rea"]]) {
60
+ if (!server.env[key2] && environment[key2] === placeholder) delete environment[key2];
61
+ }
62
+ servers.apptrace = { ...previous, ...server, env: environment };
63
+ config[key] = servers;
64
+ const output = extension === ".json" ? JSON.stringify(config, null, 2) + "\n" : (0, import_smol_toml.stringify)(config);
65
+ import_node_fs.default.mkdirSync(import_node_path.default.dirname(filename), { recursive: true, mode: 448 });
66
+ if (original !== void 0) {
67
+ import_node_fs.default.writeFileSync(`${filename}.${(0, import_node_crypto.randomUUID)()}.bak`, original, { mode: 384, flag: "wx" });
68
+ }
69
+ const temporary = `${filename}.${(0, import_node_crypto.randomUUID)()}.tmp`;
70
+ try {
71
+ import_node_fs.default.writeFileSync(temporary, output, { mode: 384, flag: "wx" });
72
+ import_node_fs.default.renameSync(temporary, filename);
73
+ } finally {
74
+ if (import_node_fs.default.existsSync(temporary)) import_node_fs.default.unlinkSync(temporary);
75
+ }
76
+ }
77
+
78
+ // src/index.ts
32
79
  var GOALS = [
33
80
  "tech_stack",
34
81
  "feature_map",
@@ -37,13 +84,15 @@ var GOALS = [
37
84
  "report"
38
85
  ];
39
86
  function usage() {
40
- console.log(`AppTrace 0.1.0
87
+ console.log(`AppTrace ${version}
41
88
 
42
89
  Usage:
43
90
  apptrace setup [--json] [--install] [--write-config <path>]
44
91
  apptrace doctor [--json]
45
92
  apptrace auth login
46
93
  apptrace auth status
94
+ apptrace auth open
95
+ apptrace setup --write-config <path> --install-skill
47
96
  apptrace targets add <app-store-url-or-id>
48
97
  apptrace targets list
49
98
  apptrace analyze [app-store-url-or-id] [--target <target-id>] [--goals ...] [--force]
@@ -111,9 +160,9 @@ function withCore(operation) {
111
160
  } });
112
161
  return operation(core).finally(() => core.close());
113
162
  }
114
- function interactive(command, args, env) {
163
+ function interactive(command, args, env, onSpawn) {
115
164
  return new Promise((resolve, reject) => {
116
- const isWindowsCommand = process.platform === "win32" && [".cmd", ".bat"].includes(import_node_path.default.extname(command).toLowerCase());
165
+ const isWindowsCommand = process.platform === "win32" && [".cmd", ".bat"].includes(import_node_path2.default.extname(command).toLowerCase());
117
166
  const child = isWindowsCommand ? (0, import_node_child_process.spawn)(
118
167
  process.env.ComSpec || "cmd.exe",
119
168
  ["/d", "/s", "/c", `"${command}" ${args.join(" ")}`],
@@ -126,6 +175,15 @@ function interactive(command, args, env) {
126
175
  stdio: "inherit",
127
176
  env: env ? { ...process.env, ...env } : process.env
128
177
  });
178
+ if (child.pid && onSpawn) {
179
+ try {
180
+ onSpawn(child.pid);
181
+ } catch (error) {
182
+ child.kill();
183
+ reject(error);
184
+ return;
185
+ }
186
+ }
129
187
  child.on("error", reject);
130
188
  child.on("exit", (code) => resolve(code));
131
189
  });
@@ -162,19 +220,19 @@ async function doctor(json) {
162
220
  }
163
221
  function createEngineLauncher(workspaceRoot) {
164
222
  const reaScript = require.resolve("rea-agents/scripts/rea.mjs");
165
- const tools = import_node_path.default.join(workspaceRoot, "tools");
166
- const javaHome = import_node_path.default.join(tools, "jdk");
167
- const ghidraHome = import_node_path.default.join(tools, "ghidra_12.1.4_PUBLIC");
168
- const ghidraUserHome = import_node_path.default.join(workspaceRoot, ".ghidra-home");
169
- import_node_fs.default.mkdirSync(import_node_path.default.join(tools, "bin"), { recursive: true, mode: 448 });
170
- import_node_fs.default.mkdirSync(ghidraUserHome, { recursive: true, mode: 448 });
171
- const launcher = import_node_path.default.join(
223
+ const tools = import_node_path2.default.join(workspaceRoot, "tools");
224
+ const javaHome = import_node_path2.default.join(tools, "jdk");
225
+ const ghidraHome = import_node_path2.default.join(tools, "ghidra_12.1.4_PUBLIC");
226
+ const ghidraUserHome = import_node_path2.default.join(workspaceRoot, ".ghidra-home");
227
+ import_node_fs2.default.mkdirSync(import_node_path2.default.join(tools, "bin"), { recursive: true, mode: 448 });
228
+ import_node_fs2.default.mkdirSync(ghidraUserHome, { recursive: true, mode: 448 });
229
+ const launcher = import_node_path2.default.join(
172
230
  tools,
173
231
  "bin",
174
232
  process.platform === "win32" ? "rea.cmd" : "rea"
175
233
  );
176
234
  if (process.platform === "win32") {
177
- import_node_fs.default.writeFileSync(
235
+ import_node_fs2.default.writeFileSync(
178
236
  launcher,
179
237
  [
180
238
  "@echo off",
@@ -188,7 +246,7 @@ function createEngineLauncher(workspaceRoot) {
188
246
  { mode: 448 }
189
247
  );
190
248
  } else {
191
- import_node_fs.default.writeFileSync(
249
+ import_node_fs2.default.writeFileSync(
192
250
  launcher,
193
251
  [
194
252
  "#!/usr/bin/env bash",
@@ -207,13 +265,17 @@ function createEngineLauncher(workspaceRoot) {
207
265
  return launcher;
208
266
  }
209
267
  async function installTools(config, json = false) {
210
- if (import_node_fs.default.existsSync(config.enginePath) && import_node_fs.default.existsSync(config.ipatoolPath)) {
268
+ if ((0, import_metadata.missingSetupTools)(config).length === 0) {
269
+ const managedLauncher = import_node_path2.default.join(config.workspaceRoot, "tools", "bin", process.platform === "win32" ? "rea.cmd" : "rea");
270
+ if (import_node_path2.default.resolve(config.analysisEnginePath) === managedLauncher) {
271
+ createEngineLauncher(config.workspaceRoot);
272
+ }
211
273
  process.stderr.write("Tools already available; download skipped.\n");
212
274
  return;
213
275
  }
214
276
  if (process.platform === "win32" && process.arch === "x64") {
215
- const script2 = import_node_path.default.resolve(__dirname, "../../scripts/bootstrap-tools.ps1");
216
- if (!import_node_fs.default.existsSync(script2)) {
277
+ const script2 = import_node_path2.default.resolve(__dirname, "scripts/bootstrap-tools.ps1");
278
+ if (!import_node_fs2.default.existsSync(script2)) {
217
279
  throw new import_metadata.CoreFailure("TOOL_INSTALLER_MISSING", `\u627E\u4E0D\u5230\u5B89\u88C5\u811A\u672C\uFF1A${script2}`);
218
280
  }
219
281
  process.stderr.write(
@@ -228,10 +290,12 @@ async function installTools(config, json = false) {
228
290
  "-WorkspaceRoot",
229
291
  config.workspaceRoot
230
292
  ], {
293
+ // PowerShell 7's inherited module paths can hide Windows PowerShell 5.1 modules.
294
+ PSModulePath: import_node_path2.default.join(process.env.SystemRoot || "C:\\Windows", "System32", "WindowsPowerShell", "v1.0", "Modules"),
231
295
  APPTRACE_WORKSPACE_ROOT: config.workspaceRoot,
232
- APPTRACE_TOOLS_DIR: import_node_path.default.join(config.workspaceRoot, "tools"),
296
+ APPTRACE_TOOLS_DIR: import_node_path2.default.join(config.workspaceRoot, "tools"),
233
297
  ...process.env.APPTRACE_IPATOOL_SHA256 ? { APPTRACE_IPATOOL_SHA256: process.env.APPTRACE_IPATOOL_SHA256 } : {}
234
- });
298
+ }, (pid) => (0, import_metadata.trackInstallationChild)(config, pid));
235
299
  if (code2 !== 0) {
236
300
  throw new import_metadata.CoreFailure(
237
301
  "TOOL_INSTALL_FAILED",
@@ -246,11 +310,11 @@ async function installTools(config, json = false) {
246
310
  if (process.platform !== "darwin" || process.arch !== "arm64") {
247
311
  throw new import_metadata.CoreFailure(
248
312
  "TOOL_INSTALL_UNSUPPORTED",
249
- "v1 \u81EA\u52A8\u5B89\u88C5\u5668\u5F53\u524D\u53EA\u652F\u6301 macOS arm64\uFF1B\u5176\u4ED6\u5E73\u53F0\u8BF7\u4F7F\u7528 APPTRACE_ENGINE_PATH \u548C APPTRACE_IPATOOL_PATH\u3002"
313
+ "\u81EA\u52A8\u5B89\u88C5\u5668\u5F53\u524D\u652F\u6301 macOS arm64 \u548C Windows x64\uFF1B\u5176\u4ED6\u5E73\u53F0\u8BF7\u4F7F\u7528 APPTRACE_ENGINE_PATH \u548C APPTRACE_IPATOOL_PATH\u3002"
250
314
  );
251
315
  }
252
- const script = import_node_path.default.resolve(__dirname, "../../scripts/bootstrap-tools.sh");
253
- if (!import_node_fs.default.existsSync(script)) {
316
+ const script = import_node_path2.default.resolve(__dirname, "scripts/bootstrap-tools.sh");
317
+ if (!import_node_fs2.default.existsSync(script)) {
254
318
  throw new import_metadata.CoreFailure("TOOL_INSTALLER_MISSING", `\u627E\u4E0D\u5230\u5B89\u88C5\u811A\u672C\uFF1A${script}`);
255
319
  }
256
320
  process.stderr.write(
@@ -258,8 +322,8 @@ async function installTools(config, json = false) {
258
322
  );
259
323
  const code = await interactive("/bin/bash", [script], {
260
324
  APPTRACE_WORKSPACE_ROOT: config.workspaceRoot,
261
- APPTRACE_TOOLS_DIR: import_node_path.default.join(config.workspaceRoot, "tools")
262
- });
325
+ APPTRACE_TOOLS_DIR: import_node_path2.default.join(config.workspaceRoot, "tools")
326
+ }, (pid) => (0, import_metadata.trackInstallationChild)(config, pid));
263
327
  if (code !== 0) {
264
328
  throw new import_metadata.CoreFailure(
265
329
  "TOOL_INSTALL_FAILED",
@@ -272,62 +336,35 @@ async function installTools(config, json = false) {
272
336
  }
273
337
  async function setup(json, args) {
274
338
  const configPath = option(args, "--write-config");
275
- const config = (0, import_metadata.loadConfig)();
339
+ if (hasFlag(args, "--install-skill") && !configPath) throw new import_metadata.CoreFailure("CONFIG_REQUIRED", "\u5B89\u88C5 Skill \u65F6\u8BF7\u540C\u65F6\u6307\u5B9A --write-config\u3002");
340
+ let config = (0, import_metadata.loadConfig)();
276
341
  if (hasFlag(args, "--install")) {
277
- await installTools(config, json);
342
+ await (0, import_metadata.runInstallation)(config, () => installTools(config, json));
343
+ config = (0, import_metadata.loadConfig)();
278
344
  }
279
345
  await doctor(json);
280
346
  if (json && !configPath) return;
281
347
  const serverEntry = resolveMcpEntry();
282
348
  const env = {
283
349
  APPTRACE_WORKSPACE_ROOT: config.workspaceRoot,
284
- APPTRACE_IPATOOL_PATH: config.ipatoolPath
350
+ APPTRACE_CLI_PATH: __filename
285
351
  };
286
- if (import_node_fs.default.existsSync(config.enginePath)) {
352
+ const ipatool = (0, import_metadata.resolveTool)(config.ipatoolPath);
353
+ if (ipatool) env.APPTRACE_IPATOOL_PATH = ipatool;
354
+ if (import_node_fs2.default.existsSync(config.enginePath)) {
287
355
  env.APPTRACE_ENGINE_PATH = config.enginePath;
288
356
  }
289
- if (import_node_fs.default.existsSync(config.analysisEnginePath)) {
357
+ if (import_node_fs2.default.existsSync(config.analysisEnginePath)) {
290
358
  env.APPTRACE_ANALYSIS_ENGINE_PATH = config.analysisEnginePath;
291
359
  }
292
360
  if (configPath) {
293
- const absolute = import_node_path.default.resolve(configPath);
294
- const extension = import_node_path.default.extname(absolute).toLowerCase();
295
- if (extension === ".json") {
296
- import_node_fs.default.mkdirSync(import_node_path.default.dirname(absolute), { recursive: true, mode: 448 });
297
- import_node_fs.default.writeFileSync(
298
- absolute,
299
- `${JSON.stringify(
300
- {
301
- mcpServers: {
302
- apptrace: {
303
- command: process.execPath,
304
- args: [serverEntry],
305
- env
306
- }
307
- }
308
- },
309
- null,
310
- 2
311
- )}
312
- `,
313
- { mode: 384 }
314
- );
315
- } else if (extension === ".toml") {
316
- import_node_fs.default.mkdirSync(import_node_path.default.dirname(absolute), { recursive: true, mode: 448 });
317
- const envToml = Object.entries(env).map(([key, value]) => `${key} = ${JSON.stringify(value)}`).join(", ");
318
- const toml = [
319
- "[mcp_servers.apptrace]",
320
- `command = ${JSON.stringify(process.execPath)}`,
321
- `args = [${JSON.stringify(serverEntry)}]`,
322
- `env = { ${envToml} }`,
323
- ""
324
- ].join("\n");
325
- import_node_fs.default.writeFileSync(absolute, toml, { mode: 384 });
326
- } else {
327
- throw new import_metadata.CoreFailure(
328
- "INVALID_CONFIG_FORMAT",
329
- "--write-config \u53EA\u652F\u6301 .json \u6216 .toml \u6587\u4EF6\u3002"
330
- );
361
+ const absolute = import_node_path2.default.resolve(configPath);
362
+ writeMcpConfig(absolute, { command: process.execPath, args: [serverEntry], env });
363
+ if (hasFlag(args, "--install-skill")) {
364
+ const destination = import_node_path2.default.join(import_node_path2.default.dirname(absolute), "skills", "apptrace", "SKILL.md");
365
+ import_node_fs2.default.mkdirSync(import_node_path2.default.dirname(destination), { recursive: true });
366
+ if (import_node_fs2.default.existsSync(destination)) import_node_fs2.default.copyFileSync(destination, `${destination}.${Date.now()}.bak`);
367
+ import_node_fs2.default.copyFileSync(import_node_path2.default.join(__dirname, "skills", "apptrace", "SKILL.md"), destination);
331
368
  }
332
369
  if (json) {
333
370
  process.stderr.write(`MCP config written: ${absolute}
@@ -353,12 +390,19 @@ function resolveMcpEntry() {
353
390
  try {
354
391
  return require.resolve("@zhuixin/mcp");
355
392
  } catch {
356
- return import_node_path.default.resolve(__dirname, "../../mcp/dist/index.js");
393
+ return import_node_path2.default.resolve(__dirname, "../../mcp/dist/index.js");
357
394
  }
358
395
  }
359
396
  async function authLogin() {
360
397
  const config = (0, import_metadata.loadConfig)();
361
- const code = await interactive(config.ipatoolPath, ["auth", "login"]);
398
+ const ipatool = (0, import_metadata.resolveTool)(config.ipatoolPath);
399
+ if (!ipatool) {
400
+ throw new import_metadata.CoreFailure(
401
+ "TOOL_NOT_INSTALLED",
402
+ `\u627E\u4E0D\u5230 ipatool\uFF08${config.ipatoolPath}\uFF09\u3002\u8BF7\u5148\u8FD0\u884C apptrace setup --install\uFF0C\u6210\u529F\u540E\u518D\u8FD0\u884C apptrace auth login\u3002`
403
+ );
404
+ }
405
+ const code = await interactive(ipatool, ["auth", "login"]);
362
406
  if (code !== 0) {
363
407
  throw new import_metadata.CoreFailure(
364
408
  "AUTH_FAILED",
@@ -557,6 +601,11 @@ async function main() {
557
601
  usage();
558
602
  return;
559
603
  }
604
+ if (command === "setup-worker") {
605
+ if (!subcommand || !/^[0-9a-f-]{36}$/.test(subcommand)) throw new import_metadata.CoreFailure("INVALID_INPUT", "\u65E0\u6548\u7684\u5B89\u88C5\u64CD\u4F5C\u6807\u8BC6\u3002");
606
+ const config = (0, import_metadata.loadConfig)();
607
+ return (0, import_metadata.runInstallation)(config, () => installTools(config), subcommand);
608
+ }
560
609
  if (command === "setup") {
561
610
  const setupArgs = subcommand?.startsWith("--") && subcommand ? [subcommand, ...args] : args;
562
611
  return setup(hasFlag(setupArgs, "--json"), setupArgs);
@@ -566,6 +615,10 @@ async function main() {
566
615
  return doctor(hasFlag(doctorArgs, "--json"));
567
616
  }
568
617
  if (command === "auth") {
618
+ if (subcommand === "open") {
619
+ printJson(await new import_metadata.OnboardingService((0, import_metadata.loadConfig)(), __filename).openLogin());
620
+ return;
621
+ }
569
622
  if (subcommand === "login") return authLogin();
570
623
  if (subcommand === "status") return authStatus();
571
624
  }
@@ -611,6 +664,7 @@ async function main() {
611
664
  }
612
665
  if (command === "capabilities") return capabilities(subcommand);
613
666
  if (command === "mcp") {
667
+ process.env.APPTRACE_CLI_PATH = __filename;
614
668
  const { startMcpServer } = await import("@zhuixin/mcp");
615
669
  await startMcpServer((0, import_metadata.loadConfig)());
616
670
  return;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.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 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\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 0.1.0\n\nUsage:\n apptrace setup [--json] [--install] [--write-config <path>]\n apptrace doctor [--json]\n apptrace auth login\n apptrace auth status\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 `未知分析目标:${invalid.join(\", \")}。可选:${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: 在 iPhone App Store 获取或购买该应用后重试。\");\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): 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 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 (\n fs.existsSync(config.enginePath) &&\n fs.existsSync(config.ipatoolPath)\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\", `找不到安装脚本:${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 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 });\n if (code !== 0) {\n throw new CoreFailure(\n \"TOOL_INSTALL_FAILED\",\n `工具安装失败,退出码:${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 \"v1 自动安装器当前只支持 macOS arm64;其他平台请使用 APPTRACE_ENGINE_PATH 和 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\", `找不到安装脚本:${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 });\n if (code !== 0) {\n throw new CoreFailure(\n \"TOOL_INSTALL_FAILED\",\n `工具安装失败,退出码:${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 const config = loadConfig();\n if (hasFlag(args, \"--install\")) {\n await installTools(config, json);\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_IPATOOL_PATH: config.ipatoolPath\n };\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 const extension = path.extname(absolute).toLowerCase();\n if (extension === \".json\") {\n fs.mkdirSync(path.dirname(absolute), { recursive: true, mode: 0o700 });\n fs.writeFileSync(\n absolute,\n `${JSON.stringify(\n {\n mcpServers: {\n apptrace: {\n command: process.execPath,\n args: [serverEntry],\n env\n }\n }\n },\n null,\n 2\n )}\\n`,\n { mode: 0o600 }\n );\n } else if (extension === \".toml\") {\n fs.mkdirSync(path.dirname(absolute), { recursive: true, mode: 0o700 });\n const envToml = Object.entries(env)\n .map(([key, value]) => `${key} = ${JSON.stringify(value)}`)\n .join(\", \");\n const toml = [\n \"[mcp_servers.apptrace]\",\n `command = ${JSON.stringify(process.execPath)}`,\n `args = [${JSON.stringify(serverEntry)}]`,\n `env = { ${envToml} }`,\n \"\"\n ].join(\"\\n\");\n fs.writeFileSync(absolute, toml, { mode: 0o600 });\n } else {\n throw new CoreFailure(\n \"INVALID_CONFIG_FORMAT\",\n \"--write-config 只支持 .json 或 .toml 文件。\"\n );\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\n首次写入配置后重载一次 Agent 客户端。\n`);\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 code = await interactive(config.ipatoolPath, [\"auth\", \"login\"]);\n if (code !== 0) {\n throw new CoreFailure(\n \"AUTH_FAILED\",\n `ipatool 登录没有成功,退出码:${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\", \"缺少应用链接、ID 或 --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 \"用法: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 \"原生命令只支持 search 或 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 \"用法: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\") {\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 === \"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\", \"缺少应用链接或 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\", \"缺少 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\", \"缺少 job ID。\");\n return getJob(jobId);\n }\n if (command === \"search\") {\n if (!subcommand) throw new CoreFailure(\"TARGET_REQUIRED\", \"缺少 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\", \"缺少 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\", \"缺少 engine 文件路径。\");\n return installEngine(file, args);\n }\n if (command === \"compare\") {\n if (!subcommand) throw new CoreFailure(\"TARGET_REQUIRED\", \"缺少 target ID。\");\n return compare(subcommand, args[0], args[1], args);\n }\n if (command === \"capabilities\") return capabilities(subcommand);\n if (command === \"mcp\") {\n const { startMcpServer } = await import(\"@zhuixin/mcp\");\n await startMcpServer(loadConfig());\n return;\n }\n usage();\n throw new CoreFailure(\"UNKNOWN_COMMAND\", `未知命令:${command}`);\n}\n\nvoid main().catch((error) => {\n printError(error);\n process.exitCode = 1;\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AACA,gCAAsB;AACtB,qBAAe;AACf,uBAAiB;AACjB,sBASO;AACP,oBAMO;AAEP,IAAM,QAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,QAAc;AACrB,UAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAwBb;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,6CAAU,QAAQ,KAAK,IAAI,CAAC,2BAAO,MAAM,KAAK,IAAI,CAAC;AAAA,IACrD;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,wGAAuC;AAAA,IACvD;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,KACwB;AACxB,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,mBACJ,QAAQ,aAAa,WACrB,CAAC,QAAQ,MAAM,EAAE,SAAS,iBAAAA,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,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,iBAAAD,QAAK,KAAK,eAAe,OAAO;AAC9C,QAAM,WAAW,iBAAAA,QAAK,KAAK,OAAO,KAAK;AACvC,QAAM,aAAa,iBAAAA,QAAK,KAAK,OAAO,sBAAsB;AAC1D,QAAM,iBAAiB,iBAAAA,QAAK,KAAK,eAAe,cAAc;AAC9D,iBAAAE,QAAG,UAAU,iBAAAF,QAAK,KAAK,OAAO,KAAK,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACtE,iBAAAE,QAAG,UAAU,gBAAgB,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC7D,QAAM,WAAW,iBAAAF,QAAK;AAAA,IACpB;AAAA,IACA;AAAA,IACA,QAAQ,aAAa,UAAU,YAAY;AAAA,EAC7C;AACA,MAAI,QAAQ,aAAa,SAAS;AAChC,mBAAAE,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,mBAAAA,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,MACE,eAAAA,QAAG,WAAW,OAAO,UAAU,KAC/B,eAAAA,QAAG,WAAW,OAAO,WAAW,GAChC;AACA,YAAQ,OAAO,MAAM,8CAA8C;AACnE;AAAA,EACF;AACA,MAAI,QAAQ,aAAa,WAAW,QAAQ,SAAS,OAAO;AAC1D,UAAMC,UAAS,iBAAAH,QAAK,QAAQ,WAAW,mCAAmC;AAC1E,QAAI,CAAC,eAAAE,QAAG,WAAWC,OAAM,GAAG;AAC1B,YAAM,IAAI,4BAAY,0BAA0B,mDAAWA,OAAM,EAAE;AAAA,IACrE;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,MACD,yBAAyB,OAAO;AAAA,MAChC,oBAAoB,iBAAAH,QAAK,KAAK,OAAO,eAAe,OAAO;AAAA,MAC3D,GAAI,QAAQ,IAAI,0BACZ,EAAE,yBAAyB,QAAQ,IAAI,wBAAwB,IAC/D,CAAC;AAAA,IACP,CAAC;AACD,QAAII,UAAS,GAAG;AACd,YAAM,IAAI;AAAA,QACR;AAAA,QACA,qEAAcA,SAAQ,SAAS;AAAA,MACjC;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,iBAAAL,QAAK,QAAQ,WAAW,kCAAkC;AACzE,MAAI,CAAC,eAAAE,QAAG,WAAW,MAAM,GAAG;AAC1B,UAAM,IAAI,4BAAY,0BAA0B,mDAAW,MAAM,EAAE;AAAA,EACrE;AACA,UAAQ,OAAO;AAAA,IACb;AAAA,EACF;AACA,QAAM,OAAO,MAAM,YAAY,aAAa,CAAC,MAAM,GAAG;AAAA,IACpD,yBAAyB,OAAO;AAAA,IAChC,oBAAoB,iBAAAF,QAAK,KAAK,OAAO,eAAe,OAAO;AAAA,EAC7D,CAAC;AACD,MAAI,SAAS,GAAG;AACd,UAAM,IAAI;AAAA,MACR;AAAA,MACA,qEAAc,QAAQ,SAAS;AAAA,IACjC;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,QAAM,aAAS,4BAAW;AAC1B,MAAI,QAAQ,MAAM,WAAW,GAAG;AAC9B,UAAM,aAAa,QAAQ,IAAI;AAAA,EACjC;AACA,QAAM,OAAO,IAAI;AACjB,MAAI,QAAQ,CAAC,WAAY;AACzB,QAAM,cAAc,gBAAgB;AACpC,QAAM,MAA8B;AAAA,IAClC,yBAAyB,OAAO;AAAA,IAChC,uBAAuB,OAAO;AAAA,EAChC;AACA,MAAI,eAAAE,QAAG,WAAW,OAAO,UAAU,GAAG;AACpC,QAAI,uBAAuB,OAAO;AAAA,EACpC;AAGA,MAAI,eAAAA,QAAG,WAAW,OAAO,kBAAkB,GAAG;AAC5C,QAAI,gCAAgC,OAAO;AAAA,EAC7C;AACA,MAAI,YAAY;AACd,UAAM,WAAW,iBAAAF,QAAK,QAAQ,UAAU;AACxC,UAAM,YAAY,iBAAAA,QAAK,QAAQ,QAAQ,EAAE,YAAY;AACrD,QAAI,cAAc,SAAS;AACzB,qBAAAE,QAAG,UAAU,iBAAAF,QAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACrE,qBAAAE,QAAG;AAAA,QACD;AAAA,QACA,GAAG,KAAK;AAAA,UACN;AAAA,YACE,YAAY;AAAA,cACV,UAAU;AAAA,gBACR,SAAS,QAAQ;AAAA,gBACjB,MAAM,CAAC,WAAW;AAAA,gBAClB;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,UACA;AAAA,UACA;AAAA,QACF,CAAC;AAAA;AAAA,QACD,EAAE,MAAM,IAAM;AAAA,MAChB;AAAA,IACF,WAAW,cAAc,SAAS;AAChC,qBAAAA,QAAG,UAAU,iBAAAF,QAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACrE,YAAM,UAAU,OAAO,QAAQ,GAAG,EAC/B,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,GAAG,MAAM,KAAK,UAAU,KAAK,CAAC,EAAE,EACzD,KAAK,IAAI;AACZ,YAAM,OAAO;AAAA,QACX;AAAA,QACA,aAAa,KAAK,UAAU,QAAQ,QAAQ,CAAC;AAAA,QAC7C,WAAW,KAAK,UAAU,WAAW,CAAC;AAAA,QACtC,WAAW,OAAO;AAAA,QAClB;AAAA,MACF,EAAE,KAAK,IAAI;AACX,qBAAAE,QAAG,cAAc,UAAU,MAAM,EAAE,MAAM,IAAM,CAAC;AAAA,IAClD,OAAO;AACL,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;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;AACD;AAOA,SAAS,kBAA0B;AACjC,MAAI;AACF,WAAO,gBAAgB,cAAc;AAAA,EACvC,QAAQ;AACN,WAAO,iBAAAF,QAAK,QAAQ,WAAW,yBAAyB;AAAA,EAC1D;AACF;AAEA,eAAe,YAA2B;AACxC,QAAM,aAAS,4BAAW;AAC1B,QAAM,OAAO,MAAM,YAAY,OAAO,aAAa,CAAC,QAAQ,OAAO,CAAC;AACpE,MAAI,SAAS,GAAG;AACd,UAAM,IAAI;AAAA,MACR;AAAA,MACA,6EAAsB,QAAQ,SAAS;AAAA,IACzC;AAAA,EACF;AACA,QAAM,WAAW;AACnB;AAEA,eAAe,aAA4B;AACzC,QAAM,SAAS,OAAO,SAAS;AAC7B,UAAM,SAAS,MAAM,KAAK,OAAO,uBAAS,eAAe,EAAE,OAAO,KAAK,CAAC;AACxE,cAAU,MAAM;AAAA,EAClB,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,oEAAuB;AAAA,EAClE;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,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,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,qDAAa;AACnE,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,8BAAe;AACzE,WAAO,OAAO,UAAU;AAAA,EAC1B;AACA,MAAI,YAAY,UAAU,eAAe,OAAO;AAC9C,UAAM,QAAQ,WAAW,IAAI;AAC7B,QAAI,CAAC,MAAO,OAAM,IAAI,4BAAY,gBAAgB,2BAAY;AAC9D,WAAO,OAAO,KAAK;AAAA,EACrB;AACA,MAAI,YAAY,UAAU;AACxB,QAAI,CAAC,WAAY,OAAM,IAAI,4BAAY,mBAAmB,8BAAe;AACzE,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,8BAAe;AACzE,WAAO,SAAS,UAAU;AAAA,EAC5B;AACA,MAAI,YAAY,YAAY,eAAe,WAAW;AACpD,UAAM,OAAO,KAAK,CAAC;AACnB,QAAI,CAAC,KAAM,OAAM,IAAI,4BAAY,mBAAmB,oDAAiB;AACrE,WAAO,cAAc,MAAM,IAAI;AAAA,EACjC;AACA,MAAI,YAAY,WAAW;AACzB,QAAI,CAAC,WAAY,OAAM,IAAI,4BAAY,mBAAmB,8BAAe;AACzE,WAAO,QAAQ,YAAY,KAAK,CAAC,GAAG,KAAK,CAAC,GAAG,IAAI;AAAA,EACnD;AACA,MAAI,YAAY,eAAgB,QAAO,aAAa,UAAU;AAC9D,MAAI,YAAY,OAAO;AACrB,UAAM,EAAE,eAAe,IAAI,MAAM,OAAO,cAAc;AACtD,UAAM,mBAAe,4BAAW,CAAC;AACjC;AAAA,EACF;AACA,QAAM;AACN,QAAM,IAAI,4BAAY,mBAAmB,iCAAQ,OAAO,EAAE;AAC5D;AAEA,KAAK,KAAK,EAAE,MAAM,CAAC,UAAU;AAC3B,aAAW,KAAK;AAChB,UAAQ,WAAW;AACrB,CAAC;","names":["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 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>]\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 `未知分析目标:${invalid.join(\", \")}。可选:${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: 在 iPhone App Store 获取或购买该应用后重试。\");\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\", `找不到安装脚本:${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 `工具安装失败,退出码:${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 \"自动安装器当前支持 macOS arm64 和 Windows x64;其他平台请使用 APPTRACE_ENGINE_PATH 和 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\", `找不到安装脚本:${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 `工具安装失败,退出码:${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\", \"安装 Skill 时请同时指定 --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\n首次写入配置后重载一次 Agent 客户端。\n`);\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(${config.ipatoolPath})。请先运行 apptrace setup --install,成功后再运行 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 登录没有成功,退出码:${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\", \"缺少应用链接、ID 或 --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 \"用法: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 \"原生命令只支持 search 或 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 \"用法: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\", \"无效的安装操作标识。\");\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\", \"缺少应用链接或 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\", \"缺少 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\", \"缺少 job ID。\");\n return getJob(jobId);\n }\n if (command === \"search\") {\n if (!subcommand) throw new CoreFailure(\"TARGET_REQUIRED\", \"缺少 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\", \"缺少 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\", \"缺少 engine 文件路径。\");\n return installEngine(file, args);\n }\n if (command === \"compare\") {\n if (!subcommand) throw new CoreFailure(\"TARGET_REQUIRED\", \"缺少 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\", `未知命令:${command}`);\n}\n\nvoid main().catch((error) => {\n printError(error);\n process.exitCode = 1;\n});\n","{\n \"name\": \"@zhuixin/cli\",\n \"version\": \"0.2.1\",\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 只支持 .json 或 .toml 文件。\");\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"]}
@@ -0,0 +1,116 @@
1
+ param(
2
+ [string]$WorkspaceRoot = $env:APPTRACE_WORKSPACE_ROOT
3
+ )
4
+
5
+ $ErrorActionPreference = "Stop"
6
+ $ProgressPreference = "SilentlyContinue"
7
+ $ProductRoot = Split-Path -Parent $PSScriptRoot
8
+ if (-not $WorkspaceRoot) {
9
+ $WorkspaceRoot = Join-Path $env:APPDATA "AppTrace\workspace"
10
+ }
11
+ $Tools = if ($env:APPTRACE_TOOLS_DIR) { $env:APPTRACE_TOOLS_DIR } else { Join-Path $WorkspaceRoot "tools" }
12
+ $Downloads = Join-Path $Tools "downloads"
13
+ New-Item -ItemType Directory -Force -Path $Tools, $Downloads | Out-Null
14
+ # Resolve 8.3 paths before PowerShell archive/move cmdlets process their parents.
15
+ $Tools = (Get-Item -LiteralPath $Tools).FullName
16
+ $Downloads = (Get-Item -LiteralPath $Downloads).FullName
17
+
18
+ function Assert-ToolPath([string]$Target) {
19
+ $RootPath = [IO.Path]::GetFullPath($Tools).TrimEnd('\') + '\'
20
+ $TargetPath = [IO.Path]::GetFullPath($Target)
21
+ if (-not $TargetPath.StartsWith($RootPath, [StringComparison]::OrdinalIgnoreCase)) {
22
+ throw "Refusing filesystem operation outside tools: $TargetPath"
23
+ }
24
+ }
25
+
26
+ function Remove-ToolDirectory([string]$Target) {
27
+ Assert-ToolPath $Target
28
+ if (Test-Path -LiteralPath $Target) { Remove-Item -LiteralPath $Target -Recurse -Force }
29
+ }
30
+
31
+ function Get-Sha256([string]$Path) {
32
+ (Get-FileHash -Algorithm SHA256 -Path $Path).Hash.ToLowerInvariant()
33
+ }
34
+
35
+ function Assert-Sha256([string]$Path, [string]$Expected) {
36
+ if (-not $Expected) { return }
37
+ $Actual = Get-Sha256 $Path
38
+ if ($Actual -ne $Expected.ToLowerInvariant()) {
39
+ throw "SHA-256 mismatch for $Path. Expected $Expected, got $Actual"
40
+ }
41
+ }
42
+
43
+ function Download([string]$Url, [string]$Destination) {
44
+ if (Test-Path $Destination) { return }
45
+ Write-Host "Downloading $Url"
46
+ Invoke-WebRequest -Uri $Url -OutFile "$Destination.part" -UseBasicParsing
47
+ [IO.File]::Move("$Destination.part", $Destination)
48
+ }
49
+
50
+ # Ghidra 12.1.4
51
+ $GhidraZip = Join-Path $Downloads "ghidra_12.1.4_PUBLIC_20260921.zip"
52
+ $GhidraUrl = "https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_12.1.4_build/ghidra_12.1.4_PUBLIC_20260921.zip"
53
+ $GhidraSha = "ddac49f903da9d5bac833e5cc79395098b9c33cfd3279be5f31bd00387d2d4db"
54
+ Download $GhidraUrl $GhidraZip
55
+ Assert-Sha256 $GhidraZip $GhidraSha
56
+ $GhidraHome = Join-Path $Tools "ghidra_12.1.4_PUBLIC"
57
+ if (-not (Test-Path $GhidraHome)) {
58
+ Expand-Archive -Path $GhidraZip -DestinationPath $Tools -Force
59
+ }
60
+
61
+ # Temurin JDK 21 x64
62
+ $JdkApi = "https://api.adoptium.net/v3/assets/latest/21/hotspot?architecture=x64&image_type=jdk&os=windows&vendor=eclipse"
63
+ $JdkMeta = Invoke-RestMethod -Uri $JdkApi -UseBasicParsing
64
+ if (-not $JdkMeta -or -not $JdkMeta[0].binary.package) {
65
+ throw "Adoptium returned no Windows x64 JDK package."
66
+ }
67
+ $JdkPackage = $JdkMeta[0].binary.package
68
+ $JdkZip = Join-Path $Downloads $JdkPackage.name
69
+ Download $JdkPackage.link $JdkZip
70
+ Assert-Sha256 $JdkZip $JdkPackage.checksum
71
+ $JdkHome = Join-Path $Tools "jdk"
72
+ if (-not (Test-Path (Join-Path $JdkHome "bin\java.exe"))) {
73
+ $TempJdk = Join-Path $Tools "jdk.tmp"
74
+ Remove-ToolDirectory $TempJdk
75
+ New-Item -ItemType Directory -Force -Path $TempJdk | Out-Null
76
+ Expand-Archive -Path $JdkZip -DestinationPath $TempJdk -Force
77
+ $Java = Get-ChildItem -Path $TempJdk -Recurse -Filter java.exe |
78
+ Where-Object { $_.FullName -match "\\bin\\java.exe$" } |
79
+ Select-Object -First 1
80
+ if (-not $Java) { throw "java.exe not found in JDK archive." }
81
+ $ExtractedHome = Split-Path -Parent (Split-Path -Parent $Java.FullName)
82
+ Remove-ToolDirectory $JdkHome
83
+ Assert-ToolPath $ExtractedHome
84
+ Assert-ToolPath $JdkHome
85
+ Move-Item -LiteralPath $ExtractedHome -Destination $JdkHome
86
+ Remove-ToolDirectory $TempJdk
87
+ }
88
+
89
+ # ipatool 2.7.0 Windows x64
90
+ $IpatoolUrl = "https://github.com/majd/ipatool/releases/download/v2.7.0/ipatool-2.7.0-windows-amd64.tar.gz"
91
+ $IpatoolZip = Join-Path $Downloads "ipatool-2.7.0-windows-amd64.tar.gz"
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
+ }
112
+
113
+ Write-Host "Ghidra: $GhidraHome"
114
+ Write-Host "JDK: $JdkHome"
115
+ Write-Host "ipatool: $(Join-Path $IpatoolHome 'bin\ipatool.exe')"
116
+ Write-Host "Windows tool bootstrap complete."
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ WORKSPACE_ROOT="${APPTRACE_WORKSPACE_ROOT:-$ROOT/.apptrace/default}"
6
+ TOOLS="${APPTRACE_TOOLS_DIR:-$WORKSPACE_ROOT/tools}"
7
+ DOWNLOADS="${APPTRACE_DOWNLOADS_DIR:-$TOOLS/downloads}"
8
+ mkdir -p "$DOWNLOADS" "$TOOLS"
9
+
10
+ # GitHub Release assets are often slow or unreachable from mainland China.
11
+ # The mirror only changes transport; the official SHA-256 below is still enforced.
12
+ GITHUB_MIRROR="${GITHUB_MIRROR:-https://gh-proxy.com/}"
13
+
14
+ say() {
15
+ printf '\n==> %s\n' "$*"
16
+ }
17
+
18
+ fail() {
19
+ printf 'ERROR: %s\n' "$*" >&2
20
+ exit 1
21
+ }
22
+
23
+ sha256() {
24
+ if command -v openssl >/dev/null 2>&1; then
25
+ openssl dgst -sha256 "$1" | awk '{print $NF}'
26
+ else
27
+ LC_ALL=C shasum -a 256 "$1" | awk '{print $1}'
28
+ fi
29
+ }
30
+
31
+ download() {
32
+ local url="$1"
33
+ local dest="$2"
34
+ if [ -s "$dest" ]; then
35
+ printf 'cached: %s\n' "$dest"
36
+ return 0
37
+ fi
38
+
39
+ local candidates=("$url")
40
+ if [ -n "$GITHUB_MIRROR" ]; then
41
+ case "$url" in
42
+ https://github.com/*)
43
+ candidates=("$GITHUB_MIRROR$url" "$url")
44
+ ;;
45
+ esac
46
+ fi
47
+
48
+ local candidate
49
+ for candidate in "${candidates[@]}"; do
50
+ printf 'download: %s\n' "$candidate"
51
+ if curl -L --fail --silent --show-error --retry 3 --connect-timeout 20 \
52
+ --max-time 900 -o "$dest.part" "$candidate"; then
53
+ mv "$dest.part" "$dest"
54
+ printf 'saved: %s (%s bytes)\n' "$dest" "$(wc -c < "$dest" | tr -d ' ')"
55
+ return 0
56
+ fi
57
+ rm -f "$dest.part"
58
+ done
59
+ fail "download failed for all URLs: $url"
60
+ }
61
+
62
+ verify_sha256() {
63
+ local file="$1"
64
+ local expected="$2"
65
+ local actual
66
+ actual="$(sha256 "$file")"
67
+ if [ "$actual" != "$expected" ]; then
68
+ fail "SHA-256 mismatch for $file: expected $expected, got $actual"
69
+ fi
70
+ printf 'sha256 ok: %s\n' "$(basename "$file")"
71
+ }
72
+
73
+ # ---------------------------------------------------------------------------
74
+ # Ghidra 12.1.4
75
+ # ---------------------------------------------------------------------------
76
+ GHIDRA_ZIP="$DOWNLOADS/ghidra_12.1.4_PUBLIC_20260921.zip"
77
+ GHIDRA_SHA256="ddac49f903da9d5bac833e5cc79395098b9c33cfd3279be5f31bd00387d2d4db"
78
+ GHIDRA_URL="https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_12.1.4_build/ghidra_12.1.4_PUBLIC_20260921.zip"
79
+
80
+ say "Ghidra 12.1.4"
81
+ download "$GHIDRA_URL" "$GHIDRA_ZIP"
82
+ verify_sha256 "$GHIDRA_ZIP" "$GHIDRA_SHA256"
83
+ if [ ! -d "$TOOLS/ghidra_12.1.4_PUBLIC" ]; then
84
+ say "Extracting Ghidra"
85
+ unzip -q "$GHIDRA_ZIP" -d "$TOOLS"
86
+ fi
87
+
88
+ if [ "$(uname -s)" = "Darwin" ] && [ "$(uname -m)" = "arm64" ]; then
89
+ say "Ghidra macOS arm64 natives"
90
+ APPTRACE_TOOLS_DIR="$TOOLS" APPTRACE_WORKSPACE_ROOT="$WORKSPACE_ROOT" \
91
+ "$ROOT/scripts/build-ghidra-arm64.sh"
92
+ fi
93
+
94
+ # ---------------------------------------------------------------------------
95
+ # Temurin JDK 21 for macOS arm64
96
+ # ---------------------------------------------------------------------------
97
+ JDK_JSON="$DOWNLOADS/adoptium-jdk21-mac-aarch64.json"
98
+ JDK_API="https://api.adoptium.net/v3/assets/latest/21/hotspot?architecture=aarch64&image_type=jdk&os=mac&vendor=eclipse"
99
+
100
+ say "Temurin JDK 21 (macOS arm64)"
101
+ download "$JDK_API" "$JDK_JSON"
102
+
103
+ JDK_LINE="$(python3 - "$JDK_JSON" <<'PY'
104
+ import json
105
+ import sys
106
+
107
+ with open(sys.argv[1], "r", encoding="utf-8") as handle:
108
+ assets = json.load(handle)
109
+ if not assets:
110
+ raise SystemExit("Adoptium returned no assets")
111
+ package = assets[0]["binary"]["package"]
112
+ print(package["name"])
113
+ print(package["checksum"])
114
+ print(package["link"])
115
+ PY
116
+ )"
117
+
118
+ JDK_NAME="$(printf '%s\n' "$JDK_LINE" | sed -n '1p')"
119
+ JDK_SHA256="$(printf '%s\n' "$JDK_LINE" | sed -n '2p')"
120
+ JDK_URL="$(printf '%s\n' "$JDK_LINE" | sed -n '3p')"
121
+ [ -n "$JDK_NAME" ] && [ -n "$JDK_SHA256" ] && [ -n "$JDK_URL" ] || \
122
+ fail "Unable to parse Adoptium metadata"
123
+
124
+ JDK_TGZ="$DOWNLOADS/$JDK_NAME"
125
+ download "$JDK_URL" "$JDK_TGZ"
126
+ verify_sha256 "$JDK_TGZ" "$JDK_SHA256"
127
+
128
+ if [ ! -x "$TOOLS/jdk/bin/java" ]; then
129
+ say "Extracting JDK"
130
+ rm -rf "$TOOLS/jdk.tmp"
131
+ mkdir -p "$TOOLS/jdk.tmp"
132
+ LC_ALL=C tar -xzf "$JDK_TGZ" -C "$TOOLS/jdk.tmp"
133
+ JDK_HOME="$(find "$TOOLS/jdk.tmp" -type f -path '*/Contents/Home/bin/java' -print | sed -n '1p' | sed 's#/bin/java$##')"
134
+ [ -n "$JDK_HOME" ] || fail "JDK Contents/Home not found in archive"
135
+ rm -rf "$TOOLS/jdk"
136
+ mv "$JDK_HOME" "$TOOLS/jdk"
137
+ rm -rf "$TOOLS/jdk.tmp"
138
+ fi
139
+
140
+ # ---------------------------------------------------------------------------
141
+ # ipatool 2.7.0 for macOS arm64
142
+ # ---------------------------------------------------------------------------
143
+ IPATOOL_TGZ="$DOWNLOADS/ipatool-2.7.0-macos-arm64.tar.gz"
144
+ IPATOOL_URL="https://github.com/majd/ipatool/releases/download/v2.7.0/ipatool-2.7.0-macos-arm64.tar.gz"
145
+ IPATOOL_SHA256="5ae100d4520cf4b3f4374239e5925a9ad9a8dc02abc16bae27479e7ae99aca33"
146
+
147
+ say "ipatool 2.7.0 (macOS arm64)"
148
+ download "$IPATOOL_URL" "$IPATOOL_TGZ"
149
+ verify_sha256 "$IPATOOL_TGZ" "$IPATOOL_SHA256"
150
+
151
+ if [ ! -x "$TOOLS/ipatool/bin/ipatool" ]; then
152
+ say "Extracting ipatool"
153
+ rm -rf "$TOOLS/ipatool"
154
+ mkdir -p "$TOOLS/ipatool"
155
+ LC_ALL=C tar -xzf "$IPATOOL_TGZ" -C "$TOOLS/ipatool"
156
+ IPATOOL_BIN="$(find "$TOOLS/ipatool" -type f -perm -111 -name 'ipatool*' -print | sed -n '1p')"
157
+ [ -n "$IPATOOL_BIN" ] || fail "ipatool binary not found after extraction"
158
+ mkdir -p "$TOOLS/ipatool/bin"
159
+ ln -sf "$(basename "$IPATOOL_BIN")" "$TOOLS/ipatool/bin/ipatool"
160
+ xattr -dr com.apple.quarantine "$TOOLS/ipatool" 2>/dev/null || true
161
+ codesign --force --sign - "$IPATOOL_BIN" 2>/dev/null || true
162
+ fi
163
+
164
+ say "Installed versions"
165
+ "$TOOLS/jdk/bin/java" -version
166
+ "$TOOLS/ipatool/bin/ipatool" --version || true
167
+ printf 'Ghidra: %s\n' "$TOOLS/ghidra_12.1.4_PUBLIC"
168
+ printf 'JDK: %s\n' "$TOOLS/jdk"
169
+ printf 'ipatool:%s\n' "$TOOLS/ipatool/bin/ipatool"
@@ -0,0 +1,129 @@
1
+ ---
2
+ name: apptrace
3
+ description: Analyze a locally authorized application package with the AppTrace MCP server. Use for App Store metadata, IPA acquisition, application integrity, technology-stack discovery, route maps, network/storage findings, native inspection, function search, decompilation, version comparison, and evidence reports. Do not use for ordinary source-repository architecture analysis.
4
+ ---
5
+
6
+ # AppTrace
7
+
8
+ AppTrace is an agent-native local application analysis workspace. The product
9
+ surface is Target -> Artifact -> Finding -> Evidence -> Report. Analysis engines
10
+ are implementation details.
11
+
12
+ ## Start here
13
+
14
+ If AppTrace tools are not available yet and the user asks to set up AppTrace,
15
+ use the available shell to install `@zhuixin/cli` and run
16
+ `apptrace setup --write-config <Codex config.toml path> --install-skill`.
17
+ Use the client's actual configuration path (normally `~/.codex/config.toml`).
18
+ The fixed installer/config writer handles paths and merging; do not invent
19
+ installation scripts. Explain once that the client must reload its MCP servers.
20
+ Do not claim tools are available until they are discovered. Other clients can
21
+ use their normal JSON configuration path without `--install-skill`.
22
+
23
+ When the user provides an App Store URL or App ID and asks what the app uses,
24
+ what it contains, or for a report:
25
+
26
+ 1. Call `analyze_app` with the source and the smallest useful set of goals.
27
+ If it returns `awaiting_setup`, preserve the complete `resume.arguments`.
28
+ Follow `next_tool` automatically: `prepare_environment` installs the fixed
29
+ toolchain, `open_login` opens the user's local Windows sign-in window, and
30
+ `get_setup_status(wait_seconds=20)` waits for progress. Report meaningful
31
+ stage changes; never ask the user to run installation commands that a tool
32
+ can run. When state is `ready`, call `analyze_app` with the saved arguments.
33
+ This is agent-driven resumption, not an independently scheduled analysis.
34
+ When analysis is running it returns `runId`.
35
+ 2. Use `get_analysis_run` to wait for or retrieve the completed result.
36
+ 3. Prefer goals `tech_stack`, `feature_map`, and `report` for a first pass.
37
+ 4. Add `network` when the user asks about APIs or data flow.
38
+ 5. Add `native_risk` when the user asks about native code, encryption, or
39
+ decompilation.
40
+ 6. Answer from the returned structured summary first. Only inspect raw
41
+ Evidence when the user asks a specific follow-up question.
42
+
43
+ ## Authentication
44
+
45
+ If a tool returns `AUTH_REQUIRED` or `KEYCHAIN_LOCKED`:
46
+
47
+ 1. Call `open_login`. Tell the user to enter credentials only in the local window.
48
+ 2. Poll `get_setup_status(wait_seconds=20, require_account=true)` while `login_pending`; do not open
49
+ repeated windows. When ready, resume the saved request without asking the
50
+ user to repeat their goal.
51
+ 3. Never ask for, capture, screenshot, log, or relay passwords, verification
52
+ codes, keychain passphrases, cookies, or tokens. Only the user handles them.
53
+ 4. If a login attempt is cancelled or fails, explain the state and wait for the
54
+ user to request another attempt. Do not repeatedly reopen a dismissed window.
55
+ 5. On hosts without automatic login windows, give the single fallback command
56
+ `apptrace auth login` and resume after the user confirms completion.
57
+
58
+ For `failed`, distinguish account-check failure from installation failure.
59
+ Do not infer that the user is logged out from a timeout or unknown response.
60
+ Inspect the installation log only for tool-install failures, then retry
61
+ `prepare_environment(retry=true)` at most once after addressing the cause.
62
+ Never read or send login terminal output to an analysis tool.
63
+
64
+ If a tool returns `LICENSE_REQUIRED`, explain that the Apple account needs to
65
+ obtain the app from the iPhone App Store first. Do not attempt to bypass it.
66
+
67
+ ## Evidence rules
68
+
69
+ - Treat static observations as observations, not proof of runtime behavior.
70
+ - Keep source paths, line/column ranges, hashes, and Evidence IDs.
71
+ - Do not dump complete semantic graphs or raw JSON into chat.
72
+ - Page large Evidence results and cite the Evidence ID.
73
+ - Clearly separate observed facts, inference, unknown coverage, and limitations.
74
+ - A bounded search is a bounded negative result, not proof of absence.
75
+
76
+ ## Native analysis
77
+
78
+ - Check the encryption state before suggesting decompilation.
79
+ - `cryptid != 0` means the code region is declared encrypted. Do not treat
80
+ pseudocode from that region as trustworthy original implementation.
81
+ - Never ask the user to guess a function name. Call `search_native` first.
82
+ - Select an exact procedure name from the search result, then call
83
+ `decompile_function`.
84
+ - Pseudocode is reconstructed analysis output, not original source.
85
+
86
+ ## Structured findings
87
+
88
+ Use `search_findings` for routes, modules, assets, network endpoints, storage,
89
+ native symbols, risks, and limitations. It reads the persisted index and returns
90
+ source locations and Evidence IDs.
91
+
92
+ Prefer a narrow query and type filter. If the result is incomplete, use `limit`
93
+ and `offset`; do not assume a bounded search proves absence.
94
+
95
+ ## Version comparison
96
+
97
+ Use `compare_versions` with two artifact IDs from the same target. Compare
98
+ technology, routes, modules, assets and network candidates as static findings.
99
+ Added/removed coverage is only complete when both artifacts have complete
100
+ Evidence for that finding type.
101
+
102
+ ## Safety
103
+
104
+ - Analyze only apps the user owns or is authorized to inspect.
105
+ - Do not execute the target app, installer, or extracted scripts.
106
+ - Do not access services discovered in the app.
107
+ - Do not reproduce third-party credentials, unlicensed assets, or private code.
108
+ - Do not claim FairPlay decryption or complete source recovery.
109
+
110
+ ## Useful tools
111
+
112
+ - `prepare_environment`
113
+ - `get_setup_status`
114
+ - `open_login`
115
+ - `analyze_app`
116
+ - `get_analysis_run`
117
+ - `auth_status`
118
+ - `list_targets`
119
+ - `get_job`
120
+ - `search_findings`
121
+ - `search_native`
122
+ - `decompile_function`
123
+ - `list_evidence`
124
+ - `compare_versions`
125
+ - `generate_report`
126
+ - `list_capabilities`
127
+
128
+ Use low-level capabilities only when the user's question requires a focused
129
+ follow-up.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhuixin/cli",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "license": "Apache-2.0",
5
5
  "description": "AppTrace CLI for evidence-backed application analysis",
6
6
  "repository": {
@@ -35,9 +35,10 @@
35
35
  ],
36
36
  "dependencies": {
37
37
  "rea-agents": "6.4.0",
38
- "@zhuixin/mcp": "0.1.2",
39
- "@zhuixin/shared": "0.1.2",
40
- "@zhuixin/metadata": "0.1.2"
38
+ "smol-toml": "^1.9.0",
39
+ "@zhuixin/metadata": "0.2.1",
40
+ "@zhuixin/mcp": "0.2.1",
41
+ "@zhuixin/shared": "0.2.1"
41
42
  },
42
43
  "devDependencies": {
43
44
  "tsup": "^8.5.0"