@ubean/devtools 0.6.1 → 0.6.3
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/README.md +209 -0
- package/README.zh-CN.md +209 -0
- package/dist/client/assets/{browser-agent-rpc-BXhoSh1z-BpvIxhz4.js → browser-agent-rpc-BXhoSh1z-BTqFol0H.js} +1 -1
- package/dist/client/assets/index-CqTOBCCw.js +82 -0
- package/dist/client/index.html +2 -2
- package/dist/index.d.ts +215 -30
- package/dist/index.js +184 -227
- package/package.json +11 -10
- package/dist/client/assets/index-DJNELO3b.js +0 -82
package/dist/index.js
CHANGED
|
@@ -1319,35 +1319,66 @@ function createTerminalServer() {
|
|
|
1319
1319
|
};
|
|
1320
1320
|
}
|
|
1321
1321
|
//#endregion
|
|
1322
|
-
//#region
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1322
|
+
//#region src/node/rpc/types.ts
|
|
1323
|
+
/**
|
|
1324
|
+
* 身份包装器:校验定义形状,同时**保留 `name` 的字面量类型**。
|
|
1325
|
+
*
|
|
1326
|
+
* 直接用 `satisfies RpcFunctionDefinition` 不行 —— 那个类型至少要一个类型参数(`NAME`),
|
|
1327
|
+
* 不传参就把 `name` 拓宽成 `string`,而 `RpcDefinitionsToFunctionsWithNamespace<'ubean', T>`
|
|
1328
|
+
* 恰恰靠字面量名推导 wire 签名。泛型参数 `const D extends …` 同时满足两件事:约束检查 + 字面量保留。
|
|
1329
|
+
*/
|
|
1330
|
+
function defineUbeanRpc(definition) {
|
|
1331
|
+
return definition;
|
|
1327
1332
|
}
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1333
|
+
/**
|
|
1334
|
+
* 本包 RPC 的命名空间。所有裸名注册后成为 `<UBEAN_RPC_NAMESPACE>:<bare-name>`。
|
|
1335
|
+
*/
|
|
1336
|
+
const UBEAN_RPC_NAMESPACE = "ubean";
|
|
1337
|
+
/**
|
|
1338
|
+
* `ubean:ai:stream` —— AI 流式响应的 shared state key。
|
|
1339
|
+
*
|
|
1340
|
+
* **不是** scoped key:`ctx.scope('ubean').rpc.sharedState.get('ai:stream')` 会再加一层前缀
|
|
1341
|
+
* (`ubean:ai:stream` 本身已含 `:`,`qualifyName` 原样返回,因此两种写法恰好都落到这个 key)。
|
|
1342
|
+
* 这里显式写全名,避免读者以为它经过 scope。
|
|
1343
|
+
*/
|
|
1344
|
+
const UBEAN_AI_STREAM_STATE_KEY = "ubean:ai:stream";
|
|
1345
|
+
/**
|
|
1346
|
+
* anchor dock 的共享 iframe 配置。
|
|
1347
|
+
*
|
|
1348
|
+
* `frameId` / `subTabs` 属于 **`DevframeViewIframe`**(不是 `DevframeDockDefaults`),
|
|
1349
|
+
* 因此不能写在 `DevframeDefinition.dock` 里 —— 它们必须经
|
|
1350
|
+
* `initHub({ devframes: [{ devframe, dock }] })`(或 `ctx.install(def, { dock })`)传入。
|
|
1351
|
+
* 这里集中一处,CLI 与测试都从这里取。
|
|
1352
|
+
*
|
|
1353
|
+
* - `frameId`:所有视图 dock 共用一个 iframe 元素(软导航池)。
|
|
1354
|
+
* - `subTabs`:客户端 host 自动挂 frame-nav 适配器,运行时发现视图清单。
|
|
1355
|
+
*
|
|
1356
|
+
* **不要设 `visibility: 'false'`**:它会隐藏 anchor 自己的 dock 按钮,而 iframe 只在 anchor
|
|
1357
|
+
* 被激活时才挂载 —— 隐藏 anchor 就永远发现不了视图清单,外壳里只剩一个空的 dock 侧栏
|
|
1358
|
+
* (实测 2026-10-11:面板整个打不开)。
|
|
1359
|
+
*/
|
|
1360
|
+
const UBEAN_ANCHOR_DOCK = {
|
|
1361
|
+
frameId: "ubean",
|
|
1362
|
+
subTabs: { protocol: "postmessage" }
|
|
1363
|
+
};
|
|
1331
1364
|
//#endregion
|
|
1332
1365
|
//#region src/node/rpc/ai.ts
|
|
1333
1366
|
/**
|
|
1334
|
-
* AI RPC functions — `
|
|
1367
|
+
* AI RPC functions — `ai-tools` / `ai-chat` / `ai-chat-stream`(裸名,注册到 `ubean` scope)。
|
|
1335
1368
|
*
|
|
1336
|
-
* `
|
|
1337
|
-
* `
|
|
1338
|
-
* `ubean:ai:stream`
|
|
1339
|
-
* wired in `createAiServer`. The client subscribes to that sharedState
|
|
1340
|
-
* and filters by `requestId`.
|
|
1369
|
+
* - `ai-chat` 非流式(返回完整响应);
|
|
1370
|
+
* - `ai-chat-stream` 流式:分片由 `createAiServer` 里的 `onStreamChunk` 推入
|
|
1371
|
+
* `ubean:ai:stream` shared state,客户端订阅后按 `requestId` 过滤。
|
|
1341
1372
|
*/
|
|
1342
1373
|
function createAiRpcFunctions(ai) {
|
|
1343
1374
|
return [
|
|
1344
|
-
|
|
1345
|
-
name: "
|
|
1375
|
+
defineUbeanRpc({
|
|
1376
|
+
name: "ai-tools",
|
|
1346
1377
|
type: "query",
|
|
1347
1378
|
setup: () => ({ handler: () => ai.getToolDefinitions() })
|
|
1348
1379
|
}),
|
|
1349
|
-
|
|
1350
|
-
name: "
|
|
1380
|
+
defineUbeanRpc({
|
|
1381
|
+
name: "ai-chat",
|
|
1351
1382
|
type: "action",
|
|
1352
1383
|
setup: () => ({ handler: (params) => ai.chat({
|
|
1353
1384
|
messages: params.messages,
|
|
@@ -1356,8 +1387,8 @@ function createAiRpcFunctions(ai) {
|
|
|
1356
1387
|
model: params.model
|
|
1357
1388
|
}) })
|
|
1358
1389
|
}),
|
|
1359
|
-
|
|
1360
|
-
name: "
|
|
1390
|
+
defineUbeanRpc({
|
|
1391
|
+
name: "ai-chat-stream",
|
|
1361
1392
|
type: "action",
|
|
1362
1393
|
setup: () => ({ handler: (params) => ai.chat({
|
|
1363
1394
|
messages: params.messages,
|
|
@@ -1372,36 +1403,36 @@ function createAiRpcFunctions(ai) {
|
|
|
1372
1403
|
//#endregion
|
|
1373
1404
|
//#region src/node/rpc/crud.ts
|
|
1374
1405
|
/**
|
|
1375
|
-
* CRUD RPC functions — `
|
|
1406
|
+
* CRUD RPC functions — `crud-create` / `crud-read` / `crud-update` / `crud-delete` /
|
|
1407
|
+
* `crud-restore`(裸名,注册到 `ubean` scope)。
|
|
1376
1408
|
*
|
|
1377
|
-
*
|
|
1378
|
-
*
|
|
1379
|
-
* layer is purely a transport adapter.
|
|
1409
|
+
* 每个都直接委派给 `createCrudServer` —— 它封装了全部文件脚手架、备份与 hook 逻辑,
|
|
1410
|
+
* RPC 层只是传输适配。
|
|
1380
1411
|
*/
|
|
1381
1412
|
function createCrudRpcFunctions(crud) {
|
|
1382
1413
|
return [
|
|
1383
|
-
|
|
1384
|
-
name: "
|
|
1414
|
+
defineUbeanRpc({
|
|
1415
|
+
name: "crud-create",
|
|
1385
1416
|
type: "action",
|
|
1386
1417
|
setup: () => ({ handler: (params) => crud.create(params) })
|
|
1387
1418
|
}),
|
|
1388
|
-
|
|
1389
|
-
name: "
|
|
1419
|
+
defineUbeanRpc({
|
|
1420
|
+
name: "crud-read",
|
|
1390
1421
|
type: "query",
|
|
1391
1422
|
setup: () => ({ handler: (params) => crud.read(params) })
|
|
1392
1423
|
}),
|
|
1393
|
-
|
|
1394
|
-
name: "
|
|
1424
|
+
defineUbeanRpc({
|
|
1425
|
+
name: "crud-update",
|
|
1395
1426
|
type: "action",
|
|
1396
1427
|
setup: () => ({ handler: (params) => crud.update(params) })
|
|
1397
1428
|
}),
|
|
1398
|
-
|
|
1399
|
-
name: "
|
|
1429
|
+
defineUbeanRpc({
|
|
1430
|
+
name: "crud-delete",
|
|
1400
1431
|
type: "action",
|
|
1401
1432
|
setup: () => ({ handler: (params) => crud.delete(params) })
|
|
1402
1433
|
}),
|
|
1403
|
-
|
|
1404
|
-
name: "
|
|
1434
|
+
defineUbeanRpc({
|
|
1435
|
+
name: "crud-restore",
|
|
1405
1436
|
type: "action",
|
|
1406
1437
|
setup: () => ({ handler: (path) => crud.restore(path) })
|
|
1407
1438
|
})
|
|
@@ -1410,19 +1441,22 @@ function createCrudRpcFunctions(crud) {
|
|
|
1410
1441
|
//#endregion
|
|
1411
1442
|
//#region src/node/rpc/info.ts
|
|
1412
1443
|
/**
|
|
1413
|
-
* Info RPC functions — `
|
|
1444
|
+
* Info RPC functions — `get-info` 与 `get-env`(裸名,注册到 `ubean` scope)。
|
|
1414
1445
|
*
|
|
1415
|
-
*
|
|
1416
|
-
*
|
|
1417
|
-
|
|
1446
|
+
* sharedState 模型下 `get-info` 只是给**还没订阅** `ubean:info` 的客户端的兜底;正常客户端
|
|
1447
|
+
* 直接 `client.sharedState.get('ubean:info')`。
|
|
1448
|
+
*/
|
|
1449
|
+
/**
|
|
1450
|
+
* 返回定义数组(**裸名**)。调用方用
|
|
1451
|
+
* `RpcDefinitionsToFunctionsWithNamespace<'ubean', typeof …>` 推导 wire 签名。
|
|
1418
1452
|
*/
|
|
1419
1453
|
function createInfoRpcFunctions(opts) {
|
|
1420
|
-
return [
|
|
1421
|
-
name: "
|
|
1454
|
+
return [defineUbeanRpc({
|
|
1455
|
+
name: "get-info",
|
|
1422
1456
|
type: "query",
|
|
1423
1457
|
setup: () => ({ handler: () => opts.state.value() })
|
|
1424
|
-
}),
|
|
1425
|
-
name: "
|
|
1458
|
+
}), defineUbeanRpc({
|
|
1459
|
+
name: "get-env",
|
|
1426
1460
|
type: "query",
|
|
1427
1461
|
setup: () => ({ handler: () => maskSensitiveEnv(opts.getEnvData()) })
|
|
1428
1462
|
})];
|
|
@@ -1430,15 +1464,14 @@ function createInfoRpcFunctions(opts) {
|
|
|
1430
1464
|
//#endregion
|
|
1431
1465
|
//#region src/node/rpc/playground.ts
|
|
1432
1466
|
/**
|
|
1433
|
-
* Playground RPC function — `
|
|
1467
|
+
* Playground RPC function — `playground-invoke`(裸名,注册到 `ubean` scope)。
|
|
1434
1468
|
*
|
|
1435
|
-
*
|
|
1436
|
-
*
|
|
1437
|
-
* used elsewhere in ubean.
|
|
1469
|
+
* 在进程内把 HTTP 请求转给 Hono app(无网络往返),与框架其余处的
|
|
1470
|
+
* `internalFetch` / `callInternal` 同一思路。
|
|
1438
1471
|
*/
|
|
1439
1472
|
function createPlaygroundRpcFunctions(opts) {
|
|
1440
|
-
return [
|
|
1441
|
-
name: "
|
|
1473
|
+
return [defineUbeanRpc({
|
|
1474
|
+
name: "playground-invoke",
|
|
1442
1475
|
type: "action",
|
|
1443
1476
|
setup: () => ({ handler: async (params) => {
|
|
1444
1477
|
const app = opts.getApp?.();
|
|
@@ -1477,35 +1510,36 @@ function createPlaygroundRpcFunctions(opts) {
|
|
|
1477
1510
|
//#endregion
|
|
1478
1511
|
//#region src/node/rpc/terminal.ts
|
|
1479
1512
|
/**
|
|
1480
|
-
* Terminal RPC functions — `
|
|
1513
|
+
* Terminal RPC functions — `terminal-start` / `terminal-input` / `terminal-resize` /
|
|
1514
|
+
* `terminal-poll` / `terminal-kill`(裸名,注册到 `ubean` scope)。
|
|
1481
1515
|
*
|
|
1482
|
-
*
|
|
1483
|
-
*
|
|
1516
|
+
* 委派给 `createTerminalServer`(管理 shell 进程与输出缓冲)。客户端通过
|
|
1517
|
+
* `terminal-poll` 拉取输出。
|
|
1484
1518
|
*/
|
|
1485
1519
|
function createTerminalRpcFunctions(terminal) {
|
|
1486
1520
|
return [
|
|
1487
|
-
|
|
1488
|
-
name: "
|
|
1521
|
+
defineUbeanRpc({
|
|
1522
|
+
name: "terminal-start",
|
|
1489
1523
|
type: "action",
|
|
1490
1524
|
setup: () => ({ handler: (params) => Promise.resolve(terminal.start(params)) })
|
|
1491
1525
|
}),
|
|
1492
|
-
|
|
1493
|
-
name: "
|
|
1526
|
+
defineUbeanRpc({
|
|
1527
|
+
name: "terminal-input",
|
|
1494
1528
|
type: "action",
|
|
1495
1529
|
setup: () => ({ handler: (params) => Promise.resolve(terminal.input(params.sessionId, params.data)) })
|
|
1496
1530
|
}),
|
|
1497
|
-
|
|
1498
|
-
name: "
|
|
1531
|
+
defineUbeanRpc({
|
|
1532
|
+
name: "terminal-resize",
|
|
1499
1533
|
type: "action",
|
|
1500
1534
|
setup: () => ({ handler: (params) => Promise.resolve(terminal.resize(params.sessionId, params.cols, params.rows)) })
|
|
1501
1535
|
}),
|
|
1502
|
-
|
|
1503
|
-
name: "
|
|
1536
|
+
defineUbeanRpc({
|
|
1537
|
+
name: "terminal-poll",
|
|
1504
1538
|
type: "query",
|
|
1505
1539
|
setup: () => ({ handler: (params) => Promise.resolve(terminal.poll(params.sessionId)) })
|
|
1506
1540
|
}),
|
|
1507
|
-
|
|
1508
|
-
name: "
|
|
1541
|
+
defineUbeanRpc({
|
|
1542
|
+
name: "terminal-kill",
|
|
1509
1543
|
type: "action",
|
|
1510
1544
|
setup: () => ({ handler: (params) => Promise.resolve(terminal.kill(params.sessionId)) })
|
|
1511
1545
|
})
|
|
@@ -1514,11 +1548,7 @@ function createTerminalRpcFunctions(terminal) {
|
|
|
1514
1548
|
//#endregion
|
|
1515
1549
|
//#region src/node/rpc/index.ts
|
|
1516
1550
|
/**
|
|
1517
|
-
*
|
|
1518
|
-
* intentionally loose (`any[]`) because the DTK `ctx.rpc.register()` accepts
|
|
1519
|
-
* definitions parameterized on `ViteDevToolsNodeContext`, and the exact
|
|
1520
|
-
* generic instantiation varies per function — the individual functions are
|
|
1521
|
-
* already type-checked at their definition site.
|
|
1551
|
+
* 全部 RPC 定义(裸名)。`setup` 里逐个 `ctx.scope('ubean').rpc.register(fn)`。
|
|
1522
1552
|
*/
|
|
1523
1553
|
function createAllRpcFunctions(deps) {
|
|
1524
1554
|
return [
|
|
@@ -1650,7 +1680,7 @@ function emptyDevToolsInfo(startTime) {
|
|
|
1650
1680
|
}
|
|
1651
1681
|
/**
|
|
1652
1682
|
* Create or retrieve the `ubean:info` shared state.
|
|
1653
|
-
* Called once during `
|
|
1683
|
+
* Called once during the devframe's `setup(ctx)`.
|
|
1654
1684
|
*/
|
|
1655
1685
|
async function initSharedState(ctx, initialValue) {
|
|
1656
1686
|
return ctx.rpc.sharedState.get(UBEAN_INFO_STATE_KEY, { initialValue });
|
|
@@ -1668,163 +1698,63 @@ function refreshSharedState(state, info) {
|
|
|
1668
1698
|
//#region src/node/index.ts
|
|
1669
1699
|
/**
|
|
1670
1700
|
* Path to the pre-built client SPA. Resolved relative to the built server
|
|
1671
|
-
* bundle (`dist/index.
|
|
1701
|
+
* bundle (`dist/index.js`), so this points to `dist/client/` at runtime.
|
|
1672
1702
|
*/
|
|
1673
1703
|
const CLIENT_DIST = resolve(dirname(fileURLToPath(import.meta.url)), "client");
|
|
1674
1704
|
/**
|
|
1675
|
-
*
|
|
1705
|
+
* 构建 `@ubean/devtools` 的 devframe 定义。
|
|
1676
1706
|
*
|
|
1677
|
-
*
|
|
1678
|
-
*
|
|
1679
|
-
*
|
|
1680
|
-
*
|
|
1707
|
+
* 与旧 DTK 接线的区别:
|
|
1708
|
+
* - 不再手动 `ctx.views.hostStatic` / 手写 11 个 dock entry —— hub 的 `ctx.install()`
|
|
1709
|
+
* 负责服务 SPA、注册 anchor dock 与 `__connection.json`;
|
|
1710
|
+
* - 不再有那段 `__connection.json` 转发 hack(hub 的 `mountConnectionMeta` 原生支持);
|
|
1711
|
+
* - RPC 走 `ctx.scope('ubean')`,裸名注册(`get-info` → wire 名 `ubean:get-info`)。
|
|
1681
1712
|
*/
|
|
1682
|
-
function
|
|
1713
|
+
function createUbeanDevframe(options) {
|
|
1714
|
+
const basePath = options.basePath ?? "/_devtools/";
|
|
1683
1715
|
return {
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
res.end(JSON.stringify({ error: "Failed to resolve connection meta" }));
|
|
1704
|
-
});
|
|
1705
|
-
};
|
|
1706
|
-
viteServer.middlewares.use("/_devtools/__connection.json", serveConnectionMeta);
|
|
1707
|
-
viteServer.middlewares.use("/__connection.json", serveConnectionMeta);
|
|
1708
|
-
}
|
|
1709
|
-
const SPA_BASE = "/_devtools/index.html";
|
|
1710
|
-
const dockEntries = [
|
|
1711
|
-
{
|
|
1712
|
-
id: "ubean:overview",
|
|
1713
|
-
title: "Overview",
|
|
1714
|
-
icon: "lucide:layout-dashboard",
|
|
1715
|
-
url: `${SPA_BASE}#/overview`,
|
|
1716
|
-
order: 100
|
|
1717
|
-
},
|
|
1718
|
-
{
|
|
1719
|
-
id: "ubean:pages",
|
|
1720
|
-
title: "Pages",
|
|
1721
|
-
icon: "lucide:file-text",
|
|
1722
|
-
url: `${SPA_BASE}#/pages`,
|
|
1723
|
-
order: 95
|
|
1724
|
-
},
|
|
1725
|
-
{
|
|
1726
|
-
id: "ubean:api",
|
|
1727
|
-
title: "API",
|
|
1728
|
-
icon: "lucide:send",
|
|
1729
|
-
url: `${SPA_BASE}#/api`,
|
|
1730
|
-
order: 90
|
|
1731
|
-
},
|
|
1732
|
-
{
|
|
1733
|
-
id: "ubean:middleware",
|
|
1734
|
-
title: "Middleware",
|
|
1735
|
-
icon: "lucide:layers",
|
|
1736
|
-
url: `${SPA_BASE}#/middleware`,
|
|
1737
|
-
order: 85
|
|
1738
|
-
},
|
|
1739
|
-
{
|
|
1740
|
-
id: "ubean:crons",
|
|
1741
|
-
title: "Crons",
|
|
1742
|
-
icon: "lucide:clock",
|
|
1743
|
-
url: `${SPA_BASE}#/crons`,
|
|
1744
|
-
order: 80
|
|
1745
|
-
},
|
|
1746
|
-
{
|
|
1747
|
-
id: "ubean:config",
|
|
1748
|
-
title: "Config",
|
|
1749
|
-
icon: "lucide:settings",
|
|
1750
|
-
url: `${SPA_BASE}#/config`,
|
|
1751
|
-
order: 75
|
|
1752
|
-
},
|
|
1753
|
-
{
|
|
1754
|
-
id: "ubean:env",
|
|
1755
|
-
title: "Env",
|
|
1756
|
-
icon: "lucide:terminal",
|
|
1757
|
-
url: `${SPA_BASE}#/env`,
|
|
1758
|
-
order: 70
|
|
1759
|
-
},
|
|
1760
|
-
{
|
|
1761
|
-
id: "ubean:api-docs",
|
|
1762
|
-
title: "API Docs",
|
|
1763
|
-
icon: "lucide:book-open",
|
|
1764
|
-
url: `${SPA_BASE}#/api-docs`,
|
|
1765
|
-
order: 65
|
|
1766
|
-
},
|
|
1767
|
-
{
|
|
1768
|
-
id: "ubean:database",
|
|
1769
|
-
title: "Database",
|
|
1770
|
-
icon: "lucide:database",
|
|
1771
|
-
url: `${SPA_BASE}#/database`,
|
|
1772
|
-
order: 60
|
|
1773
|
-
},
|
|
1774
|
-
{
|
|
1775
|
-
id: "ubean:terminal",
|
|
1776
|
-
title: "Terminal",
|
|
1777
|
-
icon: "lucide:square-terminal",
|
|
1778
|
-
url: `${SPA_BASE}#/terminal`,
|
|
1779
|
-
order: 58
|
|
1780
|
-
},
|
|
1781
|
-
{
|
|
1782
|
-
id: "ubean:ai",
|
|
1783
|
-
title: "AI",
|
|
1784
|
-
icon: "lucide:sparkles",
|
|
1785
|
-
url: `${SPA_BASE}#/ai`,
|
|
1786
|
-
order: 55
|
|
1787
|
-
}
|
|
1788
|
-
];
|
|
1789
|
-
for (const entry of dockEntries) ctx.docks.register({
|
|
1790
|
-
id: entry.id,
|
|
1791
|
-
title: entry.title,
|
|
1792
|
-
icon: entry.icon,
|
|
1793
|
-
type: "iframe",
|
|
1794
|
-
url: entry.url,
|
|
1795
|
-
category: "framework",
|
|
1796
|
-
defaultOrder: entry.order
|
|
1797
|
-
});
|
|
1716
|
+
id: "ubean",
|
|
1717
|
+
name: "Ubean DevTools",
|
|
1718
|
+
version: "0.6.1",
|
|
1719
|
+
packageName: "@ubean/devtools",
|
|
1720
|
+
homepage: "https://github.com/SoybeanJS/ubean",
|
|
1721
|
+
description: "Ubean 项目的路由 / 页面 / API / 配置 / 终端 / AI 可视化面板。",
|
|
1722
|
+
importMetaUrl: import.meta.url,
|
|
1723
|
+
icon: "https://img.soybeanjs.dev/logo-ubean.svg?v=202608192144",
|
|
1724
|
+
basePath,
|
|
1725
|
+
capabilities: { build: false },
|
|
1726
|
+
clientAssets: CLIENT_DIST,
|
|
1727
|
+
dock: {
|
|
1728
|
+
title: "Ubean",
|
|
1729
|
+
defaultOrder: 100,
|
|
1730
|
+
category: "framework"
|
|
1731
|
+
},
|
|
1732
|
+
setup: async (nodeCtx) => {
|
|
1733
|
+
const ctx = nodeCtx;
|
|
1734
|
+
const cwd = options.getCwd();
|
|
1798
1735
|
const startTime = Date.now();
|
|
1799
|
-
const
|
|
1800
|
-
const envData = snapshotEnv(
|
|
1801
|
-
const
|
|
1802
|
-
scan
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
url: tab.src,
|
|
1815
|
-
category: "framework",
|
|
1816
|
-
defaultOrder: 50
|
|
1817
|
-
});
|
|
1818
|
-
const state = await initSharedState(ctx, initialInfo);
|
|
1819
|
-
const aiStreamState = await ctx.rpc.sharedState.get("ubean:ai:stream", { initialValue: {
|
|
1736
|
+
const scoped = ctx.scope(UBEAN_RPC_NAMESPACE);
|
|
1737
|
+
const envData = snapshotEnv(cwd);
|
|
1738
|
+
const buildInfo = () => {
|
|
1739
|
+
const scan = options.getScanResult?.() ?? null;
|
|
1740
|
+
return scan ? buildDevToolsInfo({
|
|
1741
|
+
scan,
|
|
1742
|
+
configMeta: options.getConfigMeta?.() ?? null,
|
|
1743
|
+
customTabs: options.getCustomTabs?.() ?? [],
|
|
1744
|
+
ai: options.ai,
|
|
1745
|
+
startTime,
|
|
1746
|
+
envData
|
|
1747
|
+
}) : emptyDevToolsInfo(startTime);
|
|
1748
|
+
};
|
|
1749
|
+
const state = await initSharedState(ctx, buildInfo());
|
|
1750
|
+
const aiStreamState = await ctx.rpc.sharedState.get(UBEAN_AI_STREAM_STATE_KEY, { initialValue: {
|
|
1820
1751
|
requestId: "",
|
|
1821
1752
|
text: "",
|
|
1822
1753
|
done: true
|
|
1823
1754
|
} });
|
|
1824
|
-
const hooks = createDevToolsHooks();
|
|
1825
1755
|
const crud = createCrudServer({
|
|
1826
|
-
cwd
|
|
1827
|
-
hooks,
|
|
1756
|
+
cwd,
|
|
1757
|
+
hooks: createDevToolsHooks(),
|
|
1828
1758
|
scaffoldOps: options.scaffoldOps,
|
|
1829
1759
|
getEnv: () => envData,
|
|
1830
1760
|
setEnv: (env) => {
|
|
@@ -1846,20 +1776,27 @@ function ubeanDevtoolsPlugin(options = { getCwd: () => process.cwd() }) {
|
|
|
1846
1776
|
terminal: createTerminalServer(),
|
|
1847
1777
|
getApp: options.getApp
|
|
1848
1778
|
});
|
|
1849
|
-
for (const fn of fns)
|
|
1779
|
+
for (const fn of fns) scoped.rpc.register(fn);
|
|
1780
|
+
for (const tab of options.getCustomTabs?.() ?? []) ctx.docks.register({
|
|
1781
|
+
id: `ubean:custom:${tab.id}`,
|
|
1782
|
+
title: tab.label,
|
|
1783
|
+
icon: tab.icon || "lucide:plugin",
|
|
1784
|
+
type: "iframe",
|
|
1785
|
+
url: tab.src,
|
|
1786
|
+
category: "framework",
|
|
1787
|
+
defaultOrder: 50
|
|
1788
|
+
});
|
|
1850
1789
|
const refresh = () => {
|
|
1851
|
-
|
|
1852
|
-
scan: options.getScanResult(),
|
|
1853
|
-
configMeta: options.getConfigMeta?.() ?? null,
|
|
1854
|
-
customTabs: options.getCustomTabs?.() ?? [],
|
|
1855
|
-
ai: options.ai,
|
|
1856
|
-
startTime,
|
|
1857
|
-
envData
|
|
1858
|
-
}) : initialInfo;
|
|
1859
|
-
refreshSharedState(state, info);
|
|
1790
|
+
refreshSharedState(state, buildInfo());
|
|
1860
1791
|
};
|
|
1861
1792
|
options.registerRefresh?.(refresh);
|
|
1862
|
-
}
|
|
1793
|
+
}
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
function ubeanDevtoolsPlugin(options) {
|
|
1797
|
+
return {
|
|
1798
|
+
name: "ubean:devtools",
|
|
1799
|
+
__ubeanDevframe: createUbeanDevframe(options)
|
|
1863
1800
|
};
|
|
1864
1801
|
}
|
|
1865
1802
|
/**
|
|
@@ -1915,18 +1852,38 @@ function snapshotEnv(cwd) {
|
|
|
1915
1852
|
}
|
|
1916
1853
|
//#endregion
|
|
1917
1854
|
//#region src/define-tab.ts
|
|
1918
|
-
|
|
1855
|
+
/**
|
|
1856
|
+
* 自定义 tab 注册表 —— **真·进程单例**(挂在 `globalThis` 上)。
|
|
1857
|
+
*
|
|
1858
|
+
* 不能用模块级数组:dev 下同一个进程里常常有第二份 `@ubean/devtools`(CLI 动态 `import()`
|
|
1859
|
+
* 一份、用户的 `vite.config.ts` 又静态引一份;pnpm 也可能装出两份),模块级状态只在同一个模块
|
|
1860
|
+
* 实例内共享 —— 表现是「用户 `defineDevToolsTab()` 注册了 tab,面板里永远看不到」,且**没有任何
|
|
1861
|
+
* 报错**。同样的理由见 `@ubean/vue` 的 matcher 注册表与 `@ubean/islands` 的服务端组件注册表
|
|
1862
|
+
* (AGENTS.md 第 19 条)。
|
|
1863
|
+
*
|
|
1864
|
+
* 同名重复注册会覆盖 —— HMR 重新求值时因此能拿到最新的 tab 定义。
|
|
1865
|
+
*/
|
|
1866
|
+
const CUSTOM_TABS_KEY = "__ubean_devtools_custom_tabs__";
|
|
1867
|
+
function getCustomTabsRegistry() {
|
|
1868
|
+
const store = globalThis;
|
|
1869
|
+
const existing = store[CUSTOM_TABS_KEY];
|
|
1870
|
+
if (Array.isArray(existing)) return existing;
|
|
1871
|
+
const created = [];
|
|
1872
|
+
store[CUSTOM_TABS_KEY] = created;
|
|
1873
|
+
return created;
|
|
1874
|
+
}
|
|
1919
1875
|
function defineDevToolsTab(tab) {
|
|
1876
|
+
const customTabs = getCustomTabsRegistry();
|
|
1920
1877
|
const exists = customTabs.findIndex((t) => t.id === tab.id);
|
|
1921
1878
|
if (exists !== -1) customTabs[exists] = tab;
|
|
1922
1879
|
else customTabs.push(tab);
|
|
1923
1880
|
return tab;
|
|
1924
1881
|
}
|
|
1925
1882
|
function getCustomTabs() {
|
|
1926
|
-
return [...
|
|
1883
|
+
return [...getCustomTabsRegistry()];
|
|
1927
1884
|
}
|
|
1928
1885
|
function clearCustomTabs() {
|
|
1929
|
-
|
|
1886
|
+
getCustomTabsRegistry().length = 0;
|
|
1930
1887
|
}
|
|
1931
1888
|
//#endregion
|
|
1932
|
-
export { UBEAN_INFO_STATE_KEY, buildDevToolsInfo, clearCustomTabs, createAiServer, createAllRpcFunctions, createCrudServer, createDevToolsHooks, createTerminalServer,
|
|
1889
|
+
export { UBEAN_AI_STREAM_STATE_KEY, UBEAN_ANCHOR_DOCK, UBEAN_INFO_STATE_KEY, UBEAN_RPC_NAMESPACE, buildDevToolsInfo, clearCustomTabs, createAiServer, createAllRpcFunctions, createCrudServer, createDevToolsHooks, createTerminalServer, createUbeanDevframe, defineDevToolsTab, emptyDevToolsInfo, getCustomTabs, maskSensitiveEnv, ubeanDevtoolsPlugin };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ubean/devtools",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "DevTools for the ubean framework — server middleware, RPC, and the iframe SPA client",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -27,22 +27,23 @@
|
|
|
27
27
|
"@codemirror/state": "6.7.6",
|
|
28
28
|
"@codemirror/theme-one-dark": "6.1.3",
|
|
29
29
|
"@codemirror/view": "6.43.14",
|
|
30
|
+
"@devframes/hub": "^1.2.5",
|
|
31
|
+
"@devframes/hub-ui": "^1.2.5",
|
|
32
|
+
"@devframes/vite": "^1.2.5",
|
|
30
33
|
"@soybeanjs/unocss-preset": "^0.2.0",
|
|
31
34
|
"@types/node": "^26.6.5",
|
|
32
35
|
"@vean/ui": "^0.50.2",
|
|
33
36
|
"@vean/unocss": "^0.50.2",
|
|
34
|
-
"@vitejs/devtools": "^0.7.6",
|
|
35
|
-
"@vitejs/devtools-kit": "^0.7.6",
|
|
36
37
|
"@vitejs/plugin-vue": "^6.0.10",
|
|
37
38
|
"@xterm/addon-fit": "^0.11.0",
|
|
38
39
|
"@xterm/xterm": "^6.0.0",
|
|
39
|
-
"devframe": "^1.2.
|
|
40
|
+
"devframe": "^1.2.5",
|
|
40
41
|
"hono": "4.13.13",
|
|
41
42
|
"typescript": "npm:typescript-native-bridge@latest",
|
|
42
|
-
"unocss": "^66.
|
|
43
|
-
"vite": "npm:@voidzero-dev/vite-plus-core@1.
|
|
44
|
-
"vite-plus": "1.
|
|
45
|
-
"vue": "^3.5.
|
|
43
|
+
"unocss": "^66.11.0",
|
|
44
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@1.3.0",
|
|
45
|
+
"vite-plus": "1.3.0",
|
|
46
|
+
"vue": "^3.5.44"
|
|
46
47
|
},
|
|
47
48
|
"peerDependencies": {
|
|
48
49
|
"hono": "^4.0.0",
|
|
@@ -57,8 +58,8 @@
|
|
|
57
58
|
}
|
|
58
59
|
},
|
|
59
60
|
"optionalDependencies": {
|
|
60
|
-
"@ai-sdk/openai-compatible": "3.0.
|
|
61
|
-
"ai": "7.0.
|
|
61
|
+
"@ai-sdk/openai-compatible": "3.0.70",
|
|
62
|
+
"ai": "7.0.142"
|
|
62
63
|
},
|
|
63
64
|
"scripts": {
|
|
64
65
|
"build": "pnpm run build:server && pnpm run build:client",
|