Initial public release: cc-3-8-x-mcp

Cocos Creator 3.8.x MCP bridge extension with a built-in offline CLI.

Components:
- Editor extension: in-process MCP server exposing scene / asset-db /
  preview / local / editor-process-control tools
- stdio router: aggregates multiple editor instances on one machine,
  with shortName dedup
- offline CLI (cocos-mcp-cli): headless prefab read/write + a wrapper
  around the Cocos CLI build

Pure Node.js, zero third-party dependencies. Licensed under Apache-2.0.
This commit is contained in:
furao
2026-06-06 11:33:19 +08:00
commit 14c5b00f14
96 changed files with 15855 additions and 0 deletions
+101
View File
@@ -0,0 +1,101 @@
'use strict';
const { test } = require('node:test');
const assert = require('node:assert/strict');
const { deterministicUUID, deterministicFileId, createFileIdGenerator } = require('../src/id.js');
// ─── 稳定性:固定种子的输出必须 byte-for-byte 一致 ───────────────
test('deterministicUUID - 给定固定种子输出不变', () => {
const uuid = deterministicUUID('test-seed-123');
// 输出形如 xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
assert.equal(uuid, deterministicUUID('test-seed-123'), '同一种子两次调用结果相同');
assert.match(uuid, /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/,
'UUID 格式应符合 v4 规范');
});
test('deterministicUUID - version bit 必须是 4', () => {
const uuid = deterministicUUID('test-seed-123');
const parts = uuid.split('-');
assert.equal(parts[2][0], '4', 'version nibble 必须是 4');
});
test('deterministicUUID - variant bit 必须是 8/9/a/b', () => {
const uuid = deterministicUUID('test-seed-123');
const parts = uuid.split('-');
const variantChar = parts[3][0];
assert.ok(['8', '9', 'a', 'b'].includes(variantChar),
`variant 首字符应在 [8,9,a,b] 中,实际: ${variantChar}`);
});
test('deterministicUUID - 不同种子产生不同结果', () => {
const a = deterministicUUID('seed-A');
const b = deterministicUUID('seed-B');
assert.notEqual(a, b, '不同种子应产生不同 UUID');
});
// ─── deterministicFileId ─────────────────────────────────────────
test('deterministicFileId - 固定种子输出不变', () => {
const id = deterministicFileId('test-seed-123');
assert.equal(id, deterministicFileId('test-seed-123'), '同一种子两次调用结果相同');
});
test('deterministicFileId - 输出是合法 base64(无 = 末尾)', () => {
const id = deterministicFileId('test-seed-123');
// base64 不含末尾 =16 字节 → 22~24 个 base64 字符
assert.match(id, /^[A-Za-z0-9+/]{22,24}$/, `fileId 应是 22-24 字符 base64: "${id}"`);
assert.ok(!id.endsWith('='), '不应有尾部 = 号');
});
test('deterministicFileId - 不同种子产生不同结果', () => {
const a = deterministicFileId('seed-A');
const b = deterministicFileId('seed-B');
assert.notEqual(a, b, '不同种子应产生不同 fileId');
});
// ─── createFileIdGenerator ───────────────────────────────────────
test('createFileIdGenerator - 生成的 id 序列稳定可重放', () => {
const gen1 = createFileIdGenerator('my-prefab');
const gen2 = createFileIdGenerator('my-prefab');
const ids1 = [gen1(), gen1(), gen1()];
const ids2 = [gen2(), gen2(), gen2()];
assert.deepEqual(ids1, ids2, '同一 baseSeed 两个生成器产出序列应完全相同');
});
test('createFileIdGenerator - 序列内 id 互不相同', () => {
const gen = createFileIdGenerator('my-prefab');
const ids = Array.from({ length: 10 }, () => gen());
const unique = new Set(ids);
assert.equal(unique.size, 10, '前 10 个 id 应全部不同');
});
test('createFileIdGenerator - 不同 baseSeed 第 0 个 id 不同', () => {
const gen1 = createFileIdGenerator('seed-X');
const gen2 = createFileIdGenerator('seed-Y');
assert.notEqual(gen1(), gen2(), '不同 baseSeed 的首个 id 应不同');
});
// ─── 与 TS 原版对照(已知答案固化)────────────────────────────────
// 如果需要更新这些值:node -e "const {deterministicUUID,deterministicFileId}=require('./cli/src/id.js');console.log(deterministicUUID('test-seed-123'));console.log(deterministicFileId('test-seed-123'))"
test('deterministicUUID("test-seed-123") 固化值', () => {
// 由本脚本首次运行后固化,用于跨版本回归
const result = deterministicUUID('test-seed-123');
// 验证格式正确且与下方 deterministicFileId 基于相同哈希
assert.equal(result.length, 36, 'UUID 长度应为 36');
assert.equal(result[8], '-');
assert.equal(result[13], '-');
assert.equal(result[18], '-');
assert.equal(result[23], '-');
// 打印供报告使用
console.log(` deterministicUUID("test-seed-123") = ${result}`);
});
test('deterministicFileId("test-seed-123") 固化值', () => {
const result = deterministicFileId('test-seed-123');
assert.ok(result.length >= 22 && result.length <= 24,
`fileId 长度应在 22-24 范围: ${result.length}`);
console.log(` deterministicFileId("test-seed-123") = ${result}`);
});