@world-engines/project-host 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.
Files changed (218) hide show
  1. package/LICENSE +46 -0
  2. package/assets/gui/WELogo-title.svg +11 -0
  3. package/assets/gui/app.js +1326 -0
  4. package/assets/gui/index.html +118 -0
  5. package/assets/gui/local-preview-asset-urls.js +61 -0
  6. package/assets/gui/local-preview-bridge.js +393 -0
  7. package/assets/gui/spatial-template-panel.js +233 -0
  8. package/assets/gui/styles.css +115 -0
  9. package/assets/gui/terrain-render.js +120 -0
  10. package/assets/gui/workspace-status.css +17 -0
  11. package/assets/gui/workspace-status.js +255 -0
  12. package/assets/gui/world-map-globe.js +187 -0
  13. package/assets/gui/world-map-worker.js +62 -0
  14. package/assets/gui/world-map.css +30 -0
  15. package/assets/gui/world-map.js +2466 -0
  16. package/dist/account-model-proxy.d.ts +21 -0
  17. package/dist/account-model-proxy.js +470 -0
  18. package/dist/authoring-browser-runtime.d.ts +7 -0
  19. package/dist/authoring-browser-runtime.js +110 -0
  20. package/dist/authoring-ingestion.d.ts +38 -0
  21. package/dist/authoring-ingestion.js +410 -0
  22. package/dist/authoring-model-connection.d.ts +166 -0
  23. package/dist/authoring-model-connection.js +757 -0
  24. package/dist/chinese-lexical.d.ts +152 -0
  25. package/dist/chinese-lexical.js +555 -0
  26. package/dist/chromium-preview-browser-driver.d.ts +43 -0
  27. package/dist/chromium-preview-browser-driver.js +575 -0
  28. package/dist/cli/download.d.ts +12 -0
  29. package/dist/cli/download.js +50 -0
  30. package/dist/cli/dump.d.ts +32 -0
  31. package/dist/cli/dump.js +91 -0
  32. package/dist/cli/ingest.d.ts +49 -0
  33. package/dist/cli/ingest.js +225 -0
  34. package/dist/cli/review.d.ts +10 -0
  35. package/dist/cli/review.js +44 -0
  36. package/dist/cli/upload.d.ts +57 -0
  37. package/dist/cli/upload.js +112 -0
  38. package/dist/cli-command.d.ts +60 -0
  39. package/dist/cli-command.js +613 -0
  40. package/dist/cli.d.ts +2 -0
  41. package/dist/cli.js +182 -0
  42. package/dist/desktop-authoring-runtime.d.ts +88 -0
  43. package/dist/desktop-authoring-runtime.js +1602 -0
  44. package/dist/desktop-sidecar.d.ts +11 -0
  45. package/dist/desktop-sidecar.js +143 -0
  46. package/dist/document-dump.d.ts +113 -0
  47. package/dist/document-dump.js +431 -0
  48. package/dist/document-graph-extractor.d.ts +125 -0
  49. package/dist/document-graph-extractor.js +588 -0
  50. package/dist/document-ingestion-journal.d.ts +68 -0
  51. package/dist/document-ingestion-journal.js +471 -0
  52. package/dist/document-ingestion.d.ts +183 -0
  53. package/dist/document-ingestion.js +976 -0
  54. package/dist/document-query-path-proof.d.ts +30 -0
  55. package/dist/document-query-path-proof.js +34 -0
  56. package/dist/document-query-path-renderer.d.ts +50 -0
  57. package/dist/document-query-path-renderer.js +75 -0
  58. package/dist/document-query-proof-paths.d.ts +22 -0
  59. package/dist/document-query-proof-paths.js +71 -0
  60. package/dist/document-query.d.ts +97 -0
  61. package/dist/document-query.js +1106 -0
  62. package/dist/document-relation-role.d.ts +14 -0
  63. package/dist/document-relation-role.js +86 -0
  64. package/dist/document-source.d.ts +107 -0
  65. package/dist/document-source.js +255 -0
  66. package/dist/embedding-cache.d.ts +35 -0
  67. package/dist/embedding-cache.js +317 -0
  68. package/dist/frozen-review-local-pipeline.d.ts +88 -0
  69. package/dist/frozen-review-local-pipeline.js +609 -0
  70. package/dist/frozen-review-source-capture.d.ts +15 -0
  71. package/dist/frozen-review-source-capture.js +551 -0
  72. package/dist/frozen-review-wasm-encryptor.d.ts +16 -0
  73. package/dist/frozen-review-wasm-encryptor.js +72 -0
  74. package/dist/frozen-review.d.ts +131 -0
  75. package/dist/frozen-review.js +802 -0
  76. package/dist/graph-traversal.d.ts +23 -0
  77. package/dist/graph-traversal.js +100 -0
  78. package/dist/gui/graph-layout-worker.js +1 -0
  79. package/dist/gui/graph-layout.wasm +0 -0
  80. package/dist/gui/graph.css +1 -0
  81. package/dist/gui/graph.js +76 -0
  82. package/dist/gui/plane-generation.js +112 -0
  83. package/dist/gui/plane-generator.js +310 -0
  84. package/dist/gui/plane-generator_bg.wasm +0 -0
  85. package/dist/gui/procedural-city.js +595 -0
  86. package/dist/gui/road-generation.js +129 -0
  87. package/dist/gui/road-parcels.js +1584 -0
  88. package/dist/gui/score.css +1 -0
  89. package/dist/gui/score.js +54 -0
  90. package/dist/gui/spatial-runtime.js +1231 -0
  91. package/dist/gui/spatial-templates.js +200 -0
  92. package/dist/gui/trigger.css +1 -0
  93. package/dist/gui/trigger.js +142 -0
  94. package/dist/harness-callback-transport.d.ts +17 -0
  95. package/dist/harness-callback-transport.js +128 -0
  96. package/dist/harness-document-ingestion.d.ts +77 -0
  97. package/dist/harness-document-ingestion.js +343 -0
  98. package/dist/index.d.ts +43 -0
  99. package/dist/index.js +43 -0
  100. package/dist/ingestion-source-reconciliation.d.ts +88 -0
  101. package/dist/ingestion-source-reconciliation.js +253 -0
  102. package/dist/ladybug-runtime.d.ts +117 -0
  103. package/dist/ladybug-runtime.js +886 -0
  104. package/dist/local-account-embedding.d.ts +34 -0
  105. package/dist/local-account-embedding.js +294 -0
  106. package/dist/local-gallery.d.ts +35 -0
  107. package/dist/local-gallery.js +227 -0
  108. package/dist/local-gui-auth.d.ts +96 -0
  109. package/dist/local-gui-auth.js +703 -0
  110. package/dist/local-gui-contract.d.ts +53 -0
  111. package/dist/local-gui-contract.js +5 -0
  112. package/dist/local-gui-image-assets.d.ts +17 -0
  113. package/dist/local-gui-image-assets.js +96 -0
  114. package/dist/local-gui-preview-client.d.ts +3 -0
  115. package/dist/local-gui-preview-client.js +111 -0
  116. package/dist/local-gui-score.d.ts +72 -0
  117. package/dist/local-gui-score.js +184 -0
  118. package/dist/local-gui-server.d.ts +10 -0
  119. package/dist/local-gui-server.js +987 -0
  120. package/dist/local-gui-version.d.ts +17 -0
  121. package/dist/local-gui-version.js +147 -0
  122. package/dist/local-gui-workspace.d.ts +14 -0
  123. package/dist/local-gui-workspace.js +171 -0
  124. package/dist/local-model-settings.d.ts +28 -0
  125. package/dist/local-model-settings.js +124 -0
  126. package/dist/local-preview-assets.d.ts +28 -0
  127. package/dist/local-preview-assets.js +124 -0
  128. package/dist/local-preview-bound-resources.d.ts +12 -0
  129. package/dist/local-preview-bound-resources.js +80 -0
  130. package/dist/local-preview-message-store.d.ts +63 -0
  131. package/dist/local-preview-message-store.js +214 -0
  132. package/dist/local-preview-player-read-models.d.ts +47 -0
  133. package/dist/local-preview-player-read-models.js +90 -0
  134. package/dist/local-preview-projections.d.ts +61 -0
  135. package/dist/local-preview-projections.js +117 -0
  136. package/dist/local-preview-runtime.d.ts +105 -0
  137. package/dist/local-preview-runtime.js +553 -0
  138. package/dist/local-preview-session.d.ts +73 -0
  139. package/dist/local-preview-session.js +395 -0
  140. package/dist/local-preview-source.d.ts +40 -0
  141. package/dist/local-preview-source.js +192 -0
  142. package/dist/local-preview-view-events.d.ts +101 -0
  143. package/dist/local-preview-view-events.js +179 -0
  144. package/dist/local-preview-view-state-sqlite.d.ts +24 -0
  145. package/dist/local-preview-view-state-sqlite.js +263 -0
  146. package/dist/local-preview-view-state.d.ts +93 -0
  147. package/dist/local-preview-view-state.js +108 -0
  148. package/dist/local-preview-vite-config.d.ts +6 -0
  149. package/dist/local-preview-vite-config.js +122 -0
  150. package/dist/local-preview-world-effects.d.ts +9 -0
  151. package/dist/local-preview-world-effects.js +55 -0
  152. package/dist/local-preview-worldline-store.d.ts +47 -0
  153. package/dist/local-preview-worldline-store.js +282 -0
  154. package/dist/local-preview-worldlines.d.ts +95 -0
  155. package/dist/local-preview-worldlines.js +136 -0
  156. package/dist/mcp-async-dispatcher.d.ts +3 -0
  157. package/dist/mcp-async-dispatcher.js +129 -0
  158. package/dist/mcp-line-queue.d.ts +14 -0
  159. package/dist/mcp-line-queue.js +184 -0
  160. package/dist/mcp-pipe-internal.d.ts +6 -0
  161. package/dist/mcp-pipe-internal.js +151 -0
  162. package/dist/mcp-pipe-response.d.ts +14 -0
  163. package/dist/mcp-pipe-response.js +50 -0
  164. package/dist/mcp-stdio-response.d.ts +12 -0
  165. package/dist/mcp-stdio-response.js +27 -0
  166. package/dist/mcp.d.ts +27 -0
  167. package/dist/mcp.js +194 -0
  168. package/dist/prepared-embedding-cache.d.ts +38 -0
  169. package/dist/prepared-embedding-cache.js +238 -0
  170. package/dist/preview-browser-adapter.d.ts +53 -0
  171. package/dist/preview-browser-adapter.js +171 -0
  172. package/dist/preview-runtime.d.ts +67 -0
  173. package/dist/preview-runtime.js +460 -0
  174. package/dist/project-auth-transport.d.ts +27 -0
  175. package/dist/project-auth-transport.js +121 -0
  176. package/dist/project-auth.d.ts +51 -0
  177. package/dist/project-auth.js +108 -0
  178. package/dist/project-git-capture-recovery.d.ts +16 -0
  179. package/dist/project-git-capture-recovery.js +347 -0
  180. package/dist/project-git-cli.d.ts +5 -0
  181. package/dist/project-git-cli.js +40 -0
  182. package/dist/project-git-scenario-source.d.ts +10 -0
  183. package/dist/project-git-scenario-source.js +240 -0
  184. package/dist/project-git-view-source.d.ts +13 -0
  185. package/dist/project-git-view-source.js +134 -0
  186. package/dist/project-git.d.ts +97 -0
  187. package/dist/project-git.js +656 -0
  188. package/dist/project-host.d.ts +59 -0
  189. package/dist/project-host.js +471 -0
  190. package/dist/project-writer-lease.d.ts +18 -0
  191. package/dist/project-writer-lease.js +430 -0
  192. package/dist/public-error.d.ts +7 -0
  193. package/dist/public-error.js +61 -0
  194. package/dist/retrieval-benchmark.d.ts +46 -0
  195. package/dist/retrieval-benchmark.js +58 -0
  196. package/dist/safe-project-path.d.ts +8 -0
  197. package/dist/safe-project-path.js +93 -0
  198. package/dist/scenario-writer.d.ts +32 -0
  199. package/dist/scenario-writer.js +191 -0
  200. package/dist/transfer/download.d.ts +159 -0
  201. package/dist/transfer/download.js +598 -0
  202. package/dist/transfer/transport.d.ts +26 -0
  203. package/dist/transfer/transport.js +1 -0
  204. package/dist/transfer/upload.d.ts +162 -0
  205. package/dist/transfer/upload.js +354 -0
  206. package/dist/transfer-types.d.ts +95 -0
  207. package/dist/transfer-types.js +1 -0
  208. package/dist/transfer.d.ts +18 -0
  209. package/dist/transfer.js +273 -0
  210. package/dist/trigger-runtime.d.ts +121 -0
  211. package/dist/trigger-runtime.js +153 -0
  212. package/dist/vendor/wasm-core/asset-hashes.json +5 -0
  213. package/dist/vendor/wasm-core/wasm_core.d.ts +490 -0
  214. package/dist/vendor/wasm-core/wasm_core.js +1 -0
  215. package/dist/vendor/wasm-core/wasm_core_bg.wasm +0 -0
  216. package/dist/view-watcher.d.ts +9 -0
  217. package/dist/view-watcher.js +34 -0
  218. package/package.json +110 -0
