unity-open-mcp 1.1.0 → 1.3.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 (174) hide show
  1. package/README.md +33 -2
  2. package/dist/agent-identity.d.ts +12 -0
  3. package/dist/agent-identity.js +2 -0
  4. package/dist/agent-identity.js.map +1 -1
  5. package/dist/batch-spawn.d.ts +39 -0
  6. package/dist/batch-spawn.js +287 -69
  7. package/dist/batch-spawn.js.map +1 -1
  8. package/dist/capabilities/build-capabilities.js +2 -0
  9. package/dist/capabilities/build-capabilities.js.map +1 -1
  10. package/dist/capabilities/discovery.d.ts +51 -0
  11. package/dist/capabilities/discovery.js +87 -0
  12. package/dist/capabilities/discovery.js.map +1 -0
  13. package/dist/capabilities/lifecycle.d.ts +6 -0
  14. package/dist/capabilities/lifecycle.js +17 -1
  15. package/dist/capabilities/lifecycle.js.map +1 -1
  16. package/dist/capabilities/tool-groups.js +1 -0
  17. package/dist/capabilities/tool-groups.js.map +1 -1
  18. package/dist/child-supervision.d.ts +6 -0
  19. package/dist/child-supervision.js +12 -0
  20. package/dist/child-supervision.js.map +1 -1
  21. package/dist/cli/args.d.ts +24 -2
  22. package/dist/cli/args.js +97 -2
  23. package/dist/cli/args.js.map +1 -1
  24. package/dist/cli/cli.js +42 -3
  25. package/dist/cli/cli.js.map +1 -1
  26. package/dist/cli/help-text.d.ts +1 -0
  27. package/dist/cli/help-text.js +71 -3
  28. package/dist/cli/help-text.js.map +1 -1
  29. package/dist/cli/json-guards.d.ts +2 -0
  30. package/dist/cli/json-guards.js +5 -0
  31. package/dist/cli/json-guards.js.map +1 -0
  32. package/dist/cli/setup-command.d.ts +88 -0
  33. package/dist/cli/setup-command.js +444 -0
  34. package/dist/cli/setup-command.js.map +1 -0
  35. package/dist/cli/update-command.d.ts +30 -0
  36. package/dist/cli/update-command.js +271 -0
  37. package/dist/cli/update-command.js.map +1 -0
  38. package/dist/compile-verify.d.ts +3 -0
  39. package/dist/compile-verify.js +4 -0
  40. package/dist/compile-verify.js.map +1 -1
  41. package/dist/constants.d.ts +10 -2
  42. package/dist/constants.js +10 -2
  43. package/dist/constants.js.map +1 -1
  44. package/dist/dialog-dismiss.d.ts +108 -12
  45. package/dist/dialog-dismiss.js +294 -109
  46. package/dist/dialog-dismiss.js.map +1 -1
  47. package/dist/dialog-policy.d.ts +55 -9
  48. package/dist/dialog-policy.js +85 -15
  49. package/dist/dialog-policy.js.map +1 -1
  50. package/dist/editor-search.d.ts +2 -0
  51. package/dist/editor-search.js +8 -0
  52. package/dist/editor-search.js.map +1 -0
  53. package/dist/gate-error.d.ts +3 -0
  54. package/dist/gate-error.js.map +1 -1
  55. package/dist/index.js +55 -1
  56. package/dist/index.js.map +1 -1
  57. package/dist/instance-discovery.d.ts +16 -0
  58. package/dist/instance-discovery.js +45 -0
  59. package/dist/instance-discovery.js.map +1 -1
  60. package/dist/jobs/adapters.d.ts +15 -0
  61. package/dist/jobs/adapters.js +147 -0
  62. package/dist/jobs/adapters.js.map +1 -0
  63. package/dist/jobs/handler.d.ts +3 -0
  64. package/dist/jobs/handler.js +29 -0
  65. package/dist/jobs/handler.js.map +1 -0
  66. package/dist/jobs/job-manager.d.ts +123 -0
  67. package/dist/jobs/job-manager.js +290 -0
  68. package/dist/jobs/job-manager.js.map +1 -0
  69. package/dist/live-client.d.ts +50 -2
  70. package/dist/live-client.js +221 -26
  71. package/dist/live-client.js.map +1 -1
  72. package/dist/local-tools.d.ts +17 -0
  73. package/dist/local-tools.js +47 -0
  74. package/dist/local-tools.js.map +1 -0
  75. package/dist/process-diagnostics.d.ts +47 -20
  76. package/dist/process-diagnostics.js +30 -15
  77. package/dist/process-diagnostics.js.map +1 -1
  78. package/dist/project-path.d.ts +86 -0
  79. package/dist/project-path.js +159 -0
  80. package/dist/project-path.js.map +1 -0
  81. package/dist/release-lookup.d.ts +32 -0
  82. package/dist/release-lookup.js +147 -0
  83. package/dist/release-lookup.js.map +1 -0
  84. package/dist/routers.d.ts +17 -3
  85. package/dist/routers.js +28 -8
  86. package/dist/routers.js.map +1 -1
  87. package/dist/running-unity.d.ts +21 -0
  88. package/dist/running-unity.js +50 -0
  89. package/dist/running-unity.js.map +1 -1
  90. package/dist/schema-defaults.d.ts +4 -3
  91. package/dist/schema-defaults.js +20 -6
  92. package/dist/schema-defaults.js.map +1 -1
  93. package/dist/server-instructions.js +7 -1
  94. package/dist/server-instructions.js.map +1 -1
  95. package/dist/server.d.ts +10 -3
  96. package/dist/server.js +24 -25
  97. package/dist/server.js.map +1 -1
  98. package/dist/setup/portable-config.d.ts +60 -0
  99. package/dist/setup/portable-config.js +249 -0
  100. package/dist/setup/portable-config.js.map +1 -0
  101. package/dist/skill/SKILL.md +68 -0
  102. package/dist/skill/copy-references.d.ts +2 -0
  103. package/dist/skill/copy-references.js +16 -0
  104. package/dist/skill/copy-references.js.map +1 -0
  105. package/dist/skill/generate-skill.js +5 -3
  106. package/dist/skill/generate-skill.js.map +1 -1
  107. package/dist/skill/references/batch-and-gates.md +142 -0
  108. package/dist/skill/references/compile-and-safe-mode.md +163 -0
  109. package/dist/skill/references/discovery-and-groups.md +30 -0
  110. package/dist/skill/references/routing-and-lifecycle.md +62 -0
  111. package/dist/skill/references/senses-and-tests.md +41 -0
  112. package/dist/skill/references/yaml-and-offline-work.md +33 -0
  113. package/dist/templates/mcp-wrapper.sh +35 -0
  114. package/dist/tool-contract.d.ts +8 -0
  115. package/dist/tool-contract.js +253 -0
  116. package/dist/tool-contract.js.map +1 -0
  117. package/dist/tool-router.d.ts +62 -7
  118. package/dist/tool-router.js +535 -61
  119. package/dist/tool-router.js.map +1 -1
  120. package/dist/tool-session-state.d.ts +6 -0
  121. package/dist/tool-session-state.js +14 -0
  122. package/dist/tool-session-state.js.map +1 -1
  123. package/dist/tools/agent-capabilities.js +9 -0
  124. package/dist/tools/agent-capabilities.js.map +1 -1
  125. package/dist/tools/batch-execute.js +17 -14
  126. package/dist/tools/batch-execute.js.map +1 -1
  127. package/dist/tools/bridge-status.js +4 -5
  128. package/dist/tools/bridge-status.js.map +1 -1
  129. package/dist/tools/compile-check.js +6 -4
  130. package/dist/tools/compile-check.js.map +1 -1
  131. package/dist/tools/component-get.js +2 -2
  132. package/dist/tools/component-get.js.map +1 -1
  133. package/dist/tools/component-modify.js +4 -4
  134. package/dist/tools/component-modify.js.map +1 -1
  135. package/dist/tools/execute-csharp.js +8 -4
  136. package/dist/tools/execute-csharp.js.map +1 -1
  137. package/dist/tools/gameobject-modify.js +2 -1
  138. package/dist/tools/gameobject-modify.js.map +1 -1
  139. package/dist/tools/index.d.ts +2 -0
  140. package/dist/tools/index.js +10 -2
  141. package/dist/tools/index.js.map +1 -1
  142. package/dist/tools/inputsim-key.js +12 -14
  143. package/dist/tools/inputsim-key.js.map +1 -1
  144. package/dist/tools/inputsim-pointer.js +3 -3
  145. package/dist/tools/inputsim-pointer.js.map +1 -1
  146. package/dist/tools/inputsim-step.js +7 -8
  147. package/dist/tools/inputsim-step.js.map +1 -1
  148. package/dist/tools/inputsim-touch.js +9 -10
  149. package/dist/tools/inputsim-touch.js.map +1 -1
  150. package/dist/tools/jobs.d.ts +40 -0
  151. package/dist/tools/jobs.js +18 -0
  152. package/dist/tools/jobs.js.map +1 -0
  153. package/dist/tools/manage-tools.js +10 -5
  154. package/dist/tools/manage-tools.js.map +1 -1
  155. package/dist/tools/project-commands.d.ts +40 -0
  156. package/dist/tools/project-commands.js +23 -0
  157. package/dist/tools/project-commands.js.map +1 -0
  158. package/dist/tools/read-compile-errors.js +14 -93
  159. package/dist/tools/read-compile-errors.js.map +1 -1
  160. package/dist/tools/resource-pressure.js +27 -17
  161. package/dist/tools/resource-pressure.js.map +1 -1
  162. package/dist/tools/scan-paths.js +4 -4
  163. package/dist/tools/scan-paths.js.map +1 -1
  164. package/dist/tools/schema-fragments.d.ts +15 -3
  165. package/dist/tools/schema-fragments.js +23 -13
  166. package/dist/tools/schema-fragments.js.map +1 -1
  167. package/dist/tools/upgrade.d.ts +40 -0
  168. package/dist/tools/upgrade.js +55 -0
  169. package/dist/tools/upgrade.js.map +1 -0
  170. package/dist/unity-install-discovery.d.ts +24 -0
  171. package/dist/unity-install-discovery.js +113 -4
  172. package/dist/unity-install-discovery.js.map +1 -1
  173. package/package.json +4 -3
  174. package/templates/mcp-wrapper.sh +35 -0
