@co0ontty/wand 1.41.3 → 1.42.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.
@@ -1 +1,3 @@
1
+ import { type EmbeddedVendorAssetPath } from "./embedded-assets.js";
2
+ export declare function vendorAssetUrl(relPath: EmbeddedVendorAssetPath): string;
1
3
  export declare function renderApp(configPath: string): string;
@@ -1,36 +1,13 @@
1
1
  // Main entry point for web-ui module
2
2
  // Combines CSS and JavaScript into a single HTML document
3
- import { readFileSync, existsSync } from "node:fs";
4
- import { createHash } from "node:crypto";
5
- import path from "node:path";
6
- import { fileURLToPath } from "node:url";
3
+ import { EMBEDDED_WEB_ASSETS } from "./embedded-assets.js";
7
4
  import { getCSSStyles } from "./styles.js";
8
5
  import { getScriptContent } from "./scripts.js";
9
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
6
  // Use String.fromCharCode to avoid template literal interpretation of </script>
11
7
  const scriptClose = String.fromCharCode(60, 47) + "script>";
12
8
  const scriptOpen = "<" + "script";
13
- // Vendor assets are served with immutable cache headers, so the URL must
14
- // change whenever the file content changes — otherwise the browser keeps the
15
- // stale copy across upgrades. We append ?v=<sha-prefix> derived from the
16
- // on-disk bundle so each new build busts the cache automatically.
17
- const vendorHashCache = new Map();
18
- function vendorAssetUrl(relPath) {
19
- if (!vendorHashCache.has(relPath)) {
20
- const fullPath = path.join(__dirname, "content", relPath);
21
- let hash = "0";
22
- try {
23
- if (existsSync(fullPath)) {
24
- const buf = readFileSync(fullPath);
25
- hash = createHash("md5").update(buf).digest("hex").slice(0, 8);
26
- }
27
- }
28
- catch {
29
- hash = String(Date.now()).slice(-8);
30
- }
31
- vendorHashCache.set(relPath, hash);
32
- }
33
- return `${relPath}?v=${vendorHashCache.get(relPath)}`;
9
+ export function vendorAssetUrl(relPath) {
10
+ return `${relPath}?v=${EMBEDDED_WEB_ASSETS.vendor[relPath].hash}`;
34
11
  }
35
12
  export function renderApp(configPath) {
36
13
  const cssStyles = getCSSStyles();
@@ -1,6 +1,7 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
+ import { EMBEDDED_WEB_ASSETS } from "./embedded-assets.js";
4
5
  function escapeHtml(value) {
5
6
  return String(value)
6
7
  .replace(/&/g, "&amp;")
@@ -10,7 +11,7 @@ function escapeHtml(value) {
10
11
  .replace(/'/g, "&#39;");
11
12
  }
12
13
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
- let _scriptCache = null;
14
+ let _scriptCache = EMBEDDED_WEB_ASSETS.scriptsJs;
14
15
  let _scriptCacheMtimeMs = 0;
15
16
  export function getScriptContent(configPath) {
16
17
  const scriptPath = path.join(__dirname, "content", "scripts.js");
@@ -22,11 +23,9 @@ export function getScriptContent(configPath) {
22
23
  }
23
24
  }
24
25
  catch {
25
- // During self-update npm can briefly replace the global package directory.
26
- // Keep serving the already-loaded UI until /api/restart switches process.
27
- if (_scriptCache === null) {
28
- _scriptCache = fs.readFileSync(scriptPath, "utf-8");
29
- }
26
+ // During self-update npm can replace the global package directory while the
27
+ // old process is still serving requests. The embedded build asset keeps the
28
+ // app shell renderable even when dist/web-ui/content has disappeared.
30
29
  }
31
30
  // Inject the config path
32
31
  return _scriptCache.replace("${escapeHtml(configPath)}", escapeHtml(configPath));
@@ -1,12 +1,13 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
+ import { EMBEDDED_WEB_ASSETS } from "./embedded-assets.js";
4
5
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
5
6
  // 用 mtime 做缓存键,磁盘上 CSS 一变(npm run build / 手工 edit dist/)下次请求
6
7
  // 就会自动 re-read。否则进程启动时缓存的 CSS 会粘住整个生命周期,UI 改动看不到效果,
7
8
  // 必须重启 wand 才能生效——开发 / 修 UI 的时候这点尤其难受。
8
9
  // 同步 stat 的成本:本地 fs,~几十微秒,相对一次 HTML 渲染可忽略。
9
- let _cssCache = null;
10
+ let _cssCache = EMBEDDED_WEB_ASSETS.stylesCss;
10
11
  let _cssCacheMtimeMs = 0;
11
12
  export function getCSSStyles() {
12
13
  const cssPath = path.join(__dirname, "content", "styles.css");
@@ -18,10 +19,8 @@ export function getCSSStyles() {
18
19
  }
19
20
  }
20
21
  catch {
21
- // 文件丢了就退化到旧缓存(如果有),还没缓存过就抛出原错误让 server 知道。
22
- if (_cssCache === null) {
23
- _cssCache = fs.readFileSync(cssPath, "utf-8");
24
- }
22
+ // Self-update can remove the package directory before the old process exits.
23
+ // Keep serving the embedded build CSS until the process restarts.
25
24
  }
26
25
  return _cssCache;
27
26
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@co0ontty/wand",
3
- "version": "1.41.3",
3
+ "version": "1.42.0",
4
4
  "description": "A web terminal for local CLI tools like Claude.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,10 +16,11 @@
16
16
  ],
17
17
  "preferGlobal": true,
18
18
  "scripts": {
19
- "build": "node scripts/bundle-wterm.js && node scripts/bundle-qrcode.js && tsc -p tsconfig.json && npm run build:copy-content && node scripts/stamp-build-info.js && node scripts/fix-dist-permissions.js",
19
+ "generate:web-assets": "node scripts/generate-web-assets.js",
20
+ "build": "node scripts/bundle-wterm.js && node scripts/bundle-qrcode.js && npm run generate:web-assets && tsc -p tsconfig.json && npm run build:copy-content && node scripts/stamp-build-info.js && node scripts/fix-dist-permissions.js",
20
21
  "build:copy-content": "cp -r src/web-ui/content dist/web-ui/",
21
- "dev": "tsx src/cli.ts web",
22
- "check": "tsc --noEmit -p tsconfig.json",
22
+ "dev": "npm run generate:web-assets && tsx src/cli.ts web",
23
+ "check": "npm run generate:web-assets && tsc --noEmit -p tsconfig.json",
23
24
  "prepublishOnly": "TAG=$(git tag --sort=-v:refname --list 'v*' | head -1) && VER=${TAG#v} && npm version $VER --no-git-tag-version --allow-same-version && npm run build"
24
25
  },
25
26
  "keywords": [