wowdump 0.2.1 → 0.3.2

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.
Files changed (63) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +17 -111
  3. package/dist/adapters/reader.js +33 -0
  4. package/dist/analysis/disassemble.js +77 -0
  5. package/dist/{frida-runtime.js → analysis/frida-runtime.js} +3 -25
  6. package/dist/analysis/runtime-script.js +36 -0
  7. package/dist/cli.js +563 -0
  8. package/dist/core/profile-engine.js +238 -0
  9. package/dist/frida-worker.js +99 -0
  10. package/dist/reader/broker.js +475 -0
  11. package/dist/reader/client.js +1 -0
  12. package/dist/reader/launcher.js +219 -0
  13. package/dist/reader/main.js +100 -0
  14. package/dist/reader/protocol.js +1 -0
  15. package/dist/reader/windows.js +242 -0
  16. package/dist/reader-main.js +2 -0
  17. package/dist/toolchain.js +123 -0
  18. package/package.json +19 -37
  19. package/skills/wowdump/SKILL.md +22 -0
  20. package/skills/wowdump/references/commands.md +63 -0
  21. package/skills/wowdump/references/disassemble.md +18 -0
  22. package/skills/wowdump/references/dynamic.md +54 -0
  23. package/skills/wowdump/references/evidence-workflow.md +41 -0
  24. package/skills/wowdump/references/profiles.md +34 -0
  25. package/skills/wowdump/references/request-schema.md +28 -0
  26. package/skills/wowdump/references/workflow.md +44 -0
  27. package/skills/wowdump/scripts/dynamic-session.js +133 -0
  28. package/dist/agent.js +0 -1335
  29. package/dist/analysis-path.js +0 -38
  30. package/dist/analysis-process-log.js +0 -146
  31. package/dist/broker-client.js +0 -411
  32. package/dist/broker-codec.js +0 -148
  33. package/dist/broker-core.js +0 -1045
  34. package/dist/broker-gateway.js +0 -447
  35. package/dist/broker-ledger.js +0 -196
  36. package/dist/broker-main.js +0 -291
  37. package/dist/broker-protocol.js +0 -119
  38. package/dist/broker-runtime.js +0 -1283
  39. package/dist/broker-server.js +0 -466
  40. package/dist/build-bundle-loader.js +0 -183
  41. package/dist/build-bundle.js +0 -11
  42. package/dist/discovery.js +0 -59
  43. package/dist/dry-run.js +0 -38
  44. package/dist/error-log.js +0 -71
  45. package/dist/focus-errors.js +0 -63
  46. package/dist/focus-service.js +0 -1855
  47. package/dist/focused-session.js +0 -1357
  48. package/dist/mcp-main.js +0 -51
  49. package/dist/mcp.js +0 -924
  50. package/dist/observability.js +0 -41
  51. package/dist/process-log-lock.js +0 -195
  52. package/dist/processes.js +0 -47
  53. package/dist/runtime-config.js +0 -399
  54. package/dist/session.js +0 -145
  55. package/dist/storage.js +0 -12
  56. package/dist/wow-analysis.js +0 -1430
  57. package/resources/builds/retail/12.0.7.68974/build-profile.json +0 -290
  58. package/resources/builds/retail/12.0.7.68974/data-sources.json +0 -1633
  59. package/resources/builds/retail/12.0.7.68974/lua-targets.jsonl +0 -5130
  60. package/resources/builds/retail/12.0.7.68974/manifest.json +0 -63
  61. package/resources/builds/retail/12.0.7.68974/signatures.json +0 -260
  62. /package/dist/{adapters.js → core/build-adapters.js} +0 -0
  63. /package/dist/{types.js → core/types.js} +0 -0