@@ -0,0 +1,159 @@
1
+ // Portable Unity project-path resolution.
2
+ //
3
+ // One resolver shared by the stdio server bootstrap (`index.ts`), the thin
4
+ // CLI (`routers.ts` → `resolveEnv`), and the `setup` config writers. It turns
5
+ // whatever the caller has — an env var, a `--project` flag, or nothing but the
6
+ // spawn cwd — into ONE absolute Unity project root, so committed MCP client
7
+ // config never has to carry a per-machine path.
8
+ //
9
+ // The module is deliberately light (only `node:fs` + `node:path`): `index.ts`
10
+ // imports it statically on the pre-`getEnv()` path, which must not pull the
11
+ // heavy ALL_TOOLS graph.
12
+ import { existsSync, statSync } from "node:fs";
13
+ import * as nodePath from "node:path";
14
+ /** Directories that must exist directly under a Unity project root. */
15
+ export const UNITY_ROOT_MARKERS = ["Assets", "Packages", "ProjectSettings"];
16
+ export class ProjectPathError extends Error {
17
+ code;
18
+ constructor(code, message) {
19
+ super(message);
20
+ this.code = code;
21
+ this.name = "ProjectPathError";
22
+ }
23
+ }
24
+ /**
25
+ * Resolve the Unity project root. First match wins:
26
+ *
27
+ * 1. `flagPath` (CLI `--project`) — an explicit argument beats ambient state.
28
+ * 2. `envPath` (`UNITY_PROJECT_PATH`) — keeps every existing client config
29
+ * working, and wins over `--project-from-cwd` so a machine-local env
30
+ * override can still redirect a committed config.
31
+ * 3. `projectFromCwd` (+ optional `unitySubpath`) — the portable path.
32
+ * 4. nothing → `ProjectPathError` with an actionable message.
33
+ *
34
+ * Relative `flagPath` / `envPath` values resolve against `cwd`, so
35
+ * `--project Client` works from a monorepo root. The result is always
36
+ * absolute: bridge port hashing and instance locks are keyed on it.
37
+ *
38
+ * Does NOT touch the filesystem — validation is `validateUnityProjectRoot`,
39
+ * which callers apply according to how strict they need to be.
40
+ */
41
+ export function resolveProjectPath(options = {}) {
42
+ const p = options.pathApi ?? nodePath;
43
+ const cwd = options.cwd ?? process.cwd();
44
+ const flag = trimmed(options.flagPath);
45
+ if (flag)
46
+ return { absolute: absolutize(p, cwd, flag), source: "flag" };
47
+ const env = trimmed(options.envPath);
48
+ if (env)
49
+ return { absolute: absolutize(p, cwd, env), source: "env" };
50
+ if (options.projectFromCwd) {
51
+ const subpath = trimmed(options.unitySubpath);
52
+ if (subpath) {
53
+ if (p.isAbsolute(subpath)) {
54
+ throw new ProjectPathError("subpath_not_relative", `--unity-subpath must be relative to the workspace (received '${subpath}').`);
55
+ }
56
+ return {
57
+ absolute: absolutize(p, cwd, subpath),
58
+ source: "cwd+subpath",
59
+ };
60
+ }
61
+ return { absolute: absolutize(p, cwd, "."), source: "cwd" };
62
+ }
63
+ if (trimmed(options.unitySubpath)) {
64
+ throw new ProjectPathError("subpath_without_cwd", "--unity-subpath requires --project-from-cwd (or an explicit project path).");
65
+ }
66
+ throw new ProjectPathError("missing_project_path", missingProjectPathMessage());
67
+ }
68
+ /** Shared wording so the server, the CLI, and setup all suggest the same fix. */
69
+ export function missingProjectPathMessage() {
70
+ return [
71
+ "No Unity project path. Use one of:",
72
+ " --project-from-cwd run from the Unity project root",
73
+ " --project-from-cwd --unity-subpath Client monorepo: Unity project in a subfolder",
74
+ " --project <path> explicit path (absolute or relative)",
75
+ " UNITY_PROJECT_PATH=<path> environment variable",
76
+ ].join("\n");
77
+ }
78
+ /**
79
+ * Check that `absolute` looks like a Unity project root (`Assets/`,
80
+ * `Packages/`, `ProjectSettings/`). Never throws — callers decide whether a
81
+ * miss is fatal.
82
+ */
83
+ export function validateUnityProjectRoot(absolute) {
84
+ const missing = UNITY_ROOT_MARKERS.filter((marker) => {
85
+ const candidate = nodePath.join(absolute, marker);
86
+ try {
87
+ return !existsSync(candidate) || !statSync(candidate).isDirectory();
88
+ }
89
+ catch {
90
+ return true;
91
+ }
92
+ });
93
+ return { valid: missing.length === 0, missing };
94
+ }
95
+ /** One-line explanation of a failed {@link validateUnityProjectRoot}. */
96
+ export function notUnityProjectMessage(resolved, validation) {
97
+ return (`${resolved.absolute} is not a Unity project root (from ${resolved.source}): ` +
98
+ `missing ${validation.missing.map((m) => `${m}/`).join(", ")}.`);
99
+ }
100
+ /**
101
+ * Pre-parse the portable project-path flags out of the stdio server's argv.
102
+ * Unknown tokens are ignored on purpose: MCP clients pass through arbitrary
103
+ * extra arguments, and the server has always tolerated them.
104
+ */
105
+ export function parseServerFlags(argv) {
106
+ const flags = {
107
+ projectFromCwd: false,
108
+ unitySubpath: undefined,
109
+ error: undefined,
110
+ };
111
+ for (let i = 0; i < argv.length; i++) {
112
+ const tok = argv[i];
113
+ if (tok === "--project-from-cwd") {
114
+ flags.projectFromCwd = true;
115
+ continue;
116
+ }
117
+ if (tok === "--unity-subpath") {
118
+ const value = argv[i + 1];
119
+ if (!value || value.startsWith("-")) {
120
+ flags.error = "--unity-subpath requires a relative path (for example: Client).";
121
+ return flags;
122
+ }
123
+ flags.unitySubpath = value;
124
+ i++;
125
+ continue;
126
+ }
127
+ const inline = matchInline(tok, "--unity-subpath");
128
+ if (inline !== undefined) {
129
+ if (inline.length === 0) {
130
+ flags.error = "--unity-subpath requires a relative path (for example: Client).";
131
+ return flags;
132
+ }
133
+ flags.unitySubpath = inline;
134
+ continue;
135
+ }
136
+ }
137
+ return flags;
138
+ }
139
+ function matchInline(tok, flag) {
140
+ const prefix = `${flag}=`;
141
+ return tok.startsWith(prefix) ? tok.slice(prefix.length) : undefined;
142
+ }
143
+ function trimmed(value) {
144
+ if (typeof value !== "string")
145
+ return undefined;
146
+ const t = value.trim();
147
+ return t.length > 0 ? t : undefined;
148
+ }
149
+ /**
150
+ * Resolve `input` against `cwd` and normalize. `p.resolve` already returns an
151
+ * absolute path and strips a trailing separator, which matters because the
152
+ * bridge port hash is taken over this exact string.
153
+ */
154
+ function absolutize(p, cwd, input) {
155
+ return p.isAbsolute(input)
156
+ ? p.normalize(p.resolve(input))
157
+ : p.normalize(p.resolve(cwd, input));
158
+ }
159
+ //# sourceMappingURL=project-path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-path.js","sourceRoot":"","sources":["../src/project-path.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ export declare const PACKAGE_NAME = "unity-open-mcp";
2
+ export type ReleaseSource = "npm" | "github";
3
+ export interface LatestVersion {
4
+ version: string;
5
+ source: ReleaseSource;
6
+ fallbackReason?: string;
7
+ }
8
+ export interface BridgeTagConfirmation {
9
+ confirmed: boolean;
10
+ error?: string;
11
+ }
12
+ /**
13
+ * Latest stable trio version: npm `latest` dist-tag first, GitHub `v*`
14
+ * releases as the fallback. Throws when neither source answers.
15
+ */
16
+ export declare function resolveLatestVersion(fetchImpl: typeof globalThis.fetch): Promise<LatestVersion>;
17
+ /**
18
+ * Whether the `bridge-v<version>` release tag exists on GitHub. Never throws:
19
+ * a failed lookup is reported as `confirmed: false` with the reason, because
20
+ * the caller's decision ("do not schedule a UPM re-pin to a tag we cannot
21
+ * prove exists") is the same either way.
22
+ */
23
+ export declare function confirmBridgeReleaseTag(version: string, fetchImpl: typeof globalThis.fetch): Promise<BridgeTagConfirmation>;
24
+ interface Semver {
25
+ major: number;
26
+ minor: number;
27
+ patch: number;
28
+ prerelease: string[];
29
+ }
30
+ export declare function parseSemver(value: string): Semver | null;
31
+ export declare function compareSemver(a: string, b: string): number | null;
32
+ export {};
@@ -0,0 +1,147 @@
1
+ // Published-release lookups shared by the `update` CLI command and the
2
+ // `unity_open_mcp_upgrade` route.
3
+ //
4
+ // Both callers need the same two facts — "what is the latest trio version?"
5
+ // and "does the bridge release tag for version X exist?" — and both must get
6
+ // them WITHOUT going through the Unity Editor: the bridge's upgrade tool runs
7
+ // on the Editor main thread, where a 10 s registry timeout would freeze the
8
+ // Editor and the whole bridge request queue. The lookups therefore live here,
9
+ // in the MCP server process, and the bridge only ever receives a resolved
10
+ // `target_version`.
11
+ //
12
+ // Pure `fetch` + hand-rolled semver; no runtime dependencies.
13
+ import { isRecord } from "./cli/json-guards.js";
14
+ export const PACKAGE_NAME = "unity-open-mcp";
15
+ const NPM_LATEST_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
16
+ const GITHUB_RELEASES_URL = "https://api.github.com/repos/AlexeyPerov/Unity-Open-MCP/releases?per_page=30";
17
+ // matching-refs is a PREFIX query: asking for bridge-v1.2.3 also lists
18
+ // bridge-v1.2.30, so only the exact ref name proves the tag exists. Mirrors
19
+ // LatestVersionCheck.ContainsExactBridgeTag on the bridge.
20
+ const GITHUB_BRIDGE_TAG_URL = "https://api.github.com/repos/AlexeyPerov/Unity-Open-MCP/git/matching-refs/tags/bridge-v";
21
+ const REQUEST_TIMEOUT_MS = 10_000;
22
+ /**
23
+ * Latest stable trio version: npm `latest` dist-tag first, GitHub `v*`
24
+ * releases as the fallback. Throws when neither source answers.
25
+ */
26
+ export async function resolveLatestVersion(fetchImpl) {
27
+ let npmFailure = "npm registry lookup failed";
28
+ try {
29
+ const body = await fetchJson(fetchImpl, NPM_LATEST_URL);
30
+ const version = isRecord(body) ? body.version : undefined;
31
+ if (typeof version === "string" && parseSemver(version)) {
32
+ return { version, source: "npm" };
33
+ }
34
+ npmFailure = "npm registry returned no valid version";
35
+ }
36
+ catch (error) {
37
+ npmFailure = error instanceof Error ? error.message : String(error);
38
+ }
39
+ try {
40
+ const body = await fetchJson(fetchImpl, GITHUB_RELEASES_URL);
41
+ if (!Array.isArray(body))
42
+ throw new Error("GitHub returned an invalid release list");
43
+ const versions = body
44
+ .filter(isRecord)
45
+ .filter((release) => release.draft !== true && release.prerelease !== true)
46
+ .map((release) => release.tag_name)
47
+ .filter((tag) => typeof tag === "string" && /^v\d+\.\d+\.\d+$/.test(tag))
48
+ .map((tag) => tag.slice(1))
49
+ .filter((version) => parseSemver(version) !== null)
50
+ .sort((a, b) => compareSemver(b, a) ?? 0);
51
+ if (versions.length === 0)
52
+ throw new Error("GitHub has no stable trio release tag");
53
+ return { version: versions[0], source: "github", fallbackReason: npmFailure };
54
+ }
55
+ catch (error) {
56
+ const githubFailure = error instanceof Error ? error.message : String(error);
57
+ throw new Error(`npm: ${npmFailure}; GitHub: ${githubFailure}`);
58
+ }
59
+ }
60
+ /**
61
+ * Whether the `bridge-v<version>` release tag exists on GitHub. Never throws:
62
+ * a failed lookup is reported as `confirmed: false` with the reason, because
63
+ * the caller's decision ("do not schedule a UPM re-pin to a tag we cannot
64
+ * prove exists") is the same either way.
65
+ */
66
+ export async function confirmBridgeReleaseTag(version, fetchImpl) {
67
+ if (!/^\d+\.\d+\.\d+$/.test(version)) {
68
+ return { confirmed: false, error: "Target version must be a plain X.Y.Z." };
69
+ }
70
+ try {
71
+ const body = await fetchJson(fetchImpl, `${GITHUB_BRIDGE_TAG_URL}${version}`);
72
+ if (!Array.isArray(body)) {
73
+ return { confirmed: false, error: "GitHub returned an invalid ref list." };
74
+ }
75
+ const exact = `refs/tags/bridge-v${version}`;
76
+ return body.some((ref) => isRecord(ref) && ref.ref === exact)
77
+ ? { confirmed: true }
78
+ : { confirmed: false, error: `Release tag bridge-v${version} was not found.` };
79
+ }
80
+ catch (error) {
81
+ const message = error instanceof Error ? error.message : String(error);
82
+ return { confirmed: false, error: `Bridge tag check failed: ${message}` };
83
+ }
84
+ }
85
+ async function fetchJson(fetchImpl, url) {
86
+ const controller = new AbortController();
87
+ const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
88
+ try {
89
+ const response = await fetchImpl(url, {
90
+ headers: { Accept: "application/json", "User-Agent": "unity-open-mcp-update" },
91
+ signal: controller.signal,
92
+ });
93
+ if (!response.ok) {
94
+ throw new Error(`${new URL(url).hostname} returned HTTP ${response.status}`);
95
+ }
96
+ return await response.json();
97
+ }
98
+ finally {
99
+ clearTimeout(timer);
100
+ }
101
+ }
102
+ export function parseSemver(value) {
103
+ const match = value.match(/^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/);
104
+ if (!match)
105
+ return null;
106
+ return {
107
+ major: Number(match[1]),
108
+ minor: Number(match[2]),
109
+ patch: Number(match[3]),
110
+ prerelease: match[4]?.split(".") ?? [],
111
+ };
112
+ }
113
+ export function compareSemver(a, b) {
114
+ const left = parseSemver(a);
115
+ const right = parseSemver(b);
116
+ if (!left || !right)
117
+ return null;
118
+ for (const key of ["major", "minor", "patch"]) {
119
+ if (left[key] !== right[key])
120
+ return left[key] > right[key] ? 1 : -1;
121
+ }
122
+ if (left.prerelease.length === 0 || right.prerelease.length === 0) {
123
+ if (left.prerelease.length === right.prerelease.length)
124
+ return 0;
125
+ return left.prerelease.length === 0 ? 1 : -1;
126
+ }
127
+ const length = Math.max(left.prerelease.length, right.prerelease.length);
128
+ for (let index = 0; index < length; index++) {
129
+ const l = left.prerelease[index];
130
+ const r = right.prerelease[index];
131
+ if (l === undefined)
132
+ return -1;
133
+ if (r === undefined)
134
+ return 1;
135
+ if (l === r)
136
+ continue;
137
+ const lNumeric = /^\d+$/.test(l);
138
+ const rNumeric = /^\d+$/.test(r);
139
+ if (lNumeric && rNumeric)
140
+ return Number(l) > Number(r) ? 1 : -1;
141
+ if (lNumeric !== rNumeric)
142
+ return lNumeric ? -1 : 1;
143
+ return l > r ? 1 : -1;
144
+ }
145
+ return 0;
146
+ }
147
+ //# sourceMappingURL=release-lookup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"release-lookup.js","sourceRoot":"","sources":["../src/release-lookup.ts"],"names":[],"mappings":"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"}
package/dist/routers.d.ts CHANGED
@@ -5,8 +5,12 @@ import { PingCache } from "./ping-cache.js";
5
5
  import { ResourceRouter } from "./resource-router.js";
