unity-agentic-tools 0.6.1 → 0.7.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 +18 -2
- package/dist/bridge-install.d.ts +6 -1
- package/dist/bridge-install.d.ts.map +1 -1
- package/dist/bridge-install.js +75 -4
- package/dist/bridge-install.js.map +1 -1
- package/dist/cli.js +432 -161
- package/dist/cli.js.map +1 -1
- package/dist/editor-client.d.ts +8 -1
- package/dist/editor-client.d.ts.map +1 -1
- package/dist/editor-client.js +256 -54
- package/dist/editor-client.js.map +1 -1
- package/dist/index.d.ts +0 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +39 -972
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +16 -239
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +8 -13
- package/dist/cli-output.d.ts +0 -7
- package/dist/cli-output.d.ts.map +0 -1
- package/dist/cli-output.js +0 -24
- package/dist/cli-output.js.map +0 -1
- package/dist/doc-indexer-cli.js +0 -3218
- package/dist/guid-cache.d.ts +0 -45
- package/dist/guid-cache.d.ts.map +0 -1
- package/dist/guid-cache.js +0 -111
- package/dist/guid-cache.js.map +0 -1
- package/dist/project-search.d.ts +0 -16
- package/dist/project-search.d.ts.map +0 -1
- package/dist/project-search.js +0 -538
- package/dist/project-search.js.map +0 -1
- package/dist/scanner.d.ts +0 -150
- package/dist/scanner.d.ts.map +0 -1
- package/dist/scanner.js +0 -223
- package/dist/scanner.js.map +0 -1
- package/dist/setup.d.ts +0 -25
- package/dist/setup.d.ts.map +0 -1
- package/dist/setup.js +0 -185
- package/dist/setup.js.map +0 -1
- package/native/index.d.ts +0 -335
- package/native/index.js +0 -330
- package/native/unity-file-tools.darwin-arm64.node +0 -0
- package/native/unity-file-tools.darwin-x64.node +0 -0
- package/native/unity-file-tools.linux-x64-gnu.node +0 -0
- package/native/unity-file-tools.win32-x64-msvc.node +0 -0
package/dist/cli.js
CHANGED
|
@@ -2129,37 +2129,33 @@ var require_commander = __commonJS((exports2) => {
|
|
|
2129
2129
|
var require_package = __commonJS((exports2, module2) => {
|
|
2130
2130
|
module2.exports = {
|
|
2131
2131
|
name: "unity-agentic-tools",
|
|
2132
|
-
packageManager: "bun@
|
|
2133
|
-
version: "0.
|
|
2132
|
+
packageManager: "bun@1.3.14",
|
|
2133
|
+
version: "0.7.0",
|
|
2134
2134
|
description: "Compact Unity Editor command runner for AI agents",
|
|
2135
2135
|
exports: {
|
|
2136
2136
|
".": {
|
|
2137
2137
|
import: "./dist/index.js",
|
|
2138
2138
|
types: "./dist/index.d.ts"
|
|
2139
2139
|
},
|
|
2140
|
-
"./cli": "./dist/cli.js"
|
|
2141
|
-
"./scanner": "./dist/scanner.js"
|
|
2140
|
+
"./cli": "./dist/cli.js"
|
|
2142
2141
|
},
|
|
2143
2142
|
files: [
|
|
2144
2143
|
"dist",
|
|
2145
|
-
"native",
|
|
2146
2144
|
"README.md",
|
|
2147
2145
|
"LICENSE"
|
|
2148
2146
|
],
|
|
2149
2147
|
scripts: {
|
|
2150
|
-
build: "bun run clean && bun run build:types && bun run build:lib && bun run build:cli
|
|
2148
|
+
build: "bun run clean && bun run build:types && bun run build:lib && bun run build:cli",
|
|
2151
2149
|
"build:types": "tsc --project tsconfig.json",
|
|
2152
|
-
"build:lib": "bun build src/index.ts --outdir=dist --target=bun --format=cjs
|
|
2153
|
-
"build:cli": "bun build src/cli.ts --outdir=dist --target=bun --format=cjs
|
|
2154
|
-
"build:rust": "cd ../rust-core && bun run build",
|
|
2155
|
-
"build:all": "bun run build:rust || echo 'Rust build skipped' && bun run build",
|
|
2150
|
+
"build:lib": "bun build src/index.ts --outdir=dist --target=bun --format=cjs",
|
|
2151
|
+
"build:cli": "bun build src/cli.ts --outdir=dist --target=bun --format=cjs && chmod +x dist/cli.js",
|
|
2156
2152
|
dev: "bun build src/index.ts --watch",
|
|
2157
2153
|
clean: "rm -rf dist",
|
|
2158
2154
|
rebuild: "bun run clean && bun run build",
|
|
2159
2155
|
test: "bun test",
|
|
2160
2156
|
"test:watch": "bun test --watch",
|
|
2161
|
-
"test:
|
|
2162
|
-
"test:
|
|
2157
|
+
"test:unity-headless": "bun test/run-headless-validation.ts",
|
|
2158
|
+
"test:stress": "bun test/run-bridge-stress.ts"
|
|
2163
2159
|
},
|
|
2164
2160
|
dependencies: {
|
|
2165
2161
|
commander: "^14.0.2"
|
|
@@ -2168,7 +2164,6 @@ var require_package = __commonJS((exports2, module2) => {
|
|
|
2168
2164
|
"@types/node": "^25.0.0",
|
|
2169
2165
|
"@vitest/ui": "^4.0.16",
|
|
2170
2166
|
typescript: "^5.3.0",
|
|
2171
|
-
"unity-file-tools": "workspace:*",
|
|
2172
2167
|
vitest: "^4.0.16"
|
|
2173
2168
|
},
|
|
2174
2169
|
type: "commonjs",
|
|
@@ -2212,6 +2207,10 @@ var {
|
|
|
2212
2207
|
Help
|
|
2213
2208
|
} = import__.default;
|
|
2214
2209
|
|
|
2210
|
+
// src/bridge-install.ts
|
|
2211
|
+
var import_fs2 = require("fs");
|
|
2212
|
+
var import_path2 = require("path");
|
|
2213
|
+
|
|
2215
2214
|
// src/packages.ts
|
|
2216
2215
|
var import_fs = require("fs");
|
|
2217
2216
|
var import_path = require("path");
|
|
@@ -2275,15 +2274,21 @@ function remove_package(project_path, name) {
|
|
|
2275
2274
|
}
|
|
2276
2275
|
|
|
2277
2276
|
// src/bridge-install.ts
|
|
2277
|
+
var __dirname = "/home/runner/work/unity-agentic-tools/unity-agentic-tools/unity-agentic-tools/src";
|
|
2278
2278
|
var BRIDGE_PACKAGE_NAME = "com.unity-agentic-tools.editor-bridge";
|
|
2279
|
-
var
|
|
2280
|
-
function install_bridge_package(project_path) {
|
|
2279
|
+
var REMOTE_BRIDGE_PACKAGE_VERSION = "https://github.com/taconotsandwich/unity-agentic-tools.git?path=unity-package";
|
|
2280
|
+
function install_bridge_package(project_path, options = {}) {
|
|
2281
|
+
const target = resolve_bridge_package_target(options);
|
|
2282
|
+
if ("error" in target) {
|
|
2283
|
+
return target;
|
|
2284
|
+
}
|
|
2281
2285
|
const manifest_result = load_manifest(project_path);
|
|
2282
2286
|
if ("error" in manifest_result) {
|
|
2283
2287
|
return manifest_result;
|
|
2284
2288
|
}
|
|
2285
2289
|
const existing_version = manifest_result.manifest.dependencies[BRIDGE_PACKAGE_NAME];
|
|
2286
|
-
|
|
2290
|
+
const should_preserve_existing_file = !options.remote && !options.local_package_path && !options.require_local;
|
|
2291
|
+
if (should_preserve_existing_file && typeof existing_version === "string" && existing_version.startsWith("file:")) {
|
|
2287
2292
|
return {
|
|
2288
2293
|
success: true,
|
|
2289
2294
|
action: "preserved",
|
|
@@ -2291,12 +2296,75 @@ function install_bridge_package(project_path) {
|
|
|
2291
2296
|
version: existing_version
|
|
2292
2297
|
};
|
|
2293
2298
|
}
|
|
2294
|
-
return add_package(project_path, BRIDGE_PACKAGE_NAME,
|
|
2299
|
+
return add_package(project_path, BRIDGE_PACKAGE_NAME, target.version);
|
|
2300
|
+
}
|
|
2301
|
+
function resolve_bridge_package_target(options) {
|
|
2302
|
+
if (options.remote && (options.local_package_path || options.require_local)) {
|
|
2303
|
+
return { error: "Use either --local or --remote, not both." };
|
|
2304
|
+
}
|
|
2305
|
+
if (options.remote) {
|
|
2306
|
+
return { version: REMOTE_BRIDGE_PACKAGE_VERSION };
|
|
2307
|
+
}
|
|
2308
|
+
if (options.local_package_path) {
|
|
2309
|
+
const local_path = import_path2.resolve(options.local_package_path);
|
|
2310
|
+
return local_package_target(local_path);
|
|
2311
|
+
}
|
|
2312
|
+
if (options.require_local) {
|
|
2313
|
+
const detected_path = detect_local_bridge_package_path();
|
|
2314
|
+
if (detected_path) {
|
|
2315
|
+
return local_package_target(detected_path);
|
|
2316
|
+
}
|
|
2317
|
+
return {
|
|
2318
|
+
error: "Local bridge package could not be auto-detected. Pass --local <path-to-unity-package> or run from a source checkout."
|
|
2319
|
+
};
|
|
2320
|
+
}
|
|
2321
|
+
return { version: REMOTE_BRIDGE_PACKAGE_VERSION };
|
|
2322
|
+
}
|
|
2323
|
+
function detect_local_bridge_package_path() {
|
|
2324
|
+
const candidate = import_path2.resolve(__dirname, "..", "..", "unity-package");
|
|
2325
|
+
return is_bridge_package(candidate) ? candidate : undefined;
|
|
2326
|
+
}
|
|
2327
|
+
function local_package_target(package_path) {
|
|
2328
|
+
const normalized_path = normalize_local_bridge_package_path(package_path);
|
|
2329
|
+
if (!normalized_path) {
|
|
2330
|
+
return {
|
|
2331
|
+
error: `Local bridge package not found at ${package_path}. Expected package.json with name "${BRIDGE_PACKAGE_NAME}".`
|
|
2332
|
+
};
|
|
2333
|
+
}
|
|
2334
|
+
return { version: to_file_dependency(normalized_path) };
|
|
2335
|
+
}
|
|
2336
|
+
function normalize_local_bridge_package_path(package_path) {
|
|
2337
|
+
if (is_bridge_package(package_path)) {
|
|
2338
|
+
return package_path;
|
|
2339
|
+
}
|
|
2340
|
+
const nested_package_path = import_path2.join(package_path, "unity-package");
|
|
2341
|
+
if (is_bridge_package(nested_package_path)) {
|
|
2342
|
+
return nested_package_path;
|
|
2343
|
+
}
|
|
2344
|
+
return;
|
|
2345
|
+
}
|
|
2346
|
+
function is_bridge_package(package_path) {
|
|
2347
|
+
const package_json_path = import_path2.join(package_path, "package.json");
|
|
2348
|
+
if (!import_fs2.existsSync(package_json_path)) {
|
|
2349
|
+
return false;
|
|
2350
|
+
}
|
|
2351
|
+
try {
|
|
2352
|
+
const parsed = JSON.parse(import_fs2.readFileSync(package_json_path, "utf-8"));
|
|
2353
|
+
return is_record(parsed) && parsed.name === BRIDGE_PACKAGE_NAME;
|
|
2354
|
+
} catch {
|
|
2355
|
+
return false;
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
function is_record(value) {
|
|
2359
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2360
|
+
}
|
|
2361
|
+
function to_file_dependency(package_path) {
|
|
2362
|
+
return `file:${package_path.replace(/\\/g, "/")}`;
|
|
2295
2363
|
}
|
|
2296
2364
|
|
|
2297
2365
|
// src/cleanup.ts
|
|
2298
|
-
var
|
|
2299
|
-
var
|
|
2366
|
+
var import_fs3 = require("fs");
|
|
2367
|
+
var import_path3 = require("path");
|
|
2300
2368
|
var CONFIG_DIR = ".unity-agentic";
|
|
2301
2369
|
var GUID_CACHE_FILE = "guid-cache.json";
|
|
2302
2370
|
var PACKAGE_CACHE_FILE = "package-cache.json";
|
|
@@ -2306,10 +2374,10 @@ var DOC_INDEX_FILE = "doc-index.json";
|
|
|
2306
2374
|
var EDITOR_LOCK_FILE = "editor.json";
|
|
2307
2375
|
var LAST_EDITOR_CONFIG_FILE = "editor.last.json";
|
|
2308
2376
|
function cleanup(options = {}) {
|
|
2309
|
-
const projectPath =
|
|
2310
|
-
const configPath =
|
|
2377
|
+
const projectPath = import_path3.resolve(options.project || process.cwd());
|
|
2378
|
+
const configPath = import_path3.join(projectPath, CONFIG_DIR);
|
|
2311
2379
|
const modes = resolve_cleanup_modes(options);
|
|
2312
|
-
if (!
|
|
2380
|
+
if (!import_fs3.existsSync(configPath)) {
|
|
2313
2381
|
return {
|
|
2314
2382
|
success: true,
|
|
2315
2383
|
project_path: projectPath,
|
|
@@ -2377,16 +2445,16 @@ function resolve_cleanup_modes(options) {
|
|
|
2377
2445
|
return modes;
|
|
2378
2446
|
}
|
|
2379
2447
|
function remove_stale_lock_files(configPath, filesRemoved) {
|
|
2380
|
-
const editorLockPath =
|
|
2381
|
-
if (
|
|
2448
|
+
const editorLockPath = import_path3.join(configPath, EDITOR_LOCK_FILE);
|
|
2449
|
+
if (import_fs3.existsSync(editorLockPath) && is_stale_editor_lock(editorLockPath)) {
|
|
2382
2450
|
remove_file(configPath, EDITOR_LOCK_FILE, filesRemoved);
|
|
2383
2451
|
}
|
|
2384
2452
|
remove_file(configPath, LAST_EDITOR_CONFIG_FILE, filesRemoved);
|
|
2385
2453
|
}
|
|
2386
2454
|
function is_stale_editor_lock(filePath) {
|
|
2387
2455
|
try {
|
|
2388
|
-
const parsed = JSON.parse(
|
|
2389
|
-
if (!
|
|
2456
|
+
const parsed = JSON.parse(import_fs3.readFileSync(filePath, "utf-8"));
|
|
2457
|
+
if (!is_record2(parsed)) {
|
|
2390
2458
|
return true;
|
|
2391
2459
|
}
|
|
2392
2460
|
const pid = parsed.pid;
|
|
@@ -2403,10 +2471,10 @@ function is_process_alive(pid) {
|
|
|
2403
2471
|
process.kill(pid, 0);
|
|
2404
2472
|
return true;
|
|
2405
2473
|
} catch (err) {
|
|
2406
|
-
return
|
|
2474
|
+
return is_record2(err) && err.code === "EPERM";
|
|
2407
2475
|
}
|
|
2408
2476
|
}
|
|
2409
|
-
function
|
|
2477
|
+
function is_record2(value) {
|
|
2410
2478
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2411
2479
|
}
|
|
2412
2480
|
function remove_files(configPath, files, filesRemoved) {
|
|
@@ -2415,38 +2483,38 @@ function remove_files(configPath, files, filesRemoved) {
|
|
|
2415
2483
|
}
|
|
2416
2484
|
}
|
|
2417
2485
|
function remove_file(configPath, file, filesRemoved) {
|
|
2418
|
-
const filePath =
|
|
2419
|
-
if (!
|
|
2486
|
+
const filePath = import_path3.join(configPath, file);
|
|
2487
|
+
if (!import_fs3.existsSync(filePath)) {
|
|
2420
2488
|
return;
|
|
2421
2489
|
}
|
|
2422
2490
|
try {
|
|
2423
|
-
|
|
2491
|
+
import_fs3.unlinkSync(filePath);
|
|
2424
2492
|
filesRemoved.push(file);
|
|
2425
2493
|
} catch {}
|
|
2426
2494
|
}
|
|
2427
2495
|
function removeDirectoryRecursive(dir) {
|
|
2428
|
-
if (!
|
|
2496
|
+
if (!import_fs3.existsSync(dir)) {
|
|
2429
2497
|
return;
|
|
2430
2498
|
}
|
|
2431
|
-
const entries =
|
|
2499
|
+
const entries = import_fs3.readdirSync(dir, { withFileTypes: true });
|
|
2432
2500
|
for (const entry of entries) {
|
|
2433
|
-
const fullPath =
|
|
2501
|
+
const fullPath = import_path3.join(dir, entry.name);
|
|
2434
2502
|
if (entry.isDirectory()) {
|
|
2435
2503
|
removeDirectoryRecursive(fullPath);
|
|
2436
2504
|
} else {
|
|
2437
|
-
|
|
2505
|
+
import_fs3.unlinkSync(fullPath);
|
|
2438
2506
|
}
|
|
2439
2507
|
}
|
|
2440
|
-
|
|
2508
|
+
import_fs3.rmdirSync(dir);
|
|
2441
2509
|
}
|
|
2442
2510
|
|
|
2443
2511
|
// src/editor-discovery.ts
|
|
2444
|
-
var
|
|
2445
|
-
var
|
|
2512
|
+
var import_fs5 = require("fs");
|
|
2513
|
+
var import_path5 = require("path");
|
|
2446
2514
|
|
|
2447
2515
|
// src/gitignore.ts
|
|
2448
|
-
var
|
|
2449
|
-
var
|
|
2516
|
+
var import_fs4 = require("fs");
|
|
2517
|
+
var import_path4 = require("path");
|
|
2450
2518
|
var AGENTIC_DIR = ".unity-agentic";
|
|
2451
2519
|
var GITIGNORE_ENTRY = `${AGENTIC_DIR}/`;
|
|
2452
2520
|
var ACCEPTED_AGENTIC_PATTERNS = new Set([
|
|
@@ -2456,20 +2524,20 @@ var ACCEPTED_AGENTIC_PATTERNS = new Set([
|
|
|
2456
2524
|
`/${GITIGNORE_ENTRY}`
|
|
2457
2525
|
]);
|
|
2458
2526
|
function ensure_gitignore_ignores_agentic_dir(project_path) {
|
|
2459
|
-
const gitignore_path =
|
|
2460
|
-
if (!
|
|
2461
|
-
|
|
2527
|
+
const gitignore_path = import_path4.join(project_path, ".gitignore");
|
|
2528
|
+
if (!import_fs4.existsSync(gitignore_path)) {
|
|
2529
|
+
import_fs4.writeFileSync(gitignore_path, `${GITIGNORE_ENTRY}
|
|
2462
2530
|
`);
|
|
2463
2531
|
return;
|
|
2464
2532
|
}
|
|
2465
|
-
const content =
|
|
2533
|
+
const content = import_fs4.readFileSync(gitignore_path, "utf-8");
|
|
2466
2534
|
if (gitignore_ignores_agentic_dir(content)) {
|
|
2467
2535
|
return;
|
|
2468
2536
|
}
|
|
2469
2537
|
const separator = content.length === 0 || content.endsWith(`
|
|
2470
2538
|
`) ? "" : `
|
|
2471
2539
|
`;
|
|
2472
|
-
|
|
2540
|
+
import_fs4.writeFileSync(gitignore_path, `${content}${separator}${GITIGNORE_ENTRY}
|
|
2473
2541
|
`);
|
|
2474
2542
|
}
|
|
2475
2543
|
function gitignore_ignores_agentic_dir(content) {
|
|
@@ -2486,7 +2554,7 @@ function gitignore_ignores_agentic_dir(content) {
|
|
|
2486
2554
|
}
|
|
2487
2555
|
|
|
2488
2556
|
// src/util.ts
|
|
2489
|
-
function
|
|
2557
|
+
function is_record3(value) {
|
|
2490
2558
|
return typeof value === "object" && value !== null;
|
|
2491
2559
|
}
|
|
2492
2560
|
|
|
@@ -2507,7 +2575,7 @@ async function request_editor_at_port(options) {
|
|
|
2507
2575
|
method,
|
|
2508
2576
|
...Object.keys(wire_params).length > 0 ? { params: wire_params } : {}
|
|
2509
2577
|
};
|
|
2510
|
-
return new Promise((
|
|
2578
|
+
return new Promise((resolve3) => {
|
|
2511
2579
|
let resolved = false;
|
|
2512
2580
|
let ws;
|
|
2513
2581
|
const timer = setTimeout(() => {
|
|
@@ -2516,7 +2584,7 @@ async function request_editor_at_port(options) {
|
|
|
2516
2584
|
try {
|
|
2517
2585
|
ws?.close();
|
|
2518
2586
|
} catch {}
|
|
2519
|
-
|
|
2587
|
+
resolve3({
|
|
2520
2588
|
jsonrpc: "2.0",
|
|
2521
2589
|
id: request_id,
|
|
2522
2590
|
error: { code: -32001, message: `Timeout after ${timeout}ms waiting for response to ${method}` }
|
|
@@ -2535,7 +2603,7 @@ async function request_editor_at_port(options) {
|
|
|
2535
2603
|
ws.close();
|
|
2536
2604
|
} catch {}
|
|
2537
2605
|
}, 200);
|
|
2538
|
-
|
|
2606
|
+
resolve3({
|
|
2539
2607
|
jsonrpc: "2.0",
|
|
2540
2608
|
id: request_id,
|
|
2541
2609
|
result: { queued: true }
|
|
@@ -2550,7 +2618,7 @@ async function request_editor_at_port(options) {
|
|
|
2550
2618
|
resolved = true;
|
|
2551
2619
|
clearTimeout(timer);
|
|
2552
2620
|
ws.close();
|
|
2553
|
-
|
|
2621
|
+
resolve3(data);
|
|
2554
2622
|
}
|
|
2555
2623
|
}
|
|
2556
2624
|
} catch {}
|
|
@@ -2559,7 +2627,7 @@ async function request_editor_at_port(options) {
|
|
|
2559
2627
|
if (!resolved) {
|
|
2560
2628
|
resolved = true;
|
|
2561
2629
|
clearTimeout(timer);
|
|
2562
|
-
|
|
2630
|
+
resolve3({
|
|
2563
2631
|
jsonrpc: "2.0",
|
|
2564
2632
|
id: request_id,
|
|
2565
2633
|
error: { code: -32002, message: `WebSocket connection failed to ${url}. Is the Unity Editor running?` }
|
|
@@ -2570,7 +2638,7 @@ async function request_editor_at_port(options) {
|
|
|
2570
2638
|
if (!resolved) {
|
|
2571
2639
|
resolved = true;
|
|
2572
2640
|
clearTimeout(timer);
|
|
2573
|
-
|
|
2641
|
+
resolve3({
|
|
2574
2642
|
jsonrpc: "2.0",
|
|
2575
2643
|
id: request_id,
|
|
2576
2644
|
error: { code: -32003, message: "WebSocket connection closed before response received" }
|
|
@@ -2581,7 +2649,7 @@ async function request_editor_at_port(options) {
|
|
|
2581
2649
|
if (!resolved) {
|
|
2582
2650
|
resolved = true;
|
|
2583
2651
|
clearTimeout(timer);
|
|
2584
|
-
|
|
2652
|
+
resolve3({
|
|
2585
2653
|
jsonrpc: "2.0",
|
|
2586
2654
|
id: request_id,
|
|
2587
2655
|
error: { code: -32002, message: `Failed to connect: ${err instanceof Error ? err.message : String(err)}` }
|
|
@@ -2592,9 +2660,9 @@ async function request_editor_at_port(options) {
|
|
|
2592
2660
|
}
|
|
2593
2661
|
async function ping_editor(port, timeout_ms = 2000) {
|
|
2594
2662
|
const url = `ws://127.0.0.1:${port}/unity-agentic`;
|
|
2595
|
-
return new Promise((
|
|
2663
|
+
return new Promise((resolve3) => {
|
|
2596
2664
|
const timer = setTimeout(() => {
|
|
2597
|
-
|
|
2665
|
+
resolve3({ reachable: false, error: `Timeout after ${timeout_ms}ms` });
|
|
2598
2666
|
}, timeout_ms);
|
|
2599
2667
|
try {
|
|
2600
2668
|
const ws = new WebSocket(url);
|
|
@@ -2603,15 +2671,15 @@ async function ping_editor(port, timeout_ms = 2000) {
|
|
|
2603
2671
|
try {
|
|
2604
2672
|
ws.close();
|
|
2605
2673
|
} catch {}
|
|
2606
|
-
|
|
2674
|
+
resolve3({ reachable: true });
|
|
2607
2675
|
};
|
|
2608
2676
|
ws.onerror = (err) => {
|
|
2609
2677
|
clearTimeout(timer);
|
|
2610
|
-
|
|
2678
|
+
resolve3({ reachable: false, error: describe_websocket_error(err, `WebSocket connection failed to ${url}`) });
|
|
2611
2679
|
};
|
|
2612
2680
|
} catch (err) {
|
|
2613
2681
|
clearTimeout(timer);
|
|
2614
|
-
|
|
2682
|
+
resolve3({ reachable: false, error: describe_websocket_error(err, `WebSocket connection failed to ${url}`) });
|
|
2615
2683
|
}
|
|
2616
2684
|
});
|
|
2617
2685
|
}
|
|
@@ -2622,7 +2690,7 @@ function describe_websocket_error(err, fallback) {
|
|
|
2622
2690
|
if (err instanceof Error && err.message.trim().length > 0) {
|
|
2623
2691
|
return err.message;
|
|
2624
2692
|
}
|
|
2625
|
-
if (
|
|
2693
|
+
if (is_record3(err)) {
|
|
2626
2694
|
if (typeof err.message === "string" && err.message.trim().length > 0) {
|
|
2627
2695
|
return err.message;
|
|
2628
2696
|
}
|
|
@@ -2646,13 +2714,13 @@ var BRIDGE_PORT_RANGE_END = 53791;
|
|
|
2646
2714
|
var LAST_KNOWN_CONFIG_FILE = "editor.last.json";
|
|
2647
2715
|
var LAST_KNOWN_EDITOR_CONFIGS = new Map;
|
|
2648
2716
|
function read_editor_config(project_path) {
|
|
2649
|
-
const config_path =
|
|
2650
|
-
if (!
|
|
2717
|
+
const config_path = import_path5.join(project_path, ".unity-agentic", "editor.json");
|
|
2718
|
+
if (!import_fs5.existsSync(config_path)) {
|
|
2651
2719
|
return { error: `Editor bridge not found at ${config_path}. Is the Unity Editor running with the bridge package installed?` };
|
|
2652
2720
|
}
|
|
2653
2721
|
let config;
|
|
2654
2722
|
try {
|
|
2655
|
-
const raw =
|
|
2723
|
+
const raw = import_fs5.readFileSync(config_path, "utf-8");
|
|
2656
2724
|
config = JSON.parse(raw);
|
|
2657
2725
|
} catch (err) {
|
|
2658
2726
|
return { error: `Failed to parse editor.json: ${err instanceof Error ? err.message : String(err)}` };
|
|
@@ -2751,7 +2819,7 @@ async function discover_bridge_infos(timeout_ms, preferred_ports = []) {
|
|
|
2751
2819
|
return results.filter((result) => result !== null);
|
|
2752
2820
|
}
|
|
2753
2821
|
function parse_bridge_info(result, port) {
|
|
2754
|
-
if (!
|
|
2822
|
+
if (!is_record3(result)) {
|
|
2755
2823
|
return null;
|
|
2756
2824
|
}
|
|
2757
2825
|
const project_path = typeof result.project_path === "string" ? result.project_path : null;
|
|
@@ -2780,20 +2848,20 @@ function remember_editor_config(project_path, config) {
|
|
|
2780
2848
|
}
|
|
2781
2849
|
function forget_cached_editor_config(project_path, normalized_project_path) {
|
|
2782
2850
|
LAST_KNOWN_EDITOR_CONFIGS.delete(normalized_project_path);
|
|
2783
|
-
const config_path =
|
|
2851
|
+
const config_path = import_path5.join(project_path, ".unity-agentic", LAST_KNOWN_CONFIG_FILE);
|
|
2784
2852
|
try {
|
|
2785
|
-
if (
|
|
2786
|
-
|
|
2853
|
+
if (import_fs5.existsSync(config_path)) {
|
|
2854
|
+
import_fs5.unlinkSync(config_path);
|
|
2787
2855
|
}
|
|
2788
2856
|
} catch {}
|
|
2789
2857
|
}
|
|
2790
2858
|
function read_cached_editor_config(project_path, normalized_project_path) {
|
|
2791
|
-
const config_path =
|
|
2792
|
-
if (!
|
|
2859
|
+
const config_path = import_path5.join(project_path, ".unity-agentic", LAST_KNOWN_CONFIG_FILE);
|
|
2860
|
+
if (!import_fs5.existsSync(config_path)) {
|
|
2793
2861
|
return;
|
|
2794
2862
|
}
|
|
2795
2863
|
try {
|
|
2796
|
-
const raw =
|
|
2864
|
+
const raw = import_fs5.readFileSync(config_path, "utf-8");
|
|
2797
2865
|
const parsed = JSON.parse(raw);
|
|
2798
2866
|
if (typeof parsed.port !== "number" || typeof parsed.pid !== "number" || typeof parsed.version !== "string") {
|
|
2799
2867
|
return;
|
|
@@ -2810,12 +2878,12 @@ function read_cached_editor_config(project_path, normalized_project_path) {
|
|
|
2810
2878
|
}
|
|
2811
2879
|
}
|
|
2812
2880
|
function write_cached_editor_config(project_path, config) {
|
|
2813
|
-
const config_dir =
|
|
2814
|
-
const config_path =
|
|
2881
|
+
const config_dir = import_path5.join(project_path, ".unity-agentic");
|
|
2882
|
+
const config_path = import_path5.join(config_dir, LAST_KNOWN_CONFIG_FILE);
|
|
2815
2883
|
try {
|
|
2816
|
-
|
|
2884
|
+
import_fs5.mkdirSync(config_dir, { recursive: true });
|
|
2817
2885
|
ensure_gitignore_ignores_agentic_dir(project_path);
|
|
2818
|
-
|
|
2886
|
+
import_fs5.writeFileSync(config_path, JSON.stringify({
|
|
2819
2887
|
port: config.port,
|
|
2820
2888
|
pid: config.pid,
|
|
2821
2889
|
version: config.version,
|
|
@@ -2824,10 +2892,10 @@ function write_cached_editor_config(project_path, config) {
|
|
|
2824
2892
|
} catch {}
|
|
2825
2893
|
}
|
|
2826
2894
|
function normalize_project_path(project_path) {
|
|
2827
|
-
const resolved_path =
|
|
2895
|
+
const resolved_path = import_path5.resolve(project_path);
|
|
2828
2896
|
let normalized = resolved_path;
|
|
2829
2897
|
try {
|
|
2830
|
-
normalized = typeof
|
|
2898
|
+
normalized = typeof import_fs5.realpathSync.native === "function" ? import_fs5.realpathSync.native(resolved_path) : import_fs5.realpathSync(resolved_path);
|
|
2831
2899
|
} catch {
|
|
2832
2900
|
normalized = resolved_path;
|
|
2833
2901
|
}
|
|
@@ -2838,7 +2906,7 @@ function is_pid_alive(pid) {
|
|
|
2838
2906
|
process.kill(pid, 0);
|
|
2839
2907
|
return true;
|
|
2840
2908
|
} catch (err) {
|
|
2841
|
-
if (
|
|
2909
|
+
if (is_record3(err) && err.code === "EPERM") {
|
|
2842
2910
|
return true;
|
|
2843
2911
|
}
|
|
2844
2912
|
return false;
|
|
@@ -2865,14 +2933,29 @@ var TRANSITION_TOLERANT_COMMAND_SEMANTICS = {
|
|
|
2865
2933
|
retry_delays_ms: [250, 500, 1000, 1500, 2000],
|
|
2866
2934
|
discovery_timeout_ms: 250
|
|
2867
2935
|
};
|
|
2868
|
-
var
|
|
2869
|
-
"
|
|
2870
|
-
"
|
|
2871
|
-
"
|
|
2872
|
-
"
|
|
2873
|
-
"
|
|
2874
|
-
"
|
|
2875
|
-
"
|
|
2936
|
+
var READ_RUN_TARGETS = new Set([
|
|
2937
|
+
"scene.hierarchy",
|
|
2938
|
+
"scene.query",
|
|
2939
|
+
"query.assets",
|
|
2940
|
+
"query.asset",
|
|
2941
|
+
"query.scene",
|
|
2942
|
+
"query.object",
|
|
2943
|
+
"play.state",
|
|
2944
|
+
"ui.snapshot",
|
|
2945
|
+
"ui.query",
|
|
2946
|
+
"input.map",
|
|
2947
|
+
"tests.results",
|
|
2948
|
+
"UnityAgenticTools.Util.Hierarchy.Snapshot",
|
|
2949
|
+
"UnityAgenticTools.Util.Hierarchy.Query",
|
|
2950
|
+
"UnityAgenticTools.Query.Assets.Find",
|
|
2951
|
+
"UnityAgenticTools.Query.Assets.Info",
|
|
2952
|
+
"UnityAgenticTools.Query.Scene.Hierarchy",
|
|
2953
|
+
"UnityAgenticTools.Query.Scene.Object",
|
|
2954
|
+
"UnityAgenticTools.Util.PlayMode.GetState",
|
|
2955
|
+
"UnityAgenticTools.Util.UI.Snapshot",
|
|
2956
|
+
"UnityAgenticTools.Util.UI.Query",
|
|
2957
|
+
"UnityAgenticTools.Util.Input.Map",
|
|
2958
|
+
"UnityAgenticTools.Util.TestRunner.GetResults"
|
|
2876
2959
|
]);
|
|
2877
2960
|
var PLAY_MODE_RUN_TARGETS = new Set([
|
|
2878
2961
|
"play.enter",
|
|
@@ -2890,36 +2973,131 @@ var PLAY_MODE_RUN_TARGETS = new Set([
|
|
|
2890
2973
|
var READ_RETRYABLE_CODES = new Set([-32000, -32002, -32003, CLIENT_DISCOVERY_UNAVAILABLE_CODE]);
|
|
2891
2974
|
var COMMAND_RETRYABLE_CODES = new Set([-32002, CLIENT_DISCOVERY_UNAVAILABLE_CODE]);
|
|
2892
2975
|
var TRANSITION_TOLERANT_COMMAND_RETRYABLE_CODES = new Set([-32000, -32002, -32003, CLIENT_DISCOVERY_UNAVAILABLE_CODE]);
|
|
2976
|
+
var RELOAD_TOLERANCE_MS = 30000;
|
|
2977
|
+
var RELOAD_POLL_INTERVAL_MS = 500;
|
|
2893
2978
|
async function call_editor(options) {
|
|
2894
2979
|
const semantics = get_action_semantics(options.method, "unary", options.params);
|
|
2895
|
-
const
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2980
|
+
const explicit_retries = options.retries !== undefined;
|
|
2981
|
+
const max_retries = options.retries ?? semantics.default_retries;
|
|
2982
|
+
const delays = semantics.retry_delays_ms;
|
|
2983
|
+
const started = Date.now();
|
|
2984
|
+
let last_response;
|
|
2985
|
+
for (let attempt = 0;; attempt++) {
|
|
2986
|
+
last_response = await call_editor_once(options, semantics);
|
|
2987
|
+
if (!should_retry_response(last_response, semantics)) {
|
|
2988
|
+
return last_response;
|
|
2989
|
+
}
|
|
2990
|
+
if (attempt < max_retries) {
|
|
2991
|
+
await sleep(delays[Math.min(attempt, delays.length - 1)]);
|
|
2992
|
+
continue;
|
|
2901
2993
|
}
|
|
2902
|
-
if (
|
|
2903
|
-
|
|
2904
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
2994
|
+
if (explicit_retries || !within_reload_tolerance(options, started)) {
|
|
2995
|
+
return last_response;
|
|
2905
2996
|
}
|
|
2997
|
+
await sleep(RELOAD_POLL_INTERVAL_MS);
|
|
2906
2998
|
}
|
|
2907
|
-
|
|
2999
|
+
}
|
|
3000
|
+
function within_reload_tolerance(options, started) {
|
|
3001
|
+
if (options.port !== undefined) {
|
|
3002
|
+
return false;
|
|
3003
|
+
}
|
|
3004
|
+
if (Date.now() - started >= RELOAD_TOLERANCE_MS) {
|
|
3005
|
+
return false;
|
|
3006
|
+
}
|
|
3007
|
+
return !("error" in read_editor_config(options.project_path));
|
|
3008
|
+
}
|
|
3009
|
+
function sleep(ms) {
|
|
3010
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
3011
|
+
}
|
|
3012
|
+
var READINESS_FIELDS = [
|
|
3013
|
+
"is_playing",
|
|
3014
|
+
"is_paused",
|
|
3015
|
+
"is_compiling",
|
|
3016
|
+
"is_updating",
|
|
3017
|
+
"is_playmode_transitioning",
|
|
3018
|
+
"is_reloading",
|
|
3019
|
+
"is_stable"
|
|
3020
|
+
];
|
|
3021
|
+
async function read_editor_readiness(port, timeout_ms) {
|
|
3022
|
+
const response = await request_editor_at_port({
|
|
3023
|
+
port,
|
|
3024
|
+
method: "editor.bridge.getInfo",
|
|
3025
|
+
timeout: timeout_ms
|
|
3026
|
+
});
|
|
3027
|
+
if (response.error) {
|
|
3028
|
+
return { error: response.error.message };
|
|
3029
|
+
}
|
|
3030
|
+
if (!is_record3(response.result)) {
|
|
3031
|
+
return { error: "Bridge returned no readiness fields" };
|
|
3032
|
+
}
|
|
3033
|
+
const info = response.result;
|
|
3034
|
+
const readiness = {};
|
|
3035
|
+
for (const field of READINESS_FIELDS) {
|
|
3036
|
+
const value = info[field];
|
|
3037
|
+
if (typeof value !== "boolean") {
|
|
3038
|
+
return { error: `Bridge did not report ${field}; the installed package may be older than this CLI` };
|
|
3039
|
+
}
|
|
3040
|
+
readiness[field] = value;
|
|
3041
|
+
}
|
|
3042
|
+
return readiness;
|
|
2908
3043
|
}
|
|
2909
3044
|
async function stream_editor(options) {
|
|
2910
|
-
const { method, params, timeout = 30000, on_event } = options;
|
|
3045
|
+
const { method, params, timeout = 30000, on_event, on_error } = options;
|
|
2911
3046
|
const semantics = get_action_semantics(method, "stream", params);
|
|
2912
3047
|
const config = await resolve_config(options, semantics.discovery_timeout_ms);
|
|
2913
3048
|
if ("error" in config) {
|
|
2914
3049
|
throw new Error(config.error);
|
|
2915
3050
|
}
|
|
2916
|
-
const MAX_RECONNECTS = 5;
|
|
2917
|
-
let reconnect_count = 0;
|
|
2918
3051
|
let stopped = false;
|
|
2919
|
-
|
|
3052
|
+
let reconnect_count = 0;
|
|
3053
|
+
let reconnect_deadline;
|
|
3054
|
+
let active_socket;
|
|
3055
|
+
return new Promise((resolve4, reject) => {
|
|
2920
3056
|
let resolved = false;
|
|
3057
|
+
function stop() {
|
|
3058
|
+
stopped = true;
|
|
3059
|
+
try {
|
|
3060
|
+
active_socket?.close();
|
|
3061
|
+
} catch {}
|
|
3062
|
+
}
|
|
3063
|
+
function fail(error) {
|
|
3064
|
+
if (stopped)
|
|
3065
|
+
return;
|
|
3066
|
+
stop();
|
|
3067
|
+
if (!resolved) {
|
|
3068
|
+
resolved = true;
|
|
3069
|
+
reject(error);
|
|
3070
|
+
return;
|
|
3071
|
+
}
|
|
3072
|
+
on_error?.(error);
|
|
3073
|
+
}
|
|
3074
|
+
function schedule_reconnect() {
|
|
3075
|
+
if (stopped)
|
|
3076
|
+
return;
|
|
3077
|
+
reconnect_deadline ??= Date.now() + RELOAD_TOLERANCE_MS;
|
|
3078
|
+
if (Date.now() >= reconnect_deadline) {
|
|
3079
|
+
fail(new Error(`Stream lost: could not reconnect within ${RELOAD_TOLERANCE_MS}ms`));
|
|
3080
|
+
return;
|
|
3081
|
+
}
|
|
3082
|
+
reconnect_count += 1;
|
|
3083
|
+
const delay = Math.min(500 * reconnect_count, 3000);
|
|
3084
|
+
setTimeout(() => {
|
|
3085
|
+
if (stopped)
|
|
3086
|
+
return;
|
|
3087
|
+
resolve_config(options, semantics.discovery_timeout_ms).then((fresh_config) => {
|
|
3088
|
+
if (stopped)
|
|
3089
|
+
return;
|
|
3090
|
+
if ("error" in fresh_config) {
|
|
3091
|
+
schedule_reconnect();
|
|
3092
|
+
return;
|
|
3093
|
+
}
|
|
3094
|
+
connect(`ws://127.0.0.1:${fresh_config.port}/unity-agentic`);
|
|
3095
|
+
});
|
|
3096
|
+
}, delay);
|
|
3097
|
+
}
|
|
2921
3098
|
function connect(url) {
|
|
2922
3099
|
const ws = new WebSocket(url);
|
|
3100
|
+
active_socket = ws;
|
|
2923
3101
|
const request_id = generate_editor_request_id();
|
|
2924
3102
|
const request = {
|
|
2925
3103
|
jsonrpc: "2.0",
|
|
@@ -2928,57 +3106,77 @@ async function stream_editor(options) {
|
|
|
2928
3106
|
...params ? { params } : {}
|
|
2929
3107
|
};
|
|
2930
3108
|
let connected = false;
|
|
2931
|
-
|
|
2932
|
-
|
|
3109
|
+
let attempt_finished = false;
|
|
3110
|
+
function finish_attempt() {
|
|
3111
|
+
if (attempt_finished) {
|
|
3112
|
+
return false;
|
|
3113
|
+
}
|
|
3114
|
+
attempt_finished = true;
|
|
3115
|
+
clearTimeout(timer);
|
|
3116
|
+
return true;
|
|
3117
|
+
}
|
|
3118
|
+
const timer = setTimeout(() => {
|
|
3119
|
+
if (connected || !finish_attempt() || stopped)
|
|
3120
|
+
return;
|
|
3121
|
+
if (!resolved) {
|
|
3122
|
+
stopped = true;
|
|
2933
3123
|
reject(new Error(`Timeout connecting to ${url}`));
|
|
3124
|
+
return;
|
|
2934
3125
|
}
|
|
2935
|
-
|
|
3126
|
+
try {
|
|
3127
|
+
ws.close();
|
|
3128
|
+
} catch {}
|
|
3129
|
+
schedule_reconnect();
|
|
3130
|
+
}, timeout);
|
|
2936
3131
|
ws.onopen = () => {
|
|
2937
3132
|
connected = true;
|
|
2938
|
-
|
|
2939
|
-
clearTimeout(timer);
|
|
3133
|
+
clearTimeout(timer);
|
|
2940
3134
|
reconnect_count = 0;
|
|
3135
|
+
reconnect_deadline = undefined;
|
|
2941
3136
|
ws.send(JSON.stringify(request));
|
|
2942
3137
|
if (!resolved) {
|
|
2943
3138
|
resolved = true;
|
|
2944
|
-
|
|
2945
|
-
stopped = true;
|
|
2946
|
-
ws.close();
|
|
2947
|
-
} });
|
|
3139
|
+
resolve4({ close: stop });
|
|
2948
3140
|
}
|
|
2949
3141
|
};
|
|
2950
3142
|
ws.onmessage = (event) => {
|
|
3143
|
+
let data;
|
|
2951
3144
|
try {
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
3145
|
+
data = JSON.parse(String(event.data));
|
|
3146
|
+
} catch {
|
|
3147
|
+
return;
|
|
3148
|
+
}
|
|
3149
|
+
if (typeof data !== "object" || data === null) {
|
|
3150
|
+
return;
|
|
3151
|
+
}
|
|
3152
|
+
if ("method" in data && !("id" in data)) {
|
|
3153
|
+
on_event(data);
|
|
3154
|
+
return;
|
|
3155
|
+
}
|
|
3156
|
+
const frame = data;
|
|
3157
|
+
if (frame.id === request_id && frame.error) {
|
|
3158
|
+
fail(new Error(`Subscription rejected: ${frame.error.message ?? "unknown error"}`));
|
|
3159
|
+
}
|
|
2957
3160
|
};
|
|
2958
3161
|
ws.onerror = () => {
|
|
2959
|
-
if (
|
|
2960
|
-
|
|
2961
|
-
|
|
3162
|
+
if (connected || !finish_attempt() || stopped)
|
|
3163
|
+
return;
|
|
3164
|
+
if (!resolved) {
|
|
3165
|
+
stopped = true;
|
|
2962
3166
|
reject(new Error(`WebSocket connection failed to ${url}`));
|
|
3167
|
+
return;
|
|
2963
3168
|
}
|
|
3169
|
+
schedule_reconnect();
|
|
2964
3170
|
};
|
|
2965
3171
|
ws.onclose = () => {
|
|
2966
|
-
if (stopped)
|
|
2967
|
-
return;
|
|
2968
|
-
if (!connected && !resolved)
|
|
3172
|
+
if (!finish_attempt() || stopped)
|
|
2969
3173
|
return;
|
|
2970
|
-
if (
|
|
3174
|
+
if (!resolved) {
|
|
3175
|
+
stopped = true;
|
|
3176
|
+
reject(new Error(`WebSocket connection failed to ${url}`));
|
|
2971
3177
|
return;
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
setTimeout(async () => {
|
|
2975
|
-
if (stopped)
|
|
2976
|
-
return;
|
|
2977
|
-
const fresh_config = await resolve_config(options, semantics.discovery_timeout_ms);
|
|
2978
|
-
if ("error" in fresh_config)
|
|
2979
|
-
return;
|
|
2980
|
-
connect(`ws://127.0.0.1:${fresh_config.port}/unity-agentic`);
|
|
2981
|
-
}, delay);
|
|
3178
|
+
}
|
|
3179
|
+
schedule_reconnect();
|
|
2982
3180
|
};
|
|
2983
3181
|
}
|
|
2984
3182
|
connect(`ws://127.0.0.1:${config.port}/unity-agentic`);
|
|
@@ -3015,7 +3213,7 @@ function get_action_semantics(method, kind = "unary", params) {
|
|
|
3015
3213
|
kind: "stream"
|
|
3016
3214
|
};
|
|
3017
3215
|
}
|
|
3018
|
-
if (
|
|
3216
|
+
if (is_read_invoke(method, params)) {
|
|
3019
3217
|
return TRANSITION_TOLERANT_READ_SEMANTICS;
|
|
3020
3218
|
}
|
|
3021
3219
|
if (is_play_mode_transition_invoke(method, params)) {
|
|
@@ -3023,6 +3221,16 @@ function get_action_semantics(method, kind = "unary", params) {
|
|
|
3023
3221
|
}
|
|
3024
3222
|
return DEFAULT_ACTION_SEMANTICS;
|
|
3025
3223
|
}
|
|
3224
|
+
function is_read_invoke(method, params) {
|
|
3225
|
+
if (method !== "editor.invoke" || !params) {
|
|
3226
|
+
return false;
|
|
3227
|
+
}
|
|
3228
|
+
if (params.type !== "UnityAgenticTools.Commands.Registry" || params.member !== "Run") {
|
|
3229
|
+
return false;
|
|
3230
|
+
}
|
|
3231
|
+
const target = parse_registry_run_target(params.args);
|
|
3232
|
+
return typeof target === "string" && READ_RUN_TARGETS.has(target);
|
|
3233
|
+
}
|
|
3026
3234
|
function is_play_mode_transition_invoke(method, params) {
|
|
3027
3235
|
if (method !== "editor.invoke" || !params) {
|
|
3028
3236
|
return false;
|
|
@@ -3087,19 +3295,45 @@ function resolve_bridge_options(options) {
|
|
|
3087
3295
|
function build_registry_args(values) {
|
|
3088
3296
|
return JSON.stringify(values);
|
|
3089
3297
|
}
|
|
3090
|
-
function
|
|
3298
|
+
function resolve_install_options(options) {
|
|
3299
|
+
if (options.remote && options.local !== undefined) {
|
|
3300
|
+
return { error: "Use either --local or --remote, not both." };
|
|
3301
|
+
}
|
|
3302
|
+
if (options.remote) {
|
|
3303
|
+
return { remote: true };
|
|
3304
|
+
}
|
|
3305
|
+
if (typeof options.local === "string") {
|
|
3306
|
+
return {
|
|
3307
|
+
local_package_path: path.resolve(options.local),
|
|
3308
|
+
require_local: true
|
|
3309
|
+
};
|
|
3310
|
+
}
|
|
3311
|
+
if (options.local === true) {
|
|
3312
|
+
return { require_local: true };
|
|
3313
|
+
}
|
|
3314
|
+
return {};
|
|
3315
|
+
}
|
|
3316
|
+
function print_json(value, pretty) {
|
|
3317
|
+
console.log(JSON.stringify(value, null, pretty ? 2 : 0));
|
|
3318
|
+
}
|
|
3319
|
+
function output_rpc_response(response, pretty) {
|
|
3091
3320
|
if (response.error) {
|
|
3092
|
-
|
|
3321
|
+
print_json({
|
|
3322
|
+
success: false,
|
|
3323
|
+
error: response.error.message,
|
|
3324
|
+
code: response.error.code,
|
|
3325
|
+
...response.error.data !== undefined ? { data: response.error.data } : {}
|
|
3326
|
+
}, pretty);
|
|
3093
3327
|
process.exitCode = 1;
|
|
3094
3328
|
return;
|
|
3095
3329
|
}
|
|
3096
|
-
|
|
3330
|
+
print_json(response.result, pretty);
|
|
3097
3331
|
if (payload_reports_failure(response.result)) {
|
|
3098
3332
|
process.exitCode = 1;
|
|
3099
3333
|
}
|
|
3100
3334
|
}
|
|
3101
3335
|
function payload_reports_failure(payload) {
|
|
3102
|
-
if (!
|
|
3336
|
+
if (!is_record4(payload)) {
|
|
3103
3337
|
return false;
|
|
3104
3338
|
}
|
|
3105
3339
|
if (payload.success === false) {
|
|
@@ -3107,9 +3341,30 @@ function payload_reports_failure(payload) {
|
|
|
3107
3341
|
}
|
|
3108
3342
|
return payload_reports_failure(payload.result);
|
|
3109
3343
|
}
|
|
3110
|
-
function
|
|
3344
|
+
function is_record4(value) {
|
|
3111
3345
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3112
3346
|
}
|
|
3347
|
+
function strip_command_listing_details(payload) {
|
|
3348
|
+
if (!is_record4(payload)) {
|
|
3349
|
+
return payload;
|
|
3350
|
+
}
|
|
3351
|
+
if (Array.isArray(payload.commands)) {
|
|
3352
|
+
return {
|
|
3353
|
+
...payload,
|
|
3354
|
+
commands: payload.commands.map((entry) => {
|
|
3355
|
+
if (!is_record4(entry)) {
|
|
3356
|
+
return entry;
|
|
3357
|
+
}
|
|
3358
|
+
const { args: _args, source: _source, ...brief } = entry;
|
|
3359
|
+
return brief;
|
|
3360
|
+
})
|
|
3361
|
+
};
|
|
3362
|
+
}
|
|
3363
|
+
if ("result" in payload) {
|
|
3364
|
+
return { ...payload, result: strip_command_listing_details(payload.result) };
|
|
3365
|
+
}
|
|
3366
|
+
return payload;
|
|
3367
|
+
}
|
|
3113
3368
|
function event_matches_topic(event, topic, type_filter) {
|
|
3114
3369
|
if (topic === "console") {
|
|
3115
3370
|
if (event.method !== "editor.console.logReceived") {
|
|
@@ -3133,13 +3388,12 @@ function event_matches_topic(event, topic, type_filter) {
|
|
|
3133
3388
|
return false;
|
|
3134
3389
|
}
|
|
3135
3390
|
function print_stream_event(event, pretty = false) {
|
|
3136
|
-
|
|
3391
|
+
print_json({
|
|
3137
3392
|
method: event.method,
|
|
3138
3393
|
...event.params ? event.params : {}
|
|
3139
|
-
};
|
|
3140
|
-
console.log(JSON.stringify(payload, null, pretty ? 2 : 0));
|
|
3394
|
+
}, pretty);
|
|
3141
3395
|
}
|
|
3142
|
-
program.command("list [query]").description("List runnable Unity commands and project script commands").option("-p, --project <path>", "Path to Unity project (defaults to cwd)").option("--timeout <ms>", "WebSocket timeout in ms", "10000").option("--port <n>", "Connect to a specific bridge port").option("--raw", "Include raw public static methods/properties for matching types").action(async (query, options) => {
|
|
3396
|
+
program.command("list [query]").description("List runnable Unity commands and project script commands").option("-p, --project <path>", "Path to Unity project (defaults to cwd)").option("--timeout <ms>", "WebSocket timeout in ms", "10000").option("--port <n>", "Connect to a specific bridge port").option("--raw", "Include raw public static methods/properties for matching types").option("--brief", "List names and descriptions only, omit args").option("--pretty", "Pretty-print JSON output").action(async (query, options) => {
|
|
3143
3397
|
const bridge = resolve_bridge_options(options);
|
|
3144
3398
|
const response = await call_editor({
|
|
3145
3399
|
...bridge,
|
|
@@ -3150,9 +3404,12 @@ program.command("list [query]").description("List runnable Unity commands and pr
|
|
|
3150
3404
|
args: build_registry_args([query || "", options.raw === true ? "true" : "false"])
|
|
3151
3405
|
}
|
|
3152
3406
|
});
|
|
3153
|
-
|
|
3407
|
+
if (options.brief === true && !response.error) {
|
|
3408
|
+
response.result = strip_command_listing_details(response.result);
|
|
3409
|
+
}
|
|
3410
|
+
output_rpc_response(response, options.pretty === true);
|
|
3154
3411
|
});
|
|
3155
|
-
program.command("run <target> [args...]").description("Run a named Unity command or raw static method/property").option("-p, --project <path>", "Path to Unity project (defaults to cwd)").option("--timeout <ms>", "WebSocket timeout in ms", "60000").option("--port <n>", "Connect to a specific bridge port").option("--args <json>", "JSON array of command arguments (overrides positional args)").option("--set <value>", "Set a static property value").option("--no-wait", "Fire and forget -- return immediately without waiting for result").action(async (target, args, options) => {
|
|
3412
|
+
program.command("run <target> [args...]").description("Run a named Unity command or raw static method/property").option("-p, --project <path>", "Path to Unity project (defaults to cwd)").option("--timeout <ms>", "WebSocket timeout in ms", "60000").option("--port <n>", "Connect to a specific bridge port").option("--args <json>", "JSON array of command arguments (overrides positional args)").option("--set <value>", "Set a static property value").option("--no-wait", "Fire and forget -- return immediately without waiting for result").option("--pretty", "Pretty-print JSON output").action(async (target, args, options) => {
|
|
3156
3413
|
const bridge = resolve_bridge_options(options);
|
|
3157
3414
|
const command_args_json = options.args || JSON.stringify(args);
|
|
3158
3415
|
const registry_args = options.set !== undefined ? [target, command_args_json, options.set] : [target, command_args_json];
|
|
@@ -3166,16 +3423,16 @@ program.command("run <target> [args...]").description("Run a named Unity command
|
|
|
3166
3423
|
args: build_registry_args(registry_args)
|
|
3167
3424
|
}
|
|
3168
3425
|
});
|
|
3169
|
-
output_rpc_response(response);
|
|
3426
|
+
output_rpc_response(response, options.pretty === true);
|
|
3170
3427
|
});
|
|
3171
3428
|
program.command("stream [topic]").description("Stream Unity bridge events over WebSocket (default: console)").option("-p, --project <path>", "Path to Unity project (defaults to cwd)").option("--timeout <ms>", "WebSocket timeout in ms", "30000").option("--port <n>", "Connect to a specific bridge port").option("-t, --type <type>", "Console log type filter").option("--duration <ms>", "Stop after duration in ms (0 = indefinite)", "0").option("--pretty", "Pretty-print JSON events").action(async (topic_raw, options) => {
|
|
3172
3429
|
const topic = (topic_raw || "console").toLowerCase();
|
|
3173
3430
|
const valid_topics = ["console", "events", "playmode", "tests"];
|
|
3174
3431
|
if (!valid_topics.includes(topic)) {
|
|
3175
|
-
|
|
3432
|
+
print_json({
|
|
3176
3433
|
success: false,
|
|
3177
3434
|
error: `Invalid stream topic "${topic}". Use: ${valid_topics.join(", ")}`
|
|
3178
|
-
},
|
|
3435
|
+
}, options.pretty === true);
|
|
3179
3436
|
process.exitCode = 1;
|
|
3180
3437
|
return;
|
|
3181
3438
|
}
|
|
@@ -3189,6 +3446,10 @@ program.command("stream [topic]").description("Stream Unity bridge events over W
|
|
|
3189
3446
|
if (event_matches_topic(event, topic, options.type)) {
|
|
3190
3447
|
print_stream_event(event, options.pretty === true);
|
|
3191
3448
|
}
|
|
3449
|
+
},
|
|
3450
|
+
on_error: (err) => {
|
|
3451
|
+
print_json({ success: false, error: err.message }, options.pretty === true);
|
|
3452
|
+
process.exit(1);
|
|
3192
3453
|
}
|
|
3193
3454
|
});
|
|
3194
3455
|
if (duration > 0) {
|
|
@@ -3203,43 +3464,53 @@ program.command("stream [topic]").description("Stream Unity bridge events over W
|
|
|
3203
3464
|
});
|
|
3204
3465
|
}
|
|
3205
3466
|
} catch (err) {
|
|
3206
|
-
|
|
3467
|
+
print_json({
|
|
3207
3468
|
success: false,
|
|
3208
3469
|
error: err instanceof Error ? err.message : String(err)
|
|
3209
|
-
},
|
|
3470
|
+
}, options.pretty === true);
|
|
3210
3471
|
process.exitCode = 1;
|
|
3211
3472
|
}
|
|
3212
3473
|
});
|
|
3213
|
-
program.command("install").description("Install the Unity command bridge package into a Unity project").option("-p, --project <path>", "Path to Unity project (defaults to cwd)").action((options) => {
|
|
3474
|
+
program.command("install").description("Install the Unity command bridge package into a Unity project").option("-p, --project <path>", "Path to Unity project (defaults to cwd)").option("--local [path]", "Install from a local unity-package path; auto-detects source checkout when omitted").option("--remote", "Force the published GitHub bridge package URL").option("--pretty", "Pretty-print JSON output").action((options) => {
|
|
3214
3475
|
const bridge = resolve_bridge_options(options);
|
|
3215
|
-
const
|
|
3476
|
+
const pretty = options.pretty === true;
|
|
3477
|
+
const install_options = resolve_install_options(options);
|
|
3478
|
+
if ("error" in install_options) {
|
|
3479
|
+
print_json({ success: false, error: install_options.error }, pretty);
|
|
3480
|
+
process.exitCode = 1;
|
|
3481
|
+
return;
|
|
3482
|
+
}
|
|
3483
|
+
const result = install_bridge_package(bridge.project_path, install_options);
|
|
3216
3484
|
if ("error" in result) {
|
|
3217
|
-
|
|
3485
|
+
print_json({ success: false, error: result.error }, pretty);
|
|
3218
3486
|
process.exitCode = 1;
|
|
3219
3487
|
return;
|
|
3220
3488
|
}
|
|
3221
|
-
|
|
3489
|
+
print_json(result, pretty);
|
|
3222
3490
|
});
|
|
3223
|
-
program.command("uninstall").description("Remove the Unity command bridge package from a Unity project").option("-p, --project <path>", "Path to Unity project (defaults to cwd)").action((options) => {
|
|
3491
|
+
program.command("uninstall").description("Remove the Unity command bridge package from a Unity project").option("-p, --project <path>", "Path to Unity project (defaults to cwd)").option("--pretty", "Pretty-print JSON output").action((options) => {
|
|
3224
3492
|
const bridge = resolve_bridge_options(options);
|
|
3493
|
+
const pretty = options.pretty === true;
|
|
3225
3494
|
const result = remove_package(bridge.project_path, BRIDGE_PACKAGE_NAME2);
|
|
3226
3495
|
if ("error" in result) {
|
|
3227
|
-
|
|
3496
|
+
print_json({ success: false, error: result.error }, pretty);
|
|
3228
3497
|
process.exitCode = 1;
|
|
3229
3498
|
return;
|
|
3230
3499
|
}
|
|
3231
|
-
|
|
3500
|
+
print_json(result, pretty);
|
|
3232
3501
|
});
|
|
3233
|
-
program.command("cleanup").description("Clean Unity Agentic Tools project state").option("-p, --project <path>", "Path to Unity project (defaults to cwd)").option("--stale", "Remove stale bridge lock state (default)").option("--cache", "Remove rebuildable project caches").option("--all", "Remove the entire .unity-agentic directory").action((options) => {
|
|
3502
|
+
program.command("cleanup").description("Clean Unity Agentic Tools project state").option("-p, --project <path>", "Path to Unity project (defaults to cwd)").option("--stale", "Remove stale bridge lock state (default)").option("--cache", "Remove rebuildable project caches").option("--all", "Remove the entire .unity-agentic directory").option("--pretty", "Pretty-print JSON output").action((options) => {
|
|
3234
3503
|
const result = cleanup(options);
|
|
3504
|
+
print_json(result, options.pretty === true);
|
|
3235
3505
|
if (!result.success) {
|
|
3236
|
-
console.log(JSON.stringify(result, null, 2));
|
|
3237
3506
|
process.exitCode = 1;
|
|
3238
|
-
return;
|
|
3239
3507
|
}
|
|
3240
|
-
console.log(JSON.stringify(result, null, 2));
|
|
3241
3508
|
});
|
|
3242
|
-
|
|
3509
|
+
async function describe_readiness(port, timeout_ms) {
|
|
3510
|
+
const readiness = await read_editor_readiness(port, timeout_ms);
|
|
3511
|
+
return "error" in readiness ? { readiness_error: readiness.error } : { readiness };
|
|
3512
|
+
}
|
|
3513
|
+
program.command("status").description("Show Unity command bridge status").option("-p, --project <path>", "Path to Unity project (defaults to current directory)").option("--timeout <ms>", "WebSocket timeout in ms", "2000").option("--port <n>", "Connect to a specific bridge port").option("--pretty", "Pretty-print JSON output").action(async (options) => {
|
|
3243
3514
|
const bridge = resolve_bridge_options(options);
|
|
3244
3515
|
const projectPath = bridge.project_path;
|
|
3245
3516
|
const status = {
|
|
@@ -3253,7 +3524,7 @@ program.command("status").description("Show Unity command bridge status").option
|
|
|
3253
3524
|
port: bridge.port,
|
|
3254
3525
|
source: "manual",
|
|
3255
3526
|
reachable: ping.reachable,
|
|
3256
|
-
...ping.reachable ?
|
|
3527
|
+
...ping.reachable ? await describe_readiness(bridge.port, bridge.timeout) : { error: ping.error }
|
|
3257
3528
|
};
|
|
3258
3529
|
} else {
|
|
3259
3530
|
const editor_config = await discover_editor_config(projectPath, bridge.timeout);
|
|
@@ -3271,11 +3542,11 @@ program.command("status").description("Show Unity command bridge status").option
|
|
|
3271
3542
|
source: editor_config.source ?? "lockfile",
|
|
3272
3543
|
project_path: editor_config.project_path,
|
|
3273
3544
|
reachable: ping.reachable,
|
|
3274
|
-
...ping.reachable ?
|
|
3545
|
+
...ping.reachable ? await describe_readiness(editor_config.port, bridge.timeout) : { error: ping.error }
|
|
3275
3546
|
};
|
|
3276
3547
|
}
|
|
3277
3548
|
}
|
|
3278
|
-
|
|
3549
|
+
print_json(status, options.pretty === true);
|
|
3279
3550
|
});
|
|
3280
3551
|
program.parse();
|
|
3281
3552
|
})
|