memorydetective 1.8.1 → 1.9.0

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 (89) hide show
  1. package/CHANGELOG.md +50 -1
  2. package/README.md +75 -9
  3. package/USAGE.md +27 -4
  4. package/dist/cli.js +106 -3
  5. package/dist/cli.js.map +1 -1
  6. package/dist/index.js +42 -22
  7. package/dist/index.js.map +1 -1
  8. package/dist/parsers/referenceTree.d.ts +73 -0
  9. package/dist/parsers/referenceTree.js +145 -0
  10. package/dist/parsers/referenceTree.js.map +1 -0
  11. package/dist/runtime/axe.js +6 -1
  12. package/dist/runtime/axe.js.map +1 -1
  13. package/dist/runtime/exec.d.ts +30 -0
  14. package/dist/runtime/exec.js +30 -3
  15. package/dist/runtime/exec.js.map +1 -1
  16. package/dist/runtime/fixTemplates.js +67 -0
  17. package/dist/runtime/fixTemplates.js.map +1 -1
  18. package/dist/runtime/leakReport.d.ts +62 -0
  19. package/dist/runtime/leakReport.js +138 -0
  20. package/dist/runtime/leakReport.js.map +1 -0
  21. package/dist/runtime/platformCheck.d.ts +54 -0
  22. package/dist/runtime/platformCheck.js +94 -0
  23. package/dist/runtime/platformCheck.js.map +1 -0
  24. package/dist/runtime/redact.d.ts +66 -0
  25. package/dist/runtime/redact.js +146 -0
  26. package/dist/runtime/redact.js.map +1 -0
  27. package/dist/runtime/responseFormatter.d.ts +55 -0
  28. package/dist/runtime/responseFormatter.js +200 -0
  29. package/dist/runtime/responseFormatter.js.map +1 -0
  30. package/dist/runtime/securityFlags.d.ts +74 -0
  31. package/dist/runtime/securityFlags.js +90 -0
  32. package/dist/runtime/securityFlags.js.map +1 -0
  33. package/dist/runtime/staticAnalysisHints.js +14 -1
  34. package/dist/runtime/staticAnalysisHints.js.map +1 -1
  35. package/dist/templates/leak-report.html +39 -0
  36. package/dist/templates/templates/leak-report.html +39 -0
  37. package/dist/tools/analyzeAbandonedMemory.d.ts +140 -0
  38. package/dist/tools/analyzeAbandonedMemory.js +287 -0
  39. package/dist/tools/analyzeAbandonedMemory.js.map +1 -0
  40. package/dist/tools/analyzeAllocations.d.ts +11 -2
  41. package/dist/tools/analyzeAllocations.js +4 -0
  42. package/dist/tools/analyzeAllocations.js.map +1 -1
  43. package/dist/tools/analyzeAnimationHitches.d.ts +32 -2
  44. package/dist/tools/analyzeAnimationHitches.js +25 -4
  45. package/dist/tools/analyzeAnimationHitches.js.map +1 -1
  46. package/dist/tools/analyzeAppLaunch.d.ts +3 -0
  47. package/dist/tools/analyzeAppLaunch.js +2 -0
  48. package/dist/tools/analyzeAppLaunch.js.map +1 -1
  49. package/dist/tools/analyzeHangs.d.ts +78 -2
  50. package/dist/tools/analyzeHangs.js +117 -4
  51. package/dist/tools/analyzeHangs.js.map +1 -1
  52. package/dist/tools/analyzeMemgraph.d.ts +21 -1
  53. package/dist/tools/analyzeMemgraph.js +52 -2
  54. package/dist/tools/analyzeMemgraph.js.map +1 -1
  55. package/dist/tools/analyzeTimeProfile.d.ts +11 -1
  56. package/dist/tools/analyzeTimeProfile.js +5 -0
  57. package/dist/tools/analyzeTimeProfile.js.map +1 -1
  58. package/dist/tools/bootAndLaunchForLeakInvestigation.d.ts +18 -9
  59. package/dist/tools/bootAndLaunchForLeakInvestigation.js +27 -0
  60. package/dist/tools/bootAndLaunchForLeakInvestigation.js.map +1 -1
  61. package/dist/tools/captureMemgraph.d.ts +22 -4
  62. package/dist/tools/captureMemgraph.js +42 -9
  63. package/dist/tools/captureMemgraph.js.map +1 -1
  64. package/dist/tools/captureScenarioState.d.ts +12 -4
  65. package/dist/tools/captureScenarioState.js +4 -0
  66. package/dist/tools/captureScenarioState.js.map +1 -1
  67. package/dist/tools/classifyCycle.js +77 -0
  68. package/dist/tools/classifyCycle.js.map +1 -1
  69. package/dist/tools/cleanupTraces.d.ts +87 -0
  70. package/dist/tools/cleanupTraces.js +232 -0
  71. package/dist/tools/cleanupTraces.js.map +1 -0
  72. package/dist/tools/compareTracesByPattern.d.ts +2 -2
  73. package/dist/tools/detectLeaksInXCTest.d.ts +116 -0
  74. package/dist/tools/detectLeaksInXCTest.js +311 -0
  75. package/dist/tools/detectLeaksInXCTest.js.map +1 -0
  76. package/dist/tools/detectLeaksInXCUITest.d.ts +8 -3
  77. package/dist/tools/detectLeaksInXCUITest.js +30 -4
  78. package/dist/tools/detectLeaksInXCUITest.js.map +1 -1
  79. package/dist/tools/diffMemgraphs.d.ts +5 -2
  80. package/dist/tools/diffMemgraphs.js +2 -0
  81. package/dist/tools/diffMemgraphs.js.map +1 -1
  82. package/dist/tools/findCycles.d.ts +1 -1
  83. package/dist/tools/recordTimeProfile.d.ts +29 -9
  84. package/dist/tools/recordTimeProfile.js +70 -7
  85. package/dist/tools/recordTimeProfile.js.map +1 -1
  86. package/dist/tools/renderCycleGraph.d.ts +1 -1
  87. package/dist/tools/verifyFix.d.ts +2 -2
  88. package/dist/types.d.ts +24 -1
  89. package/package.json +3 -3
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Redact sensitive substrings from tool responses before they leave the
3
+ * MCP boundary.
4
+ *
5
+ * Tool outputs frequently include filesystem paths (with the user's home
6
+ * directory in them), token-shaped strings, hostnames, IP addresses, and
7
+ * bundle identifiers. None of those are useful to an AI agent reasoning
8
+ * about an iOS leak, and all of them are footguns when the output ends
9
+ * up in a Slack message, a PR comment, or a screenshot. This module
10
+ * scrubs them at the formatter boundary so by-default outputs are safe
11
+ * to share without manual sweeping.
12
+ *
13
+ * Three modes are selected via the `MEMORYDETECTIVE_REDACTION` env var:
14
+ *
15
+ * - `balanced` (default): home-directory absolute paths become `~/...`,
16
+ * common secret-shaped tokens (AWS keys, GitHub PATs, Stripe secrets,
17
+ * Slack tokens, Bearer auth) are masked. Hostnames, IPs, bundle IDs,
18
+ * process names, and class names are preserved (they are usually
19
+ * useful for debugging).
20
+ *
21
+ * - `strict`: everything in `balanced`, plus hostnames, IPv4 addresses,
22
+ * and bundle identifiers. Use when the output is going to be pasted
23
+ * into a public artifact (issue tracker, blog post, social) and you
24
+ * want a wide safety margin.
25
+ *
26
+ * - `off`: no redaction. Default behavior is preserved for legacy
27
+ * workflows and for local-only debugging where the noise is genuinely
28
+ * helpful. The startup banner logs the active mode so an operator
29
+ * running `off` knows the responses are unfiltered.
30
+ *
31
+ * Redaction is structural: the value passed in keeps its shape (same
32
+ * object keys, same array lengths, same scalar types), only string
33
+ * leaves are rewritten. Numbers, booleans, null/undefined, and dates
34
+ * pass through unchanged.
35
+ */
36
+ import os from "node:os";
37
+ const TOKEN_PATTERNS = [
38
+ // AWS access key id (AKIA + 16 chars)
39
+ { re: /\bAKIA[0-9A-Z]{16}\b/g, keepPrefix: 4 },
40
+ // GitHub classic PAT
41
+ { re: /\bghp_[A-Za-z0-9]{36,}\b/g, keepPrefix: 4 },
42
+ // GitHub fine-grained PAT
43
+ { re: /\bgithub_pat_[A-Za-z0-9_]+\b/g, keepPrefix: 11 },
44
+ // Stripe live + test secret
45
+ { re: /\bsk_(?:live|test)_[A-Za-z0-9]{20,}\b/g, keepPrefix: 8 },
46
+ // Slack tokens
47
+ { re: /\bxox[bpoasr]-[A-Za-z0-9-]+\b/g, keepPrefix: 5 },
48
+ // Bearer auth tokens
49
+ { re: /\bBearer\s+[A-Za-z0-9._\-]{20,}\b/gi, keepPrefix: 7 },
50
+ ];
51
+ // Hostnames: 1+ labels followed by a TLD (2+ alpha chars). Excludes the
52
+ // .swift / .ts / .js / .json / .trace / .memgraph "extensions" we see in
53
+ // paths, since those would false-positive otherwise.
54
+ const HOST_PATTERN = /\b(?!(?:[A-Za-z0-9-]+)\.(?:swift|ts|js|json|trace|memgraph|md|yaml|yml|html|png|jpg|jpeg|gif|m4v|mp4|mov|css|sh|py|rb|go|rs|c|cpp|h|hpp|m|mm|plist|xcframework|xcconfig|xcassets)\b)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}\b/gi;
55
+ const IP_PATTERN = /\b(?:\d{1,3}\.){3}\d{1,3}\b/g;
56
+ const BUNDLE_ID_PATTERN = /\bcom\.[a-z0-9_-]+(?:\.[a-z0-9_-]+)+\b/gi;
57
+ /**
58
+ * Pure: read the active redaction mode from an env-like object.
59
+ * Defaults to `balanced` when unset or set to an unrecognized value.
60
+ *
61
+ * Threaded as a parameter for testability; production callers omit it
62
+ * and get `process.env`.
63
+ */
64
+ export function getRedactionMode(env = process.env) {
65
+ const raw = (env.MEMORYDETECTIVE_REDACTION ?? "balanced").toLowerCase();
66
+ if (raw === "off" || raw === "strict" || raw === "balanced") {
67
+ return raw;
68
+ }
69
+ return "balanced";
70
+ }
71
+ /**
72
+ * Pure: scrub a single string per the active mode. `off` returns the
73
+ * input unchanged; the other modes apply the rules described in the
74
+ * module doc.
75
+ *
76
+ * Threaded `homeDir` for testability so unit tests can pass a fake
77
+ * `/Users/test/` without depending on the real home directory.
78
+ */
79
+ export function redactString(input, mode, homeDir = os.homedir()) {
80
+ if (mode === "off")
81
+ return input;
82
+ let result = input;
83
+ // Home dir collapse first so subsequent host/IP rules don't mistakenly
84
+ // grab parts of paths.
85
+ if (homeDir && homeDir.length > 1 && result.includes(homeDir)) {
86
+ result = result.split(homeDir).join("~");
87
+ }
88
+ // Always mask token-shaped secrets, even in `balanced`.
89
+ for (const { re, keepPrefix } of TOKEN_PATTERNS) {
90
+ result = result.replace(re, (match) => match.slice(0, keepPrefix) + "***REDACTED***");
91
+ }
92
+ if (mode === "strict") {
93
+ result = result.replace(BUNDLE_ID_PATTERN, "***BUNDLE_ID***");
94
+ result = result.replace(IP_PATTERN, "***IP***");
95
+ result = result.replace(HOST_PATTERN, "***HOST***");
96
+ }
97
+ return result;
98
+ }
99
+ /**
100
+ * Pure: recursively redact strings inside an arbitrary JSON-shaped
101
+ * value. Object key names are preserved unchanged (they are part of
102
+ * the schema, not data); only string VALUES and string ARRAY items
103
+ * are scrubbed.
104
+ *
105
+ * Non-string scalars (number, boolean, null) pass through. Functions,
106
+ * symbols, and other non-JSON values are returned as-is.
107
+ */
108
+ export function redact(value, mode, homeDir = os.homedir()) {
109
+ if (mode === "off")
110
+ return value;
111
+ if (value == null)
112
+ return value;
113
+ if (typeof value === "string")
114
+ return redactString(value, mode, homeDir);
115
+ if (typeof value === "number" || typeof value === "boolean")
116
+ return value;
117
+ if (Array.isArray(value)) {
118
+ return value.map((v) => redact(v, mode, homeDir));
119
+ }
120
+ if (typeof value === "object") {
121
+ const out = {};
122
+ for (const [k, v] of Object.entries(value)) {
123
+ out[k] = redact(v, mode, homeDir);
124
+ }
125
+ return out;
126
+ }
127
+ return value;
128
+ }
129
+ /**
130
+ * Side-effecting: log the active redaction mode once per server
131
+ * startup so an operator running with `off` knows the responses
132
+ * are unfiltered.
133
+ */
134
+ let advisoryLogged = false;
135
+ export function maybeLogRedactionModeOnce(mode, writer = (line) => process.stderr.write(line)) {
136
+ if (advisoryLogged)
137
+ return;
138
+ writer(`[memorydetective] redaction mode: ${mode}. ` +
139
+ `Set MEMORYDETECTIVE_REDACTION to balanced (default), strict, or off.\n`);
140
+ advisoryLogged = true;
141
+ }
142
+ /** Test-only: reset the once-per-instance log flag. */
143
+ export function resetRedactionAdvisoryFlagForTests() {
144
+ advisoryLogged = false;
145
+ }
146
+ //# sourceMappingURL=redact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/runtime/redact.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Shared response formatter for MCP tool outputs.
3
+ *
4
+ * Tools can declare `outputFormat?: "markdown" | "json" | "both"` (defaults to
5
+ * `"json"`, preserving v1.8 behavior). When the caller asks for a different
6
+ * shape, the registration wrapper in `src/index.ts` calls
7
+ * {@link formatMcpResponse} to produce the actual response content.
8
+ *
9
+ * Two audiences are served at once:
10
+ * - **AI agents** (the typical caller) want raw JSON they can parse and chain
11
+ * into the next call without re-reasoning.
12
+ * - **Humans reading the same response** (the typical second audience: the
13
+ * dev pasting the result into a PR comment, a Slack thread, or a Jira
14
+ * ticket) want a markdown view that highlights the key fields.
15
+ *
16
+ * `outputFormat: "both"` returns BOTH content items in a single response, so a
17
+ * client can display the markdown to the user AND parse the JSON for the
18
+ * agent loop without an extra round-trip.
19
+ */
20
+ import { z } from "zod";
21
+ export declare const outputFormatField: z.ZodOptional<z.ZodEnum<["markdown", "json", "both"]>>;
22
+ export type OutputFormat = z.infer<typeof outputFormatField>;
23
+ export interface McpContentItem {
24
+ type: "text";
25
+ text: string;
26
+ }
27
+ /**
28
+ * Shape of the MCP tool response. Matches the SDK's expected type
29
+ * (which has an open index signature for arbitrary extension fields like
30
+ * `_meta`); we model it explicitly here so the formatter's return type
31
+ * can flow through `server.registerTool` without a cast.
32
+ */
33
+ export interface McpResponse {
34
+ content: McpContentItem[];
35
+ [key: string]: unknown;
36
+ }
37
+ /**
38
+ * Pure: shape the MCP response based on the caller's `outputFormat`.
39
+ *
40
+ * For `json` and `both`, the JSON is `JSON.stringify(result, null, 2)`. For
41
+ * `markdown` and `both`, the markdown is rendered via {@link renderAsMarkdown}.
42
+ */
43
+ export declare function formatMcpResponse(result: unknown, toolName: string, format: OutputFormat | undefined): McpResponse;
44
+ /**
45
+ * Pure: render an arbitrary JSON-shaped value as markdown.
46
+ *
47
+ * The rendering is intentionally generic: it does not have per-tool
48
+ * templates. A `# Tool name` header, a `## Key` for each top-level field, and
49
+ * smart formatting for arrays of objects (tables when the rows share a
50
+ * schema) and scalars. Per-tool overrides can land in v1.9.1+ if any
51
+ * specific tool's output deserves a more curated view.
52
+ *
53
+ * Exposed for tests.
54
+ */
55
+ export declare function renderAsMarkdown(value: unknown, toolName: string): string;
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Shared response formatter for MCP tool outputs.
3
+ *
4
+ * Tools can declare `outputFormat?: "markdown" | "json" | "both"` (defaults to
5
+ * `"json"`, preserving v1.8 behavior). When the caller asks for a different
6
+ * shape, the registration wrapper in `src/index.ts` calls
7
+ * {@link formatMcpResponse} to produce the actual response content.
8
+ *
9
+ * Two audiences are served at once:
10
+ * - **AI agents** (the typical caller) want raw JSON they can parse and chain
11
+ * into the next call without re-reasoning.
12
+ * - **Humans reading the same response** (the typical second audience: the
13
+ * dev pasting the result into a PR comment, a Slack thread, or a Jira
14
+ * ticket) want a markdown view that highlights the key fields.
15
+ *
16
+ * `outputFormat: "both"` returns BOTH content items in a single response, so a
17
+ * client can display the markdown to the user AND parse the JSON for the
18
+ * agent loop without an extra round-trip.
19
+ */
20
+ import { z } from "zod";
21
+ import { getRedactionMode, redact } from "./redact.js";
22
+ export const outputFormatField = z
23
+ .enum(["markdown", "json", "both"])
24
+ .optional()
25
+ .describe("Response format. Omitted or `json` (default, preserves v1.8 behavior) returns JSON.stringify of the result. `markdown` renders a human-readable view of the same data. `both` returns both content items in one response, so a client can display markdown to the user and parse JSON for the agent loop without a second call.");
26
+ /**
27
+ * Pure: shape the MCP response based on the caller's `outputFormat`.
28
+ *
29
+ * For `json` and `both`, the JSON is `JSON.stringify(result, null, 2)`. For
30
+ * `markdown` and `both`, the markdown is rendered via {@link renderAsMarkdown}.
31
+ */
32
+ export function formatMcpResponse(result, toolName, format) {
33
+ const mode = format ?? "json";
34
+ // Redaction happens at the structured-value level so both the JSON
35
+ // and the markdown views are scrubbed consistently. `off` short-circuits
36
+ // and returns the input unchanged; `balanced` (default) masks home-dir
37
+ // paths and common secret-shaped tokens; `strict` also masks hostnames,
38
+ // IPs, and bundle identifiers. See src/runtime/redact.ts.
39
+ const redacted = redact(result, getRedactionMode());
40
+ const json = JSON.stringify(redacted, null, 2);
41
+ if (mode === "json") {
42
+ return { content: [{ type: "text", text: json }] };
43
+ }
44
+ const markdown = renderAsMarkdown(redacted, toolName);
45
+ if (mode === "markdown") {
46
+ return { content: [{ type: "text", text: markdown }] };
47
+ }
48
+ // "both": markdown first so a UI that picks content[0] gets the readable
49
+ // view, then JSON so an agent looking for the structured data finds it
50
+ // without having to parse the markdown.
51
+ return {
52
+ content: [
53
+ { type: "text", text: markdown },
54
+ { type: "text", text: json },
55
+ ],
56
+ };
57
+ }
58
+ /**
59
+ * Pure: render an arbitrary JSON-shaped value as markdown.
60
+ *
61
+ * The rendering is intentionally generic: it does not have per-tool
62
+ * templates. A `# Tool name` header, a `## Key` for each top-level field, and
63
+ * smart formatting for arrays of objects (tables when the rows share a
64
+ * schema) and scalars. Per-tool overrides can land in v1.9.1+ if any
65
+ * specific tool's output deserves a more curated view.
66
+ *
67
+ * Exposed for tests.
68
+ */
69
+ export function renderAsMarkdown(value, toolName) {
70
+ const lines = [`# ${toolName}`, ""];
71
+ if (value == null || typeof value !== "object") {
72
+ lines.push(formatScalar(value));
73
+ return lines.join("\n");
74
+ }
75
+ const obj = value;
76
+ for (const [key, val] of Object.entries(obj)) {
77
+ lines.push(`## ${key}`);
78
+ lines.push("");
79
+ lines.push(formatValue(val, 0));
80
+ lines.push("");
81
+ }
82
+ return lines.join("\n").trim() + "\n";
83
+ }
84
+ function formatValue(value, depth) {
85
+ if (value == null)
86
+ return "_(null)_";
87
+ if (typeof value === "string")
88
+ return value || "_(empty)_";
89
+ if (typeof value === "number" || typeof value === "boolean")
90
+ return formatScalar(value);
91
+ if (Array.isArray(value))
92
+ return formatArray(value, depth);
93
+ if (typeof value === "object")
94
+ return formatObject(value, depth);
95
+ return String(value);
96
+ }
97
+ function formatArray(arr, depth) {
98
+ if (arr.length === 0)
99
+ return "_(empty array)_";
100
+ // Table if all entries are objects with a shared key set.
101
+ if (arr.length > 0 &&
102
+ arr.every((e) => e != null && typeof e === "object" && !Array.isArray(e))) {
103
+ const objects = arr;
104
+ const cols = collectCommonKeys(objects);
105
+ if (cols.length > 0 && cols.length <= 8) {
106
+ const header = `| ${cols.join(" | ")} |`;
107
+ const sep = `| ${cols.map(() => "---").join(" | ")} |`;
108
+ const rows = objects.slice(0, 50).map((o) => {
109
+ const cells = cols.map((c) => formatCell(o[c]));
110
+ return `| ${cells.join(" | ")} |`;
111
+ });
112
+ const tail = objects.length > 50 ? `\n_(${objects.length - 50} more rows omitted)_` : "";
113
+ return [header, sep, ...rows].join("\n") + tail;
114
+ }
115
+ }
116
+ // Otherwise bullet list of scalars / mixed.
117
+ return arr
118
+ .slice(0, 50)
119
+ .map((e) => `- ${formatCell(e)}`)
120
+ .join("\n");
121
+ }
122
+ function formatObject(obj, depth) {
123
+ const entries = Object.entries(obj);
124
+ if (entries.length === 0)
125
+ return "_(empty object)_";
126
+ if (depth >= 2) {
127
+ // Deeply nested: collapse to inline JSON to keep the markdown tidy.
128
+ return "```json\n" + JSON.stringify(obj, null, 2) + "\n```";
129
+ }
130
+ return entries
131
+ .map(([k, v]) => `- **${k}**: ${formatInline(v, depth + 1)}`)
132
+ .join("\n");
133
+ }
134
+ function formatInline(value, depth) {
135
+ if (value == null)
136
+ return "_(null)_";
137
+ if (typeof value === "string")
138
+ return value || "_(empty)_";
139
+ if (typeof value === "number" || typeof value === "boolean")
140
+ return formatScalar(value);
141
+ if (Array.isArray(value)) {
142
+ if (value.length === 0)
143
+ return "_(empty array)_";
144
+ if (value.length <= 5 && value.every((e) => typeof e !== "object" || e == null)) {
145
+ return `[${value.map((e) => formatCell(e)).join(", ")}]`;
146
+ }
147
+ return `\n${formatArray(value, depth)}`;
148
+ }
149
+ if (typeof value === "object") {
150
+ return `\n${formatObject(value, depth)}`;
151
+ }
152
+ return String(value);
153
+ }
154
+ function formatScalar(value) {
155
+ if (value == null)
156
+ return "_(null)_";
157
+ if (typeof value === "boolean")
158
+ return value ? "`true`" : "`false`";
159
+ if (typeof value === "number")
160
+ return `\`${value}\``;
161
+ return String(value);
162
+ }
163
+ function formatCell(value) {
164
+ if (value == null)
165
+ return "_";
166
+ if (typeof value === "string") {
167
+ // Escape pipes for table safety, truncate long strings.
168
+ const escaped = value.replace(/\|/g, "\\|");
169
+ return escaped.length > 80 ? escaped.slice(0, 77) + "..." : escaped;
170
+ }
171
+ if (typeof value === "number" || typeof value === "boolean") {
172
+ return formatScalar(value);
173
+ }
174
+ if (Array.isArray(value))
175
+ return `[${value.length}]`;
176
+ if (typeof value === "object") {
177
+ // Compact inline JSON, truncated.
178
+ const s = JSON.stringify(value);
179
+ return s.length > 60 ? s.slice(0, 57) + "..." : s;
180
+ }
181
+ return String(value);
182
+ }
183
+ function collectCommonKeys(objects) {
184
+ // Use the keys of the FIRST object as the column set, filtered to those
185
+ // present in at least half of the rows. Keeps the table compact when rows
186
+ // have optional fields.
187
+ if (objects.length === 0)
188
+ return [];
189
+ const firstKeys = Object.keys(objects[0]);
190
+ const threshold = Math.ceil(objects.length / 2);
191
+ return firstKeys.filter((k) => {
192
+ let hits = 0;
193
+ for (const o of objects) {
194
+ if (Object.prototype.hasOwnProperty.call(o, k))
195
+ hits += 1;
196
+ }
197
+ return hits >= threshold;
198
+ });
199
+ }
200
+ //# sourceMappingURL=responseFormatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"responseFormatter.js","sourceRoot":"","sources":["../../src/runtime/responseFormatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAEvD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC;KAC/B,IAAI,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;KAClC,QAAQ,EAAE;KACV,QAAQ,CACP,iUAAiU,CAClU,CAAC;AAoBJ;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAe,EACf,QAAgB,EAChB,MAAgC;IAEhC,MAAM,IAAI,GAAiB,MAAM,IAAI,MAAM,CAAC;IAC5C,mEAAmE;IACnE,yEAAyE;IACzE,uEAAuE;IACvE,wEAAwE;IACxE,0DAA0D;IAC1D,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC/C,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IACrD,CAAC;IACD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACtD,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;QACxB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACzD,CAAC;IACD,yEAAyE;IACzE,uEAAuE;IACvE,wCAAwC;IACxC,OAAO;QACL,OAAO,EAAE;YACP,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE;YAChC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE;SAC7B;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAc,EAAE,QAAgB;IAC/D,MAAM,KAAK,GAAa,CAAC,KAAK,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;IAC9C,IAAI,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC/C,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;QAChC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IACD,MAAM,GAAG,GAAG,KAAgC,CAAC;IAC7C,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7C,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC;AACxC,CAAC;AAED,SAAS,WAAW,CAAC,KAAc,EAAE,KAAa;IAChD,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,UAAU,CAAC;IACrC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,IAAI,WAAW,CAAC;IAC3D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,SAAS;QACzD,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;IAC7B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC3D,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,YAAY,CAAC,KAAgC,EAAE,KAAK,CAAC,CAAC;IAC5F,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,WAAW,CAAC,GAAc,EAAE,KAAa;IAChD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,iBAAiB,CAAC;IAC/C,0DAA0D;IAC1D,IACE,GAAG,CAAC,MAAM,GAAG,CAAC;QACd,GAAG,CAAC,KAAK,CACP,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAC/D,EACD,CAAC;QACD,MAAM,OAAO,GAAG,GAAgC,CAAC;QACjD,MAAM,IAAI,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YACxC,MAAM,MAAM,GAAG,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YACzC,MAAM,GAAG,GAAG,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YACvD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAChD,OAAO,KAAK,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YACpC,CAAC,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,MAAM,GAAG,EAAE,sBAAsB,CAAC,CAAC,CAAC,EAAE,CAAC;YACzF,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QAClD,CAAC;IACH,CAAC;IACD,4CAA4C;IAC5C,OAAO,GAAG;SACP,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SACZ,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;SAChC,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED,SAAS,YAAY,CAAC,GAA4B,EAAE,KAAa;IAC/D,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,kBAAkB,CAAC;IACpD,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QACf,oEAAoE;QACpE,OAAO,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC;IAC9D,CAAC;IACD,OAAO,OAAO;SACX,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,YAAY,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC;SAC5D,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED,SAAS,YAAY,CAAC,KAAc,EAAE,KAAa;IACjD,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,UAAU,CAAC;IACrC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,IAAI,WAAW,CAAC;IAC3D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,SAAS;QACzD,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;IAC7B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,iBAAiB,CAAC;QACjD,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC;YAChF,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;QAC3D,CAAC;QACD,OAAO,KAAK,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC;IAC1C,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,YAAY,CAAC,KAAgC,EAAE,KAAK,CAAC,EAAE,CAAC;IACtE,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,UAAU,CAAC;IACrC,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IACpE,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,KAAK,IAAI,CAAC;IACrD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,UAAU,CAAC,KAAc;IAChC,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,GAAG,CAAC;IAC9B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,wDAAwD;QACxD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC5C,OAAO,OAAO,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;IACtE,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;QAC5D,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;IACrD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,kCAAkC;QAClC,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAChC,OAAO,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAkC;IAC3D,wEAAwE;IACxE,0EAA0E;IAC1E,wBAAwB;IACxB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAChD,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QAC5B,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;gBAAE,IAAI,IAAI,CAAC,CAAC;QAC5D,CAAC;QACD,OAAO,IAAI,IAAI,SAAS,CAAC;IAC3B,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Security and resource-limit flags read from the environment.
3
+ *
4
+ * These flags exist to make memorydetective safer to install in shared
5
+ * setups (CI runners, teammate's machine, automation pipelines) by
6
+ * gating operations that can execute arbitrary local programs, bound
7
+ * recording durations so an unattended agent does not pile up
8
+ * multi-GB traces, and centralize where `.trace` bundles get written
9
+ * by default so cleanup is predictable.
10
+ *
11
+ * The flags are read from `process.env` lazily (each tool call calls
12
+ * `getSecurityFlags()` fresh). Defaults preserve the v1.8 behavior
13
+ * for every existing caller; the new behavior is opt-in via the env
14
+ * var.
15
+ *
16
+ * - `MEMORYDETECTIVE_ALLOW_LAUNCH=1`: gates
17
+ * `bootAndLaunchForLeakInvestigation`. Without it, the tool returns
18
+ * `ok: false` with an explanation. With it, the tool runs normally.
19
+ * Default is OFF because that tool executes `xcodebuild` and
20
+ * `xcrun simctl launch`, which are "run arbitrary local program"
21
+ * in a trusted-input sense.
22
+ *
23
+ * - `MEMORYDETECTIVE_MAX_RECORDING_SECONDS=300` (default 300): caps
24
+ * `durationSec` for `recordTimeProfile`. A caller asking for a
25
+ * recording longer than this gets a clear error rather than the
26
+ * tool silently agreeing to a 10-minute trace.
27
+ *
28
+ * - `MEMORYDETECTIVE_TRACE_ROOT=<path>` (default
29
+ * `~/Library/Application Support/memorydetective/traces`):
30
+ * directory where `.trace` bundles are written when the caller
31
+ * provides a relative `output` path. Absolute `output` paths
32
+ * bypass this default, preserving existing behavior. Also used by
33
+ * the upcoming `cleanup_traces` tool as the default scan path.
34
+ */
35
+ export interface SecurityFlags {
36
+ allowLaunch: boolean;
37
+ maxRecordingSeconds: number;
38
+ traceRoot: string;
39
+ }
40
+ export declare const DEFAULT_MAX_RECORDING_SECONDS = 300;
41
+ export declare function defaultTraceRoot(homeDir?: string): string;
42
+ /**
43
+ * Pure: read the security flags from an env-like object. Threaded as
44
+ * a parameter for testability; production callers omit it and get
45
+ * `process.env`.
46
+ *
47
+ * Parse rules:
48
+ *
49
+ * - `MEMORYDETECTIVE_ALLOW_LAUNCH` is truthy only when the value is
50
+ * literally `"1"`. Any other value (including `"true"` and `"yes"`)
51
+ * leaves it off, to keep the gate explicit.
52
+ *
53
+ * - `MEMORYDETECTIVE_MAX_RECORDING_SECONDS` accepts a positive
54
+ * integer string; anything else (missing, zero, negative, NaN)
55
+ * falls back to the default 300. The cap is bounded at 3600s (1h)
56
+ * to prevent obviously-bad configs from disabling the gate via
57
+ * absurd values.
58
+ *
59
+ * - `MEMORYDETECTIVE_TRACE_ROOT` accepts any non-empty string; empty
60
+ * or missing values fall back to the default location.
61
+ */
62
+ export declare function getSecurityFlags(env?: Readonly<Record<string, string | undefined>>, homeDir?: string): SecurityFlags;
63
+ /**
64
+ * Build the error message returned when `bootAndLaunchForLeakInvestigation`
65
+ * is invoked without `MEMORYDETECTIVE_ALLOW_LAUNCH=1`. Exposed as a
66
+ * named export so the same wording is used everywhere (and so the
67
+ * unit tests can assert on it).
68
+ */
69
+ export declare const ALLOW_LAUNCH_REQUIRED_MESSAGE = "bootAndLaunchForLeakInvestigation requires MEMORYDETECTIVE_ALLOW_LAUNCH=1 in the environment, because this tool executes xcodebuild + xcrun simctl launch against the host. Set the env var only when you trust the inputs (workspace/project paths and bundle ids) the agent will pass.";
70
+ /**
71
+ * Build the error message returned when `recordTimeProfile` is asked
72
+ * for a duration above the cap.
73
+ */
74
+ export declare function maxRecordingExceededMessage(requested: number, cap: number): string;
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Security and resource-limit flags read from the environment.
3
+ *
4
+ * These flags exist to make memorydetective safer to install in shared
5
+ * setups (CI runners, teammate's machine, automation pipelines) by
6
+ * gating operations that can execute arbitrary local programs, bound
7
+ * recording durations so an unattended agent does not pile up
8
+ * multi-GB traces, and centralize where `.trace` bundles get written
9
+ * by default so cleanup is predictable.
10
+ *
11
+ * The flags are read from `process.env` lazily (each tool call calls
12
+ * `getSecurityFlags()` fresh). Defaults preserve the v1.8 behavior
13
+ * for every existing caller; the new behavior is opt-in via the env
14
+ * var.
15
+ *
16
+ * - `MEMORYDETECTIVE_ALLOW_LAUNCH=1`: gates
17
+ * `bootAndLaunchForLeakInvestigation`. Without it, the tool returns
18
+ * `ok: false` with an explanation. With it, the tool runs normally.
19
+ * Default is OFF because that tool executes `xcodebuild` and
20
+ * `xcrun simctl launch`, which are "run arbitrary local program"
21
+ * in a trusted-input sense.
22
+ *
23
+ * - `MEMORYDETECTIVE_MAX_RECORDING_SECONDS=300` (default 300): caps
24
+ * `durationSec` for `recordTimeProfile`. A caller asking for a
25
+ * recording longer than this gets a clear error rather than the
26
+ * tool silently agreeing to a 10-minute trace.
27
+ *
28
+ * - `MEMORYDETECTIVE_TRACE_ROOT=<path>` (default
29
+ * `~/Library/Application Support/memorydetective/traces`):
30
+ * directory where `.trace` bundles are written when the caller
31
+ * provides a relative `output` path. Absolute `output` paths
32
+ * bypass this default, preserving existing behavior. Also used by
33
+ * the upcoming `cleanup_traces` tool as the default scan path.
34
+ */
35
+ import os from "node:os";
36
+ import { join as joinPath } from "node:path";
37
+ export const DEFAULT_MAX_RECORDING_SECONDS = 300;
38
+ export function defaultTraceRoot(homeDir = os.homedir()) {
39
+ return joinPath(homeDir, "Library", "Application Support", "memorydetective", "traces");
40
+ }
41
+ /**
42
+ * Pure: read the security flags from an env-like object. Threaded as
43
+ * a parameter for testability; production callers omit it and get
44
+ * `process.env`.
45
+ *
46
+ * Parse rules:
47
+ *
48
+ * - `MEMORYDETECTIVE_ALLOW_LAUNCH` is truthy only when the value is
49
+ * literally `"1"`. Any other value (including `"true"` and `"yes"`)
50
+ * leaves it off, to keep the gate explicit.
51
+ *
52
+ * - `MEMORYDETECTIVE_MAX_RECORDING_SECONDS` accepts a positive
53
+ * integer string; anything else (missing, zero, negative, NaN)
54
+ * falls back to the default 300. The cap is bounded at 3600s (1h)
55
+ * to prevent obviously-bad configs from disabling the gate via
56
+ * absurd values.
57
+ *
58
+ * - `MEMORYDETECTIVE_TRACE_ROOT` accepts any non-empty string; empty
59
+ * or missing values fall back to the default location.
60
+ */
61
+ export function getSecurityFlags(env = process.env, homeDir = os.homedir()) {
62
+ const allowLaunch = env.MEMORYDETECTIVE_ALLOW_LAUNCH === "1";
63
+ const rawMax = env.MEMORYDETECTIVE_MAX_RECORDING_SECONDS;
64
+ let maxRecordingSeconds = DEFAULT_MAX_RECORDING_SECONDS;
65
+ if (rawMax != null && rawMax !== "") {
66
+ const parsed = Number.parseInt(rawMax, 10);
67
+ if (Number.isFinite(parsed) && parsed > 0) {
68
+ maxRecordingSeconds = Math.min(parsed, 3600);
69
+ }
70
+ }
71
+ const rawRoot = env.MEMORYDETECTIVE_TRACE_ROOT;
72
+ const traceRoot = rawRoot != null && rawRoot.length > 0 ? rawRoot : defaultTraceRoot(homeDir);
73
+ return { allowLaunch, maxRecordingSeconds, traceRoot };
74
+ }
75
+ /**
76
+ * Build the error message returned when `bootAndLaunchForLeakInvestigation`
77
+ * is invoked without `MEMORYDETECTIVE_ALLOW_LAUNCH=1`. Exposed as a
78
+ * named export so the same wording is used everywhere (and so the
79
+ * unit tests can assert on it).
80
+ */
81
+ export const ALLOW_LAUNCH_REQUIRED_MESSAGE = "bootAndLaunchForLeakInvestigation requires MEMORYDETECTIVE_ALLOW_LAUNCH=1 in the environment, because this tool executes xcodebuild + xcrun simctl launch against the host. Set the env var only when you trust the inputs (workspace/project paths and bundle ids) the agent will pass.";
82
+ /**
83
+ * Build the error message returned when `recordTimeProfile` is asked
84
+ * for a duration above the cap.
85
+ */
86
+ export function maxRecordingExceededMessage(requested, cap) {
87
+ return (`recordTimeProfile durationSec=${requested} exceeds the configured cap of ${cap}s ` +
88
+ `(MEMORYDETECTIVE_MAX_RECORDING_SECONDS). Lower durationSec, or raise the cap with the env var.`);
89
+ }
90
+ //# sourceMappingURL=securityFlags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"securityFlags.js","sourceRoot":"","sources":["../../src/runtime/securityFlags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,WAAW,CAAC;AAQ7C,MAAM,CAAC,MAAM,6BAA6B,GAAG,GAAG,CAAC;AAEjD,MAAM,UAAU,gBAAgB,CAAC,UAAkB,EAAE,CAAC,OAAO,EAAE;IAC7D,OAAO,QAAQ,CACb,OAAO,EACP,SAAS,EACT,qBAAqB,EACrB,iBAAiB,EACjB,QAAQ,CACT,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAAoD,OAAO,CAAC,GAAG,EAC/D,UAAkB,EAAE,CAAC,OAAO,EAAE;IAE9B,MAAM,WAAW,GAAG,GAAG,CAAC,4BAA4B,KAAK,GAAG,CAAC;IAE7D,MAAM,MAAM,GAAG,GAAG,CAAC,qCAAqC,CAAC;IACzD,IAAI,mBAAmB,GAAG,6BAA6B,CAAC;IACxD,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;QACpC,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC3C,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1C,mBAAmB,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,GAAG,CAAC,0BAA0B,CAAC;IAC/C,MAAM,SAAS,GACb,OAAO,IAAI,IAAI,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAE9E,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,SAAS,EAAE,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,6BAA6B,GACxC,0RAA0R,CAAC;AAE7R;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CACzC,SAAiB,EACjB,GAAW;IAEX,OAAO,CACL,iCAAiC,SAAS,kCAAkC,GAAG,IAAI;QACnF,gGAAgG,CACjG,CAAC;AACJ,CAAC"}
@@ -203,7 +203,20 @@ const HINTS = {
203
203
  "swiftdata.modelcontext-actor-cycle": {
204
204
  rule: null,
205
205
  url: "https://developer.apple.com/forums/thread/748042",
206
- explanation: "No static rule. The cycle is between Apple-provided types (`Actor` / `DefaultSerialModelExecutor` / `ModelContext`) SwiftLint can't reason about Apple-framework retain semantics. Apple fixed the framework-level shape in iOS 18 beta 1 (FB13844786). Until your minimum target is iOS 18+, the user-code shape persists.",
206
+ explanation: "No static rule. The cycle is between Apple-provided types (`Actor` / `DefaultSerialModelExecutor` / `ModelContext`); SwiftLint can't reason about Apple-framework retain semantics. Apple fixed the framework-level shape in iOS 18 beta 1 (FB13844786). Until your minimum target is iOS 18+, the user-code shape persists.",
207
+ },
208
+ // ─────────────────────────────────────────────────────────────────────────
209
+ // v1.9 catalog (DebugSwift borrow)
210
+ // ─────────────────────────────────────────────────────────────────────────
211
+ "uikit.viewcontroller-retained-after-pop": {
212
+ rule: "weak_delegate",
213
+ url: "https://realm.github.io/SwiftLint/weak_delegate.html",
214
+ explanation: "`weak_delegate` catches the most common shape (delegate property without `weak`), and the project-wide `weak_self` rule covers closure-captured self in `viewDidLoad`/`viewWillAppear`. Neither catches Combine sinks stored on the VC nor KVO observations that never `invalidate()`; for those you need the offline catalog match.",
215
+ },
216
+ "swiftui.observable-write-on-every-render": {
217
+ rule: null,
218
+ url: "https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app",
219
+ explanation: "No static rule. SwiftLint cannot prove that an `@Observable` mutation happens inside a `View.body` chain (it would need to reason about View body resolution, which is compiler-internal). The cycle catalog catches the heap shape it leaves behind; the trace-side perf signal (excessive body re-evaluations on the same view) is a parallel detector.",
207
220
  },
208
221
  };
209
222
  /** Returns the static-analysis hint for a given pattern, or null if unknown. */
@@ -1 +1 @@
1
- {"version":3,"file":"staticAnalysisHints.js","sourceRoot":"","sources":["../../src/runtime/staticAnalysisHints.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"staticAnalysisHints.js","sourceRoot":"","sources":["../../src/runtime/staticAnalysisHints.ts"],"names":[],"mappings":"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"}