6
6
  import { BridgeEventStream } from "./event-stream.js";
7
7
  import { ToolSessionState } from "./tool-session-state.js";
8
+ import { type ProjectPathSource } from "./project-path.js";
8
9
  export interface ResolvedEnv {
10
+ /** Normalized absolute Unity project root. */
9
11
  projectPath: string;
12
+ /** Which input produced {@link projectPath} (flag / env / cwd). */
13
+ projectPathSource: ProjectPathSource;
10
14
  port: number;
11
15
  authToken: string | undefined;
12
16
  /** Parsed UNITY_OPEN_MCP_BRIDGE_PORT (or CLI --port override), else undefined.
@@ -14,18 +18,28 @@ export interface ResolvedEnv {
14
18
  * override precedence as construction (env override ⇒ no lock refresh). */
15
19
  envPort: number | undefined;
16
20
  }
21
+ export interface ResolveEnvOptions {
22
+ /** `--project-from-cwd`: derive the Unity project root from the cwd. */
23
+ projectFromCwd?: boolean;
24
+ /** `--unity-subpath <rel>`: Unity project subfolder under the cwd. */
25
+ unitySubpath?: string;
26
+ }
17
27
  /**
18
28
  * Resolve the project path / port / auth token from explicit overrides (CLI
19
29
  * flags) or the process env (MCP server). Throws when no project path is set —
20
30
  * callers print a friendly message and exit non-zero.
21
31
  *
22
- * Mirrors the precedence in index.ts#getEnv:
23
- * projectPath: override > UNITY_PROJECT_PATH
32
+ * The project path goes through the shared `resolveProjectPath` resolver, so
33
+ * the CLI accepts exactly what the stdio server accepts: an absolute path, a
34
+ * path relative to the cwd (`--project Client` from a monorepo root), or no
35
+ * path at all with `--project-from-cwd [--unity-subpath Client]`.
36
+ *
37
+ * projectPath: --project > UNITY_PROJECT_PATH > cwd (+ subpath)
24
38
  * port: override > UNITY_OPEN_MCP_BRIDGE_PORT > lock file > hash
25
39
  * authToken: discovered from the same lock as the port (no token with an
26
40
  * explicit port override)
27
41
  */