package/dist/discovery.js DELETED
@@ -1,59 +0,0 @@
1
- import { existsSync, readFileSync, readdirSync } from "node:fs";
2
- import { basename, join, resolve } from "node:path";
3
- function parseBuildInfo(file) {
4
- if (!existsSync(file))
5
- return [];
6
- const lines = readFileSync(file, "utf8").split(/\r?\n/).filter(Boolean);
7
- if (lines.length < 2)
8
- return [];
9
- const headers = lines[0].split("|").map(header => header.split("!")[0]);
10
- return lines.slice(1).map(line => {
11
- const values = line.split("|");
12
- return Object.fromEntries(headers.map((header, i) => [header, values[i] ?? ""]));
13
- });
14
- }
15
- function flavorFromDirectory(name) {
16
- return name.startsWith("_") && name.endsWith("_") ? name.slice(1, -1) : name;
17
- }
18
- export function discoverInstalls(gameRoot) {
19
- const rootBuildRows = parseBuildInfo(join(gameRoot, ".build.info"));
20
- let entries = [];
21
- try {
22
- entries = readdirSync(gameRoot, { withFileTypes: true })
23
- .filter(entry => entry.isDirectory())
24
- .map(entry => join(gameRoot, entry.name));
25
- }
26
- catch {
27
- return [];
28
- }
29
- return entries.flatMap(root => {
30
- const executable = join(root, "Wow.exe");
31
- if (!existsSync(executable))
32
- return [];
33
- const flavor = flavorFromDirectory(basename(root));
34
- const wantedProduct = flavor.includes("classic") || flavor.includes("anniversary")
35
- ? "wow_classic" : "wow";
36
- const rootBuild = rootBuildRows.find(row => row.Product === wantedProduct) ?? rootBuildRows[0] ?? {};
37
- const version = rootBuild.Version ?? rootBuild.ProductVersion ?? rootBuild.Build;
38
- const fileVersion = rootBuild.ProductVersion;
39
- const build = {
40
- product: rootBuild.Product ?? rootBuild.ProductName,
41
- version,
42
- branch: rootBuild.Branch,
43
- fileVersion,
44
- buildKey: [flavor, version || fileVersion || "unknown"].filter(Boolean).join("@")
45
- };
46
- return [{ flavor, root, executable, build }];
47
- }).sort((a, b) => a.flavor.localeCompare(b.flavor));
48
- }
49
- export function discoverInstallsAcrossRoots(gameRoots, discover = discoverInstalls) {
50
- const installs = new Map();
51
- for (const gameRoot of gameRoots) {
52
- for (const install of discover(gameRoot)) {
53
- const key = resolve(install.executable).toLowerCase();
54
- if (!installs.has(key))
55
- installs.set(key, install);
56
- }
57
- }
58
- return [...installs.values()].sort((a, b) => a.flavor.localeCompare(b.flavor) || a.executable.localeCompare(b.executable));
59
- }
package/dist/dry-run.js DELETED
@@ -1,38 +0,0 @@
1
- import { getObservationTiers } from "./observability.js";
2
- function toAdapterFields(lookup) {
3
- if (lookup.status === "verified" && lookup.adapter) {
4
- return {
5
- adapterStatus: lookup.status,
6
- observationTiers: getObservationTiers(lookup.adapter),
7
- adapterName: lookup.adapter.name
8
- };
9
- }
10
- return {
11
- adapterStatus: lookup.status,
12
- observationTiers: [],
13
- adapterReason: lookup.reason
14
- };
15
- }
16
- function installStatus(install, registry) {
17
- return {
18
- flavor: install.flavor,
19
- buildKey: install.build.buildKey,
20
- ...toAdapterFields(registry.lookup(install.build.buildKey))
21
- };
22
- }
23
- export function createDryRunReport(installs, processes, registry) {
24
- return {
25
- mode: "dry-run",
26
- installs: installs
27
- .map(install => installStatus(install, registry))
28
- .sort((a, b) => a.flavor.localeCompare(b.flavor)),
29
- processes: processes
30
- .map(process => ({
31
- pid: process.pid,
32
- executable: process.executable,
33
- ...(process.startTime ? { startTime: process.startTime } : {}),
34
- ...installStatus(process.install, registry)
35
- }))
36
- .sort((a, b) => a.pid - b.pid)
37
- };
38
- }
package/dist/error-log.js DELETED
@@ -1,71 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import { ErrorEventSchema } from "./types.js";
3
- const DEFAULT_LIMIT = 100;
4
- const MAX_LIMIT = 1000;
5
- function validateLimit(limit) {
6
- const value = limit ?? DEFAULT_LIMIT;
7
- if (!Number.isInteger(value) || value < 1 || value > MAX_LIMIT) {
8
- throw new RangeError(`limit must be an integer between 1 and ${MAX_LIMIT}`);
9
- }
10
- return value;
11
- }
12
- function matches(event, query) {
13
- return (query.pid === undefined || event.pid === query.pid)
14
- && (query.buildKey === undefined || event.buildKey === query.buildKey)
15
- && (query.flavor === undefined || event.flavor === query.flavor)
16
- && (query.addon === undefined || event.addon === query.addon);
17
- }
18
- function schemaMessage(error) {
19
- return error.issues
20
- .map(issue => `${issue.path.length > 0 ? issue.path.join(".") : "event"}: ${issue.message}`)
21
- .join("; ");
22
- }
23
- export async function readErrorLog(file, query = {}) {
24
- const limit = validateLimit(query.limit);
25
- let contents;
26
- try {
27
- contents = await readFile(file, "utf8");
28
- }
29
- catch (error) {
30
- if (error.code === "ENOENT") {
31
- return { events: [], diagnostics: [] };
32
- }
33
- throw error;
34
- }
35
- const diagnostics = [];
36
- const matchesByTime = [];
37
- for (const [index, rawLine] of contents.split(/\r?\n/u).entries()) {
38
- if (rawLine.trim().length === 0)
39
- continue;
40
- const line = index + 1;
41
- let value;
42
- try {
43
- value = JSON.parse(rawLine);
44
- }
45
- catch (error) {
46
- diagnostics.push({
47
- line,
48
- code: "invalid_json",
49
- message: error instanceof Error ? error.message : "Invalid JSON"
50
- });
51
- continue;
52
- }
53
- const parsed = ErrorEventSchema.safeParse(value);
54
- if (!parsed.success) {
55
- diagnostics.push({
56
- line,
57
- code: "invalid_event",
58
- message: schemaMessage(parsed.error)
59
- });
60
- continue;
61
- }
62
- if (matches(parsed.data, query)) {
63
- matchesByTime.push({ event: parsed.data, line });
64
- }
65
- }
66
- matchesByTime.sort((left, right) => left.event.timestamp - right.event.timestamp || left.line - right.line);
67
- return {
68
- events: matchesByTime.slice(-limit).map(item => item.event),
69
- diagnostics
70
- };
71
- }
@@ -1,63 +0,0 @@
1
- export class FocusRequestError extends Error {
2
- code;
3
- details;
4
- constructor(code, message, details) {
5
- super(message);
6
- this.name = "FocusRequestError";
7
- this.code = code;
8
- this.details = details;
9
- }
10
- }
11
- export function asSelectorRequestError(error) {
12
- if (error instanceof FocusRequestError)
13
- return error;
14
- const message = error instanceof Error ? error.message : String(error);
15
- if (/exceeding maxTargets|TARGETS_OVER_LIMIT/i.test(message)) {
16
- return new FocusRequestError("TARGETS_OVER_LIMIT", message);
17
- }
18
- if (/must identify a target|requires (?:rvas|dataSourceIds|objectIds|ranges)|SELECTOR_EMPTY/i.test(message)) {
19
- return new FocusRequestError("SELECTOR_EMPTY", message);
20
- }
21
- return new FocusRequestError("SELECTOR_INVALID", message);
22
- }
23
- const ATTACHMENT_FATAL_CODES = new Set([
24
- "ATTACHMENT_FATAL",
25
- "PROCESS_NOT_FOUND",
26
- "PROCESS_TERMINATED",
27
- "SESSION_DETACHED",
28
- "SCRIPT_DESTROYED",
29
- "TRANSPORT_CLOSED",
30
- ]);
31
- const ATTACHMENT_FATAL_NAMES = new Set([
32
- "ProcessNotFoundError",
33
- "ProcessTerminatedError",
34
- "SessionDetachedError",
35
- "ScriptDestroyedError",
36
- ]);
37
- /** Classifies only failures that unambiguously mean the Frida target attachment is gone. */
38
- export function isAttachmentFatalError(error) {
39
- const visited = new Set();
40
- let current = error;
41
- while (current !== null && current !== undefined && !visited.has(current)) {
42
- visited.add(current);
43
- if (typeof current === "object") {
44
- const value = current;
45
- if (typeof value.code === "string" && ATTACHMENT_FATAL_CODES.has(value.code.toUpperCase()))
46
- return true;
47
- if (typeof value.name === "string" && ATTACHMENT_FATAL_NAMES.has(value.name))
48
- return true;
49
- if (typeof value.message === "string" && isAttachmentFatalMessage(value.message))
50
- return true;
51
- current = value.cause;
52
- continue;
53
- }
54
- return typeof current === "string" && isAttachmentFatalMessage(current);
55
- }
56
- return false;
57
- }
58
- function isAttachmentFatalMessage(message) {
59
- return /\bscript is destroyed\b/i.test(message)
60
- || /\bprocess (?:was |has been )?terminated\b/i.test(message)
61
- || /\btarget process (?:exited|terminated)\b/i.test(message)
62
- || /\bsession is detached\b/i.test(message);
63
- }