@aalis/plugin-webui-server 0.12.0 → 0.14.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.
package/dist/auth.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import type { IncomingMessage } from 'node:http';
2
2
  import type { UserIdentity } from '@aalis/api-authority';
3
3
  import type { ProcessService } from '@aalis/api-process';
4
- import type { Logger } from '@aalis/core';
5
4
  import type { RequestHandler } from 'express';
6
5
  export interface AuthSystem {
7
6
  /** Express 中间件:处理 ?token= 重定向、登录端点、未认证拦截、登录页 */
@@ -14,11 +13,12 @@ export interface AuthSystem {
14
13
  /**
15
14
  * 创建单 token 认证系统(单 owner)。
16
15
  *
17
- * - token 由调用方传入(参见 index.ts resolveAuthToken,支持 ephemeral / persist / fixed 三种模式)
16
+ * - token 由调用方经 getToken 提供,每次校验现取:persist 模式下 storage 晚上线时,调用方会把
17
+ * 启动时的临时 token 换成读回的持久化 token(见 index.ts 的 token 解析)
18
18
  * - HttpOnly + SameSite=Strict cookie;`?token=` 一键登录;同源 POST /api/auth/login 提交
19
19
  * - 身份恒为 `webui:console`(owner 级,单人模式语义)
20
20
  */
21
- export declare function createAuthSystem(token: string, _logger: Logger): AuthSystem;
21
+ export declare function createAuthSystem(getToken: () => string): AuthSystem;
22
22
  /**
23
23
  * 跨平台打开默认浏览器到指定 URL(无外部依赖)
24
24
  * 失败静默;此为便利功能,不影响服务启动。
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AA0E9C,MAAM,WAAW,UAAU;IACzB,gDAAgD;IAChD,UAAU,EAAE,cAAc,CAAC;IAC3B,wCAAwC;IACxC,cAAc,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC;IAC9C,0EAA0E;IAC1E,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,CAAC,GAAG,YAAY,GAAG,SAAS,CAAC;CAC3E;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CA+E3E;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,IAAI,CAwBnE"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AA0E9C,MAAM,WAAW,UAAU;IACzB,gDAAgD;IAChD,UAAU,EAAE,cAAc,CAAC;IAC3B,wCAAwC;IACxC,cAAc,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC;IAC9C,0EAA0E;IAC1E,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,CAAC,GAAG,YAAY,GAAG,SAAS,CAAC;CAC3E;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,MAAM,GAAG,UAAU,CAiFnE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,IAAI,CAwBnE"}
package/dist/auth.js CHANGED
@@ -70,15 +70,16 @@ function escapeHtml(s) {
70
70
  /**
71
71
  * 创建单 token 认证系统(单 owner)。
72
72
  *
73
- * - token 由调用方传入(参见 index.ts resolveAuthToken,支持 ephemeral / persist / fixed 三种模式)
73
+ * - token 由调用方经 getToken 提供,每次校验现取:persist 模式下 storage 晚上线时,调用方会把
74
+ * 启动时的临时 token 换成读回的持久化 token(见 index.ts 的 token 解析)
74
75
  * - HttpOnly + SameSite=Strict cookie;`?token=` 一键登录;同源 POST /api/auth/login 提交
75
76
  * - 身份恒为 `webui:console`(owner 级,单人模式语义)
76
77
  */
77
- export function createAuthSystem(token, _logger) {
78
+ export function createAuthSystem(getToken) {
78
79
  const cookieMaxAge = 30 * 24 * 3600; // 30d;进程重启 token 轮换后无效
79
80
  function tokenAuthed(req) {
80
81
  const cookieToken = parseCookieValue(req.headers.cookie, TOKEN_COOKIE);
81
- return !!cookieToken && cookieToken === token;
82
+ return !!cookieToken && cookieToken === getToken();
82
83
  }
83
84
  function identify(req) {
84
85
  return tokenAuthed(req) ? { platform: 'webui', userId: 'console' } : undefined;
@@ -88,6 +89,7 @@ export function createAuthSystem(token, _logger) {
88
89
  const qToken = typeof req.query?.token === 'string' ? req.query.token : undefined;
89
90
  // 1. ?token= 命中 → 设置 cookie → 302 到不带 query 的同路径
90
91
  if (req.method === 'GET' && qToken) {
92
+ const token = getToken();
91
93
  if (qToken === token) {
92
94
  res.setHeader('Set-Cookie', buildSetCookie(TOKEN_COOKIE, token, cookieMaxAge));
93
95
  res.redirect(302, url);
@@ -99,6 +101,7 @@ export function createAuthSystem(token, _logger) {
99
101
  // 2. 登录端点:{token}
100
102
  if (url === '/api/auth/login' && req.method === 'POST') {
101
103
  const body = req.body ?? {};
104
+ const token = getToken();
102
105
  if (typeof body.token === 'string' && body.token === token) {
103
106
  res.setHeader('Set-Cookie', buildSetCookie(TOKEN_COOKIE, token, cookieMaxAge));
104
107
  res.json({ ok: true, identity: { platform: 'webui', userId: 'console' } });
package/dist/auth.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
@@ -17,7 +17,8 @@ export interface DiscoveryEnv {
17
17
  /**
18
18
  * 发现所有前端包:在 scanDirs 各目录下逐子目录、以及 depIds(项目根 deps)里,收录 package.json
19
19
  * 标了 `aalis.client:true` 且其 `dist/index.html` 存在的包。按包名去重,再按 id 排序(确定性输出)。
20
- * 默认活跃前端由调用方按 config 或「排序后第一个」决定——本函数不做偏好判断、不认任何具体名字。
20
+ * 活跃前端由 webui-client 服务解析决定(偏好 > 优先级 > 注册顺序),本函数只负责发现、不做偏好判断、
21
+ * 不认任何具体名字。
21
22
  */
22
23
  export declare function discoverClients(scanDirs: string[], depIds: string[], env: DiscoveryEnv): ClientCandidate[];
23
24
  /** 本地扫到的一个包:它自身的元数据 + 它依赖了谁。 */
@@ -1 +1 @@
1
- {"version":3,"file":"client-discovery.d.ts","sourceRoot":"","sources":["../src/client-discovery.ts"],"names":[],"mappings":"AAMA,UAAU,eAAe;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;IACnC,qBAAqB;IACrB,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IACrC,oCAAoC;IACpC,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;IACjC,IAAI,EAAE,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC;IACrC,OAAO,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/B,mDAAmD;IACnD,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CACpD;AASD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,YAAY,GAAG,eAAe,EAAE,CA2B1G;AAED,gCAAgC;AAChC,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAAE,EAClB,GAAG,EAAE,IAAI,CAAC,YAAY,EAAE,YAAY,GAAG,aAAa,GAAG,UAAU,GAAG,MAAM,CAAC,GAC1E,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAiC7B;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,EAAE,CAGjH"}
1
+ {"version":3,"file":"client-discovery.d.ts","sourceRoot":"","sources":["../src/client-discovery.ts"],"names":[],"mappings":"AAMA,UAAU,eAAe;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;IACnC,qBAAqB;IACrB,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IACrC,oCAAoC;IACpC,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;IACjC,IAAI,EAAE,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC;IACrC,OAAO,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/B,mDAAmD;IACnD,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CACpD;AASD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,YAAY,GAAG,eAAe,EAAE,CA2B1G;AAED,gCAAgC;AAChC,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAAE,EAClB,GAAG,EAAE,IAAI,CAAC,YAAY,EAAE,YAAY,GAAG,aAAa,GAAG,UAAU,GAAG,MAAM,CAAC,GAC1E,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAiC7B;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,EAAE,CAGjH"}
@@ -1,12 +1,13 @@
1
1
  // 全动态前端发现(纯逻辑,注入 fs/path 便于单测)。
2
2
  //
3
- // 按 `aalis.client:true` 标记 + 存在 `dist/index.html` 收录前端候选——**不硬编码任何前端包名**,
4
- // 与 runtime 加载器的 marker 驱动一致(忒修斯之船:任意第三方前端,带标记+dist 即被发现)。
3
+ // 按 `aalis.client:true` 标记 + 存在 `dist/index.html` 收录前端候选——**不硬编码任何前端包名**
4
+ // (忒修斯之船:任意第三方前端,带标记+dist 即被发现)。
5
5
  // 覆盖三种拓扑:monorepo(扫 packages 同级目录)/ 独立项目(扫 node_modules/@aalis 作用域 + 根 deps)。
6
6
  /**
7
7
  * 发现所有前端包:在 scanDirs 各目录下逐子目录、以及 depIds(项目根 deps)里,收录 package.json
8
8
  * 标了 `aalis.client:true` 且其 `dist/index.html` 存在的包。按包名去重,再按 id 排序(确定性输出)。
9
- * 默认活跃前端由调用方按 config 或「排序后第一个」决定——本函数不做偏好判断、不认任何具体名字。
9
+ * 活跃前端由 webui-client 服务解析决定(偏好 > 优先级 > 注册顺序),本函数只负责发现、不做偏好判断、
10
+ * 不认任何具体名字。
10
11
  */
11
12
  export function discoverClients(scanDirs, depIds, env) {
12
13
  const out = [];
@@ -1 +1 @@
1
- {"version":3,"file":"client-discovery.js","sourceRoot":"","sources":["../src/client-discovery.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client-discovery.js","sourceRoot":"","sources":["../src/client-discovery.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { type PlatformAdapter } from '@aalis/api-platform';
2
2
  import type { StorageService } from '@aalis/api-storage';
3
- export { type WSIncoming, WSIncomingSchema } from './protocol.js';
4
3
  /**
5
4
  * 会话归属的平台名:取 sessionId 首个 `:` 前的前缀,命中**已注册平台名**才采信,否则 `'webui'`。
6
5
  *
@@ -19,20 +18,21 @@ declare const _default: import("@aalis/core").PluginDefinition<{
19
18
  config: import("@aalis/core").ServiceDescriptor<(identity: symbol) => Readonly<Record<string, unknown>>, Readonly<Record<string, unknown>>>;
20
19
  provide: import("@aalis/core").ServiceDescriptor<(identity: symbol) => import("@aalis/core").Provide, import("@aalis/core").Provide>;
21
20
  services: import("@aalis/core").ServiceDescriptor<(identity: symbol) => import("@aalis/core").Services, import("@aalis/core").Services>;
22
- hostConfig: import("@aalis/core/dist/composition/descriptors.js").OptionalUse<import("@aalis/core").HostConfig, import("@aalis/core").ServiceRef<import("@aalis/core").HostConfig>>;
23
- app: import("@aalis/core/dist/composition/descriptors.js").OptionalUse<import("@aalis/core").AppService, import("@aalis/core").ServiceRef<import("@aalis/core").AppService>>;
24
- plugins: import("@aalis/core/dist/composition/descriptors.js").OptionalUse<import("@aalis/core").PluginManagerService, import("@aalis/core").ServiceRef<import("@aalis/core").PluginManagerService>>;
25
- storage: import("@aalis/core/dist/composition/descriptors.js").OptionalUse<StorageService, import("@aalis/core").ServiceRef<StorageService>>;
26
- authority: import("@aalis/core/dist/composition/descriptors.js").OptionalUse<import("@aalis/api-authority").AuthorityService, import("@aalis/core").ServiceRef<import("@aalis/api-authority").AuthorityService>>;
27
- commands: import("@aalis/core/dist/composition/descriptors.js").OptionalUse<import("@aalis/api-commands").CommandService, import("@aalis/api-commands").BoundCommands>;
28
- platform: import("@aalis/core/dist/composition/descriptors.js").OptionalUse<PlatformAdapter, import("@aalis/core").ServiceRef<PlatformAdapter>>;
29
- process: import("@aalis/core/dist/composition/descriptors.js").OptionalUse<import("@aalis/api-process").ProcessService, import("@aalis/core").ServiceRef<import("@aalis/api-process").ProcessService>>;
30
- sessionConfirm: import("@aalis/core/dist/composition/descriptors.js").OptionalUse<import("@aalis/api-session-confirm").SessionConfirmService, import("@aalis/core").ServiceRef<import("@aalis/api-session-confirm").SessionConfirmService>>;
31
- tools: import("@aalis/core/dist/composition/descriptors.js").OptionalUse<import("@aalis/api-tools").ToolService, import("@aalis/api-tools").BoundTools>;
32
- llm: import("@aalis/core/dist/composition/descriptors.js").OptionalUse<import("@aalis/api-llm").LLMModel, import("@aalis/core").ServiceRef<import("@aalis/api-llm").LLMModel>>;
33
- persona: import("@aalis/core/dist/composition/descriptors.js").OptionalUse<import("@aalis/api-persona").PersonaService, import("@aalis/core").ServiceRef<import("@aalis/api-persona").PersonaService>>;
34
- agent: import("@aalis/core/dist/composition/descriptors.js").OptionalUse<import("@aalis/api-agent").AgentService, import("@aalis/api-agent").BoundAgent>;
35
- memory: import("@aalis/core/dist/composition/descriptors.js").OptionalUse<import("@aalis/api-memory").MemoryService, import("@aalis/core").ServiceRef<import("@aalis/api-memory").MemoryService>>;
21
+ hostConfig: import("@aalis/core").OptionalUse<import("@aalis/api-host-config").HostConfig, import("@aalis/core").ServiceRef<import("@aalis/api-host-config").HostConfig>>;
22
+ app: import("@aalis/core").OptionalUse<import("@aalis/core").AppService, import("@aalis/core").ServiceRef<import("@aalis/core").AppService>>;
23
+ plugins: import("@aalis/core").OptionalUse<import("@aalis/core").PluginManagerService, import("@aalis/core").ServiceRef<import("@aalis/core").PluginManagerService>>;
24
+ source: import("@aalis/core").OptionalUse<import("@aalis/api-plugin-source").PluginSourceService, import("@aalis/core").ServiceRef<import("@aalis/api-plugin-source").PluginSourceService>>;
25
+ packageManager: import("@aalis/core").OptionalUse<import("@aalis/api-package-manager").PackageManagerService, import("@aalis/core").ServiceRef<import("@aalis/api-package-manager").PackageManagerService>>;
26
+ storage: import("@aalis/core").OptionalUse<StorageService, import("@aalis/core").ServiceRef<StorageService>>;
27
+ authority: import("@aalis/core").OptionalUse<import("@aalis/api-authority").AuthorityService, import("@aalis/core").ServiceRef<import("@aalis/api-authority").AuthorityService>>;
28
+ commands: import("@aalis/core").OptionalUse<import("@aalis/api-commands").CommandService, import("@aalis/api-commands").BoundCommands>;
29
+ platform: import("@aalis/core").OptionalUse<PlatformAdapter, import("@aalis/core").ServiceRef<PlatformAdapter>>;
30
+ process: import("@aalis/core").OptionalUse<import("@aalis/api-process").ProcessService, import("@aalis/core").ServiceRef<import("@aalis/api-process").ProcessService>>;
31
+ sessionConfirm: import("@aalis/core").OptionalUse<import("@aalis/api-session-confirm").SessionConfirmService, import("@aalis/core").ServiceRef<import("@aalis/api-session-confirm").SessionConfirmService>>;
32
+ tools: import("@aalis/core").OptionalUse<import("@aalis/api-tools").ToolService, import("@aalis/api-tools").BoundTools>;
33
+ llm: import("@aalis/core").OptionalUse<import("@aalis/api-llm").LLMModel, import("@aalis/core").ServiceRef<import("@aalis/api-llm").LLMModel>>;
34
+ persona: import("@aalis/core").OptionalUse<import("@aalis/api-persona").PersonaService, import("@aalis/core").ServiceRef<import("@aalis/api-persona").PersonaService>>;
35
+ agent: import("@aalis/core").OptionalUse<import("@aalis/api-agent").AgentService, import("@aalis/api-agent").BoundAgent>;
36
36
  }>;
37
37
  export default _default;
38
38
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiBA,OAAO,EAIL,KAAK,eAAe,EAGrB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAmIzD,OAAO,EAAE,KAAK,UAAU,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAmJlE;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,CAK5F;;;;;;;;;;;;;;;;;;;;;;;AA8BD,wBAQG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAmBA,OAAO,EAIL,KAAK,eAAe,EAGrB,MAAM,qBAAqB,CAAC;AAK7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAgRzD;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,CAK5F;;;;;;;;;;;;;;;;;;;;;;;;AA+BD,wBAQG"}
package/dist/index.js CHANGED
@@ -9,16 +9,18 @@ import { pathToFileURL } from 'node:url';
9
9
  import { agent } from '@aalis/api-agent';
10
10
  import { authority } from '@aalis/api-authority';
11
11
  import { commands } from '@aalis/api-commands';
12
+ import { hostConfig } from '@aalis/api-host-config';
12
13
  import { listLLMModels, llm } from '@aalis/api-llm';
13
- import { memory } from '@aalis/api-memory';
14
+ import { packageManager } from '@aalis/api-package-manager';
14
15
  import { persona } from '@aalis/api-persona';
15
16
  import { aggregatePlatformDetails, getPlatformAdapters, getPlatformNames, platform, } from '@aalis/api-platform';
17
+ import { pluginSource } from '@aalis/api-plugin-source';
16
18
  import { createProcessGateway, processService } from '@aalis/api-process';
17
19
  import { sessionConfirm } from '@aalis/api-session-confirm';
18
20
  import { createStorageGateway, readTailLines, storage } from '@aalis/api-storage';
19
21
  import { tools } from '@aalis/api-tools';
20
22
  import { DEFAULT_SUBSYSTEM_METADATA, webuiClient, webuiServer } from '@aalis/api-webui';
21
- import { appService, config, definePlugin, events, hostConfig, LogHub, lifecycle, logger, optional, pluginsService, provide, services, } from '@aalis/core';
23
+ import { appService, config, definePlugin, events, LogHub, lifecycle, logger, optional, pluginsService, provide, services, } from '@aalis/core';
22
24
  import { parseLogLine } from '@aalis/schema-log';
23
25
  import express from 'express';
24
26
  import { WebSocket, WebSocketServer } from 'ws';
@@ -28,7 +30,7 @@ import { renderClientSwitchPage } from './client-switch-page.js';
28
30
  import { createRouteGate } from './gate.js';
29
31
  import { registerFileRoutes } from './routes/files.js';
30
32
  import { registerMarketplaceRoutes } from './routes/marketplace.js';
31
- import { registerPluginRoutes } from './routes/plugins.js';
33
+ import { registerPluginRoutes, saveAfterApply } from './routes/plugins.js';
32
34
  import { registerProxyRoutes } from './routes/proxy.js';
33
35
  import { registerUploadedFilesRoutes } from './routes/uploaded-files.js';
34
36
  import { createWsHeartbeat } from './ws-heartbeat.js';
@@ -65,7 +67,7 @@ const configSchema = {
65
67
  type: 'boolean',
66
68
  label: '启动时自动打开浏览器',
67
69
  default: true,
68
- description: '启动时以含 token 的 URL 自动开启默认浏览器;SSH/headless 环境建议关闭',
70
+ description: '访问 token 为新生成时(persist 模式首次生成、ephemeral 模式每次生成)以含 token 的 URL 自动开启默认浏览器;沿用已有 token(persist 读回已持久化的 token、fixed 用配置的 fixedToken)时不打开;fixed 模式 fixedToken 为空时按 persist 处理。SSH/headless 环境建议关闭',
69
71
  },
70
72
  tokenMode: {
71
73
  type: 'select',
@@ -94,7 +96,7 @@ const configSchema = {
94
96
  type: 'string',
95
97
  label: '插件市场 npm 源',
96
98
  default: 'https://registry.npmjs.org',
97
- description: '插件市场检索用的 npm registry 基址。注意 npm 的 search API 并非所有镜像都支持(淘宝等国内源不支持),默认官方源;国内可填支持 search 的镜像或代理。安装走 package-manager(遵循本机 npm 配置)。',
99
+ description: '插件市场检索用的 npm registry 基址。注意 npm 的 search API 并非所有镜像都支持(淘宝等国内源不支持),默认官方源;国内可填支持 search 的镜像或代理。安装走 package-manager(遵循本机 npm 配置),卡片上的最新版与可更新也按安装使用的源查。',
98
100
  },
99
101
  // 活跃前端不在此配置:前端即 `webui-client` 服务的多 provider,活跃者由「服务偏好」
100
102
  // (servicePreferences['webui-client'])决定——在 WebUI「服务」页的下拉框切换。
@@ -102,7 +104,6 @@ const configSchema = {
102
104
  // ===== WebSocket 消息协议 =====
103
105
  // 入站消息类型 + 校验 schema 见 ./protocol.ts(zod 强校验)
104
106
  import { WSIncomingSchema } from './protocol.js';
105
- export { WSIncomingSchema } from './protocol.js';
106
107
  // ===== 日志文件读取(与 @aalis/runtime 的 file-logger 格式对偶)=====
107
108
  // 行格式契约(format ↔ parse)由 @aalis/schema-log 的 parseLogLine 唯一持有,此处只负责
108
109
  // 读取「持久化日志单一数据源」并按 cursor 尾读分页。
@@ -172,6 +173,8 @@ const uses = {
172
173
  hostConfig: optional(hostConfig),
173
174
  app: optional(appService),
174
175
  plugins: optional(pluginsService),
176
+ source: optional(pluginSource),
177
+ packageManager: optional(packageManager),
175
178
  storage: optional(storage),
176
179
  authority: optional(authority),
177
180
  commands: optional(commands),
@@ -183,7 +186,6 @@ const uses = {
183
186
  llm: optional(llm),
184
187
  persona: optional(persona),
185
188
  agent: optional(agent),
186
- memory: optional(memory),
187
189
  };
188
190
  export default definePlugin({
189
191
  name,
@@ -206,10 +208,6 @@ async function startWebuiServer(caps) {
206
208
  ? config.tokenMode
207
209
  : 'persist',
208
210
  fixedToken: config.fixedToken ?? '',
209
- relationGraphDefaultSpacing: (() => {
210
- const v = Number(config.relationGraphDefaultSpacing);
211
- return Number.isFinite(v) && v > 0 ? v : 120;
212
- })(),
213
211
  marketplaceRegistry: config.marketplaceRegistry?.trim() || 'https://registry.npmjs.org',
214
212
  };
215
213
  // 创建 storage gateway;所有文件读写(token、access、文件管理)都走这里
@@ -221,32 +219,10 @@ async function startWebuiServer(caps) {
221
219
  // 所有模式都会写出 data:/webui/access.txt 便于查找访问 URL
222
220
  const tokenFileUri = 'data:/webui/token';
223
221
  const accessFileUri = 'data:/webui/access.txt';
224
- async function resolveAuthToken() {
225
- if (uiConfig.tokenMode === 'fixed' && uiConfig.fixedToken.trim()) {
226
- return uiConfig.fixedToken.trim();
227
- }
228
- if (uiConfig.tokenMode === 'persist' || uiConfig.tokenMode === 'fixed') {
229
- try {
230
- const raw = await storage.readFile(tokenFileUri, 'utf-8');
231
- const existing = (typeof raw === 'string' ? raw : raw.toString('utf-8')).trim();
232
- if (existing)
233
- return existing;
234
- }
235
- catch {
236
- /* not exists or unreadable */
237
- }
238
- const fresh = Buffer.from(crypto.getRandomValues(new Uint8Array(24))).toString('hex');
239
- try {
240
- await storage.writeFile(tokenFileUri, fresh);
241
- }
242
- catch (err) {
243
- logger.warn(`持久化 token 失败,本次仍可使用但重启会再生成: ${err.message}`);
244
- }
245
- return fresh;
246
- }
247
- // ephemeral
248
- return Buffer.from(crypto.getRandomValues(new Uint8Array(24))).toString('hex');
249
- }
222
+ const fixedToken = uiConfig.tokenMode === 'fixed' ? uiConfig.fixedToken.trim() : '';
223
+ let authToken = fixedToken || Buffer.from(crypto.getRandomValues(new Uint8Array(24))).toString('hex');
224
+ /** 监听成功后的访问 URL;持久化 token 晚于监听读回时,据此重写 access.txt */
225
+ let listeningUrl;
250
226
  /** 返回是否真的写成了:调用方据此决定能不能宣称「凭据已写入」 */
251
227
  async function writeAccessFile(url, token) {
252
228
  const lines = [
@@ -269,8 +245,50 @@ async function startWebuiServer(caps) {
269
245
  return false;
270
246
  }
271
247
  }
272
- const authToken = await resolveAuthToken();
273
- const auth = createAuthSystem(authToken, logger.child('auth'));
248
+ // persist 的读回跟随 storage:storage 是 optional、不参与激活拓扑,可能晚于本插件上线
249
+ // (加载顺序、启动时配置出错后修好、运行时才启用)。只在 apply 里读一次会读空:内存里是新随机
250
+ // token,持久化的那份不生效,已登录的浏览器全部被登出;没有 token 文件时也永远建不起来。
251
+ // follow 对已在线的 storage 同步首挂,晚上线时补读。持久化的那份优先(浏览器里 30 天的 cookie
252
+ // 就是它);只认第一次成功,之后 storage 换人不再换 token。
253
+ let tokenSettled = uiConfig.tokenMode === 'ephemeral' || fixedToken !== '';
254
+ // autoOpen 只在 token 是本次新生成时打开浏览器:沿用已有 token(fixed、persist 读回)时浏览器里的 cookie 仍有效,
255
+ // 再开一页只是重复。app:ready 是粘性事件,本插件每次 bounce 都会重新收到,不按此判断就每次重载多开一个标签页
256
+ let tokenFresh = fixedToken === '';
257
+ let tokenLoading;
258
+ caps.storage.follow(() => {
259
+ if (tokenSettled)
260
+ return;
261
+ tokenLoading = (async () => {
262
+ try {
263
+ const raw = await storage.readFile(tokenFileUri, 'utf-8');
264
+ const existing = (typeof raw === 'string' ? raw : raw.toString('utf-8')).trim();
265
+ if (existing) {
266
+ tokenSettled = true;
267
+ tokenFresh = false;
268
+ if (existing !== authToken) {
269
+ authToken = existing;
270
+ if (listeningUrl)
271
+ void writeAccessFile(listeningUrl, authToken);
272
+ }
273
+ return;
274
+ }
275
+ }
276
+ catch {
277
+ /* 不存在或读不了 */
278
+ }
279
+ try {
280
+ await storage.writeFile(tokenFileUri, authToken);
281
+ tokenSettled = true;
282
+ }
283
+ catch (err) {
284
+ logger.warn(`持久化 token 失败,本次仍可使用但重启会再生成: ${err.message}`);
285
+ }
286
+ })();
287
+ });
288
+ // storage 已在线时 follow 是同步首挂:等读回完成再继续,认证从第一个请求起就用持久化的 token
289
+ if (tokenLoading)
290
+ await tokenLoading;
291
+ const auth = createAuthSystem(() => authToken);
274
292
  const expressApp = express();
275
293
  expressApp.use(express.json({ limit: '10mb' }));
276
294
  expressApp.use(auth.middleware);
@@ -367,21 +385,17 @@ async function startWebuiServer(caps) {
367
385
  // 获取系统状态
368
386
  expressApp.get('/api/status', gate(), (_req, res) => {
369
387
  const personaSvc = caps.persona.current;
370
- // 判断上传能力。media / file-reader / cli 在本插件里只做在场探测,为一个布尔值反向依赖
388
+ // 判断上传能力。media / file-reader 在本插件里只做在场探测,为一个布尔值反向依赖
371
389
  // 它们的包不值当(file-reader 还是插件包),故按名动态查——动态查到的不算声明依赖。
372
390
  const hasMedia = services.get('media') !== undefined;
373
391
  const llmHasVision = listLLMModels(caps.llm).some(e => e.instance.capabilities.includes('vision'));
374
392
  const hasFileReader = services.get('file-reader') !== undefined;
393
+ const appName = caps.hostConfig.current?.get('name') ?? 'Aalis';
375
394
  res.json({
376
- name: personaSvc?.getPersonaName() ?? caps.hostConfig.require().get('name'),
377
- services: {
378
- 'webui-server': services.get(webuiServer) !== undefined,
379
- cli: services.get('cli') !== undefined,
380
- llm: caps.llm.current !== undefined,
381
- agent: caps.agent.current !== undefined,
382
- memory: caps.memory.current !== undefined,
383
- persona: personaSvc !== undefined,
384
- },
395
+ /** 对话对象的显示名:装有人设时是人设名,否则同 appName */
396
+ name: personaSvc?.getPersonaName() ?? appName,
397
+ /** 全局配置里的应用名称,每次实时读配置文档 */
398
+ appName,
385
399
  /** 上传能力:客户端据此决定显示哪些上传按钮 */
386
400
  uploadCapabilities: {
387
401
  /** 是否支持图片上传(media 可用 或 LLM 声明了 vision) */
@@ -424,6 +438,7 @@ async function startWebuiServer(caps) {
424
438
  registerPluginRoutes(expressApp, {
425
439
  app: caps.app,
426
440
  plugins: caps.plugins,
441
+ source: caps.source,
427
442
  hostConfig: caps.hostConfig,
428
443
  tools: caps.tools,
429
444
  commands: caps.commands,
@@ -437,20 +452,10 @@ async function startWebuiServer(caps) {
437
452
  logger,
438
453
  plugins: caps.plugins,
439
454
  services,
440
- // package-manager 由插件提供,本包不反向依赖那个插件包:按名动态查,缺席时装卸路由回 503
441
- packageManager: () => services.get('package-manager'),
455
+ packageManager: caps.packageManager,
442
456
  }, gate, uiConfig.marketplaceRegistry, () => collectLocalPackageDeps(localScanDirs, fsScanEnv), () => discoverAndProvideClients());
443
- // 获取历史日志:从 data/latest.log 读尾部 N 条(lazy load)。
457
+ // 历史日志从 data/latest.log 读(lazy load)。
444
458
  // 单进程内 LogHub 不再缓存 buffer——历史以文件为单一数据源。
445
- expressApp.get('/api/logs', gate(), async (_req, res) => {
446
- try {
447
- const entries = await readLogFileTail(storage, 200);
448
- res.json(entries);
449
- }
450
- catch (err) {
451
- res.status(500).json({ error: err.message });
452
- }
453
- });
454
459
  // 尾部 N 条(首屏 / 显式刷新)
455
460
  expressApp.get('/api/logs/tail', gate(), async (req, res) => {
456
461
  const limit = Math.min(Math.max(Number(req.query.limit) || 200, 1), 5000);
@@ -487,8 +492,9 @@ async function startWebuiServer(caps) {
487
492
  displayNameMap.set(p.instanceId, p.displayName);
488
493
  }
489
494
  }
490
- // 根激活提供核心服务——显示包名,别把内部 id 'root' 裸露给用户。
491
- displayNameMap.set('root', '@aalis/core');
495
+ // 根激活归宿主:core 的内置服务与宿主登记的 host-config / plugin-source 都在这里(与市场依赖图同一标签)。
496
+ // 别把内部 id 'root' 裸露给用户。
497
+ displayNameMap.set('root', '宿主');
492
498
  const serviceNames = services.names();
493
499
  const detail = {};
494
500
  for (const svcName of serviceNames) {
@@ -524,7 +530,12 @@ async function startWebuiServer(caps) {
524
530
  res.status(404).json({ ok: false, error: `service "${svcName}" has no provider with contextId "${contextId}"` });
525
531
  return;
526
532
  }
527
- const host = caps.hostConfig.require();
533
+ // 偏好要跨重启保留就得写进文档:宿主没提供文档时不动运行态
534
+ const host = caps.hostConfig.current;
535
+ if (!host) {
536
+ res.status(503).json({ ok: false, error: '宿主未提供配置文档(host-config),无法保存偏好' });
537
+ return;
538
+ }
528
539
  if (!services.prefer(svcName, contextId)) {
529
540
  res.status(409).json({ ok: false, error: `service "${svcName}" rejected provider preference "${contextId}"` });
530
541
  return;
@@ -534,18 +545,24 @@ async function startWebuiServer(caps) {
534
545
  // 重挂与偏好同属内存态,必须在等落盘之前一起生效:save 拒绝时才不会留下「解析选 B、静态挂 A」。
535
546
  if (svcName === 'webui-client')
536
547
  remountActiveClient();
537
- await caps.app.require().saveConfig();
548
+ if (!(await saveAfterApply(host, res, 'restart')))
549
+ return;
538
550
  res.json({ ok: true });
539
551
  });
540
552
  /** 清除服务偏好 */
541
553
  expressApp.delete('/api/services/:name/prefer', gate(), async (req, res) => {
542
554
  const svcName = String(req.params.name);
543
- const host = caps.hostConfig.require();
555
+ const host = caps.hostConfig.current;
556
+ if (!host) {
557
+ res.status(503).json({ ok: false, error: '宿主未提供配置文档(host-config),无法保存偏好' });
558
+ return;
559
+ }
544
560
  services.unprefer(svcName);
545
561
  host.removeServicePreference(svcName);
546
562
  if (svcName === 'webui-client')
547
563
  remountActiveClient();
548
- await caps.app.require().saveConfig();
564
+ if (!(await saveAfterApply(host, res, 'restart')))
565
+ return;
549
566
  res.json({ ok: true });
550
567
  });
551
568
  // 前端切换已统一到「服务偏好」:前端是 webui-client 服务的多 provider,切换走
@@ -680,18 +697,17 @@ async function startWebuiServer(caps) {
680
697
  // 触发指定 provider 重新探测远端模型列表(用于 webui 上的"刷新模型"按钮)
681
698
  // 仅对在 LLMModel 上实现了 refresh() 的 provider 生效(远端动态发现型,如 Ollama / OpenAI)。
682
699
  // 同 provider 下所有 model entries 共享同一份 refresh 闭包,调任一个 entry 即可。
700
+ // 按 providerId 找条目,与 /api/llm-providers 的聚合同一口径:条目的 contextId 是 `<provider>/<model>`,不等于 provider
683
701
  expressApp.post('/api/llm-providers/:contextId/refresh', gate(), async (req, res) => {
684
702
  const contextId = req.params.contextId;
685
- if (!contextId) {
686
- res.status(400).json({ error: 'contextId is required' });
687
- return;
688
- }
689
703
  try {
690
- const llmEntries = caps.llm.all();
691
- const target = llmEntries.find(e => e.contextId === contextId && typeof e.instance.refresh === 'function');
704
+ const llmEntries = caps.llm.all().filter(e => e.instance.providerId === contextId);
705
+ const target = llmEntries.find(e => typeof e.instance.refresh === 'function');
692
706
  if (!target) {
693
707
  res.status(404).json({
694
- error: `no refreshable LLM provider registered for contextId="${contextId}" (provider 可能为静态注册型,不支持运行时刷新)`,
708
+ error: llmEntries.length > 0
709
+ ? `提供者 ${contextId} 不支持运行时刷新模型列表(例如关闭了模型发现 discoverModels)`
710
+ : `提供者 ${contextId} 当前没有已注册的模型,无法刷新`,
695
711
  });
696
712
  return;
697
713
  }
@@ -806,15 +822,13 @@ async function startWebuiServer(caps) {
806
822
  try {
807
823
  const models = await provider.instance.listModels();
808
824
  for (const m of models) {
809
- const isModelInfo = typeof m === 'object' && m !== null && 'id' in m;
810
- const modelId = isModelInfo ? m.id : String(m);
811
- const modelCaps = isModelInfo ? m.capabilities : [];
825
+ const modelId = String(m);
812
826
  aggregated.push({
813
827
  value: modelId,
814
828
  model: modelId,
815
829
  provider: provider.label ?? provider.contextId,
816
830
  contextId: provider.contextId,
817
- capabilities: modelCaps,
831
+ capabilities: [],
818
832
  });
819
833
  flatValues.push(modelId);
820
834
  }
@@ -872,8 +886,17 @@ async function startWebuiServer(caps) {
872
886
  allClients.add(ws);
873
887
  heartbeat.track(ws);
874
888
  ws.on('message', async (data) => {
889
+ // 非 JSON 帧与形状不对的帧同属协议违规:都只记一行告警,外层 catch 留给真正的处理失败
890
+ let raw;
875
891
  try {
876
- const parseResult = WSIncomingSchema.safeParse(JSON.parse(data.toString()));
892
+ raw = JSON.parse(data.toString());
893
+ }
894
+ catch {
895
+ logger.warn('WebUI 收到协议违规消息: 非 JSON 帧');
896
+ return;
897
+ }
898
+ try {
899
+ const parseResult = WSIncomingSchema.safeParse(raw);
877
900
  if (!parseResult.success) {
878
901
  logger.warn(`WebUI 收到协议违规消息: ${parseResult.error.issues.map(i => `${i.path.join('.')}: ${i.message}`).join('; ')}`);
879
902
  return;
@@ -1314,7 +1337,7 @@ async function startWebuiServer(caps) {
1314
1337
  });
1315
1338
  /**
1316
1339
  * 全动态发现前端:按 `aalis.client:true` 标记扫描,**不硬编码任何前端包名**
1317
- * (与 runtime 加载器的 marker 驱动一致;忒修斯之船——任意第三方前端带标记+dist 即被发现)。
1340
+ * (忒修斯之船——任意第三方前端带标记+dist 即被发现)。
1318
1341
  * 覆盖三种拓扑:monorepo(扫 packages 同级目录)/ 独立项目(扫 node_modules/@aalis + 根 deps)。
1319
1342
  *
1320
1343
  * **幂等**,可重复调用:装完一个 `aalis-interface` 包后要立刻重跑,否则它不会出现在服务页
@@ -1323,8 +1346,6 @@ async function startWebuiServer(caps) {
1323
1346
  *
1324
1347
  * 只登记新候选,**不切换活跃前端**:换前端是用户在服务页的显式选择,装了个新的不该把
1325
1348
  * 他正在用的顶掉。
1326
- *
1327
- * @returns 本次新发现的候选数
1328
1349
  */
1329
1350
  function discoverAndProvideClients() {
1330
1351
  const projectRequire = createRequire(pathToFileURL(resolve(process.cwd(), 'package.json')));
@@ -1382,10 +1403,13 @@ async function startWebuiServer(caps) {
1382
1403
  clientProviderDisposers.set(candidate.id, provide(webuiClient, { getClientDir: () => candidate.dir }, { label: candidate.label, onBehalfOf: candidate.id }));
1383
1404
  logger.info(`发现前端: ${candidate.label} (${candidate.dir})`);
1384
1405
  }
1385
- return fresh.length;
1386
1406
  }
1387
1407
  // 启动服务器
1388
- events.on('app:ready', () => {
1408
+ events.on('app:ready', async () => {
1409
+ // 冷启动时 storage 的激活在 app:ready 之前收敛;等晚到的读回落定,listen / autoOpen /
1410
+ // access.txt 用的就是持久化的 token
1411
+ if (tokenLoading)
1412
+ await tokenLoading;
1389
1413
  discoverAndProvideClients();
1390
1414
  // 活跃前端 = 解析后的 webui-client 服务(servicePreferences 偏好 > 优先级 > 注册顺序);零前端则 404。
1391
1415
  const activeDir = currentClientDir();
@@ -1415,6 +1439,7 @@ async function startWebuiServer(caps) {
1415
1439
  // 展示/打开一律用回环;局域网访问地址由用户按本机 IP 自行替换。
1416
1440
  const displayHost = uiConfig.host === '0.0.0.0' || uiConfig.host === '::' ? '127.0.0.1' : uiConfig.host;
1417
1441
  const url = `http://${displayHost}:${uiConfig.port}/`;
1442
+ listeningUrl = url;
1418
1443
  const accessUrl = `${url}?token=${authToken}`;
1419
1444
  logger.info(`WebUI 已启动: ${url}`);
1420
1445
  // 多前端时提示恢复页 URL:万一切到无切换 UI 的前端被卡住,可在此切回(详见 client-switch-page.ts)。
@@ -1426,7 +1451,7 @@ async function startWebuiServer(caps) {
1426
1451
  : uiConfig.tokenMode === 'fixed'
1427
1452
  ? 'token 来自配置 fixedToken,固定不变'
1428
1453
  : 'token 已持久化到 storage data:/webui/token,重启沿用';
1429
- // 不把 token 打进日志(会落 latest.log / 被 /api/logs 回放 / 贴日志求助时外泄)。
1454
+ // 不把 token 打进日志(会落 latest.log / 被 /api/logs/* 回放 / 贴日志求助时外泄)。
1430
1455
  // 仅打不带 token 的 URL,完整一键登录链接见 access.txt(该文件应 0o600,见 token 落盘)。
1431
1456
  logger.info(`首次访问 URL(${tokenHint}): ${url} —— 完整一键登录链接见 ${accessFileUri}`);
1432
1457
  void (async () => {
@@ -1449,7 +1474,7 @@ async function startWebuiServer(caps) {
1449
1474
  }
1450
1475
  logger.info(`访问凭据已写入: ${accessFileUri}(绝对路径: ${absHint})`);
1451
1476
  })();
1452
- if (uiConfig.autoOpen)
1477
+ if (uiConfig.autoOpen && tokenFresh)
1453
1478
  openBrowser(accessUrl, createProcessGateway(caps.process));
1454
1479
  });
1455
1480
  });
@@ -1522,11 +1547,6 @@ async function startWebuiServer(caps) {
1522
1547
  const webuiService = {
1523
1548
  getPort: () => uiConfig.port,
1524
1549
  getHost: () => uiConfig.host,
1525
- setClientDir(dir) {
1526
- clientDist = dir;
1527
- mountStaticDir(dir);
1528
- logger.info(`前端已切换: ${dir}`);
1529
- },
1530
1550
  registerPage(page, contextId) {
1531
1551
  const list = registeredPages.get(contextId) ?? [];
1532
1552
  const entry = { ...page, pluginName: contextId };