@co0ontty/wand 4.89.5 → 4.90.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 (197) hide show
  1. package/README.md +5 -3
  2. package/dist/agent-dispatch.js +4 -9
  3. package/dist/ai-team-employee-binding.js +10 -9
  4. package/dist/ai-team-runner.d.ts +2 -1
  5. package/dist/ai-team-runner.js +38 -2
  6. package/dist/ai-team-types.js +3 -2
  7. package/dist/ai-text-runner.d.ts +61 -0
  8. package/dist/ai-text-runner.js +653 -0
  9. package/dist/avatar-input.d.ts +2 -0
  10. package/dist/avatar-input.js +19 -0
  11. package/dist/bounded-process-output.d.ts +5 -0
  12. package/dist/bounded-process-output.js +9 -0
  13. package/dist/build-info.json +3 -3
  14. package/dist/command-error.d.ts +6 -0
  15. package/dist/command-error.js +8 -0
  16. package/dist/config.js +11 -42
  17. package/dist/conversation-service.d.ts +12 -2
  18. package/dist/conversation-service.js +104 -25
  19. package/dist/conversation-session-preview.d.ts +2 -0
  20. package/dist/conversation-session-preview.js +4 -0
  21. package/dist/decision-client.js +22 -6
  22. package/dist/decision-expert-employee.js +2 -2
  23. package/dist/decision-expert-identity.d.ts +1 -1
  24. package/dist/decision-expert-identity.js +1 -1
  25. package/dist/decision-expert-service.d.ts +1 -0
  26. package/dist/decision-expert-service.js +82 -76
  27. package/dist/decision-hardware.js +4 -3
  28. package/dist/decision-types.d.ts +4 -0
  29. package/dist/decision-types.js +4 -0
  30. package/dist/deployment-lock.d.ts +13 -0
  31. package/dist/deployment-lock.js +189 -0
  32. package/dist/employee-text.js +1 -1
  33. package/dist/fixed-employee-avatar.d.ts +7 -0
  34. package/dist/fixed-employee-avatar.js +13 -0
  35. package/dist/git-quick-commit.d.ts +5 -40
  36. package/dist/git-quick-commit.js +8 -636
  37. package/dist/hr-employee-identity.d.ts +5 -0
  38. package/dist/hr-employee-identity.js +5 -0
  39. package/dist/hr-employee.d.ts +5 -0
  40. package/dist/hr-employee.js +21 -0
  41. package/dist/middleware/path-safety.js +1 -1
  42. package/dist/milestone-scope.js +1 -1
  43. package/dist/model-auto-assign.d.ts +1 -1
  44. package/dist/model-auto-assign.js +1 -1
  45. package/dist/models.d.ts +4 -0
  46. package/dist/models.js +21 -1
  47. package/dist/npm-update-utils.js +5 -44
  48. package/dist/pi-auto-resources.js +6 -3
  49. package/dist/plush-avatar.d.ts +10 -1
  50. package/dist/plush-avatar.js +13 -1
  51. package/dist/plush-cat-appearance.d.ts +97 -0
  52. package/dist/plush-cat-appearance.js +118 -0
  53. package/dist/plush-cat-whiskers.d.ts +6 -0
  54. package/dist/plush-cat-whiskers.js +42 -0
  55. package/dist/process-manager.d.ts +22 -21
  56. package/dist/process-manager.js +49 -83
  57. package/dist/prompt-optimizer.d.ts +1 -1
  58. package/dist/prompt-optimizer.js +1 -1
  59. package/dist/render-host.js +1 -19
  60. package/dist/scheduled-task-service.d.ts +41 -0
  61. package/dist/scheduled-task-service.js +294 -0
  62. package/dist/scheduled-task-state.d.ts +11 -0
  63. package/dist/scheduled-task-state.js +65 -0
  64. package/dist/scheduled-task-time.d.ts +4 -0
  65. package/dist/scheduled-task-time.js +82 -0
  66. package/dist/scheduled-task-types.d.ts +71 -0
  67. package/dist/scheduled-task-types.js +1 -0
  68. package/dist/server-ai-team-routes.js +3 -17
  69. package/dist/server-employee-routes.d.ts +3 -0
  70. package/dist/server-employee-routes.js +20 -38
  71. package/dist/server-file-routes.js +44 -1
  72. package/dist/server-pi-recommendation-routes.js +13 -4
  73. package/dist/server-request.d.ts +2 -0
  74. package/dist/server-request.js +9 -0
  75. package/dist/server-resume-routes.js +1 -1
  76. package/dist/server-scheduled-task-routes.d.ts +4 -0
  77. package/dist/server-scheduled-task-routes.js +34 -0
  78. package/dist/server-session-routes.d.ts +5 -13
  79. package/dist/server-session-routes.js +35 -70
  80. package/dist/server-task-routes.d.ts +6 -21
  81. package/dist/server-task-routes.js +25 -120
  82. package/dist/server-team-dispatch-routes.js +12 -2
  83. package/dist/server-workspace-routes.d.ts +1 -1
  84. package/dist/server-workspace-routes.js +33 -78
  85. package/dist/server.js +55 -20
  86. package/dist/session-model.d.ts +15 -0
  87. package/dist/session-model.js +23 -0
  88. package/dist/session-registry.d.ts +5 -1
  89. package/dist/session-registry.js +20 -11
  90. package/dist/session-request.d.ts +9 -0
  91. package/dist/session-request.js +27 -0
  92. package/dist/session-topic.d.ts +1 -1
  93. package/dist/session-topic.js +1 -1
  94. package/dist/session-transport.d.ts +3 -1
  95. package/dist/session-transport.js +11 -5
  96. package/dist/silicon-employee-draft.d.ts +2 -2
  97. package/dist/silicon-employee-draft.js +3 -3
  98. package/dist/socket-probe.d.ts +2 -0
  99. package/dist/socket-probe.js +20 -0
  100. package/dist/storage-schedules.d.ts +20 -0
  101. package/dist/storage-schedules.js +78 -0
  102. package/dist/storage-schema.d.ts +3 -0
  103. package/dist/storage-schema.js +702 -0
  104. package/dist/storage.d.ts +42 -8
  105. package/dist/storage.js +182 -715
  106. package/dist/structured-claude-adapter.js +12 -0
  107. package/dist/structured-claude-protocol.d.ts +4 -0
  108. package/dist/structured-claude-protocol.js +18 -4
  109. package/dist/structured-codex-adapter.js +11 -0
  110. package/dist/structured-exec-pump.js +79 -38
  111. package/dist/structured-failure.d.ts +21 -0
  112. package/dist/structured-failure.js +14 -0
  113. package/dist/structured-gemini-adapter.js +12 -2
  114. package/dist/structured-grok-adapter.js +12 -2
  115. package/dist/structured-opencode-adapter.js +8 -1
  116. package/dist/structured-qoder-adapter.js +14 -0
  117. package/dist/structured-session-manager.d.ts +13 -18
  118. package/dist/structured-session-manager.js +147 -517
  119. package/dist/system-employee.d.ts +1 -1
  120. package/dist/system-employee.js +1 -1
  121. package/dist/task-archive-policy.js +1 -1
  122. package/dist/task-read-model.d.ts +27 -0
  123. package/dist/task-read-model.js +47 -0
  124. package/dist/task-retention.d.ts +1 -1
  125. package/dist/task-retention.js +1 -1
  126. package/dist/task-title-service.d.ts +30 -0
  127. package/dist/task-title-service.js +122 -0
  128. package/dist/task-title.d.ts +1 -1
  129. package/dist/task-title.js +1 -1
  130. package/dist/terminal-daemon-client.js +1 -18
  131. package/dist/user-memory.js +1 -1
  132. package/dist/web-ui/asset-runtime.d.ts +12 -0
  133. package/dist/web-ui/asset-runtime.js +6 -0
  134. package/dist/web-ui/composer-avatar-activity.d.ts +6 -0
  135. package/dist/web-ui/composer-avatar-activity.js +29 -0
  136. package/dist/web-ui/content/ai-teams.js +13 -13
  137. package/dist/web-ui/content/module-manifest.json +1 -0
  138. package/dist/web-ui/content/modules/5478be410b112154/chat-render-PY6DIXNZ.js +24 -0
  139. package/dist/web-ui/content/modules/5478be410b112154/chunk-2YZLCGYN.js +1 -0
  140. package/dist/web-ui/content/modules/5478be410b112154/chunk-6CFHW3FA.js +111 -0
  141. package/dist/web-ui/content/modules/5478be410b112154/chunk-6XQEBIEX.js +1 -0
  142. package/dist/web-ui/content/modules/5478be410b112154/chunk-DDSO7OX7.js +1 -0
  143. package/dist/web-ui/content/modules/5478be410b112154/chunk-G3CXNMA2.js +20 -0
  144. package/dist/web-ui/content/modules/5478be410b112154/chunk-LGVAPHRS.js +36 -0
  145. package/dist/web-ui/content/modules/5478be410b112154/chunk-NURCNMRU.js +1 -0
  146. package/dist/web-ui/content/modules/5478be410b112154/chunk-PCMFGZKX.js +1 -0
  147. package/dist/web-ui/content/modules/5478be410b112154/chunk-QRNA4D7E.js +1 -0
  148. package/dist/web-ui/content/modules/5478be410b112154/chunk-TMRPHNJO.js +1 -0
  149. package/dist/web-ui/content/modules/5478be410b112154/chunk-WC7RK7LP.js +1 -0
  150. package/dist/web-ui/content/modules/5478be410b112154/chunk-YOXXNXOX.js +2 -0
  151. package/dist/web-ui/content/modules/5478be410b112154/chunk-YX475LXO.js +1 -0
  152. package/dist/web-ui/content/modules/5478be410b112154/employee-card-MDWKU4V3.js +1 -0
  153. package/dist/web-ui/content/modules/5478be410b112154/host-56WPSON2.js +1 -0
  154. package/dist/web-ui/content/modules/5478be410b112154/host-5QOHKHNC.js +1 -0
  155. package/dist/web-ui/content/modules/5478be410b112154/host-AWIJ5L2K.js +1 -0
  156. package/dist/web-ui/content/modules/5478be410b112154/host-BOZLGIT5.js +69 -0
  157. package/dist/web-ui/content/modules/5478be410b112154/host-EZV22Q7Y.js +4 -0
  158. package/dist/web-ui/content/modules/5478be410b112154/host-IW43Z5ZF.js +1 -0
  159. package/dist/web-ui/content/modules/5478be410b112154/host-JT7NIUQL.js +1 -0
  160. package/dist/web-ui/content/modules/5478be410b112154/host-JWEIDB55.js +3 -0
  161. package/dist/web-ui/content/modules/5478be410b112154/host-OU5TRYH6.js +2 -0
  162. package/dist/web-ui/content/modules/5478be410b112154/host-PRSTCPVE.js +5 -0
  163. package/dist/web-ui/content/modules/5478be410b112154/host-TWPJI3HE.js +1 -0
  164. package/dist/web-ui/content/modules/5478be410b112154/host-WZGJSNML.js +1 -0
  165. package/dist/web-ui/content/modules/5478be410b112154/host-X6KQVFD6.js +1 -0
  166. package/dist/web-ui/content/modules/5478be410b112154/host-YKCQFMQR.js +1 -0
  167. package/dist/web-ui/content/modules/5478be410b112154/host-bridge-2LZX6UUZ.js +1 -0
  168. package/dist/web-ui/content/modules/5478be410b112154/library-bridge-MQFJGTGY.js +4 -0
  169. package/dist/web-ui/content/modules/5478be410b112154/page-T3VUCU2W.js +3 -0
  170. package/dist/web-ui/content/modules/5478be410b112154/panel-HJBGBBPY.js +1 -0
  171. package/dist/web-ui/content/modules/5478be410b112154/runtime.js +920 -0
  172. package/dist/web-ui/content/modules/5478be410b112154/sha256-ANXV72UC.js +1 -0
  173. package/dist/web-ui/content/modules/5478be410b112154/task-board-host-AVMDIHIV.js +2 -0
  174. package/dist/web-ui/content/modules/5478be410b112154/workspace-agent-picker-KI7QVSK3.js +1 -0
  175. package/dist/web-ui/content/modules/5478be410b112154/workspace-window-L542FGP2.js +1 -0
  176. package/dist/web-ui/content/plush-avatar.js +177 -177
  177. package/dist/web-ui/content/scripts.js +1 -994
  178. package/dist/web-ui/content/styles.css +1 -1
  179. package/dist/web-ui/content/tailwind.css +1 -1
  180. package/dist/web-ui/embedded-assets.d.ts +32 -0
  181. package/dist/web-ui/embedded-assets.js +10 -8
  182. package/dist/web-ui/markdown.d.ts +3 -2
  183. package/dist/web-ui/markdown.js +61 -48
  184. package/dist/web-ui/message-file-target.d.ts +13 -0
  185. package/dist/web-ui/message-file-target.js +85 -0
  186. package/dist/web-ui/provider-identity.d.ts +1 -1
  187. package/dist/web-ui/provider-identity.js +3 -4
  188. package/dist/web-ui/react/local-preview/controller.d.ts +2 -0
  189. package/dist/web-ui/react/local-preview/controller.js +12 -0
  190. package/dist/web-ui/scripts.d.ts +10 -0
  191. package/dist/web-ui/scripts.js +42 -5
  192. package/package.json +6 -3
  193. package/browser-extension/icons/wand-128.png +0 -0
  194. package/browser-extension/icons/wand-16.png +0 -0
  195. package/browser-extension/icons/wand-32.png +0 -0
  196. package/browser-extension/icons/wand-48.png +0 -0
  197. package/dist/web-ui/content/licenses/codex-LICENSE.txt +0 -21
