@akiojin/unity-mcp-server 5.0.0 → 5.1.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/README.md
CHANGED
|
@@ -72,6 +72,13 @@ More details:
|
|
|
72
72
|
- Node.js 18.x / 20.x / 22.x LTS (23+ not supported)
|
|
73
73
|
- MCP client (Claude Desktop, Cursor, etc.)
|
|
74
74
|
|
|
75
|
+
## Native SQLite preload (optional)
|
|
76
|
+
|
|
77
|
+
The server uses `fast-sql`, which can preload a native `better-sqlite3` binding when a prebuilt binary is packaged.
|
|
78
|
+
|
|
79
|
+
- `UNITY_MCP_SKIP_NATIVE_BUILD=1` to skip native preload (forces sql.js fallback)
|
|
80
|
+
- `UNITY_MCP_FORCE_NATIVE=1` to require the prebuilt binary (install fails if missing)
|
|
81
|
+
|
|
75
82
|
## Troubleshooting
|
|
76
83
|
|
|
77
84
|
- Troubleshooting index: [`docs/troubleshooting/README.md`](https://github.com/akiojin/unity-mcp-server/blob/main/docs/troubleshooting/README.md)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akiojin/unity-mcp-server",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"description": "MCP server and Unity Editor bridge — enables AI assistants to control Unity for AI-assisted workflows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/core/server.js",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"test:verbose": "VERBOSE_TEST=true node --test tests/**/*.test.js",
|
|
28
28
|
"prepare": "cd .. && husky || true",
|
|
29
29
|
"prepublishOnly": "npm run test:ci",
|
|
30
|
-
"postinstall": "
|
|
30
|
+
"postinstall": "node scripts/ensure-better-sqlite3.mjs",
|
|
31
31
|
"test:ci:unity": "timeout 60 node --test tests/unit/core/codeIndex.test.js tests/unit/core/config.test.js tests/unit/core/indexWatcher.test.js tests/unit/core/projectInfo.test.js tests/unit/core/server.test.js tests/unit/core/startupPerformance.test.js || exit 0",
|
|
32
32
|
"test:unity": "node tests/run-unity-integration.mjs",
|
|
33
33
|
"test:nounity": "npm run test:integration",
|
|
@@ -68,6 +68,8 @@
|
|
|
68
68
|
"files": [
|
|
69
69
|
"src/",
|
|
70
70
|
"bin/",
|
|
71
|
+
"scripts/",
|
|
72
|
+
"prebuilt/",
|
|
71
73
|
"README.md",
|
|
72
74
|
"LICENSE"
|
|
73
75
|
],
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
6
|
+
|
|
7
|
+
const ABI_BY_NODE_MAJOR = new Map([
|
|
8
|
+
[18, 115],
|
|
9
|
+
[20, 120],
|
|
10
|
+
[22, 131]
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
export function parseEnvFlag(value) {
|
|
14
|
+
if (!value) return false;
|
|
15
|
+
const normalized = String(value).trim().toLowerCase();
|
|
16
|
+
return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function resolveNodeMajor(nodeVersion) {
|
|
20
|
+
if (!nodeVersion) return null;
|
|
21
|
+
const major = Number.parseInt(String(nodeVersion).split('.')[0], 10);
|
|
22
|
+
return Number.isFinite(major) ? major : null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function resolveNodeAbi({ nodeVersion, nodeAbi } = {}) {
|
|
26
|
+
const abi = Number.parseInt(nodeAbi, 10);
|
|
27
|
+
if (Number.isFinite(abi)) return abi;
|
|
28
|
+
const major = resolveNodeMajor(nodeVersion);
|
|
29
|
+
if (!major) return null;
|
|
30
|
+
return ABI_BY_NODE_MAJOR.get(major) ?? null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function resolveRuntimeInfo({
|
|
34
|
+
env = process.env,
|
|
35
|
+
platform = process.platform,
|
|
36
|
+
arch = process.arch,
|
|
37
|
+
nodeVersion = process.versions?.node,
|
|
38
|
+
nodeAbi = process.versions?.modules
|
|
39
|
+
} = {}) {
|
|
40
|
+
const resolvedPlatform = env.UNITY_MCP_PLATFORM || platform;
|
|
41
|
+
const resolvedArch = env.UNITY_MCP_ARCH || arch;
|
|
42
|
+
const envNodeMajor = env.UNITY_MCP_NODE_MAJOR
|
|
43
|
+
? Number.parseInt(env.UNITY_MCP_NODE_MAJOR, 10)
|
|
44
|
+
: null;
|
|
45
|
+
const resolvedNodeMajor = Number.isFinite(envNodeMajor)
|
|
46
|
+
? envNodeMajor
|
|
47
|
+
: resolveNodeMajor(nodeVersion);
|
|
48
|
+
const envNodeAbi = env.UNITY_MCP_NODE_ABI ? Number.parseInt(env.UNITY_MCP_NODE_ABI, 10) : null;
|
|
49
|
+
const resolvedNodeAbi = Number.isFinite(envNodeAbi)
|
|
50
|
+
? envNodeAbi
|
|
51
|
+
: resolveNodeAbi({ nodeVersion, nodeAbi });
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
platform: resolvedPlatform,
|
|
55
|
+
arch: resolvedArch,
|
|
56
|
+
nodeMajor: resolvedNodeMajor,
|
|
57
|
+
nodeAbi: resolvedNodeAbi
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function buildPlatformKey({ platform, arch, nodeMajor }) {
|
|
62
|
+
if (!platform || !arch || !nodeMajor) return null;
|
|
63
|
+
return `${platform}-${arch}-node${nodeMajor}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function resolvePackageRoot(env = process.env) {
|
|
67
|
+
if (env.UNITY_MCP_PACKAGE_ROOT) {
|
|
68
|
+
return path.resolve(env.UNITY_MCP_PACKAGE_ROOT);
|
|
69
|
+
}
|
|
70
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
71
|
+
return path.resolve(scriptDir, '..');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function resolvePrebuiltRoot(packageRoot, env = process.env) {
|
|
75
|
+
if (env.UNITY_MCP_PREBUILT_DIR) {
|
|
76
|
+
return path.resolve(env.UNITY_MCP_PREBUILT_DIR);
|
|
77
|
+
}
|
|
78
|
+
return path.join(packageRoot, 'prebuilt', 'better-sqlite3');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function resolveBetterSqlite3Root(packageRoot, env = process.env) {
|
|
82
|
+
if (env.UNITY_MCP_BETTER_SQLITE3_ROOT) {
|
|
83
|
+
return path.resolve(env.UNITY_MCP_BETTER_SQLITE3_ROOT);
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
const req = createRequire(path.join(packageRoot, 'package.json'));
|
|
87
|
+
const pkgPath = req.resolve('better-sqlite3/package.json');
|
|
88
|
+
return path.dirname(pkgPath);
|
|
89
|
+
} catch {
|
|
90
|
+
return path.join(packageRoot, 'node_modules', 'better-sqlite3');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function resolvePrebuiltBindingPath(prebuiltRoot, platformKey) {
|
|
95
|
+
if (!prebuiltRoot || !platformKey) return null;
|
|
96
|
+
return path.join(prebuiltRoot, platformKey, 'better_sqlite3.node');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function resolveInstalledBindingPath(moduleRoot) {
|
|
100
|
+
return path.join(moduleRoot, 'build', 'Release', 'better_sqlite3.node');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function ensureBinExecutable({ packageRoot, env = process.env, logger = console } = {}) {
|
|
104
|
+
if (parseEnvFlag(env.UNITY_MCP_SKIP_BIN_CHMOD)) return false;
|
|
105
|
+
const binPath = path.join(packageRoot, 'bin', 'unity-mcp-server.js');
|
|
106
|
+
try {
|
|
107
|
+
if (fs.existsSync(binPath)) {
|
|
108
|
+
fs.chmodSync(binPath, 0o755);
|
|
109
|
+
logger.info(`[ensure-better-sqlite3] chmod +x ${binPath}`);
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
} catch (err) {
|
|
113
|
+
logger.warn(
|
|
114
|
+
`[ensure-better-sqlite3] Failed to chmod ${binPath}: ${err?.message || String(err)}`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function ensureBetterSqlite3({
|
|
121
|
+
env = process.env,
|
|
122
|
+
logger = console,
|
|
123
|
+
packageRoot,
|
|
124
|
+
prebuiltRoot,
|
|
125
|
+
moduleRoot,
|
|
126
|
+
platform,
|
|
127
|
+
arch,
|
|
128
|
+
nodeVersion,
|
|
129
|
+
nodeAbi
|
|
130
|
+
} = {}) {
|
|
131
|
+
const skipNative = parseEnvFlag(env.UNITY_MCP_SKIP_NATIVE_BUILD);
|
|
132
|
+
const forceNative = parseEnvFlag(env.UNITY_MCP_FORCE_NATIVE);
|
|
133
|
+
|
|
134
|
+
const resolvedPackageRoot = packageRoot || resolvePackageRoot(env);
|
|
135
|
+
const resolvedPrebuiltRoot = prebuiltRoot || resolvePrebuiltRoot(resolvedPackageRoot, env);
|
|
136
|
+
const resolvedModuleRoot = moduleRoot || resolveBetterSqlite3Root(resolvedPackageRoot, env);
|
|
137
|
+
|
|
138
|
+
if (skipNative) {
|
|
139
|
+
logger.info('[ensure-better-sqlite3] Native preload skipped (UNITY_MCP_SKIP_NATIVE_BUILD).');
|
|
140
|
+
ensureBinExecutable({ packageRoot: resolvedPackageRoot, env, logger });
|
|
141
|
+
return { action: 'skip' };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!fs.existsSync(resolvedModuleRoot)) {
|
|
145
|
+
const message = `[ensure-better-sqlite3] better-sqlite3 module not found at ${resolvedModuleRoot}`;
|
|
146
|
+
if (forceNative) {
|
|
147
|
+
throw new Error(message);
|
|
148
|
+
}
|
|
149
|
+
logger.warn(message);
|
|
150
|
+
ensureBinExecutable({ packageRoot: resolvedPackageRoot, env, logger });
|
|
151
|
+
return { action: 'no-module' };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const runtimeInfo = resolveRuntimeInfo({
|
|
155
|
+
env,
|
|
156
|
+
platform,
|
|
157
|
+
arch,
|
|
158
|
+
nodeVersion,
|
|
159
|
+
nodeAbi
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const platformKey = buildPlatformKey(runtimeInfo);
|
|
163
|
+
if (!platformKey) {
|
|
164
|
+
const message = '[ensure-better-sqlite3] Unable to resolve platform key';
|
|
165
|
+
if (forceNative) throw new Error(message);
|
|
166
|
+
logger.warn(message);
|
|
167
|
+
ensureBinExecutable({ packageRoot: resolvedPackageRoot, env, logger });
|
|
168
|
+
return { action: 'fallback' };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const prebuiltPath = resolvePrebuiltBindingPath(resolvedPrebuiltRoot, platformKey);
|
|
172
|
+
if (!prebuiltPath || !fs.existsSync(prebuiltPath)) {
|
|
173
|
+
const message = `[ensure-better-sqlite3] Prebuilt binary not found for ${platformKey}`;
|
|
174
|
+
if (forceNative) throw new Error(message);
|
|
175
|
+
logger.info(message);
|
|
176
|
+
ensureBinExecutable({ packageRoot: resolvedPackageRoot, env, logger });
|
|
177
|
+
return { action: 'fallback', platformKey, prebuiltPath };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const destPath = resolveInstalledBindingPath(resolvedModuleRoot);
|
|
181
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
182
|
+
fs.copyFileSync(prebuiltPath, destPath);
|
|
183
|
+
logger.info(`[ensure-better-sqlite3] Installed prebuilt ${platformKey} -> ${destPath}`);
|
|
184
|
+
|
|
185
|
+
ensureBinExecutable({ packageRoot: resolvedPackageRoot, env, logger });
|
|
186
|
+
return { action: 'copied', platformKey, prebuiltPath, destPath };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function isCliInvocation() {
|
|
190
|
+
if (!process.argv[1]) return false;
|
|
191
|
+
const invoked = path.resolve(process.argv[1]);
|
|
192
|
+
const current = path.resolve(fileURLToPath(import.meta.url));
|
|
193
|
+
return invoked === current;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (isCliInvocation()) {
|
|
197
|
+
try {
|
|
198
|
+
ensureBetterSqlite3();
|
|
199
|
+
} catch (err) {
|
|
200
|
+
const message = err?.message || String(err);
|
|
201
|
+
console.error(`[ensure-better-sqlite3] ${message}`);
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { spawn } from 'child_process';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
const repoRoot = path.resolve(__dirname, '..');
|
|
10
|
+
const integrationRoot = path.join(repoRoot, 'tests', 'integration');
|
|
11
|
+
|
|
12
|
+
const curatedUnitTests = [
|
|
13
|
+
'tests/unit/core/codeIndex.test.js',
|
|
14
|
+
'tests/unit/core/config.test.js',
|
|
15
|
+
'tests/unit/core/indexWatcher.test.js',
|
|
16
|
+
'tests/unit/core/projectInfo.test.js',
|
|
17
|
+
'tests/unit/core/server.test.js',
|
|
18
|
+
'tests/unit/handlers/script/CodeIndexStatusToolHandler.test.js'
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
function collectIntegrationTests(dir) {
|
|
22
|
+
const entries = fs.existsSync(dir) ? fs.readdirSync(dir, { withFileTypes: true }) : [];
|
|
23
|
+
const files = [];
|
|
24
|
+
for (const entry of entries) {
|
|
25
|
+
const full = path.join(dir, entry.name);
|
|
26
|
+
if (entry.isDirectory()) {
|
|
27
|
+
files.push(...collectIntegrationTests(full));
|
|
28
|
+
} else if (entry.isFile() && entry.name.endsWith('.test.js')) {
|
|
29
|
+
files.push(full);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return files;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function detectUnityDependency(filePath) {
|
|
36
|
+
try {
|
|
37
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
38
|
+
return /UnityConnection|connect\(\)\s*=>\s*new UnityConnection/i.test(content);
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const allIntegrationTests = collectIntegrationTests(integrationRoot);
|
|
45
|
+
const nonUnityTests = [];
|
|
46
|
+
const unityTests = [];
|
|
47
|
+
|
|
48
|
+
for (const absPath of allIntegrationTests) {
|
|
49
|
+
const relPath = path.relative(repoRoot, absPath);
|
|
50
|
+
if (detectUnityDependency(absPath)) {
|
|
51
|
+
unityTests.push(relPath);
|
|
52
|
+
} else {
|
|
53
|
+
nonUnityTests.push(relPath);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (nonUnityTests.length === 0) {
|
|
58
|
+
console.warn('[tests] No non-Unity integration tests found.');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (unityTests.length > 0) {
|
|
62
|
+
console.log('[tests] Skipping Unity-dependent integration suites in this run:');
|
|
63
|
+
for (const rel of unityTests) {
|
|
64
|
+
console.log(` - ${rel}`);
|
|
65
|
+
}
|
|
66
|
+
console.log(
|
|
67
|
+
'[tests] These suites are executed via `npm run test:unity --workspace=mcp-server` when a Unity Editor is available.'
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const testTargets = [...curatedUnitTests, ...nonUnityTests];
|
|
72
|
+
|
|
73
|
+
const child = spawn(process.execPath, ['--test', ...testTargets], {
|
|
74
|
+
stdio: 'inherit',
|
|
75
|
+
cwd: repoRoot
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
child.on('exit', code => {
|
|
79
|
+
process.exit(code ?? 1);
|
|
80
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
|
|
6
|
+
import { CodeIndexBuildToolHandler } from '../src/handlers/script/CodeIndexBuildToolHandler.js';
|
|
7
|
+
import { CodeIndexStatusToolHandler } from '../src/handlers/script/CodeIndexStatusToolHandler.js';
|
|
8
|
+
import { ProjectInfoProvider } from '../src/core/projectInfo.js';
|
|
9
|
+
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
|
|
12
|
+
const argv = process.argv.slice(2);
|
|
13
|
+
const options = {
|
|
14
|
+
throttleMs: 50,
|
|
15
|
+
pollMs: 500,
|
|
16
|
+
reset: false,
|
|
17
|
+
delayStartMs: 0
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
for (const arg of argv) {
|
|
21
|
+
if (arg.startsWith('--throttle=')) {
|
|
22
|
+
options.throttleMs = Math.max(0, Number(arg.split('=')[1] || 0));
|
|
23
|
+
} else if (arg.startsWith('--poll=')) {
|
|
24
|
+
options.pollMs = Math.max(100, Number(arg.split('=')[1] || 500));
|
|
25
|
+
} else if (arg.startsWith('--delayStart=')) {
|
|
26
|
+
options.delayStartMs = Math.max(0, Number(arg.split('=')[1] || 0));
|
|
27
|
+
} else if (arg === '--reset' || arg === '--clean') {
|
|
28
|
+
options.reset = true;
|
|
29
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
30
|
+
printHelp();
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function printHelp() {
|
|
36
|
+
console.log(`Usage: node scripts/simulate-code-index-status.mjs [--throttle=MS] [--poll=MS] [--reset]
|
|
37
|
+
|
|
38
|
+
Options:
|
|
39
|
+
--throttle=MS Delay (ms) after each file during build to keep job running (default: 50)
|
|
40
|
+
--poll=MS Poll interval for code_index_status (default: 500)
|
|
41
|
+
--delayStart=MS Delay (ms) before processing begins (default: 0)
|
|
42
|
+
--reset Delete existing code index database before running
|
|
43
|
+
`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const mockUnityConnection = {
|
|
47
|
+
isConnected() {
|
|
48
|
+
return false;
|
|
49
|
+
},
|
|
50
|
+
sendCommand() {
|
|
51
|
+
throw new Error('Unity connection not available in simulation');
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const buildHandler = new CodeIndexBuildToolHandler(mockUnityConnection);
|
|
56
|
+
const statusHandler = new CodeIndexStatusToolHandler(mockUnityConnection);
|
|
57
|
+
const projectInfo = new ProjectInfoProvider(mockUnityConnection);
|
|
58
|
+
|
|
59
|
+
(async () => {
|
|
60
|
+
const info = await projectInfo.get();
|
|
61
|
+
const dbDir = path.resolve(info.codeIndexRoot);
|
|
62
|
+
if (options.reset) {
|
|
63
|
+
try {
|
|
64
|
+
for (const file of ['code-index.db', 'code-index.db-shm', 'code-index.db-wal']) {
|
|
65
|
+
const target = path.join(dbDir, file);
|
|
66
|
+
if (fs.existsSync(target)) fs.unlinkSync(target);
|
|
67
|
+
}
|
|
68
|
+
console.log('[simulate] Existing code index removed.');
|
|
69
|
+
} catch (err) {
|
|
70
|
+
console.error('[simulate] Failed to clean code index:', err.message);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log('[simulate] Starting code_index_build with throttleMs =', options.throttleMs);
|
|
75
|
+
const buildResult = await buildHandler.execute({
|
|
76
|
+
throttleMs: options.throttleMs,
|
|
77
|
+
delayStartMs: options.delayStartMs
|
|
78
|
+
});
|
|
79
|
+
console.log('[simulate] Job ID:', buildResult.jobId);
|
|
80
|
+
|
|
81
|
+
const interval = setInterval(async () => {
|
|
82
|
+
try {
|
|
83
|
+
const status = await statusHandler.execute({});
|
|
84
|
+
const job = status.index?.buildJob;
|
|
85
|
+
const line = {
|
|
86
|
+
time: new Date().toISOString(),
|
|
87
|
+
ready: status.index?.ready,
|
|
88
|
+
rows: status.index?.rows,
|
|
89
|
+
jobStatus: job?.status ?? null,
|
|
90
|
+
processed: job?.progress?.processed ?? null,
|
|
91
|
+
total: job?.progress?.total ?? null
|
|
92
|
+
};
|
|
93
|
+
console.log('[status]', JSON.stringify(line));
|
|
94
|
+
|
|
95
|
+
if (!job || job.status !== 'running') {
|
|
96
|
+
clearInterval(interval);
|
|
97
|
+
console.log('[simulate] Build finished.');
|
|
98
|
+
process.exit(0);
|
|
99
|
+
}
|
|
100
|
+
} catch (err) {
|
|
101
|
+
console.error('[status]', err.message);
|
|
102
|
+
}
|
|
103
|
+
}, options.pollMs);
|
|
104
|
+
})();
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Sync Unity Package version with mcp-server version
|
|
5
|
+
* Called by release-please via extra-files or manual invocation
|
|
6
|
+
*
|
|
7
|
+
* Usage: node sync-unity-package-version.js <version>
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from 'node:fs';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = path.dirname(__filename);
|
|
16
|
+
|
|
17
|
+
// Get version from command line argument
|
|
18
|
+
const version = process.argv[2];
|
|
19
|
+
|
|
20
|
+
if (!version) {
|
|
21
|
+
console.error('Error: Version argument is required');
|
|
22
|
+
console.error('Usage: node sync-unity-package-version.js <version>');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Resolve Unity Package path (mcp-server and UnityMCPServer are siblings)
|
|
27
|
+
const unityPackageJsonPath = path.join(
|
|
28
|
+
__dirname,
|
|
29
|
+
'../../UnityMCPServer/Packages/unity-mcp-server/package.json'
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
// Read Unity package.json
|
|
34
|
+
const packageJsonContent = fs.readFileSync(unityPackageJsonPath, 'utf8');
|
|
35
|
+
const packageJson = JSON.parse(packageJsonContent);
|
|
36
|
+
|
|
37
|
+
// Update version
|
|
38
|
+
packageJson.version = version;
|
|
39
|
+
|
|
40
|
+
// Write back with 2-space indentation and trailing newline
|
|
41
|
+
const updatedContent = JSON.stringify(packageJson, null, 2) + '\n';
|
|
42
|
+
fs.writeFileSync(unityPackageJsonPath, updatedContent, 'utf8');
|
|
43
|
+
|
|
44
|
+
console.log(`Unity Package version synced to ${version}`);
|
|
45
|
+
process.exit(0);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error(`Error: Failed to sync Unity Package version`);
|
|
48
|
+
console.error(error.message);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|