@allbluecn/web-app 0.23.2 → 0.23.4
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/package.json +1 -1
- package/src/components/ai/global-harness-sidebar/__tests__/global-ai-prompts.test.ts +2 -0
- package/src/components/desktop/__tests__/desktop-state-listener.test.tsx +183 -0
- package/src/components/desktop/__tests__/sync-indicator.test.tsx +130 -0
- package/src/components/desktop/cloud-login.tsx +108 -6
- package/src/components/desktop/desktop-state-listener.tsx +76 -0
- package/src/components/desktop/shell-widgets.tsx +12 -5
- package/src/components/desktop/sync-indicator.tsx +62 -13
- package/src/components/desktop-agent/__tests__/agent-chat.test.tsx +194 -0
- package/src/components/desktop-agent/__tests__/agent-tool-card.test.tsx +138 -0
- package/src/components/desktop-agent/__tests__/agent-workspace-card.test.tsx +189 -0
- package/src/components/desktop-agent/agent-chat.tsx +221 -0
- package/src/components/desktop-agent/agent-tool-card.tsx +185 -0
- package/src/components/desktop-agent/agent-workspace-card.tsx +176 -0
- package/src/components/layout/sidebar-03/__tests__/user-menu-anonymous.test.tsx +4 -0
- package/src/components/layout/sidebar-03/app-sidebar.tsx +9 -2
- package/src/components/layout/sidebar-03/user-menu.tsx +63 -0
- package/src/config/sidebar-routes.tsx +14 -1
- package/src/lib/__tests__/session.test.ts +95 -0
- package/src/lib/auth/auth.config.ts +13 -0
- package/src/lib/changelog/sdk-versions.ts +1 -1
- package/src/lib/desktop-agent/__tests__/connection.test.ts +223 -0
- package/src/lib/desktop-agent/__tests__/errors.test.ts +43 -0
- package/src/lib/desktop-agent/__tests__/tool-result.test.ts +163 -0
- package/src/lib/desktop-agent/__tests__/use-desktop-agent-presence.test.tsx +181 -0
- package/src/lib/desktop-agent/availability.ts +39 -0
- package/src/lib/desktop-agent/connection.ts +72 -0
- package/src/lib/desktop-agent/errors.ts +21 -0
- package/src/lib/desktop-agent/tool-result.ts +170 -0
- package/src/lib/desktop-agent/use-desktop-agent-presence.ts +64 -0
- package/src/lib/desktop-bridge-types.ts +58 -1
- package/src/lib/hotkeys/__tests__/registry.test.ts +11 -4
- package/src/lib/hotkeys/registry.ts +9 -1
- package/src/lib/queries/README.md +1 -0
- package/src/lib/queries/__tests__/desktop-agent-queries.test.ts +80 -0
- package/src/lib/queries/desktop-agent.ts +25 -0
- package/src/lib/session.ts +15 -1
- package/src/paraglide/messages/_index.js +820 -0
- package/src/paraglide/messages/en.js +260 -0
- package/src/paraglide/messages/zh.js +260 -0
- package/src/routeTree.gen.ts +23 -0
- package/src/routes/-tests/agent-gate.test.ts +45 -0
- package/src/routes/_nav/agent/route.lazy.tsx +63 -0
- package/src/routes/_nav/agent/route.tsx +21 -0
- package/src/routes/api/sync/account-status.ts +5 -21
- package/src/server/__tests__/exchange-remote-token.test.ts +113 -0
- package/src/server/__tests__/remote-auth.test.ts +131 -1
- package/src/server/desktop-bootstrap.ts +19 -3
- package/src/server/desktop-session-merge.ts +51 -0
- package/src/server/remote-auth.ts +61 -3
- package/src/server/serverFns/auth/session.ts +28 -28
- package/src/server/serverFns/sync/transfer.ts +168 -0
- package/src/server/serverfn-public-allowlist.ts +4 -0
- package/src/server/sync/__tests__/ownership-transfer.test.ts +896 -0
- package/src/server/sync/account-status.ts +36 -0
- package/src/server/sync/ownership-transfer.ts +291 -0
- package/src/server/sync/saas-profile.ts +70 -0
|
@@ -74,6 +74,14 @@ export const appHotkeys = defineAppHotkeys({
|
|
|
74
74
|
hotkey: "ArrowDown",
|
|
75
75
|
meta: { descriptionKey: "common_hotkey_desc_chatNext", category: "聊天" },
|
|
76
76
|
},
|
|
77
|
+
// 本地文件助手(桌面 P3-B5)中止运行:绑定点 components/desktop-agent/agent-chat.tsx
|
|
78
|
+
// (仅运行中 enabled)。Mod+. 取 macOS「Command-句点 = 取消」惯例;Escape 已被
|
|
79
|
+
// close-panel 全局占用(同 scope 即冲突)。Mod 组合在 ignoreInputs auto 下输入框
|
|
80
|
+
// 内同样生效(边输入边中止)
|
|
81
|
+
"agent-abort": {
|
|
82
|
+
hotkey: "Mod+.",
|
|
83
|
+
meta: { descriptionKey: "common_hotkey_desc_agentAbort", category: "聊天" },
|
|
84
|
+
},
|
|
77
85
|
"command-palette": {
|
|
78
86
|
hotkey: "Mod+K",
|
|
79
87
|
meta: { descriptionKey: "common_hotkey_desc_commandPalette", category: "导航" },
|
|
@@ -165,7 +173,7 @@ export interface AppHotkeyConflict {
|
|
|
165
173
|
|
|
166
174
|
/**
|
|
167
175
|
* 声明级冲突检测:用官方 AppHotkeysStore.findConflicts 的冲突语义
|
|
168
|
-
* (scope 相同或任一为 global,且组合键相等或序列互为前缀)对全部
|
|
176
|
+
* (scope 相同或任一为 global,且组合键相等或序列互为前缀)对全部 26 条
|
|
169
177
|
* 声明两两比对。
|
|
170
178
|
*
|
|
171
179
|
* 注:官方 `findHotkeyConflicts()` 检测的是**运行时注册表**(HotkeyManager
|
|
@@ -65,3 +65,4 @@ export const xxxQueryOptions = (params) =>
|
|
|
65
65
|
| `agents.ts` | `agents` / `skills` / `agent-bindings` / `persona-hint` | 新增常量;harness 同键查询已收拢 |
|
|
66
66
|
| `team.ts` | `teamMembers` / `teamInvitations` / `teamUsers` | mention-dropdown 的 `teamUsers` 是另一数据源,未动(见上第 5 条) |
|
|
67
67
|
| `compliance.ts` | `compliance` / `compliance-detail` / `entity-tags` / `favorite-ids` | detail / favorite-ids 双份合并;entityTags / favoriteIds 参数化 `entityType`,`legal`(laws)/ `dictionary` 模块可渐进复用 |
|
|
68
|
+
| `desktop-agent.ts` | `desktop-agent` 前缀 / `desktop-agent,workspaces` | 本地文件助手(桌面 P3)授权目录清单;queryFn 走 desktopBridge.listWorkspaces(IPC,非 serverFn),无桥/旧壳降级 `[]` |
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
|
|
5
|
+
// desktop-agent queryOptions 工厂(桌面本地优先 P3-B5):key 结构单一来源 +
|
|
6
|
+
// queryFn 走 desktopBridge.listWorkspaces(main 侧 workspaces.json),非桌面 /
|
|
7
|
+
// 旧壳(无 listWorkspaces)降级空数组——本地桥数据,非 serverFn。
|
|
8
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
9
|
+
import { QueryClient } from '@tanstack/react-query'
|
|
10
|
+
|
|
11
|
+
const bridgeRef: { current: unknown } = { current: null }
|
|
12
|
+
|
|
13
|
+
vi.mock('@/lib/desktop-bridge-types', () => ({
|
|
14
|
+
getDesktopBridge: () => bridgeRef.current,
|
|
15
|
+
}))
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
DESKTOP_AGENT_QUERY_KEY,
|
|
19
|
+
DESKTOP_AGENT_WORKSPACES_QUERY_KEY,
|
|
20
|
+
desktopAgentWorkspacesQueryOptions,
|
|
21
|
+
} from '../desktop-agent'
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
bridgeRef.current = null
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
/** queryFn 运行时恒存在(工厂必传),类型上可选故窄化;context 形状对齐 react-query v5 调用面 */
|
|
28
|
+
function invokeQueryFn(
|
|
29
|
+
options: { queryFn?: (...args: never[]) => unknown },
|
|
30
|
+
queryKey: readonly unknown[],
|
|
31
|
+
): Promise<unknown> {
|
|
32
|
+
return (options.queryFn as (c: unknown) => Promise<unknown>)({
|
|
33
|
+
client: new QueryClient(),
|
|
34
|
+
queryKey,
|
|
35
|
+
signal: new AbortController().signal,
|
|
36
|
+
meta: undefined,
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
describe('desktop-agent queryOptions key 结构', () => {
|
|
41
|
+
it('前缀常量固定段 + workspaces 子段(invalidate 前缀覆盖全部 desktop-agent 查询)', () => {
|
|
42
|
+
expect(DESKTOP_AGENT_QUERY_KEY).toEqual(['desktop-agent'])
|
|
43
|
+
expect(DESKTOP_AGENT_WORKSPACES_QUERY_KEY).toEqual(['desktop-agent', 'workspaces'])
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('工厂产出 workspaces 常量同一引用(invalidate 与 useQuery 同源)', () => {
|
|
47
|
+
expect(desktopAgentWorkspacesQueryOptions().queryKey).toBe(DESKTOP_AGENT_WORKSPACES_QUERY_KEY)
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
describe('desktop-agent queryOptions queryFn 接线', () => {
|
|
52
|
+
it('桌面壳:调 desktopBridge.listWorkspaces() 并原样返回授权清单', async () => {
|
|
53
|
+
const listWorkspaces = vi.fn().mockResolvedValue(['/Users/x/proj-a', '/Users/x/proj-b'])
|
|
54
|
+
bridgeRef.current = { listWorkspaces }
|
|
55
|
+
const options = desktopAgentWorkspacesQueryOptions()
|
|
56
|
+
await expect(invokeQueryFn(options, options.queryKey)).resolves.toEqual([
|
|
57
|
+
'/Users/x/proj-a',
|
|
58
|
+
'/Users/x/proj-b',
|
|
59
|
+
])
|
|
60
|
+
expect(listWorkspaces).toHaveBeenCalledWith()
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('无桥(云 Web / OSS):返回空数组,不抛错', async () => {
|
|
64
|
+
bridgeRef.current = null
|
|
65
|
+
const options = desktopAgentWorkspacesQueryOptions()
|
|
66
|
+
await expect(invokeQueryFn(options, options.queryKey)).resolves.toEqual([])
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('旧壳(桥存在但无 listWorkspaces):返回空数组(optional-chaining 降级)', async () => {
|
|
70
|
+
bridgeRef.current = { appInfo: vi.fn() }
|
|
71
|
+
const options = desktopAgentWorkspacesQueryOptions()
|
|
72
|
+
await expect(invokeQueryFn(options, options.queryKey)).resolves.toEqual([])
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('桥 IPC 失败:错误透传给 react-query(error 态由消费方渲染,不吞成空清单)', async () => {
|
|
76
|
+
bridgeRef.current = { listWorkspaces: vi.fn().mockRejectedValue(new Error('ipc down')) }
|
|
77
|
+
const options = desktopAgentWorkspacesQueryOptions()
|
|
78
|
+
await expect(invokeQueryFn(options, options.queryKey)).rejects.toThrow('ipc down')
|
|
79
|
+
})
|
|
80
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
|
|
5
|
+
// desktop-agent(本地文件助手,桌面本地优先 P3-B5)queryOptions 工厂(key +
|
|
6
|
+
// queryFn 单一来源)。消费方(组件 useQuery / invalidate)一律从本模块取,禁止
|
|
7
|
+
// 再写 ['desktop-agent', ...] 内联字面量。模式说明见 ./README.md。
|
|
8
|
+
//
|
|
9
|
+
// 数据源是桌面壳桥(desktopBridge.listWorkspaces → main 侧
|
|
10
|
+
// userData/agent/workspaces.json),不是 serverFn:授权清单只存在于本机 main
|
|
11
|
+
// 进程,renderer 经 IPC 读取。非桌面(云 Web / OSS,无桥)与旧壳(无此方法)
|
|
12
|
+
// 降级空数组——入口本就被门控隐藏,降级只为让误用时不抛错。
|
|
13
|
+
import { queryOptions } from '@tanstack/react-query'
|
|
14
|
+
import { getDesktopBridge } from '@/lib/desktop-bridge-types'
|
|
15
|
+
|
|
16
|
+
/** desktop-agent 全域前缀:invalidate 用它即覆盖本模块全部查询 */
|
|
17
|
+
export const DESKTOP_AGENT_QUERY_KEY = ['desktop-agent'] as const
|
|
18
|
+
export const DESKTOP_AGENT_WORKSPACES_QUERY_KEY = [...DESKTOP_AGENT_QUERY_KEY, 'workspaces'] as const
|
|
19
|
+
|
|
20
|
+
/** 已授权工作目录清单(规范化绝对路径,main 侧 pickDirectory 写入顺序) */
|
|
21
|
+
export const desktopAgentWorkspacesQueryOptions = () =>
|
|
22
|
+
queryOptions({
|
|
23
|
+
queryKey: DESKTOP_AGENT_WORKSPACES_QUERY_KEY,
|
|
24
|
+
queryFn: async (): Promise<string[]> => (await getDesktopBridge()?.listWorkspaces?.()) ?? [],
|
|
25
|
+
})
|
package/src/lib/session.ts
CHANGED
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
import { getSession } from 'start-authjs'
|
|
19
19
|
import type { AuthSession } from 'start-authjs'
|
|
20
20
|
import { getAuthConfig } from './auth/auth.config'
|
|
21
|
+
// A10(桌面本地优先 P2):单条豁免 import(eslint 文件级放行)——
|
|
22
|
+
// desktop-session-merge 为纯数据组装 + remote-auth 纯 fs 读取(node:fs +
|
|
23
|
+
// env 守卫),无 prisma/server 重依赖链,客户端/云端部署零副作用。
|
|
24
|
+
import { getDesktopRemoteSession } from '@/server/desktop-session-merge'
|
|
21
25
|
|
|
22
26
|
export type SessionUser = NonNullable<AuthSession['user']> & {
|
|
23
27
|
id?: string
|
|
@@ -42,7 +46,17 @@ export type Session = Omit<AuthSession, 'user'> & { user?: SessionUser | null }
|
|
|
42
46
|
export async function getCurrentSession(request?: Request): Promise<Session | null> {
|
|
43
47
|
if (!request) return null
|
|
44
48
|
try {
|
|
45
|
-
|
|
49
|
+
const session = (await getSession(request, await getAuthConfig())) as Session | null
|
|
50
|
+
// 有效性口径与 getSessionFn 一致:有 user.id 才算有效(session callback
|
|
51
|
+
// 对 DB 无行用户返回 user: null 的残影不算登录态)
|
|
52
|
+
if (session?.user?.id) return session
|
|
53
|
+
// 桌面本地优先(P2 A10):authjs session 无效且为 desktop 部署时,回落
|
|
54
|
+
// remote-auth.json 的轻量云身份(user.id = 云 userId)。本函数是鉴权单点
|
|
55
|
+
// (requireAuth/authContextMiddleware 全链复用),回落在此一处生效、
|
|
56
|
+
// 604 处自动受益;非 desktop 恒 null(desktop 判定先于 fs 读取,零 IO)。
|
|
57
|
+
const remoteSession = await getDesktopRemoteSession()
|
|
58
|
+
if (remoteSession) return remoteSession
|
|
59
|
+
return session
|
|
46
60
|
} catch (error) {
|
|
47
61
|
// JWT 解密失败(AUTH_SECRET 轮换 / 跨应用同名 cookie 污染)等效于无 session:
|
|
48
62
|
// 返回 null 走未登录分支;@auth/core 会自行清理坏 cookie。
|