@@ -1,2 +1,3 @@
1
- /** Shared parser with only Wand's presentation and local-link adaptations. */
2
- export declare function renderChatMarkdown(text: string): string;
1
+ import { type MessageFileContext } from "./message-file-target.js";
2
+ /** Shared parser with source-session-bound local-file adaptations. */
3
+ export declare function renderChatMarkdown(text: string, context?: MessageFileContext): string;
@@ -1,5 +1,6 @@
1
1
  import { Marked, Renderer } from "marked";
2
2
  import { localFilePreviewHref, localHttpPreviewHref } from "./react/local-preview/controller.js";
3
+ import { messageFileTarget } from "./message-file-target.js";
3
4
  function escapeHtml(value) {
4
5
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
5
6
  .replace(/"/g, "&quot;").replace(/'/g, "&#39;");
@@ -37,18 +38,19 @@ function previewAttributes(value, href) {
37
38
  ["onclick", "if(window.__openLocalPreview){event.preventDefault();window.__openLocalPreview(this.getAttribute('data-local-preview-url'));}"],
38
39
  ];
39
40
  }
40
- function linkAttributes(target) {
41
- const path = serverFilePath(target);
41
+ function linkAttributes(target, context) {
42
+ const file = messageFileTarget(target, context);
43
+ const path = file?.path || serverFilePath(target);
42
44
  if (path) {
43
45
  const preview = /(?:\/$|\.(?:html?|)$)/i.test(path) ? localFilePreviewHref(path) : null;
44
46
  if (preview)
45
47
  return previewAttributes(path, preview);
46
48
  return [
47
- ["href", "/api/file-raw?download=1&path=" + encodeURIComponent(path)],
49
+ ["href", file?.downloadUrl || "/api/file-raw?download=1&path=" + encodeURIComponent(path)],
48
50
  ["class", "server-file-link"],
49
51
  ["data-server-file-path", path],
50
52
  ["title", "打开或下载服务端文件"],
51
- ["onclick", "if(window.__openFilePreview){event.preventDefault();window.__openFilePreview(this.getAttribute('data-server-file-path'));}"],
53
+ ...(path.startsWith("/") ? [["onclick", "if(window.__openFilePreview){event.preventDefault();window.__openFilePreview(this.getAttribute('data-server-file-path'));}"]] : []),
52
54
  ];
53
55
  }
54
56
  const preview = localHttpPreviewHref(target);
@@ -64,50 +66,61 @@ function linkAttributes(target) {
64
66
  // Marked parses Markdown; this renderer owns only Wand's output contracts.
65
67
  // Raw HTML is text, and links/images use explicit protocol allowlists. Never
66
68
  // enable raw HTML or accept renderer extensions from message content.
67
- const renderer = new Renderer();
68
- renderer.html = ({ text }) => escapeHtml(text);
69
- renderer.link = function ({ href, tokens }) {
70
- const label = this.parser.parseInline(tokens);
71
- const attributes = linkAttributes(href);
72
- if (!attributes)
73
- return "<span>" + label + "</span>";
74
- return "<a" + attributes.map(([name, value]) => " " + name + '="' + escapeHtml(value) + '"').join("") + ">" + label + "</a>";
75
- };
76
- renderer.image = ({ href, text, title }) => {
77
- if (!/^https?:\/\//i.test(href) && (!href.startsWith("/") || href.startsWith("//")))
78
- return escapeHtml(text);
79
- return '<img src="' + escapeHtml(href) + '" alt="' + escapeHtml(text) + '"' +
80
- (title ? ' title="' + escapeHtml(title) + '"' : "") + ' loading="lazy" decoding="async" role="button" tabindex="0"' +
81
- ' aria-label="查看' + escapeHtml(text || "图片") + '" onclick="event.stopPropagation();window.__openImageViewer?.(this);"' +
82
- ' onkeydown="if(event.key===\'Enter\'||event.key===\' \'){event.preventDefault();this.click();}">';
83
- };
84
- renderer.heading = function ({ depth, tokens }) {
85
- const tag = "h" + Math.min(depth + 1, 6);
86
- return "<" + tag + ">" + this.parser.parseInline(tokens) + "</" + tag + ">\n";
87
- };
88
- renderer.codespan = ({ text }) => '<code class="code-inline">' + escapeHtml(text) + "</code>";
89
- renderer.code = ({ text, lang }) => {
90
- const language = (lang || "").trim().split(/\s+/)[0];
91
- const content = text.endsWith("\n") ? text : text + "\n";
92
- return '<div class="code-block"><div class="code-block-header">' +
93
- '<span class="code-lang">' + escapeHtml(language) + "</span>" +
94
- '<button type="button" data-antd-control class="code-copy">复制</button></div>' +
95
- "<pre><code>" + escapeHtml(content) + "</code></pre></div>\n";
96
- };
97
- renderer.tablecell = function ({ header, align, tokens }) {
98
- const tag = header ? "th" : "td";
99
- const alignment = align ? ' style="text-align:' + align + '"' : "";
100
- return "<" + tag + alignment + ">" + this.parser.parseInline(tokens) + "</" + tag + ">\n";
101
- };
102
- const renderTable = renderer.table;
103
- renderer.table = function (token) {
104
- return '<div class="md-table-wrap">' + renderTable.call(this, token)
105
- .replace("<table>", '<table class="md-table">') + "</div>\n";
106
- };
107
- const markdown = new Marked({ renderer, breaks: true, gfm: true, async: false });
108
- /** Shared parser with only Wand's presentation and local-link adaptations. */
109
- export function renderChatMarkdown(text) {
69
+ function createMarkdown(context) {
70
+ const renderer = new Renderer();
71
+ renderer.html = ({ text }) => escapeHtml(text);
72
+ renderer.link = function ({ href, tokens }) {
73
+ const label = this.parser.parseInline(tokens);
74
+ const file = messageFileTarget(href, context);
75
+ const attributes = linkAttributes(href, context);
76
+ if (file?.previewUrl)
77
+ return '<span class="chat-html-preview" data-html-preview-url="' + escapeHtml(file.previewUrl) +
78
+ '" data-html-download-url="' + escapeHtml(file.downloadUrl) + '" data-html-label="' + escapeHtml(tokens.map(token => "text" in token ? String(token.text) : "").join("")) + '">' +
79
+ '<a href="' + escapeHtml(file.previewUrl) + '" class="local-preview-link" data-local-preview-url="' + escapeHtml(file.path) +
80
+ '" onclick="if(window.__openLocalPreview){event.preventDefault();window.__openLocalPreview(this.getAttribute(\'href\'));}">' + label + '</a></span>';
81
+ if (!attributes)
82
+ return "<span>" + label + "</span>";
83
+ return "<a" + attributes.map(([name, value]) => " " + name + '="' + escapeHtml(value) + '"').join("") + ">" + label + "</a>";
84
+ };
85
+ renderer.image = ({ href, text, title }) => {
86
+ const file = messageFileTarget(href, context);
87
+ const src = file?.rawUrl || href;
88
+ if (!file && !/^https?:\/\//i.test(href) && (!href.startsWith("/") || href.startsWith("//")))
89
+ return escapeHtml(text);
90
+ return '<img src="' + escapeHtml(src) + '" alt="' + escapeHtml(text) + '"' +
91
+ (title ? ' title="' + escapeHtml(title) + '"' : "") + ' loading="lazy" decoding="async" role="button" tabindex="0"' +
92
+ ' aria-label="查看' + escapeHtml(text || "图片") + '" onclick="event.stopPropagation();window.__openImageViewer?.(this);"' +
93
+ ' onkeydown="if(event.key===\'Enter\'||event.key===\' \'){event.preventDefault();this.click();}">';
94
+ };
95
+ renderer.heading = function ({ depth, tokens }) {
96
+ const tag = "h" + Math.min(depth + 1, 6);
97
+ return "<" + tag + ">" + this.parser.parseInline(tokens) + "</" + tag + ">\n";
98
+ };
99
+ renderer.codespan = ({ text }) => '<code class="code-inline">' + escapeHtml(text) + "</code>";
100
+ renderer.code = ({ text, lang }) => {
101
+ const language = (lang || "").trim().split(/\s+/)[0];
102
+ const content = text.endsWith("\n") ? text : text + "\n";
103
+ return '<div class="code-block"><div class="code-block-header">' +
104
+ '<span class="code-lang">' + escapeHtml(language) + "</span>" +
105
+ '<button type="button" data-antd-control class="code-copy">复制</button></div>' +
106
+ "<pre><code>" + escapeHtml(content) + "</code></pre></div>\n";
107
+ };
108
+ renderer.tablecell = function ({ header, align, tokens }) {
109
+ const tag = header ? "th" : "td";
110
+ const alignment = align ? ' style="text-align:' + align + '"' : "";
111
+ return "<" + tag + alignment + ">" + this.parser.parseInline(tokens) + "</" + tag + ">\n";
112
+ };
113
+ const renderTable = renderer.table;
114
+ renderer.table = function (token) {
115
+ return '<div class="md-table-wrap">' + renderTable.call(this, token)
116
+ .replace("<table>", '<table class="md-table">') + "</div>\n";
117
+ };
118
+ return new Marked({ renderer, breaks: true, gfm: true, async: false });
119
+ }
120
+ const markdown = createMarkdown({});
121
+ /** Shared parser with source-session-bound local-file adaptations. */
122
+ export function renderChatMarkdown(text, context = {}) {
110
123
  if (!text)
111
124
  return "";
112
- return '<div class="markdown-content">' + markdown.parse(text, { async: false }) + "</div>";
125
+ return '<div class="markdown-content">' + (context.cwd || context.sessionId ? createMarkdown(context) : markdown).parse(text, { async: false }) + "</div>";
113
126
  }
@@ -0,0 +1,13 @@
1
+ export interface MessageFileContext {
2
+ cwd?: string;
3
+ sessionId?: string;
4
+ }
5
+ export interface MessageFileTarget {
6
+ path: string;
7
+ rawUrl: string;
8
+ downloadUrl: string;
9
+ previewUrl: string | null;
10
+ }
11
+ /** Only explicit file references are resolved, never prose or arbitrary web URLs.
12
+ * Relative references belong to their source session, not the active page URL. */
13
+ export declare function messageFileTarget(input: string, context?: MessageFileContext): MessageFileTarget | null;
@@ -0,0 +1,85 @@
1
+ import { localFilePreviewHref } from "./react/local-preview/controller.js";
2
+ /** Only explicit file references are resolved, never prose or arbitrary web URLs.
3
+ * Relative references belong to their source session, not the active page URL. */
4
+ export function messageFileTarget(input, context = {}) {
5
+ let path = input.trim();
6
+ if (/^file:/i.test(path)) {
7
+ try {
8
+ const url = new URL(path);
9
+ if (url.hostname && url.hostname !== "localhost")
10
+ return null;
11
+ path = decodeURIComponent(url.pathname);
12
+ }
13
+ catch {
14
+ return null;
15
+ }
16
+ }
17
+ else if (/^\/api\/local-file\/[A-Za-z0-9_-]+\/$/.test(path)) {
18
+ try {
19
+ const encoded = path.split("/")[3].replace(/-/g, "+").replace(/_/g, "/");
20
+ const bytes = Uint8Array.from(atob(encoded), char => char.charCodeAt(0));
21
+ path = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
22
+ if (!path.startsWith("/"))
23
+ return null;
24
+ }
25
+ catch {
26
+ return null;
27
+ }
28
+ }
29
+ else if (/^\/api\/session-file\/[^/?#]+\?/.test(path)) {
30
+ try {
31
+ const url = new URL(path, "http://wand.invalid");
32
+ const relative = url.searchParams.get("path") || "";
33
+ if (relative.startsWith("/"))
34
+ return null;
35
+ return messageFileTarget(relative, { sessionId: decodeURIComponent(url.pathname.split("/")[3]) });
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ }
41
+ else if (path.startsWith("/api/file-raw?")) {
42
+ try {
43
+ path = new URL(path, "http://wand.invalid").searchParams.get("path") || "";
44
+ }
45
+ catch {
46
+ return null;
47
+ }
48
+ }
49
+ else {
50
+ if (/^(?:[a-z][a-z\d+.-]*:|\/\/|#)/i.test(path))
51
+ return null;
52
+ try {
53
+ path = decodeURIComponent(path);
54
+ }
55
+ catch { /* literal percent */ }
56
+ }
57
+ path = path.replace(/#L\d+(?:C\d+)?$/i, "").replace(/:\d+(?::\d+)?$/, "");
58
+ if (!path || /[\u0000-\u001f]/.test(path) || /^\/(?:api|android|macos)(?:\/|$)/.test(path))
59
+ return null;
60
+ const html = /\.html?$/i.test(path);
61
+ if (!path.startsWith("/")) {
62
+ // No trusted context: keep the label rather than guessing the server cwd.
63
+ if (!context.sessionId && !context.cwd?.startsWith("/"))
64
+ return null;
65
+ if (context.sessionId) {
66
+ const route = "/api/session-file/" + encodeURIComponent(context.sessionId) + "?path=" + encodeURIComponent(path);
67
+ return { path, rawUrl: route, downloadUrl: route + "&mode=download", previewUrl: html ? route + "&mode=preview" : null };
68
+ }
69
+ const parts = [];
70
+ for (const part of path.split("/")) {
71
+ if (!part || part === ".")
72
+ continue;
73
+ if (part === "..") {
74
+ if (!parts.length)
75
+ return null;
76
+ parts.pop();
77
+ }
78
+ else
79
+ parts.push(part);
80
+ }
81
+ path = context.cwd.replace(/\/$/, "") + "/" + parts.join("/");
82
+ }
83
+ const rawUrl = "/api/file-raw?path=" + encodeURIComponent(path);
84
+ return { path, rawUrl, downloadUrl: rawUrl + "&download=1", previewUrl: html ? localFilePreviewHref(path) : null };
85
+ }
@@ -37,7 +37,7 @@ export declare function agentToolDisplayName(provider: unknown, engine?: AgentTo
37
37
  export declare function isProviderId(value: unknown): value is ProviderId;
38
38
  export declare const CLAUDE_BRAND_COLOR = "#D97757";
39
39
  export declare const CLAUDE_LOGO_PATH = "m4.7144 15.9555 4.7174-2.6471.079-.2307-.079-.1275h-.2307l-.7893-.0486-2.6956-.0729-2.3375-.0971-2.2646-.1214-.5707-.1215-.5343-.7042.0546-.3522.4797-.3218.686.0608 1.5179.1032 2.2767.1578 1.6514.0972 2.4468.255h.3886l.0546-.1579-.1336-.0971-.1032-.0972L6.973 9.8356l-2.55-1.6879-1.3356-.9714-.7225-.4918-.3643-.4614-.1578-1.0078.6557-.7225.8803.0607.2246.0607.8925.686 1.9064 1.4754 2.4893 1.8336.3643.3035.1457-.1032.0182-.0728-.164-.2733-1.3539-2.4467-1.445-2.4893-.6435-1.032-.17-.6194c-.0607-.255-.1032-.4674-.1032-.7285L6.287.1335 6.6997 0l.9957.1336.419.3642.6192 1.4147 1.0018 2.2282 1.5543 3.0296.4553.8985.2429.8318.091.255h.1579v-.1457l.1275-1.706.2368-2.0947.2307-2.6957.0789-.7589.3764-.9107.7468-.4918.5828.2793.4797.686-.0668.4433-.2853 1.8517-.5586 2.9021-.3643 1.9429h.2125l.2429-.2429.9835-1.3053 1.6514-2.0643.7286-.8196.85-.9046.5464-.4311h1.0321l.759 1.1293-.34 1.1657-1.0625 1.3478-.8804 1.1414-1.2628 1.7-.7893 1.36.0729.1093.1882-.0183 2.8535-.607 1.5421-.2794 1.8396-.3157.8318.3886.091.3946-.3278.8075-1.967.4857-2.3072.4614-3.4364.8136-.0425.0304.0486.0607 1.5482.1457.6618.0364h1.621l3.0175.2247.7892.522.4736.6376-.079.4857-1.2142.6193-1.6393-.3886-3.825-.9107-1.3113-.3279h-.1822v.1093l1.0929 1.0686 2.0035 1.8092 2.5075 2.3314.1275.5768-.3218.4554-.34-.0486-2.2039-1.6575-.85-.7468-1.9246-1.621h-.1275v.17l.4432.6496 2.3436 3.5214.1214 1.0807-.17.3521-.6071.2125-.6679-.1214-1.3721-1.9246L14.38 17.959l-1.1414-1.9428-.1397.079-.674 7.2552-.3156.3703-.7286.2793-.6071-.4614-.3218-.7468.3218-1.4753.3886-1.9246.3157-1.53.2853-1.9004.17-.6314-.0121-.0425-.1397.0182-1.4328 1.9672-2.1796 2.9446-1.7243 1.8456-.4128.164-.7164-.3704.0667-.6618.4008-.5889 2.386-3.0357 1.4389-1.882.929-1.0868-.0062-.1579h-.0546l-6.3385 4.1164-1.1293.1457-.4857-.4554.0608-.7467.2307-.2429 1.9064-1.3114Z";
40
- export declare const CODEX_LOGO_PATH = "M8.086.457a6.105 6.105 0 013.046-.415c1.333.153 2.521.72 3.564 1.7a.117.117 0 00.107.029c1.408-.346 2.762-.224 4.061.366l.063.03.154.076c1.357.703 2.33 1.77 2.918 3.198.278.679.418 1.388.421 2.126a5.655 5.655 0 01-.18 1.631.167.167 0 00.04.155 5.982 5.982 0 011.578 2.891c.385 1.901-.01 3.615-1.183 5.14l-.182.22a6.063 6.063 0 01-2.934 1.851.162.162 0 00-.108.102c-.255.736-.511 1.364-.987 1.992-1.199 1.582-2.962 2.462-4.948 2.451-1.583-.008-2.986-.587-4.21-1.736a.145.145 0 00-.14-.032c-.518.167-1.04.191-1.604.185a5.924 5.924 0 01-2.595-.622 6.058 6.058 0 01-2.146-1.781c-.203-.269-.404-.522-.551-.821a7.74 7.74 0 01-.495-1.283 6.11 6.11 0 01-.017-3.064.166.166 0 00.008-.074.115.115 0 00-.037-.064 5.958 5.958 0 01-1.38-2.202 5.196 5.196 0 01-.333-1.589 6.915 6.915 0 01.188-2.132c.45-1.484 1.309-2.648 2.577-3.493.282-.188.55-.334.802-.438.286-.12.573-.22.861-.304a.129.129 0 00.087-.087A6.016 6.016 0 015.635 2.31C6.315 1.464 7.132.846 8.086.457zm-.804 7.85a.848.848 0 00-1.473.842l1.694 2.965-1.688 2.848a.849.849 0 001.46.864l1.94-3.272a.849.849 0 00.007-.854l-1.94-3.393zm5.446 6.24a.849.849 0 000 1.695h4.848a.849.849 0 000-1.696h-4.848z";
40
+ export declare const CODEX_LOGO_PATH = "M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z";
41
41
  export declare const PI_LOGO_PATH = "M165.29 165.29H517.36V400H400V517.36H282.65V634.72H165.29ZM282.65 282.65V400H400V282.65Z";
42
42
  export declare const PI_LOGO_DOT_PATH = "M517.36 400H634.72V634.72H517.36Z";
43
43
  export declare const GEMINI_LOGO_PATH = "M11.04 19.32Q12 21.51 12 24q0-2.49.93-4.68.96-2.19 2.58-3.81t3.81-2.55Q21.51 12 24 12q-2.49 0-4.68-.93a12.3 12.3 0 0 1-3.81-2.58 12.3 12.3 0 0 1-2.58-3.81Q12 2.49 12 0q0 2.49-.96 4.68-.93 2.19-2.55 3.81a12.3 12.3 0 0 1-3.81 2.58Q2.49 12 0 12q2.49 0 4.68.96 2.19.93 3.81 2.55t2.55 3.81";
@@ -45,13 +45,12 @@ const PROVIDER_ID_SET = new Set(PROVIDER_IDS);
45
45
  export function isProviderId(value) {
46
46
  return typeof value === "string" && PROVIDER_ID_SET.has(value);
47
47
  }
48
- // Claude paths match the existing Android BrandLogos source (simple-icons,
48
+ // Claude/Codex paths match the existing Android BrandLogos source (simple-icons,
49
49
  // CC0). Grok uses xAI's mark path already shipped by the Web new-session view.
50
50
  // Claude keeps the official Anthropic orange instead of inheriting Wand accent.
51
51
  export const CLAUDE_BRAND_COLOR = "#D97757";
52
52
  export const CLAUDE_LOGO_PATH = "m4.7144 15.9555 4.7174-2.6471.079-.2307-.079-.1275h-.2307l-.7893-.0486-2.6956-.0729-2.3375-.0971-2.2646-.1214-.5707-.1215-.5343-.7042.0546-.3522.4797-.3218.686.0608 1.5179.1032 2.2767.1578 1.6514.0972 2.4468.255h.3886l.0546-.1579-.1336-.0971-.1032-.0972L6.973 9.8356l-2.55-1.6879-1.3356-.9714-.7225-.4918-.3643-.4614-.1578-1.0078.6557-.7225.8803.0607.2246.0607.8925.686 1.9064 1.4754 2.4893 1.8336.3643.3035.1457-.1032.0182-.0728-.164-.2733-1.3539-2.4467-1.445-2.4893-.6435-1.032-.17-.6194c-.0607-.255-.1032-.4674-.1032-.7285L6.287.1335 6.6997 0l.9957.1336.419.3642.6192 1.4147 1.0018 2.2282 1.5543 3.0296.4553.8985.2429.8318.091.255h.1579v-.1457l.1275-1.706.2368-2.0947.2307-2.6957.0789-.7589.3764-.9107.7468-.4918.5828.2793.4797.686-.0668.4433-.2853 1.8517-.5586 2.9021-.3643 1.9429h.2125l.2429-.2429.9835-1.3053 1.6514-2.0643.7286-.8196.85-.9046.5464-.4311h1.0321l.759 1.1293-.34 1.1657-1.0625 1.3478-.8804 1.1414-1.2628 1.7-.7893 1.36.0729.1093.1882-.0183 2.8535-.607 1.5421-.2794 1.8396-.3157.8318.3886.091.3946-.3278.8075-1.967.4857-2.3072.4614-3.4364.8136-.0425.0304.0486.0607 1.5482.1457.6618.0364h1.621l3.0175.2247.7892.522.4736.6376-.079.4857-1.2142.6193-1.6393-.3886-3.825-.9107-1.3113-.3279h-.1822v.1093l1.0929 1.0686 2.0035 1.8092 2.5075 2.3314.1275.5768-.3218.4554-.34-.0486-2.2039-1.6575-.85-.7468-1.9246-1.621h-.1275v.17l.4432.6496 2.3436 3.5214.1214 1.0807-.17.3521-.6071.2125-.6679-.1214-1.3721-1.9246L14.38 17.959l-1.1414-1.9428-.1397.079-.674 7.2552-.3156.3703-.7286.2793-.6071-.4614-.3218-.7468.3218-1.4753.3886-1.9246.3157-1.53.2853-1.9004.17-.6314-.0121-.0425-.1397.0182-1.4328 1.9672-2.1796 2.9446-1.7243 1.8456-.4128.164-.7164-.3704.0667-.6618.4008-.5889 2.386-3.0357 1.4389-1.882.929-1.0868-.0062-.1579h-.0546l-6.3385 4.1164-1.1293.1457-.4857-.4554.0608-.7467.2307-.2429 1.9064-1.3114Z";
53
- // Codex cloud/prompt mark, LobeHub icons (MIT); license: content/licenses/codex-LICENSE.txt.
54
- export const CODEX_LOGO_PATH = "M8.086.457a6.105 6.105 0 013.046-.415c1.333.153 2.521.72 3.564 1.7a.117.117 0 00.107.029c1.408-.346 2.762-.224 4.061.366l.063.03.154.076c1.357.703 2.33 1.77 2.918 3.198.278.679.418 1.388.421 2.126a5.655 5.655 0 01-.18 1.631.167.167 0 00.04.155 5.982 5.982 0 011.578 2.891c.385 1.901-.01 3.615-1.183 5.14l-.182.22a6.063 6.063 0 01-2.934 1.851.162.162 0 00-.108.102c-.255.736-.511 1.364-.987 1.992-1.199 1.582-2.962 2.462-4.948 2.451-1.583-.008-2.986-.587-4.21-1.736a.145.145 0 00-.14-.032c-.518.167-1.04.191-1.604.185a5.924 5.924 0 01-2.595-.622 6.058 6.058 0 01-2.146-1.781c-.203-.269-.404-.522-.551-.821a7.74 7.74 0 01-.495-1.283 6.11 6.11 0 01-.017-3.064.166.166 0 00.008-.074.115.115 0 00-.037-.064 5.958 5.958 0 01-1.38-2.202 5.196 5.196 0 01-.333-1.589 6.915 6.915 0 01.188-2.132c.45-1.484 1.309-2.648 2.577-3.493.282-.188.55-.334.802-.438.286-.12.573-.22.861-.304a.129.129 0 00.087-.087A6.016 6.016 0 015.635 2.31C6.315 1.464 7.132.846 8.086.457zm-.804 7.85a.848.848 0 00-1.473.842l1.694 2.965-1.688 2.848a.849.849 0 001.46.864l1.94-3.272a.849.849 0 00.007-.854l-1.94-3.393zm5.446 6.24a.849.849 0 000 1.695h4.848a.849.849 0 000-1.696h-4.848z";
53
+ export const CODEX_LOGO_PATH = "M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z";
55
54
  // Official Pi mark from https://pi.dev/logo-auto.svg. Keep it local so provider
56
55
  // logos remain available when Wand is offline.
57
56
  export const PI_LOGO_PATH = "M165.29 165.29H517.36V400H400V517.36H282.65V634.72H165.29ZM282.65 282.65V400H400V282.65Z";
@@ -86,7 +85,7 @@ export function renderProviderLogoMarkup(value) {
86
85
  return `<svg ${common} viewBox="0 0 24 24" fill="${CLAUDE_BRAND_COLOR}"><path d="${CLAUDE_LOGO_PATH}"/></svg>`;
87
86
  }
88
87
  if (provider === "codex") {
89
- return `<svg ${common} viewBox="0 0 24 24" fill="currentColor"><path d="${CODEX_LOGO_PATH}" fill-rule="evenodd"/></svg>`;
88
+ return `<svg ${common} viewBox="0 0 24 24" fill="currentColor"><path d="${CODEX_LOGO_PATH}"/></svg>`;
90
89
  }
91
90
  return `<svg ${common} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v3M12 18v3M3 12h3M18 12h3M5.6 5.6l2.1 2.1M16.3 16.3l2.1 2.1M5.6 18.4l2.1-2.1M16.3 7.7l2.1-2.1"/><circle cx="12" cy="12" r="3"/></svg>`;
92
91
  }
@@ -14,7 +14,9 @@ export declare function localHttpPreviewHref(input: string): string | null;
14
14
  export declare function localFilePreviewHref(input: string): string | null;
15
15
  declare function openFile(value: string): boolean;
16
16
  declare function openUrl(value: string): boolean;
17
+ export declare function isArtifactPreviewUrl(value: string): boolean;
17
18
  export declare const localPreviewController: {
19
+ openPreview(previewUrl: string, label?: string): boolean;
18
20
  subscribe(listener: Listener): () => void;
19
21
  getSnapshot(): LocalPreviewSnapshot;
20
22
  isOpen(): boolean;
@@ -60,6 +60,8 @@ export function localFilePreviewHref(input) {
60
60
  }
61
61
  function normalizeLocalFile(input) {
62
62
  const trimmed = input.trim();
63
+ if (isArtifactPreviewUrl(trimmed))
64
+ return trimmed;
63
65
  if (!trimmed)
64
66
  throw new Error("请输入文件或目录路径。");
65
67
  return localFilePreviewHref(trimmed) ?? (() => { throw new Error("请输入服务端文件路径。"); })();
@@ -108,7 +110,17 @@ function openUrl(value) {
108
110
  return false;
109
111
  }
110
112
  }
113
+ export function isArtifactPreviewUrl(value) {
114
+ return /^\/api\/local-file\/[A-Za-z0-9_-]+\/$/.test(value) ||
115
+ /^\/api\/session-file\/[^/?#]+\?path=[^&#]+&mode=preview$/.test(value);
116
+ }
111
117
  export const localPreviewController = {
118
+ openPreview(previewUrl, label = "HTML 预览") {
119
+ if (!isArtifactPreviewUrl(previewUrl))
120
+ return false;
121
+ publish({ open: true, mode: "file", value: previewUrl, previewUrl, sourceLabel: label, error: null });
122
+ return true;
123
+ },
112
124
  subscribe(listener) {
113
125
  listeners.add(listener);
114
126
  return () => listeners.delete(listener);
@@ -16,3 +16,13 @@ export declare function getPlushAvatarChunk(requestedHash?: string): {
16
16
  };
17
17
  /** Blocking, content-versioned device preference bootstrap. */
18
18
  export declare function getThemePreloadAsset(): VersionedWebAsset;
19
+ export interface BrowserModuleManifest {
20
+ entry: string;
21
+ eager: readonly string[];
22
+ files: readonly string[];
23
+ features?: Record<string, string>;
24
+ }
25
+ /** Snapshot a complete build before serving its bootstrap. Old processes keep
26
+ * its lazy bytes even if npm removes/replaces content during self-update. */
27
+ export declare function getBrowserModuleManifest(): BrowserModuleManifest;
28
+ export declare function getBrowserModule(file: string): string | undefined;
@@ -24,30 +24,32 @@ function readScriptCache() {
24
24
  // Retain the last disk read, or fall back to the embedded copy.
25
25
  }
26
26
  }
27
- function injectRuntimeValues(source, configPath, chunkHash, plushHash) {
27
+ function injectRuntimeValues(source, configPath, chunkHash, plushHash, entry) {
28
28
  return source
29
29
  // This is a JS string literal, not HTML text. JSON escaping also handles
30
30
  // quotes, backslashes and newlines in nonstandard configuration paths.
31
31
  .replace('"${wandConfigPath}"', JSON.stringify(configPath))
32
32
  .replace("${aiTeamsChunkSrc}", `/assets/ai-teams.js?v=${chunkHash}`)
33
- .replace("${plushAvatarChunkSrc}", `/assets/plush-avatar.js?v=${plushHash}`);
33
+ .replace("${plushAvatarChunkSrc}", `/assets/plush-avatar.js?v=${plushHash}`)
34
+ .replace("${browserModuleSrc}", `/assets/${entry}`);
34
35
  }
35
36
  export function getScriptAsset(configPath, requestedHash) {
36
37
  readScriptCache();
37
38
  const teamHash = getAiTeamsChunk().hash;
38
39
  const plushHash = getPlushAvatarChunk().hash;
39
- const chunkHash = `${teamHash}:${plushHash}`;
40
+ const entry = getBrowserModuleManifest().entry;
41
+ const chunkHash = `${teamHash}:${plushHash}:${entry}`;
40
42
  if (!_scriptAsset || _scriptAsset.source !== _scriptCache
41
43
  || _scriptAsset.configPath !== configPath || _scriptAsset.chunkHash !== chunkHash) {
42
44
  _scriptAsset = {
43
- ...versionWebAsset(injectRuntimeValues(_scriptCache, configPath, teamHash, plushHash)),
45
+ ...versionWebAsset(injectRuntimeValues(_scriptCache, configPath, teamHash, plushHash, entry)),
44
46
  source: _scriptCache, configPath, chunkHash,
45
47
  };
46
48
  }
47
49
  if (requestedHash && requestedHash !== _scriptAsset.hash) {
48
50
  if (!_embeddedScriptAsset || _embeddedScriptAsset.configPath !== configPath) {
49
51
  _embeddedScriptAsset = {
50
- ...versionWebAsset(injectRuntimeValues(EMBEDDED_WEB_ASSETS.scriptsJs, configPath, embeddedAiTeamsChunk.hash, embeddedPlushAvatarChunk.hash)),
52
+ ...versionWebAsset(injectRuntimeValues(EMBEDDED_WEB_ASSETS.scriptsJs, configPath, embeddedAiTeamsChunk.hash, embeddedPlushAvatarChunk.hash, EMBEDDED_WEB_ASSETS.browserModuleManifest.entry)),
51
53
  configPath,
52
54
  };
53
55
  }
@@ -108,3 +110,38 @@ export function getThemePreloadAsset() {
108
110
  return themePreloadAsset;
109
111
  }
110
112
  const themePreloadAsset = versionWebAsset(EMBEDDED_WEB_ASSETS.themePreloadJs);
113
+ let browserManifest = EMBEDDED_WEB_ASSETS.browserModuleManifest;
114
+ let browserManifestKey = "";
115
+ const browserBuilds = [];
116
+ /** Snapshot a complete build before serving its bootstrap. Old processes keep
117
+ * its lazy bytes even if npm removes/replaces content during self-update. */
118
+ export function getBrowserModuleManifest() {
119
+ try {
120
+ const manifestPath = path.join(__dirname, "content/module-manifest.json");
121
+ const stat = fs.statSync(manifestPath);
122
+ const key = `${stat.mtimeMs}:${stat.size}`;
123
+ if (key !== browserManifestKey) {
124
+ const next = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
125
+ if (!next.files.every(file => /^modules\/[a-f0-9]{16}\/[A-Za-z0-9_-]+\.js$/.test(file))
126
+ || !next.files.includes(next.entry) || !next.eager.every(file => next.files.includes(file))) {
127
+ throw new Error("Invalid browser module manifest");
128
+ }
129
+ const files = new Map(next.files.map(file => [file, fs.readFileSync(path.join(__dirname, "content", file), "utf8")]));
130
+ browserBuilds.unshift(files);
131
+ browserBuilds.splice(2);
132
+ browserManifest = next;
133
+ browserManifestKey = key;
134
+ }
135
+ }
136
+ catch { /* Retain the last complete graph (or embedded build), never mix versions. */ }
137
+ return browserManifest;
138
+ }
139
+ export function getBrowserModule(file) {
140
+ getBrowserModuleManifest();
141
+ for (const build of browserBuilds) {
142
+ const content = build.get(file);
143
+ if (content !== undefined)
144
+ return content;
145
+ }
146
+ return Object.hasOwn(EMBEDDED_WEB_ASSETS.browserModules, file) ? EMBEDDED_WEB_ASSETS.browserModules[file] : undefined;
147
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@co0ontty/wand",
3
- "version": "4.89.5",
3
+ "version": "4.90.0",
4
4
  "description": "A web console for Claude Code, Codex, OpenCode, and other local CLI tools.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,7 +23,8 @@
23
23
  "preferGlobal": true,
24
24
  "scripts": {
25
25
  "generate:web-assets": "node scripts/generate-web-assets.js",
26
- "build": "node scripts/check-node-version.js && rm -rf dist && node scripts/bundle-tailwind.js && node scripts/bundle-xterm.js && node scripts/bundle-qrcode.js && node scripts/bundle-browser.js && npm run generate:web-assets && tsc -p tsconfig.json && npm run build:copy-content && node scripts/check-bundle-budget.js && (npm run build:render-bin || echo '[wand] WARNING: build:render-bin failed; dist/native/ has no wand-render (run: npm run build:render-bin)' >&2) && node scripts/stamp-build-info.js && node scripts/fix-dist-permissions.js",
26
+ "build": "node src/deployment-lock.ts --run-project -- npm run build:body",
27
+ "build:body": "node scripts/check-node-version.js && rm -rf dist && node scripts/bundle-tailwind.js && node scripts/bundle-xterm.js && node scripts/bundle-qrcode.js && node scripts/bundle-browser.js && npm run generate:web-assets && tsc -p tsconfig.json && npm run build:copy-content && node scripts/check-bundle-budget.js && (npm run build:render-bin || echo '[wand] WARNING: build:render-bin failed; dist/native/ has no wand-render (run: npm run build:render-bin)' >&2) && node scripts/stamp-build-info.js && node scripts/fix-dist-permissions.js",
27
28
  "build:copy-content": "cp -r src/web-ui/content dist/web-ui/ && cp src/decision-worker.py dist/decision-worker.py && node scripts/minify-dist-content.js",
28
29
  "build:render-bin": "node scripts/stage-render-binaries.js --all",
29
30
  "speech:install": "node scripts/install-speech-runtime.js",
@@ -31,7 +32,8 @@
31
32
  "build:render-native": "cargo build --release --manifest-path render/Cargo.toml -p wand-renderd",
32
33
  "build:structured-render-native": "cargo build --release --manifest-path render/Cargo.toml -p wand-structured-renderd",
33
34
  "dev": "node scripts/check-node-version.js && node scripts/bundle-tailwind.js && node scripts/bundle-xterm.js && node scripts/bundle-qrcode.js && node scripts/bundle-browser.js --development && npm run generate:web-assets && tsx src/cli.ts web",
34
- "check": "node scripts/check-node-version.js && node scripts/bundle-tailwind.js && node scripts/bundle-xterm.js && node scripts/bundle-qrcode.js && node scripts/bundle-browser.js && npm run generate:web-assets && tsc --noEmit -p tsconfig.json && tsc --noEmit -p tsconfig.browser.json && tsc --noEmit -p tsconfig.react.json",
35
+ "check": "node src/deployment-lock.ts --run-project -- npm run check:body",
36
+ "check:body": "node scripts/check-node-version.js && node scripts/bundle-tailwind.js && node scripts/bundle-xterm.js && node scripts/bundle-qrcode.js && node scripts/bundle-browser.js && npm run generate:web-assets && tsc --noEmit -p tsconfig.json && tsc --noEmit -p tsconfig.browser.json && tsc --noEmit -p tsconfig.react.json",
35
37
  "test": "node scripts/check-node-version.js && node --test --import tsx tests/*.test.ts",
36
38
  "check:bundle-budget": "node scripts/check-node-version.js && node scripts/check-bundle-budget.js",
37
39
  "sync:brand-assets": "node scripts/sync-brand-assets.js",
@@ -82,6 +84,7 @@
82
84
  "@xterm/xterm": "^6.0.0",
83
85
  "antd": "6.6.5",
84
86
  "esbuild": "^0.28.0",
87
+ "fast-sha256": "1.3.0",
85
88
  "qrcode": "^1.5.4",
86
89
  "react": "^19.2.7",
87
90
  "react-dom": "^19.2.7",
Binary file
Binary file
Binary file
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2023 LobeHub
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.