@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 +123 -69
- package/dist/index.js.map +1 -1
- package/dist/scripts/bootstrap-tools.ps1 +116 -0
- package/dist/scripts/bootstrap-tools.sh +169 -0
- package/dist/skills/apptrace/SKILL.md +129 -0
- package/package.json +5 -4
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
|
|
29
|
-
var
|
|
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
|
|
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(
|
|
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 =
|
|
166
|
-
const javaHome =
|
|
167
|
-
const ghidraHome =
|
|
168
|
-
const ghidraUserHome =
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const launcher =
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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 =
|
|
216
|
-
if (!
|
|
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:
|
|
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
|
-
"
|
|
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 =
|
|
253
|
-
if (!
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
350
|
+
APPTRACE_CLI_PATH: __filename
|
|
285
351
|
};
|
|
286
|
-
|
|
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 (
|
|
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 =
|
|
294
|
-
|
|
295
|
-
if (
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
|
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
|
|
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":"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","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
|
|
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
|
-
"
|
|
39
|
-
"@zhuixin/
|
|
40
|
-
"@zhuixin/
|
|
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"
|