@world-engines/authoring-bridge 0.1.0-alpha.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/LICENSE ADDED
@@ -0,0 +1,46 @@
1
+ WorldEngine 官方作者工具许可证
2
+
3
+ 版权所有 (c) 2026 Nixdorfer。保留所有权利。
4
+
5
+ 本仓库的源代码、构建材料及附带资源(统称“本作品”)不是开源软件,
6
+ 不适用任何 OSI 认证的开源许可证。
7
+
8
+ 一、官方作者工具分发许可
9
+
10
+ 版权所有者可以通过其官方网站、npm registry、签名安装包或其他官方渠道,
11
+ 复制并分发由本作品构建的 WorldEngine 作者工具及其必要运行资源
12
+ (统称“官方作者工具”)。仅版权所有者或其书面指定的发布者享有此分发权。
13
+
14
+ 二、作者用户许可
15
+
16
+ 从官方渠道取得官方作者工具的作者用户,可以:
17
+
18
+ 1. 安装和运行官方作者工具;
19
+ 2. 使用官方作者工具创作、编辑、预览、导入、导出和提交其有权处理的内容;
20
+ 3. 为上述使用目的制作合理必要的本地备份副本。
21
+
22
+ 三、未授予的权利
23
+
24
+ 除第二条明确允许的行为外,本许可证不授予作者用户或其他第三方以下权利:
25
+
26
+ 1. 修改、改编、反编译、反汇编或制作官方作者工具的派生作品;
27
+ 2. 复制、镜像、转发、再上传、出售、出租、再分发或再许可官方作者工具;
28
+ 3. 使用本作品的源代码、构建材料或任何部分开发、提供或训练其他产品或服务;
29
+ 4. 删除或规避版权、许可、签名、访问控制或其他权利管理信息。
30
+
31
+ 法律强制允许且合同不得排除的权利不受上述限制影响。
32
+
33
+ 四、源代码查看
34
+
35
+ 第三方可以在已获合法访问权限的范围内查看本作品,用于审查、安全研究或学习参考;
36
+ 查看不授予运行、复制、修改、分发、再许可或用于其他产品与服务的权利。
37
+
38
+ 五、无担保与责任限制
39
+
40
+ 本作品与官方作者工具均按“现状”提供,不附带任何明示或暗示的担保。
41
+ 在适用法律允许的最大范围内,版权所有者不对因访问或使用本作品或官方作者工具
42
+ 造成的任何损失承担责任。
43
+
44
+ 六、终止
45
+
46
+ 违反本许可证将立即终止相应的访问与使用权;终止不影响版权所有者已经产生的权利和救济。
@@ -0,0 +1,235 @@
1
+ import { type AuthoringJsonSchema } from "./schemas.js";
2
+ export type AuthoringToolDomain = "world" | "trigger" | "layout" | "retrieval" | "human" | "preview";
3
+ export interface AuthoringToolDescriptor<Name extends string = string> {
4
+ readonly name: Name;
5
+ readonly domain: AuthoringToolDomain;
6
+ /** 只有修改持久化 Scenario Part 的工具才是 project effect。 */
7
+ readonly project_effect: boolean;
8
+ readonly input_schema: AuthoringJsonSchema;
9
+ }
10
+ declare const AUTHORING_BRIDGE_TOOL_CORES: readonly [{
11
+ readonly name: "world_schema";
12
+ readonly domain: "world";
13
+ readonly project_effect: false;
14
+ }, {
15
+ readonly name: "spatial_template_library";
16
+ readonly domain: "world";
17
+ readonly project_effect: false;
18
+ }, {
19
+ readonly name: "world_search";
20
+ readonly domain: "world";
21
+ readonly project_effect: false;
22
+ }, {
23
+ readonly name: "world_get";
24
+ readonly domain: "world";
25
+ readonly project_effect: false;
26
+ }, {
27
+ readonly name: "world_traverse";
28
+ readonly domain: "world";
29
+ readonly project_effect: false;
30
+ }, {
31
+ readonly name: "world_apply";
32
+ readonly domain: "world";
33
+ readonly project_effect: true;
34
+ }, {
35
+ readonly name: "world_history";
36
+ readonly domain: "world";
37
+ readonly project_effect: false;
38
+ }, {
39
+ readonly name: "trigger_schema";
40
+ readonly domain: "trigger";
41
+ readonly project_effect: false;
42
+ }, {
43
+ readonly name: "trigger_read";
44
+ readonly domain: "trigger";
45
+ readonly project_effect: false;
46
+ }, {
47
+ readonly name: "trigger_apply";
48
+ readonly domain: "trigger";
49
+ readonly project_effect: true;
50
+ }, {
51
+ readonly name: "trigger_validate";
52
+ readonly domain: "trigger";
53
+ readonly project_effect: false;
54
+ }, {
55
+ readonly name: "trigger_simulate";
56
+ readonly domain: "trigger";
57
+ readonly project_effect: false;
58
+ }, {
59
+ readonly name: "interaction_layout_read";
60
+ readonly domain: "layout";
61
+ readonly project_effect: false;
62
+ }, {
63
+ readonly name: "interaction_layout_apply";
64
+ readonly domain: "layout";
65
+ readonly project_effect: true;
66
+ }, {
67
+ readonly name: "trigger_blueprint_layout_read";
68
+ readonly domain: "layout";
69
+ readonly project_effect: false;
70
+ }, {
71
+ readonly name: "trigger_blueprint_layout_apply";
72
+ readonly domain: "layout";
73
+ readonly project_effect: true;
74
+ }, {
75
+ readonly name: "retrieval_config_read";
76
+ readonly domain: "retrieval";
77
+ readonly project_effect: false;
78
+ }, {
79
+ readonly name: "retrieval_config_apply";
80
+ readonly domain: "retrieval";
81
+ readonly project_effect: true;
82
+ }, {
83
+ readonly name: "retrieval_probe";
84
+ readonly domain: "retrieval";
85
+ readonly project_effect: false;
86
+ }, {
87
+ readonly name: "ask_user";
88
+ readonly domain: "human";
89
+ readonly project_effect: false;
90
+ }, {
91
+ readonly name: "preview_inspect";
92
+ readonly domain: "preview";
93
+ readonly project_effect: false;
94
+ }, {
95
+ readonly name: "preview_interact";
96
+ readonly domain: "preview";
97
+ readonly project_effect: false;
98
+ }];
99
+ export type AuthoringToolName = (typeof AUTHORING_BRIDGE_TOOL_CORES)[number]["name"];
100
+ export declare const AUTHORING_BRIDGE_TOOLS: Readonly<{
101
+ input_schema: AuthoringJsonSchema;
102
+ name: "world_schema";
103
+ domain: "world";
104
+ project_effect: false;
105
+ } | {
106
+ input_schema: AuthoringJsonSchema;
107
+ name: "spatial_template_library";
108
+ domain: "world";
109
+ project_effect: false;
110
+ } | {
111
+ input_schema: AuthoringJsonSchema;
112
+ name: "world_search";
113
+ domain: "world";
114
+ project_effect: false;
115
+ } | {
116
+ input_schema: AuthoringJsonSchema;
117
+ name: "world_get";
118
+ domain: "world";
119
+ project_effect: false;
120
+ } | {
121
+ input_schema: AuthoringJsonSchema;
122
+ name: "world_traverse";
123
+ domain: "world";
124
+ project_effect: false;
125
+ } | {
126
+ input_schema: AuthoringJsonSchema;
127
+ name: "world_apply";
128
+ domain: "world";
129
+ project_effect: true;
130
+ } | {
131
+ input_schema: AuthoringJsonSchema;
132
+ name: "world_history";
133
+ domain: "world";
134
+ project_effect: false;
135
+ } | {
136
+ input_schema: AuthoringJsonSchema;
137
+ name: "trigger_schema";
138
+ domain: "trigger";
139
+ project_effect: false;
140
+ } | {
141
+ input_schema: AuthoringJsonSchema;
142
+ name: "trigger_read";
143
+ domain: "trigger";
144
+ project_effect: false;
145
+ } | {
146
+ input_schema: AuthoringJsonSchema;
147
+ name: "trigger_apply";
148
+ domain: "trigger";
149
+ project_effect: true;
150
+ } | {
151
+ input_schema: AuthoringJsonSchema;
152
+ name: "trigger_validate";
153
+ domain: "trigger";
154
+ project_effect: false;
155
+ } | {
156
+ input_schema: AuthoringJsonSchema;
157
+ name: "trigger_simulate";
158
+ domain: "trigger";
159
+ project_effect: false;
160
+ } | {
161
+ input_schema: AuthoringJsonSchema;
162
+ name: "interaction_layout_read";
163
+ domain: "layout";
164
+ project_effect: false;
165
+ } | {
166
+ input_schema: AuthoringJsonSchema;
167
+ name: "interaction_layout_apply";
168
+ domain: "layout";
169
+ project_effect: true;
170
+ } | {
171
+ input_schema: AuthoringJsonSchema;
172
+ name: "trigger_blueprint_layout_read";
173
+ domain: "layout";
174
+ project_effect: false;
175
+ } | {
176
+ input_schema: AuthoringJsonSchema;
177
+ name: "trigger_blueprint_layout_apply";
178
+ domain: "layout";
179
+ project_effect: true;
180
+ } | {
181
+ input_schema: AuthoringJsonSchema;
182
+ name: "retrieval_config_read";
183
+ domain: "retrieval";
184
+ project_effect: false;
185
+ } | {
186
+ input_schema: AuthoringJsonSchema;
187
+ name: "retrieval_config_apply";
188
+ domain: "retrieval";
189
+ project_effect: true;
190
+ } | {
191
+ input_schema: AuthoringJsonSchema;
192
+ name: "retrieval_probe";
193
+ domain: "retrieval";
194
+ project_effect: false;
195
+ } | {
196
+ input_schema: AuthoringJsonSchema;
197
+ name: "ask_user";
198
+ domain: "human";
199
+ project_effect: false;
200
+ } | {
201
+ input_schema: AuthoringJsonSchema;
202
+ name: "preview_inspect";
203
+ domain: "preview";
204
+ project_effect: false;
205
+ } | {
206
+ input_schema: AuthoringJsonSchema;
207
+ name: "preview_interact";
208
+ domain: "preview";
209
+ project_effect: false;
210
+ }>[];
211
+ /** exact-22 的唯一名称投影;Adapter 不得另行维护平行列表。 */
212
+ export declare const AUTHORING_BRIDGE_TOOL_NAMES: readonly AuthoringToolName[];
213
+ export declare function authoringToolDescriptor(name: AuthoringToolName): (typeof AUTHORING_BRIDGE_TOOLS)[number];
214
+ export interface AuthoringEffectIdentity {
215
+ readonly operation_id: string;
216
+ readonly expected_project_revision: number;
217
+ }
218
+ export declare function validateAuthoringEffectIdentity(identity: AuthoringEffectIdentity): AuthoringEffectIdentity;
219
+ export interface AuthoringToolReceiptV1 {
220
+ readonly schema_version: 1;
221
+ readonly operation_id: string;
222
+ readonly project_id: string;
223
+ readonly previous_project_revision: number;
224
+ readonly next_project_revision: number;
225
+ readonly changed_sections: readonly string[];
226
+ readonly project_snapshot_digest: string;
227
+ readonly readback_verified: boolean;
228
+ }
229
+ export interface AuthoringToolCall<Name extends AuthoringToolName = AuthoringToolName> {
230
+ readonly name: Name;
231
+ readonly input: Readonly<Record<string, unknown>>;
232
+ readonly effect_identity?: AuthoringEffectIdentity;
233
+ }
234
+ export declare function validateAuthoringToolCall(call: AuthoringToolCall): AuthoringToolCall;
235
+ export {};
@@ -0,0 +1,67 @@
1
+ import { AuthoringBridgeError } from "./errors.js";
2
+ import { AUTHORING_TOOL_INPUT_SCHEMAS, validateAuthoringToolInput, } from "./schemas.js";
3
+ const AUTHORING_BRIDGE_TOOL_CORES = [
4
+ { name: "world_schema", domain: "world", project_effect: false },
5
+ { name: "spatial_template_library", domain: "world", project_effect: false },
6
+ { name: "world_search", domain: "world", project_effect: false },
7
+ { name: "world_get", domain: "world", project_effect: false },
8
+ { name: "world_traverse", domain: "world", project_effect: false },
9
+ { name: "world_apply", domain: "world", project_effect: true },
10
+ { name: "world_history", domain: "world", project_effect: false },
11
+ { name: "trigger_schema", domain: "trigger", project_effect: false },
12
+ { name: "trigger_read", domain: "trigger", project_effect: false },
13
+ { name: "trigger_apply", domain: "trigger", project_effect: true },
14
+ { name: "trigger_validate", domain: "trigger", project_effect: false },
15
+ { name: "trigger_simulate", domain: "trigger", project_effect: false },
16
+ { name: "interaction_layout_read", domain: "layout", project_effect: false },
17
+ { name: "interaction_layout_apply", domain: "layout", project_effect: true },
18
+ { name: "trigger_blueprint_layout_read", domain: "layout", project_effect: false },
19
+ { name: "trigger_blueprint_layout_apply", domain: "layout", project_effect: true },
20
+ { name: "retrieval_config_read", domain: "retrieval", project_effect: false },
21
+ { name: "retrieval_config_apply", domain: "retrieval", project_effect: true },
22
+ { name: "retrieval_probe", domain: "retrieval", project_effect: false },
23
+ { name: "ask_user", domain: "human", project_effect: false },
24
+ { name: "preview_inspect", domain: "preview", project_effect: false },
25
+ { name: "preview_interact", domain: "preview", project_effect: false },
26
+ ];
27
+ export const AUTHORING_BRIDGE_TOOLS = AUTHORING_BRIDGE_TOOL_CORES.map((descriptor) => Object.freeze({
28
+ ...descriptor,
29
+ input_schema: AUTHORING_TOOL_INPUT_SCHEMAS[descriptor.name],
30
+ }));
31
+ /** exact-22 的唯一名称投影;Adapter 不得另行维护平行列表。 */
32
+ export const AUTHORING_BRIDGE_TOOL_NAMES = Object.freeze(AUTHORING_BRIDGE_TOOLS.map(({ name }) => name));
33
+ const DESCRIPTORS = new Map(AUTHORING_BRIDGE_TOOLS.map((descriptor) => [descriptor.name, descriptor]));
34
+ export function authoringToolDescriptor(name) {
35
+ const descriptor = DESCRIPTORS.get(name);
36
+ if (descriptor === undefined) {
37
+ throw new AuthoringBridgeError("E_SCHEMA_INVALID", `未知 AuthoringBridge tool:${name}`);
38
+ }
39
+ return descriptor;
40
+ }
41
+ export function validateAuthoringEffectIdentity(identity) {
42
+ if (typeof identity.operation_id !== "string" || identity.operation_id.trim().length === 0) {
43
+ throw new AuthoringBridgeError("E_SCHEMA_INVALID", "operation_id 不能为空");
44
+ }
45
+ if (!Number.isSafeInteger(identity.expected_project_revision) ||
46
+ identity.expected_project_revision < 0) {
47
+ throw new AuthoringBridgeError("E_SCHEMA_INVALID", "expected_project_revision 必须是非负安全整数");
48
+ }
49
+ return {
50
+ operation_id: identity.operation_id,
51
+ expected_project_revision: identity.expected_project_revision,
52
+ };
53
+ }
54
+ export function validateAuthoringToolCall(call) {
55
+ const descriptor = authoringToolDescriptor(call.name);
56
+ validateAuthoringToolInput(call.name, call.input);
57
+ if (descriptor.project_effect) {
58
+ if (call.effect_identity === undefined) {
59
+ throw new AuthoringBridgeError("E_SCHEMA_INVALID", `${call.name} 必须提供 effect_identity`);
60
+ }
61
+ validateAuthoringEffectIdentity(call.effect_identity);
62
+ }
63
+ else if (call.effect_identity !== undefined) {
64
+ throw new AuthoringBridgeError("E_SCHEMA_INVALID", `${call.name} 是只读或运行时工具,不接受 effect_identity`);
65
+ }
66
+ return call;
67
+ }
@@ -0,0 +1,23 @@
1
+ import { type AuthoringEffectIdentity, type AuthoringToolCall, type AuthoringToolDescriptor, type AuthoringToolName } from "./contract.js";
2
+ export interface AuthoringToolDispatchContext {
3
+ readonly descriptor: AuthoringToolDescriptor;
4
+ readonly call: AuthoringToolCall;
5
+ readonly effect_identity: AuthoringEffectIdentity | undefined;
6
+ }
7
+ export type AuthoringToolHandler = (context: AuthoringToolDispatchContext) => Promise<unknown> | unknown;
8
+ export type AuthoringToolHandlerMap = Partial<Record<AuthoringToolName, AuthoringToolHandler>>;
9
+ /** Adapter 必须显式声明每一个可用工具;未声明即拒绝分发。 */
10
+ export interface AuthoringBridgeAdapter {
11
+ readonly capabilities: ReadonlySet<AuthoringToolName> | readonly AuthoringToolName[];
12
+ readonly handlers: AuthoringToolHandlerMap;
13
+ }
14
+ /**
15
+ * exact-21 的唯一分发入口。它不提供 Ladybug、Trigger 或远端实现,只校验并调用 adapter。
16
+ */
17
+ export declare class AuthoringBridgeDispatcher {
18
+ #private;
19
+ private readonly adapter;
20
+ constructor(adapter: AuthoringBridgeAdapter);
21
+ dispatch(call: AuthoringToolCall): Promise<unknown>;
22
+ }
23
+ export declare function createAuthoringBridgeDispatcher(adapter: AuthoringBridgeAdapter): AuthoringBridgeDispatcher;
@@ -0,0 +1,97 @@
1
+ import { authoringToolDescriptor, validateAuthoringToolCall, } from "./contract.js";
2
+ import { AuthoringBridgeError } from "./errors.js";
3
+ function canonicalJson(value) {
4
+ if (value === null || typeof value !== "object")
5
+ return JSON.stringify(value);
6
+ if (Array.isArray(value))
7
+ return `[${value.map(canonicalJson).join(",")}]`;
8
+ const record = value;
9
+ return `{${Object.keys(record)
10
+ .sort()
11
+ .map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`)
12
+ .join(",")}}`;
13
+ }
14
+ function hasCapability(adapter, name) {
15
+ if ("has" in adapter.capabilities)
16
+ return adapter.capabilities.has(name);
17
+ return adapter.capabilities.includes(name);
18
+ }
19
+ function unavailable(kind, name) {
20
+ throw new AuthoringBridgeError("E_VALIDATION_FAILED", `${kind}: adapter 未提供 ${name}`, {
21
+ failure: kind,
22
+ tool_name: name,
23
+ });
24
+ }
25
+ function validateReceipt(receipt, identity) {
26
+ if (typeof receipt !== "object" || receipt === null || Array.isArray(receipt)) {
27
+ throw new AuthoringBridgeError("E_VALIDATION_FAILED", "E_EFFECT_RECEIPT_INVALID: apply handler 未返回 receipt");
28
+ }
29
+ const value = receipt;
30
+ const changedSections = value.changed_sections;
31
+ const validSections = Array.isArray(changedSections) &&
32
+ changedSections.every((section) => typeof section === "string" && section.length > 0) &&
33
+ new Set(changedSections).size === changedSections.length;
34
+ const changed = changedSections?.length ?? 0;
35
+ const expectedNext = identity.expected_project_revision + (changed === 0 ? 0 : 1);
36
+ if (value.schema_version !== 1 ||
37
+ value.operation_id !== identity.operation_id ||
38
+ typeof value.project_id !== "string" ||
39
+ value.project_id.trim().length === 0 ||
40
+ value.previous_project_revision !== identity.expected_project_revision ||
41
+ value.next_project_revision !== expectedNext ||
42
+ !validSections ||
43
+ typeof value.project_snapshot_digest !== "string" ||
44
+ !/^[a-f0-9]{64}$/i.test(value.project_snapshot_digest) ||
45
+ value.readback_verified !== true) {
46
+ throw new AuthoringBridgeError("E_VALIDATION_FAILED", "E_EFFECT_RECEIPT_INVALID: apply receipt 未通过 identity/readback 校验", {
47
+ operation_id: identity.operation_id,
48
+ });
49
+ }
50
+ return value;
51
+ }
52
+ /**
53
+ * exact-21 的唯一分发入口。它不提供 Ladybug、Trigger 或远端实现,只校验并调用 adapter。
54
+ */
55
+ export class AuthoringBridgeDispatcher {
56
+ adapter;
57
+ #operations = new Map();
58
+ constructor(adapter) {
59
+ this.adapter = adapter;
60
+ }
61
+ async dispatch(call) {
62
+ validateAuthoringToolCall(call);
63
+ const descriptor = authoringToolDescriptor(call.name);
64
+ if (!hasCapability(this.adapter, call.name))
65
+ unavailable("E_CAPABILITY_UNAVAILABLE", call.name);
66
+ const handler = this.adapter.handlers[call.name];
67
+ if (handler === undefined)
68
+ unavailable("E_HANDLER_UNAVAILABLE", call.name);
69
+ const effectIdentity = call.effect_identity;
70
+ if (!descriptor.project_effect) {
71
+ return handler({ descriptor, call, effect_identity: undefined });
72
+ }
73
+ const identity = effectIdentity;
74
+ const fingerprint = canonicalJson({
75
+ name: call.name,
76
+ input: call.input,
77
+ effect_identity: identity,
78
+ });
79
+ const replay = this.#operations.get(identity.operation_id);
80
+ if (replay !== undefined) {
81
+ if (replay.fingerprint !== fingerprint) {
82
+ throw new AuthoringBridgeError("E_OPERATION_ID_CONFLICT", "operation_id 已绑定到不同的 canonical call", {
83
+ operation_id: identity.operation_id,
84
+ });
85
+ }
86
+ return replay.result;
87
+ }
88
+ const result = Promise.resolve()
89
+ .then(() => handler({ descriptor, call, effect_identity: identity }))
90
+ .then((receipt) => validateReceipt(receipt, identity));
91
+ this.#operations.set(identity.operation_id, { fingerprint, result });
92
+ return result;
93
+ }
94
+ }
95
+ export function createAuthoringBridgeDispatcher(adapter) {
96
+ return new AuthoringBridgeDispatcher(adapter);
97
+ }
@@ -0,0 +1,7 @@
1
+ export declare const AUTHORING_BRIDGE_ERROR_CODES: readonly ["E_PROJECT_NOT_OPEN", "E_PROJECT_BUSY", "E_PATH_OUTSIDE_PROJECT", "E_STALE_PROJECT_REVISION", "E_SCHEMA_INVALID", "E_VALIDATION_FAILED", "E_REFERENCE_MISSING", "E_OPERATION_ID_CONFLICT", "E_OPERATION_UNCERTAIN", "E_PREVIEW_NOT_RUNNING", "E_PREVIEW_NOT_READY", "E_AUTH_REQUIRED", "E_REMOTE_FORBIDDEN", "E_CONFIRMATION_REQUIRED", "E_ROLLBACK_FAILED"];
2
+ export type AuthoringBridgeErrorCode = (typeof AUTHORING_BRIDGE_ERROR_CODES)[number];
3
+ export declare class AuthoringBridgeError extends Error {
4
+ readonly code: AuthoringBridgeErrorCode;
5
+ readonly details: Readonly<Record<string, unknown>> | undefined;
6
+ constructor(code: AuthoringBridgeErrorCode, message: string, details?: Readonly<Record<string, unknown>>);
7
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,27 @@
1
+ export const AUTHORING_BRIDGE_ERROR_CODES = [
2
+ "E_PROJECT_NOT_OPEN",
3
+ "E_PROJECT_BUSY",
4
+ "E_PATH_OUTSIDE_PROJECT",
5
+ "E_STALE_PROJECT_REVISION",
6
+ "E_SCHEMA_INVALID",
7
+ "E_VALIDATION_FAILED",
8
+ "E_REFERENCE_MISSING",
9
+ "E_OPERATION_ID_CONFLICT",
10
+ "E_OPERATION_UNCERTAIN",
11
+ "E_PREVIEW_NOT_RUNNING",
12
+ "E_PREVIEW_NOT_READY",
13
+ "E_AUTH_REQUIRED",
14
+ "E_REMOTE_FORBIDDEN",
15
+ "E_CONFIRMATION_REQUIRED",
16
+ "E_ROLLBACK_FAILED",
17
+ ];
18
+ export class AuthoringBridgeError extends Error {
19
+ code;
20
+ details;
21
+ constructor(code, message, details) {
22
+ super(message);
23
+ this.name = "AuthoringBridgeError";
24
+ this.code = code;
25
+ this.details = details;
26
+ }
27
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./contract.js";
2
+ export * from "./dispatcher.js";
3
+ export * from "./errors.js";
4
+ export * from "./schemas.js";
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./contract.js";
2
+ export * from "./dispatcher.js";
3
+ export * from "./errors.js";
4
+ export * from "./schemas.js";
@@ -0,0 +1,58 @@
1
+ export interface AuthoringJsonSchema {
2
+ readonly type: "object" | "array" | "string" | "integer" | "boolean";
3
+ readonly required?: readonly string[];
4
+ readonly properties?: Readonly<Record<string, AuthoringJsonSchema>>;
5
+ readonly additionalProperties?: boolean;
6
+ readonly items?: AuthoringJsonSchema;
7
+ readonly enum?: readonly (string | number | boolean)[];
8
+ readonly oneOf?: readonly AuthoringJsonSchema[];
9
+ readonly minimum?: number;
10
+ readonly maximum?: number;
11
+ readonly minItems?: number;
12
+ readonly minLength?: number;
13
+ }
14
+ /** world_apply 内的 spatial mutation 仍由现有第 21 个 tool 承载,不能另起 tool authority。 */
15
+ export type SpatialWorldApplyOperation = {
16
+ readonly type: "spatial_create";
17
+ readonly key: string;
18
+ readonly kind: "location" | "portal" | "region";
19
+ readonly label: string;
20
+ readonly x: number;
21
+ readonly y: number;
22
+ } | {
23
+ readonly type: "spatial_update";
24
+ readonly id: string;
25
+ readonly label?: string;
26
+ readonly x?: number;
27
+ readonly y?: number;
28
+ } | {
29
+ readonly type: "spatial_remove";
30
+ readonly id: string;
31
+ readonly confirmed: boolean;
32
+ };
33
+ export declare const AUTHORING_TOOL_INPUT_SCHEMAS: Readonly<{
34
+ readonly world_schema: AuthoringJsonSchema;
35
+ readonly spatial_template_library: AuthoringJsonSchema;
36
+ readonly world_search: AuthoringJsonSchema;
37
+ readonly world_get: AuthoringJsonSchema;
38
+ readonly world_traverse: AuthoringJsonSchema;
39
+ readonly world_apply: AuthoringJsonSchema;
40
+ readonly world_history: AuthoringJsonSchema;
41
+ readonly trigger_schema: AuthoringJsonSchema;
42
+ readonly trigger_read: AuthoringJsonSchema;
43
+ readonly trigger_apply: AuthoringJsonSchema;
44
+ readonly trigger_validate: AuthoringJsonSchema;
45
+ readonly trigger_simulate: AuthoringJsonSchema;
46
+ readonly interaction_layout_read: AuthoringJsonSchema;
47
+ readonly interaction_layout_apply: AuthoringJsonSchema;
48
+ readonly trigger_blueprint_layout_read: AuthoringJsonSchema;
49
+ readonly trigger_blueprint_layout_apply: AuthoringJsonSchema;
50
+ readonly retrieval_config_read: AuthoringJsonSchema;
51
+ readonly retrieval_config_apply: AuthoringJsonSchema;
52
+ readonly retrieval_probe: AuthoringJsonSchema;
53
+ readonly ask_user: AuthoringJsonSchema;
54
+ readonly preview_inspect: AuthoringJsonSchema;
55
+ readonly preview_interact: AuthoringJsonSchema;
56
+ }>;
57
+ export type AuthoringSchemaToolName = keyof typeof AUTHORING_TOOL_INPUT_SCHEMAS;
58
+ export declare function validateAuthoringToolInput(name: AuthoringSchemaToolName, input: unknown): Readonly<Record<string, unknown>>;
@@ -0,0 +1,190 @@
1
+ import { AuthoringBridgeError } from "./errors.js";
2
+ const string = (options = {}) => ({
3
+ type: "string",
4
+ ...options,
5
+ });
6
+ const integer = (minimum = 0, maximum) => ({ type: "integer", minimum, ...(maximum === undefined ? {} : { maximum }) });
7
+ const array = (items, minItems = 0) => ({
8
+ type: "array",
9
+ items,
10
+ minItems,
11
+ });
12
+ const opaqueObject = { type: "object", additionalProperties: true };
13
+ const object = (properties, required = []) => ({
14
+ type: "object",
15
+ properties,
16
+ required,
17
+ additionalProperties: false,
18
+ });
19
+ const worldGetProperties = {
20
+ ids: array(string({ minLength: 1 }), 1),
21
+ limit: integer(1, 200),
22
+ cursor: string({ minLength: 1 }),
23
+ include_relations: { type: "boolean" },
24
+ };
25
+ const worldGetSchema = {
26
+ ...object(worldGetProperties),
27
+ oneOf: [
28
+ object({ ids: worldGetProperties.ids, include_relations: worldGetProperties.include_relations }, ["ids"]),
29
+ object({ limit: worldGetProperties.limit, cursor: worldGetProperties.cursor, include_relations: worldGetProperties.include_relations }, ["limit"]),
30
+ ],
31
+ };
32
+ export const AUTHORING_TOOL_INPUT_SCHEMAS = Object.freeze({
33
+ world_schema: object({}),
34
+ spatial_template_library: object({}),
35
+ world_search: object({
36
+ query: string({ minLength: 1 }),
37
+ mode: string({ enum: ["keyword", "full_text", "semantic", "hybrid"] }),
38
+ limit: integer(1),
39
+ cursor: string(),
40
+ filters: opaqueObject,
41
+ }, ["query", "mode"]),
42
+ world_get: worldGetSchema,
43
+ world_traverse: object({
44
+ start_ids: array(string({ minLength: 1 }), 1),
45
+ scope_id: string({ minLength: 1 }),
46
+ direction: string({ enum: ["in", "out", "both"] }),
47
+ depth: integer(1, 4),
48
+ entity_kinds: array(string({ minLength: 1 })),
49
+ relation_kinds: array(string({ minLength: 1 })),
50
+ semantic_only: { type: "boolean" },
51
+ limit: integer(1, 1_000),
52
+ }, ["start_ids", "direction", "depth"]),
53
+ world_apply: object({ operations: array(opaqueObject, 1) }, ["operations"]),
54
+ world_history: object({ target_ids: array(string({ minLength: 1 }), 1), cursor: string(), limit: integer(1) }, ["target_ids"]),
55
+ trigger_schema: object({}),
56
+ trigger_read: object({ trigger_ids: array(string({ minLength: 1 })), include_document: { type: "boolean" } }),
57
+ trigger_apply: object({ operations: array(opaqueObject, 1) }, ["operations"]),
58
+ trigger_validate: object({ candidate_document: opaqueObject }),
59
+ trigger_simulate: object({ event: opaqueObject, state: opaqueObject }, ["event", "state"]),
60
+ interaction_layout_read: object({ viewport: string({ enum: ["pc", "mobile"] }) }, ["viewport"]),
61
+ interaction_layout_apply: object({ viewport: string({ enum: ["pc", "mobile"] }), patch: opaqueObject }, ["viewport", "patch"]),
62
+ trigger_blueprint_layout_read: object({ trigger_ids: array(string({ minLength: 1 })) }),
63
+ trigger_blueprint_layout_apply: object({ patch: opaqueObject }, ["patch"]),
64
+ retrieval_config_read: object({}),
65
+ retrieval_config_apply: object({ patch: opaqueObject }, ["patch"]),
66
+ retrieval_probe: object({ query: string({ minLength: 1 }), candidate_config: opaqueObject, top_k: integer(1) }, ["query", "candidate_config"]),
67
+ ask_user: object({ prompt: string({ minLength: 1 }), choices: array(string({ minLength: 1 })), allow_freeform: { type: "boolean" } }, ["prompt"]),
68
+ preview_inspect: object({ selector: string(), include_screenshot: { type: "boolean" }, include_console: { type: "boolean" } }),
69
+ preview_interact: object({
70
+ action: string({ enum: ["click", "type", "press", "scroll", "navigate"] }),
71
+ target: string({ minLength: 1 }),
72
+ value: string(),
73
+ }, ["action", "target"]),
74
+ });
75
+ function invalid(path, message) {
76
+ throw new AuthoringBridgeError("E_SCHEMA_INVALID", `${path}: ${message}`);
77
+ }
78
+ function validateValue(schema, value, path) {
79
+ if (schema.type === "string") {
80
+ if (typeof value !== "string")
81
+ invalid(path, "必须是 string");
82
+ if (schema.minLength !== undefined && value.length < schema.minLength)
83
+ invalid(path, `长度必须 >= ${schema.minLength}`);
84
+ if (schema.enum !== undefined && !schema.enum.includes(value))
85
+ invalid(path, "不在允许枚举中");
86
+ return;
87
+ }
88
+ if (schema.type === "integer") {
89
+ if (!Number.isSafeInteger(value))
90
+ invalid(path, "必须是安全整数");
91
+ if (schema.minimum !== undefined && value < schema.minimum)
92
+ invalid(path, `必须 >= ${schema.minimum}`);
93
+ if (schema.maximum !== undefined && value > schema.maximum)
94
+ invalid(path, `必须 <= ${schema.maximum}`);
95
+ return;
96
+ }
97
+ if (schema.type === "boolean") {
98
+ if (typeof value !== "boolean")
99
+ invalid(path, "必须是 boolean");
100
+ return;
101
+ }
102
+ if (schema.type === "array") {
103
+ if (!Array.isArray(value))
104
+ invalid(path, "必须是 array");
105
+ if (schema.minItems !== undefined && value.length < schema.minItems)
106
+ invalid(path, `元素数量必须 >= ${schema.minItems}`);
107
+ if (schema.items !== undefined)
108
+ value.forEach((item, index) => validateValue(schema.items, item, `${path}/${index}`));
109
+ return;
110
+ }
111
+ if (typeof value !== "object" || value === null || Array.isArray(value))
112
+ invalid(path, "必须是 object");
113
+ const record = value;
114
+ for (const required of schema.required ?? []) {
115
+ if (!(required in record))
116
+ invalid(`${path}/${required}`, "缺少必填字段");
117
+ }
118
+ const properties = schema.properties ?? {};
119
+ if (schema.additionalProperties === false) {
120
+ for (const key of Object.keys(record)) {
121
+ if (!(key in properties))
122
+ invalid(`${path}/${key}`, "未知字段");
123
+ }
124
+ }
125
+ for (const [key, child] of Object.entries(properties)) {
126
+ if (key in record)
127
+ validateValue(child, record[key], `${path}/${key}`);
128
+ }
129
+ if (schema.oneOf !== undefined) {
130
+ const matched = schema.oneOf.filter((candidate) => {
131
+ try {
132
+ validateValue(candidate, value, path);
133
+ return true;
134
+ }
135
+ catch (error) {
136
+ if (error instanceof AuthoringBridgeError && error.code === "E_SCHEMA_INVALID")
137
+ return false;
138
+ throw error;
139
+ }
140
+ }).length;
141
+ if (matched !== 1)
142
+ invalid(path, "必须匹配且仅匹配一个输入模式");
143
+ }
144
+ }
145
+ function validateSpatialWorldApplyOperation(value, path) {
146
+ if (typeof value !== "object" || value === null || Array.isArray(value))
147
+ invalid(path, "必须是 object");
148
+ const operation = value;
149
+ if (operation.type !== "spatial_create" && operation.type !== "spatial_update" && operation.type !== "spatial_remove"
150
+ && operation.type !== "bind_gallery_resource" && operation.type !== "unbind_resource_ref")
151
+ return;
152
+ if (typeof operation.id === "string" && operation.id.trim().length === 0)
153
+ invalid(`${path}/id`, "不能为空");
154
+ if (operation.type === "spatial_create") {
155
+ if (typeof operation.key !== "string" || !/^[a-z][a-z0-9-]{0,63}$/.test(operation.key))
156
+ invalid(`${path}/key`, "必须是稳定 kebab-case key");
157
+ if (operation.kind !== "location" && operation.kind !== "portal" && operation.kind !== "region")
158
+ invalid(`${path}/kind`, "不支持的 spatial kind");
159
+ if (typeof operation.label !== "string" || operation.label.trim().length === 0)
160
+ invalid(`${path}/label`, "不能为空");
161
+ if (typeof operation.x !== "number" || !Number.isFinite(operation.x))
162
+ invalid(`${path}/x`, "必须是有限数");
163
+ if (typeof operation.y !== "number" || !Number.isFinite(operation.y))
164
+ invalid(`${path}/y`, "必须是有限数");
165
+ }
166
+ if (operation.type === "spatial_update") {
167
+ if (typeof operation.id !== "string" || operation.id.trim().length === 0)
168
+ invalid(`${path}/id`, "不能为空");
169
+ if (operation.label !== undefined && (typeof operation.label !== "string" || operation.label.trim().length === 0))
170
+ invalid(`${path}/label`, "不能为空");
171
+ if (operation.x !== undefined && (typeof operation.x !== "number" || !Number.isFinite(operation.x)))
172
+ invalid(`${path}/x`, "必须是有限数");
173
+ if (operation.y !== undefined && (typeof operation.y !== "number" || !Number.isFinite(operation.y)))
174
+ invalid(`${path}/y`, "必须是有限数");
175
+ }
176
+ if (operation.type === "spatial_remove" && (typeof operation.id !== "string" || operation.id.trim().length === 0 || typeof operation.confirmed !== "boolean"))
177
+ invalid(path, "spatial_remove 需要 id 和 confirmed");
178
+ if (operation.type === "bind_gallery_resource" || operation.type === "unbind_resource_ref") {
179
+ const keys = Object.keys(operation).sort();
180
+ if (keys.length !== 2 || keys[0] !== "path" || keys[1] !== "type" || typeof operation.path !== "string" || operation.path.trim().length === 0) {
181
+ invalid(path, `${operation.type} 只接受非空 path`);
182
+ }
183
+ }
184
+ }
185
+ export function validateAuthoringToolInput(name, input) {
186
+ validateValue(AUTHORING_TOOL_INPUT_SCHEMAS[name], input, `$/${name}`);
187
+ if (name === "world_apply")
188
+ input.operations.forEach((operation, index) => validateSpatialWorldApplyOperation(operation, `$/world_apply/operations/${index}`));
189
+ return input;
190
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@world-engines/authoring-bridge",
3
+ "version": "0.1.0-alpha.0",
4
+ "license": "SEE LICENSE IN LICENSE",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js"
10
+ }
11
+ },
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "worldengine_source_sha256": "c29e5e0ef5129b74c18e2cfa55519fcf93150138f73331436952a10aedc42453",
19
+ "worldengine_source_identity_provenance": {
20
+ "schema": "worldengine-public-namespace-projection/v1",
21
+ "kind": "composite-source-identity; namespace projection; not-full-source-recompile",
22
+ "original_name": "@worldengine/authoring-bridge",
23
+ "original_archive_sha256": "3f7f7639ad71921caef6fe1c6d0a61c2d8809c98cd0b717d9ff6ac949743ff7b",
24
+ "original_source_sha256": "e816d08bc712469b9a36cbe34bfc51fdca719b1c88d98b99e7c8782fb7177f68",
25
+ "projected_name": "@world-engines/authoring-bridge",
26
+ "package_name_mapping": {
27
+ "@chat/blocks-editor": "@world-engines/blocks-editor",
28
+ "@chat/ladybug-bridge": "@world-engines/ladybug-bridge",
29
+ "@chat/monaco-host": "@world-engines/monaco-host",
30
+ "@chat/protocol-ts": "@world-engines/protocol-ts",
31
+ "@chat/scenario-author-source": "@world-engines/scenario-author-source",
32
+ "@chat/scenario-review-snapshot": "@world-engines/scenario-review-snapshot",
33
+ "@chat/tmw": "@world-engines/tmw",
34
+ "@chat/view-exposure": "@world-engines/view-exposure",
35
+ "@world-engines/chatplay-vite-plugin": "@world-engines/chatplay-vite-plugin",
36
+ "@worldengine/agent-kit": "@world-engines/agent-kit",
37
+ "@worldengine/authoring-bridge": "@world-engines/authoring-bridge",
38
+ "@worldengine/authoring-ui": "@world-engines/authoring-ui",
39
+ "@worldengine/create-project": "@world-engines/create-project",
40
+ "@worldengine/project-format": "@world-engines/project-format",
41
+ "@worldengine/project-host": "@world-engines/project-host",
42
+ "@worldengine/project-setup": "@world-engines/project-setup",
43
+ "@worldengine/spatial-authoring": "@world-engines/spatial-authoring",
44
+ "@worldengine/view-sdk": "@world-engines/view-sdk"
45
+ }
46
+ }
47
+ }