made-bridge 0.3.1 → 0.3.2
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/package.json +2 -2
- package/src/agent-sprites.mjs +21 -3
- package/src/board-link.mjs +4 -1
- package/src/plugins/contracts.d.ts +1 -1
- package/src/plugins/registry.mjs +3 -1
- package/src/server.mjs +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "made-bridge",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "码得(made)独立语音编程设备的电脑侧桥接器:工作台 API、设备接口、任务调度与插件运行时。一条命令启动,配合 ESP32 固件使用,源码在 github.com/jobop/made。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,4 +47,4 @@
|
|
|
47
47
|
"doctor": "node src/server.mjs --doctor",
|
|
48
48
|
"test": "node --test --experimental-test-isolation=none --test-concurrency=1"
|
|
49
49
|
}
|
|
50
|
-
}
|
|
50
|
+
}
|
package/src/agent-sprites.mjs
CHANGED
|
@@ -54,7 +54,7 @@ function readManifestFile(dir) {
|
|
|
54
54
|
|
|
55
55
|
function validateManifest(raw) {
|
|
56
56
|
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) throw new SpriteError('manifest 必须是对象');
|
|
57
|
-
if (Object.keys(raw).some((key) => !['version', 'canvas', 'layers', 'motion', 'emotions', 'actions'].includes(key))) {
|
|
57
|
+
if (Object.keys(raw).some((key) => !['version', 'canvas', 'layers', 'motion', 'emotions', 'actions', 'commands'].includes(key))) {
|
|
58
58
|
throw new SpriteError('manifest 包含不支持的字段');
|
|
59
59
|
}
|
|
60
60
|
if (!isInt(raw.version, 1, 2147483647)) throw new SpriteError('manifest.version 必须是正整数');
|
|
@@ -204,9 +204,27 @@ function validateManifest(raw) {
|
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
|
+
// 两级绑定模型(docs/SPRITES.md):commands 表(可选)定义「命令绑素材」
|
|
208
|
+
// ——抽象命令号 A1–A9 → actions 表动作名,纯编号不定语义。事件绑定(增强
|
|
209
|
+
// 插件)只发命令号;声明了本表的素材只认命令号,语义名直发废弃。
|
|
210
|
+
if (raw.commands !== undefined) {
|
|
211
|
+
const commands = raw.commands;
|
|
212
|
+
if (!commands || typeof commands !== 'object' || Array.isArray(commands)) {
|
|
213
|
+
throw new SpriteError('commands 必须是对象(A1–A9 → 动作名)');
|
|
214
|
+
}
|
|
215
|
+
const keys = Object.keys(commands);
|
|
216
|
+
if (keys.length > 9) throw new SpriteError('commands 最多 9 个命令号(A1–A9)');
|
|
217
|
+
const actionNames = new Set(Object.keys(raw.actions ?? {}));
|
|
218
|
+
for (const [slot, target] of Object.entries(commands)) {
|
|
219
|
+
if (!/^A[1-9]$/.test(slot)) throw new SpriteError(`commands.${slot} 命令号必须是 A1–A9`);
|
|
220
|
+
if (typeof target !== 'string' || !actionNames.has(target)) {
|
|
221
|
+
throw new SpriteError(`commands.${slot} 映射目标必须是 actions 表已声明的动作名`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
207
225
|
return {
|
|
208
226
|
version: raw.version, canvas: canvas, layers: raw.layers, motion: raw.motion ?? null,
|
|
209
|
-
emotions: raw.emotions ?? null, actions: raw.actions ?? null,
|
|
227
|
+
emotions: raw.emotions ?? null, actions: raw.actions ?? null, commands: raw.commands ?? null,
|
|
210
228
|
};
|
|
211
229
|
}
|
|
212
230
|
|
|
@@ -299,7 +317,7 @@ export function readSprite(packageDir) {
|
|
|
299
317
|
const value = Object.freeze({
|
|
300
318
|
dir: spriteDir, version: manifest.version, canvas: manifest.canvas,
|
|
301
319
|
layers: manifest.layers, motion: manifest.motion, emotions: manifest.emotions,
|
|
302
|
-
actions: manifest.actions,
|
|
320
|
+
actions: manifest.actions, commands: manifest.commands,
|
|
303
321
|
files: Object.freeze(files.map((file) => Object.freeze(file))),
|
|
304
322
|
});
|
|
305
323
|
cache.set(spriteDir, { stamp, value });
|
package/src/board-link.mjs
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
// 能力名:段 = 字母开头的 [A-Za-z0-9_]{0,15},1–3 段点分。大写段用于行动
|
|
2
|
+
// 系统的抽象命令号(action.A1–A9,两级绑定模型 docs/SPRITES.md);老语义
|
|
3
|
+
// 动作名与事件名全小写不受影响。
|
|
4
|
+
export const CAPABILITY_NAME = /^[a-zA-Z][a-zA-Z0-9_]{0,15}(?:\.[a-zA-Z][a-zA-Z0-9_]{0,15}){0,2}$/;
|
|
2
5
|
const FIELD_KEY = /^[a-z][a-z0-9_]{0,15}$/;
|
|
3
6
|
const COMMAND_ID = /^[1-9][0-9]{0,15}$/;
|
|
4
7
|
const MAX_ITEMS = 32;
|
|
@@ -159,7 +159,7 @@ export interface BridgeFacade {
|
|
|
159
159
|
cancelJob(id: string): Record<string, unknown>;
|
|
160
160
|
themes(): { name: string; title: string; sound: boolean; icon: boolean; background: boolean }[];
|
|
161
161
|
devices(): string[];
|
|
162
|
-
/** Action system:
|
|
162
|
+
/** Action system: command names derived from the device command catalog (`action.<name>` entries). Two-level binding model (docs/SPRITES.md): sprites declaring a `commands` map expose abstract slots `action.A1`–`action.A9` ("command binds artwork", decided by the sprite author); legacy sprites expose semantic action names (deprecated). Advisory only — the board silently ignores unknown action commands. */
|
|
163
163
|
deviceActions(deviceId: string): string[];
|
|
164
164
|
/** Enqueue a board command directly; only the board-side catalog constrains the name. */
|
|
165
165
|
send(deviceId: string, command: BoardCommand, audio?: Uint8Array): QueuedBoardCommand;
|
package/src/plugins/registry.mjs
CHANGED
|
@@ -33,7 +33,9 @@ export function validatePlugin(plugin) {
|
|
|
33
33
|
} else if (plugin.kind === 'board-plugin') {
|
|
34
34
|
const names = (key, min) => {
|
|
35
35
|
const list = plugin[key];
|
|
36
|
-
|
|
36
|
+
// 能力名与桥接器 CAPABILITY_NAME 同形(字母开头的段,1–3 段点分);
|
|
37
|
+
// 大写段用于行动系统的抽象命令号(action.A1–A9,docs/SPRITES.md)。
|
|
38
|
+
if (!Array.isArray(list) || list.length > 16 || list.length < min || list.some((name) => typeof name !== 'string' || !/^[a-zA-Z][a-zA-Z0-9_]{0,15}(?:\.[a-zA-Z][a-zA-Z0-9_]{0,15}){0,2}$/.test(name))) fail(`${key} 无效`);
|
|
37
39
|
if (new Set(list).size !== list.length) fail(`${key} 有重复项`);
|
|
38
40
|
};
|
|
39
41
|
names('events', 1);
|
package/src/server.mjs
CHANGED
|
@@ -966,6 +966,7 @@ export function createApp(config, { run, transcribe, tunnel: providedTunnel, ena
|
|
|
966
966
|
layers: plugin.sprite.layers, ...(plugin.sprite.motion ? { motion: plugin.sprite.motion } : {}),
|
|
967
967
|
...(plugin.sprite.emotions ? { emotions: plugin.sprite.emotions } : {}),
|
|
968
968
|
...(plugin.sprite.actions ? { actions: plugin.sprite.actions } : {}),
|
|
969
|
+
...(plugin.sprite.commands ? { commands: plugin.sprite.commands } : {}),
|
|
969
970
|
});
|
|
970
971
|
return;
|
|
971
972
|
}
|