unity-open-mcp 0.9.0 → 1.0.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.
- package/dist/asmdef-discovery.d.ts +35 -0
- package/dist/asmdef-discovery.js +171 -0
- package/dist/asmdef-discovery.js.map +1 -0
- package/dist/batch-spawn.d.ts +1 -0
- package/dist/batch-spawn.js +78 -1
- package/dist/batch-spawn.js.map +1 -1
- package/dist/capabilities/intent-groups.js +42 -0
- package/dist/capabilities/intent-groups.js.map +1 -1
- package/dist/capabilities/rule-catalog.js +7 -1
- package/dist/capabilities/rule-catalog.js.map +1 -1
- package/dist/capabilities/tool-groups.js +37 -2
- package/dist/capabilities/tool-groups.js.map +1 -1
- package/dist/constants.d.ts +21 -1
- package/dist/constants.js +21 -1
- package/dist/constants.js.map +1 -1
- package/dist/event-stream.d.ts +13 -3
- package/dist/event-stream.js +77 -12
- package/dist/event-stream.js.map +1 -1
- package/dist/live-client.d.ts +17 -0
- package/dist/live-client.js +122 -5
- package/dist/live-client.js.map +1 -1
- package/dist/offline/api.js +82 -30
- package/dist/offline/api.js.map +1 -1
- package/dist/offline/index-builders.d.ts +9 -0
- package/dist/offline/index-builders.js +58 -8
- package/dist/offline/index-builders.js.map +1 -1
- package/dist/offline/parse.js +5 -1
- package/dist/offline/parse.js.map +1 -1
- package/dist/process-diagnostics.d.ts +100 -19
- package/dist/process-diagnostics.js +167 -59
- package/dist/process-diagnostics.js.map +1 -1
- package/dist/project-settings.d.ts +36 -0
- package/dist/project-settings.js +96 -0
- package/dist/project-settings.js.map +1 -0
- package/dist/running-unity.d.ts +23 -0
- package/dist/running-unity.js +60 -0
- package/dist/running-unity.js.map +1 -1
- package/dist/stale-assembly-cache.d.ts +40 -0
- package/dist/stale-assembly-cache.js +76 -0
- package/dist/stale-assembly-cache.js.map +1 -0
- package/dist/tool-router.d.ts +39 -1
- package/dist/tool-router.js +397 -62
- package/dist/tool-router.js.map +1 -1
- package/dist/tools/capture-inline.js +4 -3
- package/dist/tools/capture-inline.js.map +1 -1
- package/dist/tools/editor-status.js +7 -2
- package/dist/tools/editor-status.js.map +1 -1
- package/dist/tools/execute-csharp.js +25 -2
- package/dist/tools/execute-csharp.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +34 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/inputsim-key.d.ts +40 -0
- package/dist/tools/inputsim-key.js +72 -0
- package/dist/tools/inputsim-key.js.map +1 -0
- package/dist/tools/inputsim-pointer.d.ts +40 -0
- package/dist/tools/inputsim-pointer.js +121 -0
- package/dist/tools/inputsim-pointer.js.map +1 -0
- package/dist/tools/inputsim-pointer3d.d.ts +40 -0
- package/dist/tools/inputsim-pointer3d.js +55 -0
- package/dist/tools/inputsim-pointer3d.js.map +1 -0
- package/dist/tools/inputsim-probe.d.ts +40 -0
- package/dist/tools/inputsim-probe.js +43 -0
- package/dist/tools/inputsim-probe.js.map +1 -0
- package/dist/tools/inputsim-step.d.ts +40 -0
- package/dist/tools/inputsim-step.js +50 -0
- package/dist/tools/inputsim-step.js.map +1 -0
- package/dist/tools/inputsim-touch.d.ts +40 -0
- package/dist/tools/inputsim-touch.js +83 -0
- package/dist/tools/inputsim-touch.js.map +1 -0
- package/dist/tools/memory-snapshot-capture.js +11 -5
- package/dist/tools/memory-snapshot-capture.js.map +1 -1
- package/dist/tools/pull-events.js +18 -10
- package/dist/tools/pull-events.js.map +1 -1
- package/dist/tools/read-compile-errors.js +10 -2
- package/dist/tools/read-compile-errors.js.map +1 -1
- package/dist/tools/resource-pressure.js +21 -10
- package/dist/tools/resource-pressure.js.map +1 -1
- package/dist/tools/screenshot-window.js +18 -12
- package/dist/tools/screenshot-window.js.map +1 -1
- package/dist/tools/screenshot.js +13 -6
- package/dist/tools/screenshot.js.map +1 -1
- package/dist/tools/visual-compare.d.ts +40 -0
- package/dist/tools/visual-compare.js +98 -0
- package/dist/tools/visual-compare.js.map +1 -0
- package/dist/unity-log.d.ts +51 -2
- package/dist/unity-log.js +152 -5
- package/dist/unity-log.js.map +1 -1
- package/package.json +1 -1
|
@@ -16,8 +16,9 @@
|
|
|
16
16
|
// that is the real trip point for the Bee build-driver hang. The OS
|
|
17
17
|
// `ulimit -n` soft limit is only a loose upper bound and is misleading on
|
|
18
18
|
// macOS: a GUI-launched Unity inherits `launchctl limit maxfiles`, not the
|
|
19
|
-
// MCP server's shell `ulimit`. So we measure headroom against the
|
|
20
|
-
//
|
|
19
|
+
// MCP server's shell `ulimit`. So we measure headroom against the Mono
|
|
20
|
+
// ceiling (default FD_CEILING_DEFAULT = 1024, configurable per project via
|
|
21
|
+
// `.unity-open-mcp/settings.json`), not the variable OS limit.
|
|
21
22
|
//
|
|
22
23
|
// Cross-platform probe:
|
|
23
24
|
// - macOS: `lsof -p <pid>` (no /proc on macOS). Bounded execFileSync
|
|
@@ -36,10 +37,27 @@ import { readdirSync } from "node:fs";
|
|
|
36
37
|
* misleading for a GUI-launched Unity on macOS (it inherits `launchctl limit
|
|
37
38
|
* maxfiles`, not the shell's ulimit). Documented in the tool response via
|
|
38
39
|
* `launchContextCaveat`.
|
|
40
|
+
*
|
|
41
|
+
* This is the DEFAULT used when a project has not overridden the ceiling via
|
|
42
|
+
* `.unity-open-mcp/settings.json` (`resourcePressure.fdCeiling`). The actual
|
|
43
|
+
* ceiling applied to a sample is resolved by `readFdCeiling` in
|
|
44
|
+
* `project-settings.ts` and threaded into `computeFdHeadroom` /
|
|
45
|
+
* `analyzeFdTrend` so the math scales with the configured value (relevant for
|
|
46
|
+
* Unity 6 / CoreCLR runtimes whose internal ceiling may differ from Mono's
|
|
47
|
+
* classic 1024).
|
|
48
|
+
*/
|
|
49
|
+
export const FD_CEILING_DEFAULT = 1024;
|
|
50
|
+
/**
|
|
51
|
+
* Absolute cap on the leak-detection threshold. The threshold is 10% of the
|
|
52
|
+
* resolved ceiling, but a very high configured ceiling (e.g. a CoreCLR runtime
|
|
53
|
+
* that legitimately sits over the Mono proxy) would otherwise push it so high
|
|
54
|
+
* that a real monotonic climb is missed — a climb of this many descriptors is a
|
|
55
|
+
* clear leak signal regardless of the ceiling. Kicks in once the ceiling exceeds
|
|
56
|
+
* ~5000 (10% of 5000 = 500).
|
|
39
57
|
*/
|
|
40
|
-
export const
|
|
58
|
+
export const FD_LEAK_DELTA_MAX = 500;
|
|
41
59
|
/**
|
|
42
|
-
* Headroom fraction (of
|
|
60
|
+
* Headroom fraction (of the resolved ceiling) at or below which the state is `warn`:
|
|
43
61
|
* the Editor is approaching the Mono ceiling and an agent should surface the
|
|
44
62
|
* trend so the operator can save + restart before the hang. ≥80% usage.
|
|
45
63
|
*/
|
|
@@ -54,37 +72,14 @@ export const FD_PROBE_TIMEOUT_MS = 3_000;
|
|
|
54
72
|
// ---------------------------------------------------------------------------
|
|
55
73
|
// Platform-specific probe implementations.
|
|
56
74
|
// ---------------------------------------------------------------------------
|
|
57
|
-
/**
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
catch (err) {
|
|
68
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
69
|
-
// lsof exits non-zero when the PID is gone. Treat that as not_found.
|
|
70
|
-
if (/no such file|exit code: 1/i.test(message)) {
|
|
71
|
-
return {
|
|
72
|
-
count: null,
|
|
73
|
-
method: "lsof",
|
|
74
|
-
reason: "not_found",
|
|
75
|
-
message: `lsof reported no process for PID ${pid}.`,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
return {
|
|
79
|
-
count: null,
|
|
80
|
-
method: "lsof",
|
|
81
|
-
reason: "lsof_failed",
|
|
82
|
-
message: `lsof failed for PID ${pid}: ${message}`,
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
// `lsof -p <pid>` prints a header line ("COMMAND PID USER FD TYPE ...") then
|
|
86
|
-
// one line per open fd. Subtract the header. An empty/blank stdout means the
|
|
87
|
-
// PID vanished between the spawn and the read — treat as not_found.
|
|
75
|
+
/**
|
|
76
|
+
* Parse the stdout of a successful `lsof -p <pid>` run into an fd count.
|
|
77
|
+
* Pure (no I/O) so it can be unit-tested without spawning lsof. lsof prints
|
|
78
|
+
* a header line ("COMMAND PID USER FD TYPE ...") then one line per open fd;
|
|
79
|
+
* the count is the non-empty line count minus the header. Empty stdout means
|
|
80
|
+
* the PID vanished between spawn and read.
|
|
81
|
+
*/
|
|
82
|
+
export function parseLsofStdout(stdout, pid) {
|
|
88
83
|
const lines = stdout.split(/\r?\n/).filter((l) => l.length > 0);
|
|
89
84
|
if (lines.length === 0) {
|
|
90
85
|
return {
|
|
@@ -102,6 +97,85 @@ function probeMacos(pid) {
|
|
|
102
97
|
approximate: false,
|
|
103
98
|
};
|
|
104
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Classify a failed `lsof -p <pid>` invocation. Pure (no I/O). `lsof` exits
|
|
102
|
+
* non-zero for several reasons: the PID is gone (the common case), but also
|
|
103
|
+
* privilege boundaries / SIP-protected handles — and in those cases it still
|
|
104
|
+
* prints FD rows to stdout before exiting 1. Node's `execFileSync` attaches
|
|
105
|
+
* that captured stdout to the error object as `err.stdout`; when present and
|
|
106
|
+
* non-empty, parse it instead of reporting not_found. Only map to not_found
|
|
107
|
+
* when there is no usable stdout AND the message indicates the PID is truly
|
|
108
|
+
* absent — a bare "exit code: 1" is ambiguous (privilege errors also exit 1).
|
|
109
|
+
*
|
|
110
|
+
* Two guardrails on the captured-stdout path:
|
|
111
|
+
* - `err.stdout` is only parsed when it is genuinely a non-empty string.
|
|
112
|
+
* When execFileSync fails at spawn (ENOENT/EPERM) the property exists but
|
|
113
|
+
* is undefined/null; naive String() coercion turned that into the literal
|
|
114
|
+
* "undefined" — one "line" — which parsed as a confident count of ZERO
|
|
115
|
+
* fds, exactly the failed-probe-as-ok misreport FdCountFailed exists to
|
|
116
|
+
* prevent.
|
|
117
|
+
* - When the probe was killed by the FD_PROBE_TIMEOUT_MS timeout, any
|
|
118
|
+
* captured stdout is a PARTIAL listing (lsof was still writing). The
|
|
119
|
+
* parsed count is a lower bound, so it is flagged `approximate: true`
|
|
120
|
+
* instead of being reported as an exact count.
|
|
121
|
+
*/
|
|
122
|
+
export function classifyLsofError(err, pid) {
|
|
123
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
124
|
+
const errObj = err && typeof err === "object"
|
|
125
|
+
? err
|
|
126
|
+
: undefined;
|
|
127
|
+
const capturedStdout = typeof errObj?.stdout === "string" ? errObj.stdout : "";
|
|
128
|
+
// execFileSync timeout: Node kills the child (SIGTERM by default) and sets
|
|
129
|
+
// `code: "ETIMEDOUT"` / `killed: true` on the error. Stdout captured up to
|
|
130
|
+
// that point is a partial fd listing.
|
|
131
|
+
const timedOut = errObj?.code === "ETIMEDOUT" ||
|
|
132
|
+
(errObj?.killed === true && typeof errObj?.signal === "string");
|
|
133
|
+
const capturedLines = capturedStdout.split(/\r?\n/).filter((l) => l.length > 0);
|
|
134
|
+
if (capturedLines.length > 0) {
|
|
135
|
+
return {
|
|
136
|
+
count: Math.max(0, capturedLines.length - 1),
|
|
137
|
+
method: "lsof",
|
|
138
|
+
approximate: timedOut,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
if (timedOut) {
|
|
142
|
+
return {
|
|
143
|
+
count: null,
|
|
144
|
+
method: "lsof",
|
|
145
|
+
reason: "lsof_failed",
|
|
146
|
+
message: `lsof timed out after ${FD_PROBE_TIMEOUT_MS}ms for PID ${pid} with no output.`,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
if (/no such file|no process|not found/i.test(message)) {
|
|
150
|
+
return {
|
|
151
|
+
count: null,
|
|
152
|
+
method: "lsof",
|
|
153
|
+
reason: "not_found",
|
|
154
|
+
message: `lsof reported no process for PID ${pid}.`,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
count: null,
|
|
159
|
+
method: "lsof",
|
|
160
|
+
reason: "lsof_failed",
|
|
161
|
+
message: `lsof failed for PID ${pid}: ${message}`,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/** macOS `lsof -p <pid>` scanner. Returns the line count (one line per fd). */
|
|
165
|
+
function probeMacos(pid) {
|
|
166
|
+
let stdout;
|
|
167
|
+
try {
|
|
168
|
+
stdout = execFileSync("lsof", ["-p", String(pid)], {
|
|
169
|
+
encoding: "utf8",
|
|
170
|
+
timeout: FD_PROBE_TIMEOUT_MS,
|
|
171
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
return classifyLsofError(err, pid);
|
|
176
|
+
}
|
|
177
|
+
return parseLsofStdout(stdout, pid);
|
|
178
|
+
}
|
|
105
179
|
/** Linux `/proc/<pid>/fd` directory entry count (no shell-out). */
|
|
106
180
|
function probeLinux(pid) {
|
|
107
181
|
let entries;
|
|
@@ -236,7 +310,7 @@ export function countFileDescriptors(pid) {
|
|
|
236
310
|
return currentProbe.count(pid);
|
|
237
311
|
}
|
|
238
312
|
/**
|
|
239
|
-
* Pure headroom math against the
|
|
313
|
+
* Pure headroom math against the Mono fd ceiling. The OS soft limit
|
|
240
314
|
* (`ulimit -n`) is intentionally NOT used — it is a loose upper bound and is
|
|
241
315
|
* misleading for GUI-launched Unity on macOS (inherits `launchctl limit
|
|
242
316
|
* maxfiles`, not the shell ulimit).
|
|
@@ -244,22 +318,38 @@ export function countFileDescriptors(pid) {
|
|
|
244
318
|
* `null` count (probe failed) → `state: "unknown"` with `reliable: false`. An
|
|
245
319
|
* agent must NOT treat "unknown" as "ok"; it should surface the trend (if any
|
|
246
320
|
* prior samples exist) and tell the operator the live count could not be read.
|
|
321
|
+
*
|
|
322
|
+
* `over_ceiling` (count >= ceiling) is informational: the OS-level count
|
|
323
|
+
* exceeded the Mono-ceiling proxy, but `lsof` over-counts relative to the
|
|
324
|
+
* IOSelector-registered descriptors that actually trip the hang. The router
|
|
325
|
+
* decides whether to alarm based on the TREND, not this absolute state.
|
|
326
|
+
*
|
|
327
|
+
* @param ceiling the resolved fd ceiling (default {@link FD_CEILING_DEFAULT});
|
|
328
|
+
* passed in by the router from `readFdCeiling` so the math scales with a
|
|
329
|
+
* project-configured override.
|
|
247
330
|
*/
|
|
248
|
-
export function computeFdHeadroom(count, approximate = false) {
|
|
331
|
+
export function computeFdHeadroom(count, approximate = false, ceiling = FD_CEILING_DEFAULT) {
|
|
249
332
|
if (count === null || !Number.isFinite(count)) {
|
|
250
333
|
return {
|
|
251
334
|
pressureRatio: 0,
|
|
252
|
-
headroom:
|
|
253
|
-
ceiling
|
|
335
|
+
headroom: ceiling,
|
|
336
|
+
ceiling,
|
|
254
337
|
state: "unknown",
|
|
255
338
|
reliable: false,
|
|
256
339
|
};
|
|
257
340
|
}
|
|
258
341
|
const clamped = Math.max(0, count);
|
|
259
|
-
const pressureRatio = Math.min(1, clamped /
|
|
260
|
-
const headroom = Math.max(0,
|
|
342
|
+
const pressureRatio = Math.min(1, clamped / ceiling);
|
|
343
|
+
const headroom = Math.max(0, ceiling - clamped);
|
|
261
344
|
let state;
|
|
262
|
-
if (
|
|
345
|
+
if (clamped >= ceiling) {
|
|
346
|
+
// OS-level fd count already past the Mono-ceiling proxy. Informational,
|
|
347
|
+
// not an alarm — `lsof` over-counts (mmap/assets/watchers) vs the
|
|
348
|
+
// IOSelector-registered descriptors that trip the hang. The router keys
|
|
349
|
+
// the operator warning off the TREND for this state.
|
|
350
|
+
state = "over_ceiling";
|
|
351
|
+
}
|
|
352
|
+
else if (approximate) {
|
|
263
353
|
// Windows HandleCount is naturally higher than Unix fds — only flag
|
|
264
354
|
// critical, never warn, so an agent does not over-react.
|
|
265
355
|
state = pressureRatio >= FD_CRITICAL_RATIO ? "critical" : "ok";
|
|
@@ -276,7 +366,7 @@ export function computeFdHeadroom(count, approximate = false) {
|
|
|
276
366
|
return {
|
|
277
367
|
pressureRatio,
|
|
278
368
|
headroom,
|
|
279
|
-
ceiling
|
|
369
|
+
ceiling,
|
|
280
370
|
state,
|
|
281
371
|
reliable: !approximate,
|
|
282
372
|
};
|
|
@@ -284,11 +374,20 @@ export function computeFdHeadroom(count, approximate = false) {
|
|
|
284
374
|
/**
|
|
285
375
|
* Pure trend detector over a sample list. Only considers samples with a known
|
|
286
376
|
* count for the SAME pid (a Unity restart changes the PID; the trend must not
|
|
287
|
-
* mix pre- and post-restart samples). A
|
|
288
|
-
*
|
|
289
|
-
*
|
|
377
|
+
* mix pre- and post-restart samples). A strictly non-decreasing climb of at
|
|
378
|
+
* least the leak threshold (10% of the ceiling, capped at
|
|
379
|
+
* {@link FD_LEAK_DELTA_MAX}) across ≥2 samples is `leaking`; any other
|
|
380
|
+
* net-positive first-to-last delta (a non-monotonic climb, or a monotonic climb
|
|
381
|
+
* below the threshold) is `rising`; otherwise `stable`. Fewer than two usable
|
|
382
|
+
* samples → `no_history`. `leaking` is the actionable signal; `rising` is drift.
|
|
383
|
+
*
|
|
384
|
+
* @param ceiling the resolved fd ceiling (default {@link FD_CEILING_DEFAULT});
|
|
385
|
+
* the leak threshold scales with it (capped at {@link FD_LEAK_DELTA_MAX}) so a
|
|
386
|
+
* project that configures a higher ceiling (e.g. a CoreCLR runtime) does not
|
|
387
|
+
* flag normal growth as a leak — while a very high ceiling cannot blind the
|
|
388
|
+
* detector to a real climb.
|
|
290
389
|
*/
|
|
291
|
-
export function analyzeFdTrend(samples) {
|
|
390
|
+
export function analyzeFdTrend(samples, ceiling = FD_CEILING_DEFAULT) {
|
|
292
391
|
if (samples.length === 0) {
|
|
293
392
|
return { state: "no_history", delta: null, sampleCount: 0 };
|
|
294
393
|
}
|
|
@@ -301,9 +400,11 @@ export function analyzeFdTrend(samples) {
|
|
|
301
400
|
const first = usable[0].count;
|
|
302
401
|
const last = usable[usable.length - 1].count;
|
|
303
402
|
const delta = last - first;
|
|
304
|
-
// `
|
|
305
|
-
// ≥ the
|
|
306
|
-
//
|
|
403
|
+
// `leaking` = strictly non-decreasing across all usable samples AND the total
|
|
404
|
+
// climb ≥ the leak threshold (10% of the ceiling, capped at FD_LEAK_DELTA_MAX)
|
|
405
|
+
// — a meaningful monotonic leak. `rising` = a net-positive first-to-last delta
|
|
406
|
+
// that does NOT qualify as a leak (non-monotonic, or a monotonic climb below
|
|
407
|
+
// the threshold) — drift / measurement noise, not actionable by itself.
|
|
307
408
|
let monotonic = true;
|
|
308
409
|
for (let i = 1; i < usable.length; i++) {
|
|
309
410
|
if (usable[i].count < usable[i - 1].count) {
|
|
@@ -311,7 +412,8 @@ export function analyzeFdTrend(samples) {
|
|
|
311
412
|
break;
|
|
312
413
|
}
|
|
313
414
|
}
|
|
314
|
-
|
|
415
|
+
const leakThreshold = Math.min(ceiling * 0.1, FD_LEAK_DELTA_MAX);
|
|
416
|
+
if (monotonic && delta >= leakThreshold) {
|
|
315
417
|
return { state: "leaking", delta, sampleCount: usable.length };
|
|
316
418
|
}
|
|
317
419
|
if (delta > 0) {
|
|
@@ -321,13 +423,19 @@ export function analyzeFdTrend(samples) {
|
|
|
321
423
|
}
|
|
322
424
|
/**
|
|
323
425
|
* Caveat string surfaced in the resource_pressure response so an agent (and
|
|
324
|
-
* the operator) understand the launch-context nuance: the Mono ceiling, not
|
|
325
|
-
*
|
|
326
|
-
* `launchctl limit maxfiles` (macOS) rather than the shell's ulimit.
|
|
426
|
+
* the operator) understand the launch-context nuance: the Mono ceiling, not the
|
|
427
|
+
* OS soft limit, is the trip point; a GUI-launched Unity inherits
|
|
428
|
+
* `launchctl limit maxfiles` (macOS) rather than the shell's ulimit. The
|
|
429
|
+
* resolved ceiling applied to the sample is reported separately in the
|
|
430
|
+
* response's `ceiling` / `ceilingSource` fields.
|
|
327
431
|
*/
|
|
328
|
-
export const LAUNCH_CONTEXT_CAVEAT = "Headroom is measured against Mono's internal
|
|
329
|
-
"(the real trip point for the Bee build-driver hang),
|
|
330
|
-
"(ulimit -n / launchctl limit maxfiles). A GUI-launched
|
|
331
|
-
"desktop environment's file limit, not the MCP server's
|
|
332
|
-
"OS soft limit is only a loose upper bound."
|
|
432
|
+
export const LAUNCH_CONTEXT_CAVEAT = "Headroom is measured against the Mono runtime's internal file-descriptor " +
|
|
433
|
+
"ceiling (default ~1024 — the real trip point for the Bee build-driver hang), " +
|
|
434
|
+
"NOT the OS soft limit (ulimit -n / launchctl limit maxfiles). A GUI-launched " +
|
|
435
|
+
"Unity inherits the desktop environment's file limit, not the MCP server's " +
|
|
436
|
+
"shell limit, so the OS soft limit is only a loose upper bound. The ceiling " +
|
|
437
|
+
"is configurable per project via `.unity-open-mcp/settings.json` " +
|
|
438
|
+
"(`resourcePressure.fdCeiling`) for runtimes whose internal limit differs " +
|
|
439
|
+
"from Mono's classic 1024 (e.g. Unity 6 / CoreCLR); the resolved value and " +
|
|
440
|
+
"its source are in the response's `ceiling` and `ceilingSource` fields.";
|
|
333
441
|
//# sourceMappingURL=process-diagnostics.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-diagnostics.js","sourceRoot":"","sources":["../src/process-diagnostics.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,eAAe;AACf,EAAE;AACF,+EAA+E;AAC/E,8EAA8E;AAC9E,6EAA6E;AAC7E,6EAA6E;AAC7E,8EAA8E;AAC9E,yEAAyE;AACzE,sBAAsB;AACtB,EAAE;AACF,4EAA4E;AAC5E,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,sEAAsE;AACtE,4EAA4E;AAC5E,6EAA6E;AAC7E,
|
|
1
|
+
{"version":3,"file":"process-diagnostics.js","sourceRoot":"","sources":["../src/process-diagnostics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Below this configured ceiling the value is rejected → default. */
|
|
2
|
+
export declare const FD_CEILING_MIN = 64;
|
|
3
|
+
/** Above this configured ceiling the value is clamped down. */
|
|
4
|
+
export declare const FD_CEILING_MAX = 1000000;
|
|
5
|
+
/**
|
|
6
|
+
* The `resourcePressure` slice. `JsonUtility` on the C# side ignores it; this
|
|
7
|
+
* module is the only reader. Optional end-to-end so a missing slice falls back
|
|
8
|
+
* to the default ceiling.
|
|
9
|
+
*/
|
|
10
|
+
export interface ResourcePressureSettings {
|
|
11
|
+
fdCeiling?: number;
|
|
12
|
+
}
|
|
13
|
+
/** Result of resolving the fd ceiling for a project. */
|
|
14
|
+
export interface FdCeilingResolution {
|
|
15
|
+
/** The fd ceiling to apply (already clamped into range). */
|
|
16
|
+
ceiling: number;
|
|
17
|
+
/** `"config"` when a valid in-range value was read; `"default"` otherwise. */
|
|
18
|
+
source: "default" | "config";
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Resolve the settings file path for a project root. Exported for tests.
|
|
22
|
+
* `resolve` (not `join`) anchors a RELATIVE project path against the server's
|
|
23
|
+
* cwd instead of producing a relative path that would be re-interpreted by
|
|
24
|
+
* later read calls — matching how every other project-rooted path is resolved.
|
|
25
|
+
*/
|
|
26
|
+
export declare function settingsFilePath(projectPath: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Read the fd ceiling for a project from `.unity-open-mcp/settings.json`.
|
|
29
|
+
* Never throws: missing file, unparseable JSON, missing slice, or an invalid
|
|
30
|
+
* value all fall back to {@link FD_CEILING_DEFAULT} with `source: "default"`.
|
|
31
|
+
*
|
|
32
|
+
* Reads fresh on every call — `resource_pressure` is invoked infrequently
|
|
33
|
+
* (after heavy automation), and fresh reads let an operator change the setting
|
|
34
|
+
* without restarting the MCP server.
|
|
35
|
+
*/
|
|
36
|
+
export declare function readFdCeiling(projectPath: string): FdCeilingResolution;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// TS-side reader for `.unity-open-mcp/settings.json`.
|
|
2
|
+
//
|
|
3
|
+
// The same settings file is already read by C# on the Unity side
|
|
4
|
+
// (`VerifyProjectSettings.cs`, `BridgeProjectSettings.cs`) for verify + bridge
|
|
5
|
+
// tunables. Those readers use `JsonUtility`, which silently IGNORES unknown
|
|
6
|
+
// keys — so a TS-read slice in the same file does not conflict. This module is
|
|
7
|
+
// the TS-side mirror: it reads keys that are consumed entirely inside the MCP
|
|
8
|
+
// server (Node.js) by tools that deliberately do NOT depend on the bridge.
|
|
9
|
+
//
|
|
10
|
+
// The motivating consumer is `resource_pressure`, a server-side fd probe whose
|
|
11
|
+
// ceiling must be overridable for runtimes whose internal limit differs from
|
|
12
|
+
// Mono's classic 1024 (e.g. Unity 6 / CoreCLR). Because `resource_pressure`
|
|
13
|
+
// runs server-side and the bridge is the thing that dies on fd-exhaustion, the
|
|
14
|
+
// ceiling CANNOT be resolved through the bridge — it must be read here.
|
|
15
|
+
//
|
|
16
|
+
// Robustness mirrors the C# pattern: missing file / section / invalid value →
|
|
17
|
+
// defaults, never throws.
|
|
18
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
19
|
+
import { resolve } from "node:path";
|
|
20
|
+
import { STATUS_DIR_NAME } from "./constants.js";
|
|
21
|
+
import { FD_CEILING_DEFAULT } from "./process-diagnostics.js";
|
|
22
|
+
/** File name inside the `.unity-open-mcp/` directory. */
|
|
23
|
+
const SETTINGS_FILE_NAME = "settings.json";
|
|
24
|
+
/** Below this configured ceiling the value is rejected → default. */
|
|
25
|
+
export const FD_CEILING_MIN = 64;
|
|
26
|
+
/** Above this configured ceiling the value is clamped down. */
|
|
27
|
+
export const FD_CEILING_MAX = 1_000_000;
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the settings file path for a project root. Exported for tests.
|
|
30
|
+
* `resolve` (not `join`) anchors a RELATIVE project path against the server's
|
|
31
|
+
* cwd instead of producing a relative path that would be re-interpreted by
|
|
32
|
+
* later read calls — matching how every other project-rooted path is resolved.
|
|
33
|
+
*/
|
|
34
|
+
export function settingsFilePath(projectPath) {
|
|
35
|
+
return resolve(projectPath, STATUS_DIR_NAME, SETTINGS_FILE_NAME);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Clamp a configured ceiling following the C# `ClampBatchExecuteMaxCommands`
|
|
39
|
+
* pattern: below the min → default (a nonsensically-low ceiling is treated as
|
|
40
|
+
* "not configured"); above the max → the max. An in-range value is kept as-is.
|
|
41
|
+
* The `source` stays `"config"` whenever a valid number was supplied (even if
|
|
42
|
+
* clamped to the max), and drops to `"default"` only when the value is absent,
|
|
43
|
+
* non-numeric, or below the min.
|
|
44
|
+
*/
|
|
45
|
+
function resolveConfigured(raw) {
|
|
46
|
+
if (typeof raw !== "number" || !Number.isFinite(raw)) {
|
|
47
|
+
return { ceiling: FD_CEILING_DEFAULT, source: "default" };
|
|
48
|
+
}
|
|
49
|
+
if (raw < FD_CEILING_MIN) {
|
|
50
|
+
return { ceiling: FD_CEILING_DEFAULT, source: "default" };
|
|
51
|
+
}
|
|
52
|
+
if (raw > FD_CEILING_MAX) {
|
|
53
|
+
return { ceiling: FD_CEILING_MAX, source: "config" };
|
|
54
|
+
}
|
|
55
|
+
return { ceiling: Math.floor(raw), source: "config" };
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Read the fd ceiling for a project from `.unity-open-mcp/settings.json`.
|
|
59
|
+
* Never throws: missing file, unparseable JSON, missing slice, or an invalid
|
|
60
|
+
* value all fall back to {@link FD_CEILING_DEFAULT} with `source: "default"`.
|
|
61
|
+
*
|
|
62
|
+
* Reads fresh on every call — `resource_pressure` is invoked infrequently
|
|
63
|
+
* (after heavy automation), and fresh reads let an operator change the setting
|
|
64
|
+
* without restarting the MCP server.
|
|
65
|
+
*/
|
|
66
|
+
export function readFdCeiling(projectPath) {
|
|
67
|
+
let path;
|
|
68
|
+
try {
|
|
69
|
+
path = settingsFilePath(projectPath);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return { ceiling: FD_CEILING_DEFAULT, source: "default" };
|
|
73
|
+
}
|
|
74
|
+
if (!existsSync(path)) {
|
|
75
|
+
return { ceiling: FD_CEILING_DEFAULT, source: "default" };
|
|
76
|
+
}
|
|
77
|
+
let raw;
|
|
78
|
+
try {
|
|
79
|
+
raw = readFileSync(path, "utf8");
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return { ceiling: FD_CEILING_DEFAULT, source: "default" };
|
|
83
|
+
}
|
|
84
|
+
let parsed;
|
|
85
|
+
try {
|
|
86
|
+
parsed = JSON.parse(raw);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return { ceiling: FD_CEILING_DEFAULT, source: "default" };
|
|
90
|
+
}
|
|
91
|
+
if (!parsed || typeof parsed !== "object") {
|
|
92
|
+
return { ceiling: FD_CEILING_DEFAULT, source: "default" };
|
|
93
|
+
}
|
|
94
|
+
return resolveConfigured(parsed.resourcePressure?.fdCeiling);
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=project-settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-settings.js","sourceRoot":"","sources":["../src/project-settings.ts"],"names":[],"mappings":"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"}
|
package/dist/running-unity.d.ts
CHANGED
|
@@ -38,6 +38,18 @@ export declare function parseProjectPathArg(args: ReadonlyArray<string>): string
|
|
|
38
38
|
* token is a reliable end-of-path marker.
|
|
39
39
|
*/
|
|
40
40
|
export declare function isUnityCommandLine(commandLine: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* `true` when the command line identifies a Unity **worker** process
|
|
43
|
+
* (AssetImportWorker / batch import worker), not the interactive editor.
|
|
44
|
+
* Unity spawns these as children with `-batchMode -parentPid
|
|
45
|
+
* -name AssetImportWorker0 … -projectPath <same project>`; their executable
|
|
46
|
+
* basename is still `Unity`, so they pass `isUnityCommandLine` and pollute the
|
|
47
|
+
* PID scan. feedback #2 (2026-08-06): `restart_editor` / `resource_pressure`
|
|
48
|
+
* targeted an AssetImportWorker instead of the main editor on fd-exhaustion
|
|
49
|
+
* because the worker appeared first in the scan. Detecting workers here (rather
|
|
50
|
+
* than at the call sites) hardens every caller at once.
|
|
51
|
+
*/
|
|
52
|
+
export declare function isAssetImportWorker(commandLine: string): boolean;
|
|
41
53
|
/**
|
|
42
54
|
* Naive argv splitter for a `ps`-formatted command line. Honours double and
|
|
43
55
|
* single quotes and treats backslashes as literal (macOS `ps` output never
|
|
@@ -103,3 +115,14 @@ export declare function setUnityProcessScannerForTest(fake: UnityProcessScanner
|
|
|
103
115
|
export declare function findUnityForProject(projectPath: string | null | undefined): {
|
|
104
116
|
pid: number;
|
|
105
117
|
} | null;
|
|
118
|
+
/**
|
|
119
|
+
* Read the full command line of a live process by PID. Cross-platform best
|
|
120
|
+
* effort: macOS/Linux via `ps -p <pid> -o command=`, Windows via a PowerShell
|
|
121
|
+
* `Get-CimInstance Win32_Process` lookup. Returns `null` on any failure (ps
|
|
122
|
+
* missing, PID gone, parse error) — never throws.
|
|
123
|
+
*
|
|
124
|
+
* feedback #2 (2026-08-06): `restart_editor`'s dry-run now surfaces the
|
|
125
|
+
* resolved process's command line so a PID misattribution (e.g. killing an
|
|
126
|
+
* AssetImportWorker instead of the main editor) is visible before confirm.
|
|
127
|
+
*/
|
|
128
|
+
export declare function readProcessCommandLine(pid: number): string | null;
|
package/dist/running-unity.js
CHANGED
|
@@ -105,6 +105,26 @@ export function isUnityCommandLine(commandLine) {
|
|
|
105
105
|
const name = basenameSafe(unquoted);
|
|
106
106
|
return name === "Unity";
|
|
107
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* `true` when the command line identifies a Unity **worker** process
|
|
110
|
+
* (AssetImportWorker / batch import worker), not the interactive editor.
|
|
111
|
+
* Unity spawns these as children with `-batchMode -parentPid
|
|
112
|
+
* -name AssetImportWorker0 … -projectPath <same project>`; their executable
|
|
113
|
+
* basename is still `Unity`, so they pass `isUnityCommandLine` and pollute the
|
|
114
|
+
* PID scan. feedback #2 (2026-08-06): `restart_editor` / `resource_pressure`
|
|
115
|
+
* targeted an AssetImportWorker instead of the main editor on fd-exhaustion
|
|
116
|
+
* because the worker appeared first in the scan. Detecting workers here (rather
|
|
117
|
+
* than at the call sites) hardens every caller at once.
|
|
118
|
+
*/
|
|
119
|
+
export function isAssetImportWorker(commandLine) {
|
|
120
|
+
// Unity passes the worker label via `-name AssetImportWorker*` (the bridge's
|
|
121
|
+
// own ReadNameArg parses the same flag). The label carries a trailing index
|
|
122
|
+
// (AssetImportWorker0), so match the prefix without a `\b` after "Worker"
|
|
123
|
+
// (a digit is a word char and would defeat `\b`). Also catch `-importWorker`,
|
|
124
|
+
// which older Unity releases used for the same purpose.
|
|
125
|
+
return /-name\s+AssetImportWorker/.test(commandLine) ||
|
|
126
|
+
/(^|\s)-importWorker(\s|$)/.test(commandLine);
|
|
127
|
+
}
|
|
108
128
|
function trimMatching(value, ...chars) {
|
|
109
129
|
let s = value;
|
|
110
130
|
while (s.length > 0) {
|
|
@@ -244,6 +264,10 @@ export function parsePsOutput(stdout) {
|
|
|
244
264
|
continue;
|
|
245
265
|
if (!isUnityCommandLine(rest))
|
|
246
266
|
continue;
|
|
267
|
+
// feedback #2 — exclude AssetImportWorker children; they share the
|
|
268
|
+
// project path but are never the interactive editor we want to act on.
|
|
269
|
+
if (isAssetImportWorker(rest))
|
|
270
|
+
continue;
|
|
247
271
|
const projectPath = parseProjectPathArg(splitArgs(rest));
|
|
248
272
|
out.push({
|
|
249
273
|
pid,
|
|
@@ -272,6 +296,9 @@ export function parsePowerShellLines(stdout) {
|
|
|
272
296
|
const pid = Number.parseInt(pidStr, 10);
|
|
273
297
|
if (!Number.isInteger(pid) || pid <= 0)
|
|
274
298
|
continue;
|
|
299
|
+
// feedback #2 — exclude AssetImportWorker children on Windows too.
|
|
300
|
+
if (rest.length > 0 && rest !== "null" && isAssetImportWorker(rest))
|
|
301
|
+
continue;
|
|
275
302
|
const projectPath = rest.length === 0 || rest === "null"
|
|
276
303
|
? null
|
|
277
304
|
: parseProjectPathArg(splitArgsWindows(rest));
|
|
@@ -384,4 +411,37 @@ export function findUnityForProject(projectPath) {
|
|
|
384
411
|
}
|
|
385
412
|
return null;
|
|
386
413
|
}
|
|
414
|
+
/**
|
|
415
|
+
* Read the full command line of a live process by PID. Cross-platform best
|
|
416
|
+
* effort: macOS/Linux via `ps -p <pid> -o command=`, Windows via a PowerShell
|
|
417
|
+
* `Get-CimInstance Win32_Process` lookup. Returns `null` on any failure (ps
|
|
418
|
+
* missing, PID gone, parse error) — never throws.
|
|
419
|
+
*
|
|
420
|
+
* feedback #2 (2026-08-06): `restart_editor`'s dry-run now surfaces the
|
|
421
|
+
* resolved process's command line so a PID misattribution (e.g. killing an
|
|
422
|
+
* AssetImportWorker instead of the main editor) is visible before confirm.
|
|
423
|
+
*/
|
|
424
|
+
export function readProcessCommandLine(pid) {
|
|
425
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
426
|
+
return null;
|
|
427
|
+
const platform = process.platform;
|
|
428
|
+
try {
|
|
429
|
+
if (platform === "win32") {
|
|
430
|
+
const script = "Get-CimInstance Win32_Process -Filter \"ProcessId=" + pid + "\" | " +
|
|
431
|
+
"ForEach-Object { Write-Output $_.CommandLine }";
|
|
432
|
+
const stdout = execFileSync("powershell", ["-NoProfile", "-NonInteractive", "-Command", script], { encoding: "utf8", windowsHide: true });
|
|
433
|
+
const line = stdout.trim();
|
|
434
|
+
return line.length > 0 ? line : null;
|
|
435
|
+
}
|
|
436
|
+
// macOS + Linux share the `ps -p <pid> -o command=` form.
|
|
437
|
+
const stdout = execFileSync("ps", ["-p", String(pid), "-o", "command="], {
|
|
438
|
+
encoding: "utf8",
|
|
439
|
+
});
|
|
440
|
+
const line = stdout.trim();
|
|
441
|
+
return line.length > 0 ? line : null;
|
|
442
|
+
}
|
|
443
|
+
catch {
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
387
447
|
//# sourceMappingURL=running-unity.js.map
|