botmux 3.18.1 → 3.18.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.runtime-build-id +1 -1
- package/dist/bot-registry.d.ts +6 -0
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +25 -0
- package/dist/bot-registry.js.map +1 -1
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +17 -1
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +6 -2
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard.js +102 -7
- package/dist/dashboard.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +6 -0
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +6 -0
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +10 -6
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/issue-command-deps.d.ts.map +1 -1
- package/dist/im/lark/issue-command-deps.js +14 -5
- package/dist/im/lark/issue-command-deps.js.map +1 -1
- package/dist/im/lark/issue-command.d.ts +6 -3
- package/dist/im/lark/issue-command.d.ts.map +1 -1
- package/dist/im/lark/issue-command.js +23 -10
- package/dist/im/lark/issue-command.js.map +1 -1
- package/dist/im/lark/mention-mode-command.d.ts +2 -0
- package/dist/im/lark/mention-mode-command.d.ts.map +1 -0
- package/dist/im/lark/mention-mode-command.js +72 -0
- package/dist/im/lark/mention-mode-command.js.map +1 -0
- package/dist/services/chat-reply-mode-store.d.ts +21 -4
- package/dist/services/chat-reply-mode-store.d.ts.map +1 -1
- package/dist/services/chat-reply-mode-store.js +68 -1
- package/dist/services/chat-reply-mode-store.js.map +1 -1
- package/dist/services/vc-meeting-listener-output-protocol.d.ts +10 -0
- package/dist/services/vc-meeting-listener-output-protocol.d.ts.map +1 -1
- package/dist/services/vc-meeting-listener-output-protocol.js +12 -0
- package/dist/services/vc-meeting-listener-output-protocol.js.map +1 -1
- package/package.json +7 -7
package/dist/dashboard.js
CHANGED
|
@@ -8,6 +8,7 @@ import { homedir } from 'node:os';
|
|
|
8
8
|
import { fileURLToPath } from 'node:url';
|
|
9
9
|
import { createHmac, randomBytes } from 'node:crypto';
|
|
10
10
|
import { logger } from './utils/logger.js';
|
|
11
|
+
import { isStandaloneBinary } from './core/self-spawn.js';
|
|
11
12
|
import { gracefulProcessExitCode } from './pm2-graceful-exit.js';
|
|
12
13
|
import { config, isWildcardBindHost } from './config.js';
|
|
13
14
|
import { listenWithProbe } from './utils/listen-with-probe.js';
|
|
@@ -69,7 +70,7 @@ import { parseCloseResidual } from './core/close-residual.js';
|
|
|
69
70
|
import { dashboardSecretPath } from './core/dashboard-secret.js';
|
|
70
71
|
import { getGitRepoInfo } from './core/session-row-enrichment.js';
|
|
71
72
|
import { deleteWhiteboard, listWhiteboards, readWhiteboard, whiteboardEnabled } from './services/whiteboard-store.js';
|
|
72
|
-
import { isLocalDevInstall, botmuxVersionAt, botmuxCliEntryAt, botmuxInstallRoot } from './utils/install-info.js';
|
|
73
|
+
import { isLocalDevInstall, botmuxVersionAt, botmuxCliEntryAt, botmuxInstallRoot, bakedBinaryVersion } from './utils/install-info.js';
|
|
73
74
|
import { checkNode, detectBotmuxInstalls, resolveCurrentVersion, resolveCurrentVersionAt } from './utils/install-diagnostics.js';
|
|
74
75
|
import { fetchLatestVersion, fetchReleasesSince, fetchRollbackVersions, compareVersions, isCanonicalStableVersion, isNewerVersion, } from './core/update-check.js';
|
|
75
76
|
import { GITHUB_REPO } from './core/restart-report.js';
|
|
@@ -1546,6 +1547,93 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
1546
1547
|
const WEB_DIR = join(__dirname, 'dashboard-web');
|
|
1547
1548
|
const DEV_RELOAD_MARKER = join(WEB_DIR, '.botmux-dashboard-dev');
|
|
1548
1549
|
const DEV_RELOAD_VERSION = join(WEB_DIR, '.botmux-dashboard-reload');
|
|
1550
|
+
/**
|
|
1551
|
+
* Request-relative asset path → on-disk path to read, or null when we have no
|
|
1552
|
+
* such asset. The ONE place that knows whether the frontend lives on disk
|
|
1553
|
+
* (source/npm) or inside the compiled binary's virtual filesystem.
|
|
1554
|
+
*
|
|
1555
|
+
* WHY: `WEB_DIR` is derived from `__dirname`, which in the compiled single binary
|
|
1556
|
+
* is the virtual `/$bunfs/root` — a path that does not exist on disk (the
|
|
1557
|
+
* documented CLAUDE.md `__dirname` hazard). Every asset request therefore missed
|
|
1558
|
+
* and fell through to the catch-all 404: the login redirect landed on `/` and
|
|
1559
|
+
* returned `{"error":"not_found_yet","path":"/"}`, so the Dashboard was entirely
|
|
1560
|
+
* unreachable from a compiled binary while working fine from npm/source.
|
|
1561
|
+
*
|
|
1562
|
+
* In compiled mode the assets are embedded instead (see
|
|
1563
|
+
* scripts/generate-dashboard-embed.mjs) and Bun renames each one with a content
|
|
1564
|
+
* hash, so the request path cannot address them directly — the generated manifest
|
|
1565
|
+
* supplies the mapping. Extensions are preserved, so MIME lookup is unaffected.
|
|
1566
|
+
*
|
|
1567
|
+
* Path traversal is handled differently per mode, and both are closed:
|
|
1568
|
+
* • Embedded: the manifest is a build-time allowlist, so a traversal string
|
|
1569
|
+
* simply misses. The lookup still goes through `hasOwnProperty` and a string
|
|
1570
|
+
* check — a plain `map[rel]` answers `__proto__`/`constructor`/`toString` with
|
|
1571
|
+
* inherited objects and functions (MEASURED), and handing one of those onward
|
|
1572
|
+
* would only be stopped by a downstream throw. A security property should not
|
|
1573
|
+
* rest on that.
|
|
1574
|
+
* • On disk: the resolved path must stay inside WEB_DIR.
|
|
1575
|
+
*
|
|
1576
|
+
* Returns null (→ caller 404s) rather than throwing: an asset genuinely absent
|
|
1577
|
+
* from the bundle must stay a 404, exactly as a missing file on disk does.
|
|
1578
|
+
*/
|
|
1579
|
+
function resolveWebAsset(rel) {
|
|
1580
|
+
const embedded = embeddedDashboardAssets();
|
|
1581
|
+
if (embedded) {
|
|
1582
|
+
if (!Object.prototype.hasOwnProperty.call(embedded, rel))
|
|
1583
|
+
return null;
|
|
1584
|
+
const fp = embedded[rel];
|
|
1585
|
+
return typeof fp === 'string' && fp.length > 0 ? fp : null;
|
|
1586
|
+
}
|
|
1587
|
+
// Source / npm install: real directory on disk. Keep the path-traversal guard
|
|
1588
|
+
// here — `rel` is derived from the request.
|
|
1589
|
+
const fp = resolve(WEB_DIR, rel);
|
|
1590
|
+
const relToRoot = relative(resolve(WEB_DIR), fp);
|
|
1591
|
+
if (relToRoot === '..' || relToRoot.startsWith('..\\') || relToRoot.startsWith('../') || isAbsolute(relToRoot))
|
|
1592
|
+
return null;
|
|
1593
|
+
return fp;
|
|
1594
|
+
}
|
|
1595
|
+
/**
|
|
1596
|
+
* The compiled binary's embedded-asset manifest, or null when running from
|
|
1597
|
+
* source/npm (where the frontend is a real directory on disk). Resolved once and
|
|
1598
|
+
* cached — including the null.
|
|
1599
|
+
*
|
|
1600
|
+
* HOW IT GETS HERE: the compiled build's plugin (scripts/bun-native-embed-plugin.mjs)
|
|
1601
|
+
* prepends a generated block of `import … with { type: 'file' }` statements to
|
|
1602
|
+
* this module and assigns the map to `globalThis.__BOTMUX_DASHBOARD_ASSETS__`.
|
|
1603
|
+
*
|
|
1604
|
+
* It has to arrive by build-time injection rather than an ordinary import:
|
|
1605
|
+
* • A static `import './dashboard-web-embedded.js'` would make `tsc` (and a
|
|
1606
|
+
* plain source checkout) demand a generated artifact that only exists after
|
|
1607
|
+
* `bun run dashboard:bundle`.
|
|
1608
|
+
* • A runtime `require()` of it does NOT work: Bun only embeds what it can trace
|
|
1609
|
+
* statically, and MEASURED, a `createRequire` require of the manifest bundled
|
|
1610
|
+
* 1 module and embedded zero assets — the binary would still 404 everything.
|
|
1611
|
+
*/
|
|
1612
|
+
let embeddedAssetsCache;
|
|
1613
|
+
function embeddedDashboardAssets() {
|
|
1614
|
+
if (embeddedAssetsCache !== undefined)
|
|
1615
|
+
return embeddedAssetsCache;
|
|
1616
|
+
const injected = globalThis.__BOTMUX_DASHBOARD_ASSETS__;
|
|
1617
|
+
if (injected && typeof injected === 'object') {
|
|
1618
|
+
embeddedAssetsCache = injected;
|
|
1619
|
+
}
|
|
1620
|
+
else {
|
|
1621
|
+
embeddedAssetsCache = null;
|
|
1622
|
+
// A compiled binary with no injected manifest is a BUILD regression, not a
|
|
1623
|
+
// runtime condition to paper over. Say it loudly: the symptom is a 404 on
|
|
1624
|
+
// every page, which otherwise reads as a routing or auth bug.
|
|
1625
|
+
if (isStandaloneBinary()) {
|
|
1626
|
+
logger.error('[dashboard] compiled binary has no embedded frontend — every asset will 404');
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
return embeddedAssetsCache;
|
|
1630
|
+
}
|
|
1631
|
+
/** Cache-busting stamp for embedded assets, whose `mtimeMs` is 0 (MEASURED).
|
|
1632
|
+
* The baked release version changes with every binary, so it invalidates a
|
|
1633
|
+
* browser's cached copy exactly when the binary changes. */
|
|
1634
|
+
function embeddedAssetStamp() {
|
|
1635
|
+
return (bakedBinaryVersion() ?? 'embedded').replace(/[^\w.-]/g, '');
|
|
1636
|
+
}
|
|
1549
1637
|
const MIME = {
|
|
1550
1638
|
'.html': 'text/html; charset=utf-8',
|
|
1551
1639
|
'.js': 'application/javascript',
|
|
@@ -1610,12 +1698,15 @@ function injectDevReload(html) {
|
|
|
1610
1698
|
}
|
|
1611
1699
|
function serveStatic(req, res, pathname, options = {}) {
|
|
1612
1700
|
const rel = pathname === '/' ? 'index.html' : pathname.replace(/^\/+/, '');
|
|
1613
|
-
const fp =
|
|
1614
|
-
|
|
1615
|
-
const relToRoot = relative(webRoot, fp);
|
|
1616
|
-
// Path-traversal guard: resolved path must stay inside WEB_DIR.
|
|
1617
|
-
if (relToRoot === '..' || relToRoot.startsWith('..\\') || relToRoot.startsWith('../') || isAbsolute(relToRoot))
|
|
1701
|
+
const fp = resolveWebAsset(rel);
|
|
1702
|
+
if (!fp)
|
|
1618
1703
|
return false;
|
|
1704
|
+
// Asset-identity decisions below key off the REQUEST path, not `fp`: in the
|
|
1705
|
+
// compiled binary `fp` is a content-hash-renamed file in the virtual fs
|
|
1706
|
+
// (`chunks/x.js` → `/$bunfs/root/x-<hash>.js`), so `relative(WEB_DIR, fp)`
|
|
1707
|
+
// would classify every embedded asset wrongly — no chunk would be immutable
|
|
1708
|
+
// and index.html would never get its CSRF shell injected.
|
|
1709
|
+
const relToRoot = rel;
|
|
1619
1710
|
try {
|
|
1620
1711
|
const st = statSync(fp);
|
|
1621
1712
|
if (!st.isFile())
|
|
@@ -1624,7 +1715,11 @@ function serveStatic(req, res, pathname, options = {}) {
|
|
|
1624
1715
|
// a deploy never serves new JS with old CSS. Lazy chunks are content-hashed
|
|
1625
1716
|
// and can be cached immutably once the current app.js points at them.
|
|
1626
1717
|
const immutableChunk = relToRoot.startsWith('chunks/') || relToRoot.startsWith('chunks\\');
|
|
1627
|
-
|
|
1718
|
+
// `mtimeMs` is 0 for an embedded file (MEASURED), so the ETag would collapse
|
|
1719
|
+
// to size alone across a whole compiled release. Fold in the baked build
|
|
1720
|
+
// identity so a new binary always invalidates the browser's cache.
|
|
1721
|
+
const etagStamp = st.mtimeMs > 0 ? Math.floor(st.mtimeMs).toString(16) : embeddedAssetStamp();
|
|
1722
|
+
const etag = `W/"${st.size.toString(16)}-${etagStamp}"`;
|
|
1628
1723
|
const isIndex = relToRoot === 'index.html';
|
|
1629
1724
|
const devIndex = isIndex && dashboardDevReloadEnabled();
|
|
1630
1725
|
// 注入 CSRF 票据的壳是每次请求现生成的:ETag 只反映磁盘文件,走 304 会让浏览
|