28
- export declare function resolveEnv(projectPathOverride?: string, portOverride?: number): ResolvedEnv;
42
+ export declare function resolveEnv(projectPathOverride?: string, portOverride?: number, options?: ResolveEnvOptions): ResolvedEnv;
29
43
  export declare class ResolveEnvError extends Error {
30
44
  }
31
45
  export interface RouterStack {
package/dist/routers.js CHANGED
@@ -13,30 +13,50 @@ import { ResourceRouter } from "./resource-router.js";
13
13
  import { BridgeEventStream } from "./event-stream.js";
14
14
  import { resolvePort, resolveAuthToken } from "./instance-discovery.js";
15
15
  import { ToolSessionState } from "./tool-session-state.js";
16
+ import { ProjectPathError, resolveProjectPath, } from "./project-path.js";
16
17
  import { PORT_ENV_VAR, PROJECT_PATH_ENV_VAR, bridgeBaseUrl, } from "./constants.js";
17
18
  /**
18
19
  * Resolve the project path / port / auth token from explicit overrides (CLI
19
20
  * flags) or the process env (MCP server). Throws when no project path is set —
20
21
  * callers print a friendly message and exit non-zero.
21
22
  *
22
- * Mirrors the precedence in index.ts#getEnv:
23
- * projectPath: override > UNITY_PROJECT_PATH
23
+ * The project path goes through the shared `resolveProjectPath` resolver, so
24
+ * the CLI accepts exactly what the stdio server accepts: an absolute path, a
25
+ * path relative to the cwd (`--project Client` from a monorepo root), or no
26
+ * path at all with `--project-from-cwd [--unity-subpath Client]`.
27
+ *
28
+ * projectPath: --project > UNITY_PROJECT_PATH > cwd (+ subpath)
24
29
  * port: override > UNITY_OPEN_MCP_BRIDGE_PORT > lock file > hash
25
30
  * authToken: discovered from the same lock as the port (no token with an
26
31
  * explicit port override)
27
32
  */
28
- export function resolveEnv(projectPathOverride, portOverride) {
29
- const projectPath = projectPathOverride ?? process.env[PROJECT_PATH_ENV_VAR];
30
- if (!projectPath) {
31
- throw new ResolveEnvError(`${PROJECT_PATH_ENV_VAR} environment variable is required ` +
32
- "(or pass --project <path>).");
33
+ export function resolveEnv(projectPathOverride, portOverride, options = {}) {
34
+ let resolvedProject;
35
+ try {
36
+ resolvedProject = resolveProjectPath({
37
+ flagPath: projectPathOverride,
38
+ envPath: process.env[PROJECT_PATH_ENV_VAR],
39
+ cwd: process.cwd(),
40
+ projectFromCwd: options.projectFromCwd,
41
+ unitySubpath: options.unitySubpath,
42
+ });
43
+ }
44
+ catch (err) {
45
+ throw new ResolveEnvError(err instanceof ProjectPathError ? err.message : String(err));
33
46
  }
47
+ const projectPath = resolvedProject.absolute;
34
48
  const rawEnvPort = process.env[PORT_ENV_VAR];
35
49
  const envPort = rawEnvPort ? parseInt(rawEnvPort, 10) : undefined;
36
50
  const effectiveEnvPort = rawEnvPort && Number.isInteger(envPort) ? envPort : undefined;
37
51
  const port = resolvePort(projectPath, portOverride ?? effectiveEnvPort);
38
52
  const authToken = resolveAuthToken(projectPath, portOverride ?? effectiveEnvPort);
39
- return { projectPath, port, authToken, envPort: portOverride ?? effectiveEnvPort };
53
+ return {
54
+ projectPath,
55
+ projectPathSource: resolvedProject.source,
56
+ port,
57
+ authToken,
58
+ envPort: portOverride ?? effectiveEnvPort,
59
+ };
40
60
  }
41
61
  export class ResolveEnvError extends Error {
42
62
  }
@@ -1 +1 @@
1
- {"version":3,"file":"routers.js","sourceRoot":"","sources":["../src/routers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"routers.js","sourceRoot":"","sources":["../src/routers.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,14 @@ export interface RunningUnity {
6
6
  /** Normalized `-projectPath` argument, or null when it could not be parsed
7
7
  * (Unity opened without `-projectPath`, e.g. via the Hub's "Open Editor"). */
8
8
  projectPath: string | null;
9
+ /** `true` when the command line carries `-batchmode`: a headless run (a
10
+ * compile_check / verify spawn from this or another MCP server, a CI run),
11
+ * never the interactive Editor a user owns. Optional so hand-built scanner
12
+ * fixtures keep working. */
13
+ batchMode?: boolean;
9
14
  }
15
+ /** `true` when the command line runs Unity headless (`-batchmode`, any case). */
16
+ export declare function isBatchModeCommandLine(commandLine: string): boolean;
10
17
  /**
11
18
  * Pure `-projectPath` argument parser. Port of Hub
12
19
  * `running_unity.rs#parse_project_path_arg`. Accepts both the space-separated
@@ -106,6 +113,10 @@ export declare function setUnityProcessScannerForTest(fake: UnityProcessScanner
106
113
  * scan simply returns null and the caller falls back to its pre-feature
107
114
  * behavior. Documented in tests.
108
115
  *
116
+ * Skips headless batch children this server spawned and still supervises
117
+ * (see `child-supervision.ts`): they carry the same `-projectPath` but are
118
+ * tracked by the batch lease, not by Editor-ownership diagnostics.
119
+ *
109
120
  * Never throws: any scanner failure (ps missing, PowerShell slow, parse
110
121
  * error) returns null. A failed scan must not mask the underlying offline
111
122
  * state.
@@ -115,6 +126,16 @@ export declare function setUnityProcessScannerForTest(fake: UnityProcessScanner
115
126
  export declare function findUnityForProject(projectPath: string | null | undefined): {
116
127
  pid: number;
117
128
  } | null;
129
+ /**
130
+ * A headless (`-batchmode`) Unity that ANOTHER process has open on
131
+ * `projectPath`: a compile_check / verify spawn from a second MCP server, or a
132
+ * CI run. This server's own supervised child is excluded (the batch lease
133
+ * accounts for it), and the interactive Editor is `findUnityForProject`'s
134
+ * business. Never throws; null when no such process is found.
135
+ */
136
+ export declare function findHeadlessUnityForProject(projectPath: string | null | undefined): {
137
+ pid: number;
138
+ } | null;
118
139
  /**
119
140
  * Read the full command line of a live process by PID. Cross-platform best
120
141
  * effort: macOS/Linux via `ps -p <pid> -o command=`, Windows via a PowerShell
@@ -29,6 +29,11 @@
29
29
  // two projects).
30
30
  import { execFileSync } from "node:child_process";
31
31
  import { normalizePath } from "./instance-discovery.js";
32
+ import { isSupervisedBatchChildPid } from "./child-supervision.js";
33
+ /** `true` when the command line runs Unity headless (`-batchmode`, any case). */
34
+ export function isBatchModeCommandLine(commandLine) {
35
+ return /(^|\s)-batchmode(\s|$)/i.test(commandLine);
36
+ }
32
37
  /**
33
38
  * Pure `-projectPath` argument parser. Port of Hub
34
39
  * `running_unity.rs#parse_project_path_arg`. Accepts both the space-separated
@@ -272,6 +277,7 @@ export function parsePsOutput(stdout) {
272
277
  out.push({
273
278
  pid,
274
279
  projectPath: projectPath !== null ? normalizePath(projectPath) : null,
280
+ batchMode: isBatchModeCommandLine(rest),
275
281
  });
276
282
  }
277
283
  return out;
@@ -305,6 +311,7 @@ export function parsePowerShellLines(stdout) {
305
311
  out.push({
306
312
  pid,
307
313
  projectPath: projectPath !== null ? normalizePath(projectPath) : null,
314
+ batchMode: rest.length > 0 && rest !== "null" && isBatchModeCommandLine(rest),
308
315
  });
309
316
  }
310
317
  return out;
@@ -385,6 +392,10 @@ export function setUnityProcessScannerForTest(fake) {
385
392
  * scan simply returns null and the caller falls back to its pre-feature
386
393
  * behavior. Documented in tests.
387
394
  *
395
+ * Skips headless batch children this server spawned and still supervises
396
+ * (see `child-supervision.ts`): they carry the same `-projectPath` but are
397
+ * tracked by the batch lease, not by Editor-ownership diagnostics.
398
+ *
388
399
  * Never throws: any scanner failure (ps missing, PowerShell slow, parse
389
400
  * error) returns null. A failed scan must not mask the underlying offline
390
401
  * state.
@@ -405,6 +416,45 @@ export function findUnityForProject(projectPath) {
405
416
  return null;
406
417
  }
407
418
  for (const proc of found) {
419
+ // Our own headless child is not an Editor the user owns; the batch lease
420
+ // accounts for it (and restart_editor must never pick it).
421
+ if (isSupervisedBatchChildPid(proc.pid))
422
+ continue;
423
+ // Neither is a -batchmode Unity another process started (a second MCP
424
+ // server's compile_check, a CI run): it is never in Safe Mode, must never
425
+ // be killed by restart_editor, and batch-spawn reports it separately
426
+ // (findHeadlessUnityForProject).
427
+ if (proc.batchMode)
428
+ continue;
429
+ if (proc.projectPath !== null && normalizePath(proc.projectPath) === target) {
430
+ return { pid: proc.pid };
431
+ }
432
+ }
433
+ return null;
434
+ }
435
+ /**
436
+ * A headless (`-batchmode`) Unity that ANOTHER process has open on
437
+ * `projectPath`: a compile_check / verify spawn from a second MCP server, or a
438
+ * CI run. This server's own supervised child is excluded (the batch lease
439
+ * accounts for it), and the interactive Editor is `findUnityForProject`'s
440
+ * business. Never throws; null when no such process is found.
441
+ */
442
+ export function findHeadlessUnityForProject(projectPath) {
443
+ if (!projectPath)
444
+ return null;
445
+ const target = normalizePath(projectPath);
446
+ if (target.length === 0)
447
+ return null;
448
+ let found = [];
449
+ try {
450
+ found = getUnityProcessScanner().scan();
451
+ }
452
+ catch {
453
+ return null;
454
+ }
455
+ for (const proc of found) {
456
+ if (!proc.batchMode || isSupervisedBatchChildPid(proc.pid))
457
+ continue;
408
458
  if (proc.projectPath !== null && normalizePath(proc.projectPath) === target) {
409
459
  return { pid: proc.pid };
410
460
  }