uloop-cli 0.68.3 → 0.69.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.bundle.cjs +209 -113
- package/dist/cli.bundle.cjs.map +4 -4
- package/knip.json +4 -0
- package/package.json +4 -3
- package/src/__tests__/execute-tool.test.ts +10 -0
- package/src/__tests__/port-resolver.test.ts +68 -0
- package/src/__tests__/project-validator.test.ts +121 -0
- package/src/arg-parser.ts +0 -118
- package/src/cli.ts +24 -1
- package/src/compile-helpers.ts +5 -5
- package/src/default-tools.json +1 -1
- package/src/direct-unity-client.ts +8 -3
- package/src/execute-tool.ts +26 -0
- package/src/port-resolver.ts +39 -18
- package/src/project-root.ts +1 -1
- package/src/project-validator.ts +71 -0
- package/src/simple-framer.ts +2 -2
- package/src/skills/skills-manager.ts +9 -13
- package/src/skills/target-config.ts +1 -1
- package/src/spinner.ts +1 -1
- package/src/version.ts +1 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project validation for CLI.
|
|
3
|
+
* Verifies that the connected Unity instance belongs to the expected project.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Paths come from trusted Unity responses and validated project root, console.error for user warnings
|
|
7
|
+
/* eslint-disable security/detect-non-literal-fs-filename, no-console */
|
|
8
|
+
|
|
9
|
+
import assert from 'node:assert';
|
|
10
|
+
import { realpath } from 'fs/promises';
|
|
11
|
+
import { dirname } from 'path';
|
|
12
|
+
import { DirectUnityClient } from './direct-unity-client.js';
|
|
13
|
+
|
|
14
|
+
export class ProjectMismatchError extends Error {
|
|
15
|
+
constructor(
|
|
16
|
+
public readonly expectedProjectRoot: string,
|
|
17
|
+
public readonly connectedProjectRoot: string,
|
|
18
|
+
) {
|
|
19
|
+
super('PROJECT_MISMATCH');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface GetVersionResponse {
|
|
24
|
+
DataPath: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const JSON_RPC_METHOD_NOT_FOUND = -32601;
|
|
28
|
+
|
|
29
|
+
async function normalizePath(path: string): Promise<string> {
|
|
30
|
+
const resolved = await realpath(path);
|
|
31
|
+
return resolved.replace(/\/+$/, '');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function validateConnectedProject(
|
|
35
|
+
client: DirectUnityClient,
|
|
36
|
+
expectedProjectRoot: string,
|
|
37
|
+
): Promise<void> {
|
|
38
|
+
assert(client.isConnected(), 'client must be connected before validation');
|
|
39
|
+
|
|
40
|
+
let response: GetVersionResponse;
|
|
41
|
+
try {
|
|
42
|
+
response = await client.sendRequest<GetVersionResponse>('get-version', {});
|
|
43
|
+
} catch (error) {
|
|
44
|
+
// Method not found: old uLoopMCP version without get-version tool
|
|
45
|
+
if (
|
|
46
|
+
error instanceof Error &&
|
|
47
|
+
(error.message.includes(`${JSON_RPC_METHOD_NOT_FOUND}`) ||
|
|
48
|
+
/method not found/i.test(error.message) ||
|
|
49
|
+
/unknown tool/i.test(error.message))
|
|
50
|
+
) {
|
|
51
|
+
console.error(
|
|
52
|
+
'Warning: Could not verify project identity (get-version not available). Consider updating uLoopMCP package.',
|
|
53
|
+
);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (typeof response?.DataPath !== 'string' || response.DataPath.length === 0) {
|
|
60
|
+
console.error('Warning: Could not verify project identity (invalid get-version response).');
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const connectedProjectRoot = dirname(response.DataPath);
|
|
65
|
+
const normalizedExpected = await normalizePath(expectedProjectRoot);
|
|
66
|
+
const normalizedConnected = await normalizePath(connectedProjectRoot);
|
|
67
|
+
|
|
68
|
+
if (normalizedExpected !== normalizedConnected) {
|
|
69
|
+
throw new ProjectMismatchError(normalizedExpected, normalizedConnected);
|
|
70
|
+
}
|
|
71
|
+
}
|
package/src/simple-framer.ts
CHANGED
|
@@ -11,7 +11,7 @@ export function createFrame(jsonContent: string): string {
|
|
|
11
11
|
return `${CONTENT_LENGTH_HEADER} ${contentLength}${HEADER_SEPARATOR}${jsonContent}`;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
interface FrameParseResult {
|
|
15
15
|
contentLength: number;
|
|
16
16
|
headerLength: number;
|
|
17
17
|
isComplete: boolean;
|
|
@@ -43,7 +43,7 @@ export function parseFrameFromBuffer(data: Buffer): FrameParseResult {
|
|
|
43
43
|
return { contentLength, headerLength, isComplete };
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
interface FrameExtractionResult {
|
|
47
47
|
jsonContent: string | null;
|
|
48
48
|
remainingData: Buffer;
|
|
49
49
|
}
|
|
@@ -15,16 +15,16 @@ import { findUnityProjectRoot, getUnityProjectStatus } from '../project-root.js'
|
|
|
15
15
|
import { DEPRECATED_SKILLS } from './deprecated-skills.js';
|
|
16
16
|
import { loadDisabledTools } from '../tool-settings-loader.js';
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
type SkillStatus = 'installed' | 'not_installed' | 'outdated';
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
interface SkillInfo {
|
|
21
21
|
name: string;
|
|
22
22
|
status: SkillStatus;
|
|
23
23
|
path?: string;
|
|
24
24
|
source?: 'bundled' | 'project';
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
interface SkillDefinition {
|
|
28
28
|
name: string;
|
|
29
29
|
toolName?: string;
|
|
30
30
|
dirName: string;
|
|
@@ -132,7 +132,7 @@ function isSkillOutdated(skill: SkillDefinition, target: TargetConfig, global: b
|
|
|
132
132
|
return false;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
function getSkillStatus(
|
|
136
136
|
skill: SkillDefinition,
|
|
137
137
|
target: TargetConfig,
|
|
138
138
|
global: boolean,
|
|
@@ -290,7 +290,7 @@ function findEditorFolders(basePath: string, maxDepth: number = 2): string[] {
|
|
|
290
290
|
return editorFolders;
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
-
|
|
293
|
+
function collectProjectSkills(excludedRoots: string[] = []): SkillDefinition[] {
|
|
294
294
|
const projectRoot = findUnityProjectRoot();
|
|
295
295
|
if (!projectRoot) {
|
|
296
296
|
return [];
|
|
@@ -342,7 +342,7 @@ export function getAllSkillStatuses(target: TargetConfig, global: boolean): Skil
|
|
|
342
342
|
}));
|
|
343
343
|
}
|
|
344
344
|
|
|
345
|
-
|
|
345
|
+
function installSkill(skill: SkillDefinition, target: TargetConfig, global: boolean): void {
|
|
346
346
|
const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
347
347
|
const skillDir = join(baseDir, skill.dirName);
|
|
348
348
|
const skillPath = join(skillDir, target.skillFileName);
|
|
@@ -360,11 +360,7 @@ export function installSkill(skill: SkillDefinition, target: TargetConfig, globa
|
|
|
360
360
|
}
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
-
|
|
364
|
-
skill: SkillDefinition,
|
|
365
|
-
target: TargetConfig,
|
|
366
|
-
global: boolean,
|
|
367
|
-
): boolean {
|
|
363
|
+
function uninstallSkill(skill: SkillDefinition, target: TargetConfig, global: boolean): boolean {
|
|
368
364
|
const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
369
365
|
const skillDir = join(baseDir, skill.dirName);
|
|
370
366
|
|
|
@@ -376,7 +372,7 @@ export function uninstallSkill(
|
|
|
376
372
|
return true;
|
|
377
373
|
}
|
|
378
374
|
|
|
379
|
-
|
|
375
|
+
interface InstallResult {
|
|
380
376
|
installed: number;
|
|
381
377
|
updated: number;
|
|
382
378
|
skipped: number;
|
|
@@ -448,7 +444,7 @@ function isSkillDisabledByToolSettings(skill: SkillDefinition, disabledTools: st
|
|
|
448
444
|
return disabledTools.includes(toolName);
|
|
449
445
|
}
|
|
450
446
|
|
|
451
|
-
|
|
447
|
+
interface UninstallResult {
|
|
452
448
|
removed: number;
|
|
453
449
|
notFound: number;
|
|
454
450
|
}
|
package/src/spinner.ts
CHANGED
package/src/version.ts
CHANGED
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
* This file exists to avoid bundling the entire package.json into the CLI bundle.
|
|
5
5
|
* This version is automatically updated by release-please.
|
|
6
6
|
*/
|
|
7
|
-
export const VERSION = '0.
|
|
7
|
+
export const VERSION = '0.69.1'; // x-release-please-version
|