@@ -0,0 +1,151 @@
1
+ import { execFile } from "node:child_process";
2
+ import { createServer } from "node:net";
3
+ import { promisify } from "node:util";
4
+ import { AuthoringBridgeError } from "@world-engines/authoring-bridge";
5
+ import { runAsyncProjectHostMcp } from "./mcp-async-dispatcher.js";
6
+ import { createBoundedMcpLineQueue } from "./mcp-line-queue.js";
7
+ import { handleProjectHostMcpRequest } from "./mcp.js";
8
+ import { writeBoundedMcpPipeResponse } from "./mcp-pipe-response.js";
9
+ import { LocalProjectHost } from "./project-host.js";
10
+ const PIPE_HANDSHAKE_TIMEOUT_MS = 10_000;
11
+ const PIPE_HELLO_KIND = "project_host_pipe_hello";
12
+ const execFileAsync = promisify(execFile);
13
+ function isRecord(value) {
14
+ return typeof value === "object" && value !== null && !Array.isArray(value);
15
+ }
16
+ function isProjectHostPipeHello(value) {
17
+ return isRecord(value)
18
+ && value.kind === PIPE_HELLO_KIND
19
+ && typeof value.windows_sid === "string"
20
+ && /^[A-Za-z0-9-]{4,256}$/.test(value.windows_sid)
21
+ && typeof value.project_id === "string"
22
+ && /^[A-Za-z0-9_-]{1,256}$/.test(value.project_id)
23
+ && typeof value.challenge === "string"
24
+ && /^[0-9a-f-]{36}$/i.test(value.challenge);
25
+ }
26
+ async function currentWindowsSid() {
27
+ if (process.platform !== "win32")
28
+ throw new AuthoringBridgeError("E_SCHEMA_INVALID", "desktop pipe 仅支持 Windows");
29
+ const { stdout } = await execFileAsync("whoami", ["/user", "/fo", "csv", "/nh"], { windowsHide: true });
30
+ const sid = stdout
31
+ .split(",")
32
+ .map((value) => value.trim().replace(/^"|"$/g, ""))
33
+ .find((value) => /^S-1-[0-9-]+$/i.test(value));
34
+ if (sid === undefined)
35
+ throw new AuthoringBridgeError("E_SCHEMA_INVALID", "Windows identity 不可用");
36
+ return sid;
37
+ }
38
+ async function pipeProjectId(cwd) {
39
+ const host = await LocalProjectHost.open(cwd);
40
+ try {
41
+ return (await host.inspect()).project_id;
42
+ }
43
+ finally {
44
+ await host.close();
45
+ }
46
+ }
47
+ /** T027 的 inherited-capability 内部入口;不由公开 package export 或 CLI 暴露。 */
48
+ export async function runProjectHostMcpPipe(cwd, pipe, challenge, runtime = {}) {
49
+ if (!/^\\\\\.\\pipe\\worldengine-project-host-[0-9a-f-]{36}$/i.test(pipe) || !/^[0-9a-f-]{36}$/i.test(challenge)) {
50
+ throw new AuthoringBridgeError("E_SCHEMA_INVALID", "desktop pipe 参数无效");
51
+ }
52
+ const [windowsSid, projectId] = await Promise.all([currentWindowsSid(), pipeProjectId(cwd)]);
53
+ return new Promise((resolvePromise, rejectPromise) => {
54
+ let settled = false;
55
+ let closeStarted = false;
56
+ let activeSocket;
57
+ let timeout;
58
+ const clearHandshakeTimeout = () => {
59
+ if (timeout === undefined)
60
+ return;
61
+ clearTimeout(timeout);
62
+ timeout = undefined;
63
+ };
64
+ const destroyActiveSocket = () => {
65
+ activeSocket?.destroy();
66
+ activeSocket = undefined;
67
+ };
68
+ const closeServer = (done) => {
69
+ if (closeStarted) {
70
+ done();
71
+ return;
72
+ }
73
+ closeStarted = true;
74
+ try {
75
+ server.close(() => done());
76
+ }
77
+ catch {
78
+ done();
79
+ }
80
+ };
81
+ const finish = (code, destroySocket = true) => {
82
+ if (settled)
83
+ return;
84
+ settled = true;
85
+ clearHandshakeTimeout();
86
+ if (destroySocket)
87
+ destroyActiveSocket();
88
+ closeServer(() => resolvePromise(code));
89
+ };
90
+ const fail = (error) => {
91
+ if (settled)
92
+ return;
93
+ settled = true;
94
+ clearHandshakeTimeout();
95
+ destroyActiveSocket();
96
+ closeServer(() => rejectPromise(error));
97
+ };
98
+ const server = (runtime.createServer ?? createServer)((socket) => {
99
+ if (settled) {
100
+ socket.destroy();
101
+ return;
102
+ }
103
+ activeSocket = socket;
104
+ const lines = createBoundedMcpLineQueue(socket);
105
+ const iterator = lines.lines[Symbol.asyncIterator]();
106
+ void (async () => {
107
+ try {
108
+ const first = await iterator.next();
109
+ if (first.done) {
110
+ socket.destroy();
111
+ finish(1);
112
+ return;
113
+ }
114
+ let hello;
115
+ try {
116
+ hello = JSON.parse(first.value);
117
+ }
118
+ catch {
119
+ socket.destroy();
120
+ finish(1);
121
+ return;
122
+ }
123
+ if (!isProjectHostPipeHello(hello)
124
+ || hello.windows_sid !== windowsSid
125
+ || hello.project_id !== projectId
126
+ || hello.challenge !== challenge) {
127
+ socket.destroy();
128
+ finish(1);
129
+ return;
130
+ }
131
+ clearHandshakeTimeout();
132
+ const rest = { [Symbol.asyncIterator]: () => iterator };
133
+ const code = await runAsyncProjectHostMcp(cwd, rest, (line) => writeBoundedMcpPipeResponse(socket, line, lines), () => undefined, undefined, handleProjectHostMcpRequest);
134
+ socket.end();
135
+ finish(code, false);
136
+ }
137
+ catch (error) {
138
+ socket.destroy();
139
+ fail(error);
140
+ }
141
+ finally {
142
+ lines.close();
143
+ }
144
+ })();
145
+ });
146
+ server.maxConnections = 1;
147
+ server.once("error", fail);
148
+ timeout = setTimeout(() => finish(1), PIPE_HANDSHAKE_TIMEOUT_MS);
149
+ server.listen(pipe);
150
+ });
151
+ }
@@ -0,0 +1,14 @@
1
+ type PipeSocket = {
2
+ readonly write: (line: string) => boolean;
3
+ readonly once: (event: "drain" | "close" | "error", listener: () => void) => unknown;
4
+ readonly off: (event: "drain" | "close" | "error", listener: () => void) => unknown;
5
+ readonly destroy: () => void;
6
+ };
7
+ type PipeInputFlow = {
8
+ readonly pause: () => void;
9
+ readonly resume: () => void;
10
+ readonly close: () => void;
11
+ };
12
+ /** 内部 Desktop pipe 输出边界:JSON payload 不计 transport 追加的 LF delimiter。 */
13
+ export declare function writeBoundedMcpPipeResponse(socket: PipeSocket, line: string, input: PipeInputFlow): Promise<void>;
14
+ export {};
@@ -0,0 +1,50 @@
1
+ import { AuthoringBridgeError } from "@world-engines/authoring-bridge";
2
+ import { MCP_MAX_LINE_BYTES } from "./mcp-line-queue.js";
3
+ const PIPE_DRAIN_TIMEOUT_MS = 10_000;
4
+ function waitForPipeDrain(socket) {
5
+ return new Promise((resolvePromise, rejectPromise) => {
6
+ let settled = false;
7
+ const cleanup = () => {
8
+ clearTimeout(timer);
9
+ socket.off("drain", onDrain);
10
+ socket.off("close", onClose);
11
+ socket.off("error", onError);
12
+ };
13
+ const finish = (error) => {
14
+ if (settled)
15
+ return;
16
+ settled = true;
17
+ cleanup();
18
+ if (error === undefined)
19
+ resolvePromise();
20
+ else
21
+ rejectPromise(error);
22
+ };
23
+ const onDrain = () => finish();
24
+ const onClose = () => finish(new AuthoringBridgeError("E_PROJECT_BUSY", "desktop pipe closed during backpressure"));
25
+ const onError = () => finish(new AuthoringBridgeError("E_PROJECT_BUSY", "desktop pipe failed during backpressure"));
26
+ const timer = setTimeout(() => finish(new AuthoringBridgeError("E_PROJECT_BUSY", "desktop pipe backpressure timeout")), PIPE_DRAIN_TIMEOUT_MS);
27
+ socket.once("drain", onDrain);
28
+ socket.once("close", onClose);
29
+ socket.once("error", onError);
30
+ });
31
+ }
32
+ /** 内部 Desktop pipe 输出边界:JSON payload 不计 transport 追加的 LF delimiter。 */
33
+ export async function writeBoundedMcpPipeResponse(socket, line, input) {
34
+ const payload = line.endsWith("\n") ? line.slice(0, -1) : line;
35
+ if (Buffer.byteLength(payload, "utf8") > MCP_MAX_LINE_BYTES) {
36
+ throw new AuthoringBridgeError("E_SCHEMA_INVALID", "desktop pipe response exceeds limit");
37
+ }
38
+ if (socket.write(line))
39
+ return;
40
+ input.pause();
41
+ try {
42
+ await waitForPipeDrain(socket);
43
+ input.resume();
44
+ }
45
+ catch (error) {
46
+ input.close();
47
+ socket.destroy();
48
+ throw error;
49
+ }
50
+ }
@@ -0,0 +1,12 @@
1
+ type StdioOutput = {
2
+ readonly write: (line: string) => boolean;
3
+ readonly once: (event: "drain" | "close" | "error", listener: () => void) => unknown;
4
+ readonly off: (event: "drain" | "close" | "error", listener: () => void) => unknown;
5
+ };
6
+ type InputFlow = {
7
+ readonly pause: () => void;
8
+ readonly resume: () => void;
9
+ readonly close: () => void;
10
+ };
11
+ export declare function writeBoundedMcpStdioResponse(output: StdioOutput, line: string, input: InputFlow): Promise<void>;
12
+ export {};
@@ -0,0 +1,27 @@
1
+ import { AuthoringBridgeError } from "@world-engines/authoring-bridge";
2
+ const STDIO_DRAIN_TIMEOUT_MS = 10_000;
3
+ export async function writeBoundedMcpStdioResponse(output, line, input) {
4
+ if (output.write(line))
5
+ return;
6
+ input.pause();
7
+ await new Promise((resolvePromise, rejectPromise) => {
8
+ let settled = false;
9
+ const cleanup = () => { clearTimeout(timer); output.off("drain", drain); output.off("close", fail); output.off("error", fail); };
10
+ const finish = (error) => {
11
+ if (settled)
12
+ return;
13
+ settled = true;
14
+ cleanup();
15
+ if (error === undefined)
16
+ resolvePromise();
17
+ else
18
+ rejectPromise(error);
19
+ };
20
+ const drain = () => finish();
21
+ const fail = () => finish(new AuthoringBridgeError("E_PROJECT_BUSY", "stdio output backpressure"));
22
+ const timer = setTimeout(fail, STDIO_DRAIN_TIMEOUT_MS);
23
+ output.once("drain", drain);
24
+ output.once("close", fail);
25
+ output.once("error", fail);
26
+ }).then(() => input.resume(), (error) => { input.close(); throw error; });
27
+ }
package/dist/mcp.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ import type { LocalProjectHost, LocalProjectHostOptions } from "./project-host.js";
2
+ export interface McpRequest {
3
+ readonly jsonrpc: "2.0";
4
+ readonly id?: string | number | null;
5
+ readonly method: string;
6
+ readonly params?: unknown;
7
+ }
8
+ interface McpSuccess {
9
+ readonly jsonrpc: "2.0";
10
+ readonly id: string | number | null;
11
+ readonly result: Record<string, unknown>;
12
+ }
13
+ interface McpFailure {
14
+ readonly jsonrpc: "2.0";
15
+ readonly id: string | number | null;
16
+ readonly error: {
17
+ readonly code: number;
18
+ readonly message: string;
19
+ readonly data?: Record<string, unknown>;
20
+ };
21
+ }
22
+ export type McpResponse = McpSuccess | McpFailure;
23
+ export declare const MAX_MCP_IMAGE_BYTES: number;
24
+ export declare function handleProjectHostMcpRequest(host: LocalProjectHost, request: unknown): Promise<McpResponse | undefined>;
25
+ export declare function runProjectHostMcp(cwd: string, lines: AsyncIterable<string>, write: (line: string) => void, writeError: (line: string) => void, onReady?: () => void, hostOptions?: LocalProjectHostOptions): Promise<number>;
26
+ export declare function runProjectHostMcpStdio(cwd: string, onReady?: () => void, hostOptions?: LocalProjectHostOptions): Promise<number>;
27
+ export {};
package/dist/mcp.js ADDED
@@ -0,0 +1,194 @@
1
+ import { AUTHORING_BRIDGE_TOOLS, AuthoringBridgeError, authoringToolDescriptor, } from "@world-engines/authoring-bridge";
2
+ import { PROJECT_HOST_MCP_INSTRUCTIONS } from "@world-engines/agent-kit";
3
+ import { createBoundedMcpLineQueue } from "./mcp-line-queue.js";
4
+ import { runAsyncProjectHostMcp } from "./mcp-async-dispatcher.js";
5
+ import { writeBoundedMcpStdioResponse } from "./mcp-stdio-response.js";
6
+ import { toPublicError } from "./public-error.js";
7
+ const MCP_PROTOCOL_VERSION = "2025-03-26";
8
+ export const MAX_MCP_IMAGE_BYTES = 4 * 1024 * 1024;
9
+ const MCP_TOOLS = AUTHORING_BRIDGE_TOOLS.map((descriptor) => ({
10
+ name: descriptor.name,
11
+ description: `AuthoringBridge registered tool ${descriptor.name}`,
12
+ annotations: {
13
+ readOnlyHint: !descriptor.project_effect && descriptor.name !== "ask_user" && descriptor.name !== "preview_interact",
14
+ destructiveHint: descriptor.project_effect,
15
+ },
16
+ inputSchema: descriptor.project_effect
17
+ ? {
18
+ ...descriptor.input_schema,
19
+ required: [...(descriptor.input_schema.required ?? []), "operation_id", "expected_project_revision"],
20
+ properties: {
21
+ ...(descriptor.input_schema.properties ?? {}),
22
+ operation_id: { type: "string", minLength: 1 },
23
+ expected_project_revision: { type: "integer", minimum: 0 },
24
+ },
25
+ }
26
+ : descriptor.input_schema,
27
+ }));
28
+ function listedTools(host) {
29
+ return MCP_TOOLS.map((tool) => {
30
+ const available = host.isToolAvailable(tool.name);
31
+ return {
32
+ ...tool,
33
+ description: available
34
+ ? tool.description
35
+ : `${tool.description}; runtime unavailable: no real Desktop/terminal callback transport`,
36
+ _meta: {
37
+ "worldengine/toolAvailability": available ? "available" : "unavailable",
38
+ ...(available ? {} : { "worldengine/unavailableCode": "E_CAPABILITY_UNAVAILABLE" }),
39
+ },
40
+ };
41
+ });
42
+ }
43
+ function isRecord(value) {
44
+ return typeof value === "object" && value !== null && !Array.isArray(value);
45
+ }
46
+ function responseId(request) {
47
+ return request.id ?? null;
48
+ }
49
+ function success(request, result) {
50
+ return { jsonrpc: "2.0", id: responseId(request), result };
51
+ }
52
+ function failure(request, code, message, data) {
53
+ return {
54
+ jsonrpc: "2.0",
55
+ id: responseId(request),
56
+ error: { code, message, ...(data === undefined ? {} : { data }) },
57
+ };
58
+ }
59
+ function invalidRequest() {
60
+ return {
61
+ jsonrpc: "2.0",
62
+ id: null,
63
+ error: { code: -32600, message: "Invalid Request" },
64
+ };
65
+ }
66
+ function isRequestId(value) {
67
+ return value === undefined || value === null || typeof value === "string" || (typeof value === "number" && Number.isFinite(value));
68
+ }
69
+ function isMcpRequest(value) {
70
+ return isRecord(value)
71
+ && value.jsonrpc === "2.0"
72
+ && typeof value.method === "string"
73
+ && isRequestId(value.id);
74
+ }
75
+ function asArguments(params) {
76
+ if (!isRecord(params) || !isRecord(params.arguments)) {
77
+ throw new AuthoringBridgeError("E_SCHEMA_INVALID", "tools/call 必须提供 object arguments");
78
+ }
79
+ return params.arguments;
80
+ }
81
+ function toAuthoringToolCall(name, argumentsValue) {
82
+ const descriptor = authoringToolDescriptor(name);
83
+ if (!descriptor.project_effect)
84
+ return { name: name, input: argumentsValue };
85
+ const { operation_id, expected_project_revision, ...input } = argumentsValue;
86
+ return {
87
+ name: name,
88
+ input,
89
+ effect_identity: { operation_id: operation_id, expected_project_revision: expected_project_revision },
90
+ };
91
+ }
92
+ function extractMcpImage(name, value) {
93
+ if (name !== "preview_inspect" || !isRecord(value) || !isRecord(value.screenshot)) {
94
+ return { structured: value };
95
+ }
96
+ const screenshot = value.screenshot;
97
+ const mimeType = screenshot.mime_type;
98
+ const data = screenshot.data_base64;
99
+ const byteLength = screenshot.byte_length;
100
+ if ((mimeType !== "image/png" && mimeType !== "image/jpeg" && mimeType !== "image/webp")
101
+ || typeof data !== "string"
102
+ || !Number.isSafeInteger(byteLength)
103
+ || byteLength < 0
104
+ || byteLength > MAX_MCP_IMAGE_BYTES
105
+ || data.length > Math.ceil(MAX_MCP_IMAGE_BYTES / 3) * 4 + 4
106
+ || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(data)) {
107
+ throw new AuthoringBridgeError("E_SCHEMA_INVALID", "preview screenshot 不是受支持的有界图片");
108
+ }
109
+ const decoded = Buffer.from(data, "base64");
110
+ if (decoded.byteLength !== byteLength) {
111
+ throw new AuthoringBridgeError("E_SCHEMA_INVALID", "preview screenshot byte_length 与图片不一致");
112
+ }
113
+ const { data_base64: _dataBase64, ...boundedScreenshot } = screenshot;
114
+ return {
115
+ structured: { ...value, screenshot: boundedScreenshot },
116
+ image: { data, mimeType, byteLength },
117
+ };
118
+ }
119
+ function toolResult(name, value) {
120
+ const extracted = extractMcpImage(name, value);
121
+ const serialized = JSON.stringify(extracted.structured);
122
+ return {
123
+ content: [
124
+ { type: "text", text: serialized },
125
+ ...(extracted.image === undefined
126
+ ? []
127
+ : [{ type: "image", data: extracted.image.data, mimeType: extracted.image.mimeType }]),
128
+ ],
129
+ structuredContent: extracted.structured,
130
+ };
131
+ }
132
+ function initializeResult(params) {
133
+ const requestedVersion = isRecord(params) && typeof params.protocolVersion === "string"
134
+ ? params.protocolVersion
135
+ : undefined;
136
+ return {
137
+ protocolVersion: requestedVersion === MCP_PROTOCOL_VERSION ? requestedVersion : MCP_PROTOCOL_VERSION,
138
+ capabilities: { tools: { listChanged: false } },
139
+ serverInfo: {
140
+ name: "worldengine-project-host",
141
+ version: "0.1.0-alpha.0",
142
+ },
143
+ instructions: PROJECT_HOST_MCP_INSTRUCTIONS,
144
+ };
145
+ }
146
+ export async function handleProjectHostMcpRequest(host, request) {
147
+ if (!isMcpRequest(request))
148
+ return invalidRequest();
149
+ if (request.id === undefined) {
150
+ if (request.method === "notifications/initialized")
151
+ return undefined;
152
+ return undefined;
153
+ }
154
+ try {
155
+ if (request.method === "initialize")
156
+ return success(request, initializeResult(request.params));
157
+ if (request.method === "tools/list")
158
+ return success(request, { tools: listedTools(host) });
159
+ if (request.method !== "tools/call")
160
+ return failure(request, -32601, "Method not found");
161
+ if (!isRecord(request.params) || typeof request.params.name !== "string") {
162
+ return failure(request, -32602, "Invalid params");
163
+ }
164
+ const params = request.params;
165
+ const name = params.name;
166
+ const args = asArguments(params);
167
+ if (!MCP_TOOLS.some((tool) => tool.name === name))
168
+ return failure(request, -32602, "Invalid params");
169
+ return success(request, toolResult(name, await host.dispatchTool(toAuthoringToolCall(name, args))));
170
+ }
171
+ catch (error) {
172
+ const publicError = toPublicError(error);
173
+ return failure(request, error instanceof AuthoringBridgeError ? -32602 : -32603, "Request failed", {
174
+ code: publicError.code,
175
+ details: publicError.details,
176
+ });
177
+ }
178
+ }
179
+ async function runProjectHostMcpWithAsyncWriter(cwd, lines, write, writeError, onReady, hostOptions = {}) {
180
+ return runAsyncProjectHostMcp(cwd, lines, write, writeError, onReady, handleProjectHostMcpRequest, hostOptions);
181
+ }
182
+ export async function runProjectHostMcp(cwd, lines, write, writeError, onReady, hostOptions = {}) {
183
+ return runProjectHostMcpWithAsyncWriter(cwd, lines, async (line) => { write(line); }, writeError, onReady, hostOptions);
184
+ }
185
+ export async function runProjectHostMcpStdio(cwd, onReady, hostOptions = {}) {
186
+ // 首帧可能在项目 authority 打开完成前到达;必须立即订阅并缓存,不能等 async iterator 首次消费。
187
+ const stdio = createBoundedMcpLineQueue(process.stdin);
188
+ try {
189
+ return await runProjectHostMcpWithAsyncWriter(cwd, stdio.lines, (line) => writeBoundedMcpStdioResponse(process.stdout, line, stdio), (line) => process.stderr.write(line), onReady, hostOptions);
190
+ }
191
+ finally {
192
+ stdio.close();
193
+ }
194
+ }
@@ -0,0 +1,38 @@
1
+ import { type FrozenArtifactEmbeddingBatch } from "@world-engines/scenario-review-snapshot";
2
+ import type { AuthoringEmbeddingPort } from "./authoring-model-connection.js";
3
+ import { type GraphProjectionReadback, type PreparedAuthoringRecallSnapshot } from "./desktop-authoring-runtime.js";
4
+ import type { ScenarioSourceReadback } from "./scenario-writer.js";
5
+ /** 仅由显式 frozen-review embed 步骤写入;普通 search 不会生成 corpus vector。 */
6
+ export declare function persistPreparedFrozenEmbeddingBatch(options: Readonly<{
7
+ projectRoot: string;
8
+ projectId: string;
9
+ runBatch: FrozenArtifactEmbeddingBatch;
10
+ }>): FrozenArtifactEmbeddingBatch;
11
+ export interface LocalRecallPreparationHost {
12
+ readonly root: string;
13
+ readSourceReadback(): Promise<ScenarioSourceReadback>;
14
+ readGraphProjectionReadback(): Promise<GraphProjectionReadback>;
15
+ }
16
+ /**
17
+ * 只消费本地 WESP 的当前只读图投影并准备 corpus 向量;不依赖 review capture、远端
18
+ * source-ready 或结构化模型。写入前后的 revision/source CAS 仍由同一持久化模块检查。
19
+ */
20
+ export declare function prepareLocalRecallSnapshot(host: LocalRecallPreparationHost, embedding: AuthoringEmbeddingPort & Readonly<{
21
+ origin: string;
22
+ assertCurrentAccount?: () => Promise<void>;
23
+ }>, options?: Readonly<{
24
+ scopeId?: string;
25
+ signal?: AbortSignal;
26
+ assertCurrentAccount?: () => Promise<void>;
27
+ }>): Promise<PreparedAuthoringRecallSnapshot>;
28
+ /** 从 review prepare 已写入的 metadata + embedding cache 只读重建 formal snapshot。 */
29
+ export declare function createPreparedEmbeddingSnapshotReader(options: Readonly<{
30
+ projectRoot: string;
31
+ projectId: string;
32
+ embedding: AuthoringEmbeddingPort & Readonly<{
33
+ origin?: string;
34
+ assertCurrentAccount?: () => Promise<void>;
35
+ }>;
36
+ origin: string;
37
+ assertCurrentAccount?: () => Promise<void>;
38
+ }>): NonNullable<import("./desktop-authoring-runtime.js").DesktopAuthoringCapabilities["get_prepared_embedding_snapshot"]>;