@allbluecn/web-app 0.22.0 → 0.22.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.
Files changed (93) hide show
  1. package/package.json +6 -8
  2. package/public/logo/allblue-logo-app-icon.svg +1 -1
  3. package/src/components/changelog/changelog-page.tsx +24 -5
  4. package/src/components/desktop/cloud-login.tsx +91 -0
  5. package/src/components/desktop/conflict-center.tsx +148 -0
  6. package/src/components/desktop/renamed-toast.tsx +23 -0
  7. package/src/components/desktop/shell-widgets.tsx +19 -0
  8. package/src/components/desktop/sync-indicator.tsx +60 -0
  9. package/src/components/desktop/sync-status-card.tsx +60 -0
  10. package/src/components/layout/sidebar-03/app-sidebar.tsx +38 -3
  11. package/src/components/shared-kit/avatars/__tests__/avatar-shape.test.ts +17 -1
  12. package/src/components/shared-kit/avatars/avatar-shape.ts +10 -0
  13. package/src/components/shared-kit/avatars/user-avatar.tsx +2 -2
  14. package/src/lib/auth/auth.config.ts +4 -1
  15. package/src/lib/changelog/sdk-versions.ts +33 -2
  16. package/src/lib/desktop-bridge-types.ts +30 -0
  17. package/src/lib/desktop-detect.ts +2 -0
  18. package/src/lib/vector-store/__tests__/factory.test.ts +0 -7
  19. package/src/lib/vector-store/index.ts +2 -4
  20. package/src/paraglide/messages/_index.d.ts +1 -1
  21. package/src/paraglide/messages/_index.js +32 -16
  22. package/src/paraglide/messages/common_sidebar_usermenu_loginsaas3.d.ts +1 -1
  23. package/src/paraglide/messages/common_sidebar_usermenu_loginsaas3.js +3 -3
  24. package/src/paraglide/messages/en.js +11 -6
  25. package/src/paraglide/messages/{tasks_ai_desktop_notice.d.ts → settings_tabs_sync.d.ts} +5 -5
  26. package/src/paraglide/messages/settings_tabs_sync.js +29 -0
  27. package/src/paraglide/messages/zh.js +11 -6
  28. package/src/providers/index.tsx +6 -0
  29. package/src/routeTree.gen.ts +107 -49
  30. package/src/routes/_nav/route.lazy.tsx +12 -0
  31. package/src/routes/_nav/settings/-settings-tabs.tsx +1 -1
  32. package/src/routes/_nav/settings/sync/route.lazy.tsx +40 -0
  33. package/src/routes/_nav/settings/sync/route.tsx +15 -0
  34. package/src/routes/_nav/tasks/index.lazy.tsx +1 -11
  35. package/src/routes/api/billing-webhook.ts +6 -2
  36. package/src/routes/api/sync/bootstrap.ts +38 -0
  37. package/src/routes/api/sync/pull.ts +96 -0
  38. package/src/routes/api/sync/push.ts +169 -0
  39. package/src/routes/api/sync/snapshot.ts +104 -0
  40. package/src/server/__tests__/fetch-guard.test.ts +2 -2
  41. package/src/server/__tests__/resource-registry.test.ts +4 -2
  42. package/src/server/__tests__/sandbox-entitlement.test.ts +5 -0
  43. package/src/server/__tests__/security-headers.test.ts +25 -0
  44. package/src/server/__tests__/task-assigned-query.test.ts +0 -11
  45. package/src/server/__tests__/task-serverfn-crud.test.ts +1 -22
  46. package/src/server/__tests__/task-serverfn-queries.test.ts +1 -9
  47. package/src/server/ai-gateway/__tests__/chat-stream-builder-memory.test.ts +0 -11
  48. package/src/server/ai-gateway/__tests__/embedding-request.test.ts +1 -1
  49. package/src/server/ai-gateway/__tests__/provider-adapter.test.ts +2 -2
  50. package/src/server/ai-gateway/__tests__/run-store-durable.test.ts +1 -1
  51. package/src/server/ai-gateway/durability/__tests__/pg-stream.test.ts +1 -1
  52. package/src/server/ai-gateway/durability/__tests__/reaper-real-db.test.ts +1 -1
  53. package/src/server/ai-gateway/memory/index.ts +2 -2
  54. package/src/server/ai-gateway/sandbox/__tests__/instance-store.test.ts +1 -1
  55. package/src/server/ai-task/assigned-query.ts +4 -15
  56. package/src/server/desktop-guest.ts +12 -0
  57. package/src/server/fetch-guard.ts +3 -3
  58. package/src/server/middlewares/desktop-token.ts +23 -0
  59. package/src/server/remote-auth.ts +6 -0
  60. package/src/server/serverFns/__tests__/billing.test.ts +58 -1
  61. package/src/server/serverFns/__tests__/update-seats.test.ts +8 -8
  62. package/src/server/serverFns/agents/agent-crud.ts +3 -1
  63. package/src/server/serverFns/auth/session.ts +9 -0
  64. package/src/server/serverFns/billing.ts +10 -4
  65. package/src/server/serverFns/bug/bug-crud.ts +7 -2
  66. package/src/server/serverFns/story/__tests__/labels.test.ts +37 -15
  67. package/src/server/serverFns/story/story-comments.ts +2 -1
  68. package/src/server/serverFns/story/story-crud/activities.ts +1 -1
  69. package/src/server/serverFns/story/story-crud/user-brief.ts +1 -1
  70. package/src/server/serverFns/story/story-crud.ts +2 -1
  71. package/src/server/serverFns/story/story-transfer.ts +2 -1
  72. package/src/server/serverFns/tasks/task-crud.ts +0 -13
  73. package/src/server/serverFns/tasks/task-queries.ts +1 -12
  74. package/src/server/serverFns/testcase/plans.ts +1 -1
  75. package/src/server/serverFns/testcase/run-cases.ts +1 -1
  76. package/src/server/serverFns/testcase/runs.ts +1 -1
  77. package/src/server/serverFns/testcase/testcase-activities.ts +1 -1
  78. package/src/server/serverFns/testcase/testcase-board.ts +1 -1
  79. package/src/server/serverFns/testcase/testcase-crud.ts +1 -1
  80. package/src/server/serverFns/workspace.ts +1 -1
  81. package/src/server/story/types.ts +7 -22
  82. package/src/server/story/user-brief.ts +48 -0
  83. package/src/server/sync/scopes.ts +29 -0
  84. package/src/start.ts +10 -2
  85. package/src/styles/globals.css +10 -0
  86. package/src/__tests__/desktop-connection-validate.test.ts +0 -66
  87. package/src/lib/vector-store/__tests__/sqlite-vec-store.test.ts +0 -143
  88. package/src/lib/vector-store/sqlite-vec-store.ts +0 -224
  89. package/src/paraglide/messages/tasks_ai_desktop_notice.js +0 -29
  90. package/src/routes/__desktop/onboarding/route.lazy.tsx +0 -175
  91. package/src/routes/__desktop/onboarding/route.tsx +0 -11
  92. package/src/routes/_nav/settings/connection/route.lazy.tsx +0 -64
  93. package/src/routes/_nav/settings/connection/route.tsx +0 -10
@@ -1,175 +0,0 @@
1
- import { createLazyFileRoute, useNavigate } from '@tanstack/react-router'
2
- import { useState, type KeyboardEvent } from 'react'
3
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
4
- import { Button } from '@/components/ui/button'
5
- import {
6
- Dialog,
7
- DialogContent,
8
- DialogFooter,
9
- DialogHeader,
10
- DialogTitle,
11
- } from '@/components/ui/dialog'
12
- import { Input } from '@/components/ui/input'
13
- import { Label } from '@/components/ui/label'
14
- import { Cloud, Server, HardDrive, Loader2 } from 'lucide-react'
15
- import { useConnection } from '@desktop/connection-manager'
16
- import { connectionTest } from '@desktop/connection-manager/connection-test'
17
- import { toast } from '@/components/ui/toast'
18
- import { m } from "@/paraglide/messages.js";
19
- import { errorMessage } from "@/lib/toast-error";
20
-
21
- export const Route = createLazyFileRoute('/__desktop/onboarding')({
22
- component: OnboardingPage,
23
- })
24
-
25
- // SaaS 入口地址(桌面端默认云端模式落点,环境切换只需改这里)
26
- const SAAS_URL = 'https://allblue.litsex.cn'
27
-
28
- function OnboardingPage() {
29
- const { switchMode } = useConnection()
30
- const navigate = useNavigate()
31
- const [selfHostedDialogOpen, setSelfHostedDialogOpen] = useState(false)
32
- const [selfHostedUrl, setSelfHostedUrl] = useState('')
33
- const [selfHostedLabel, setSelfHostedLabel] = useState('')
34
- const [testing, setTesting] = useState(false)
35
-
36
- const handleSelectSaas = async () => {
37
- try {
38
- await switchMode({ mode: 'saas', url: SAAS_URL })
39
- } catch (e) {
40
- toast.error(m.desktop_onboarding_errors_switchFailed({ message: errorMessage(e, m.desktop_onboarding_errors_unknownError()) }))
41
- }
42
- }
43
-
44
- const handleSelectLocal = async () => {
45
- try {
46
- await switchMode({ mode: 'local' })
47
- await navigate({ to: '/workspace' })
48
- } catch (e) {
49
- toast.error(m.desktop_onboarding_errors_localFailed({ message: errorMessage(e, m.desktop_onboarding_errors_unknownError()) }))
50
- }
51
- }
52
-
53
- const handleConfirmSelfHosted = async () => {
54
- setTesting(true)
55
- try {
56
- const result = await connectionTest(selfHostedUrl)
57
- if (!result.ok) {
58
- toast.error(m.desktop_onboarding_errors_connectFailed({ message: result.error ?? m.desktop_onboarding_errors_timeout() }))
59
- return
60
- }
61
- await switchMode({ mode: 'self-hosted', url: selfHostedUrl, label: selfHostedLabel })
62
- } catch (e) {
63
- toast.error(m.desktop_onboarding_errors_switchFailed({ message: errorMessage(e, m.desktop_onboarding_errors_unknownError()) }))
64
- } finally {
65
- setTesting(false)
66
- }
67
- }
68
-
69
- // 键盘可达性:Card 为 div+onClick,补 role/tabIndex/Enter·Space 激活
70
- const handleCardKeyDown = (handler: () => void) => (e: KeyboardEvent) => {
71
- if (e.key === 'Enter' || e.key === ' ') {
72
- e.preventDefault()
73
- handler()
74
- }
75
- }
76
-
77
- return (
78
- <div className="flex min-h-screen items-center justify-center bg-background p-8">
79
- <div className="w-full max-w-4xl space-y-6">
80
- <div className="text-center space-y-2">
81
- <h1 className="text-3xl font-bold">{m.desktop_onboarding_title()}</h1>
82
- <p className="text-muted-foreground">{m.desktop_onboarding_subtitle()}</p>
83
- </div>
84
-
85
- <div className="grid gap-4 md:grid-cols-3">
86
- <Card
87
- className="cursor-pointer hover:border-primary transition-colors"
88
- onClick={handleSelectSaas}
89
- role="button"
90
- tabIndex={0}
91
- onKeyDown={handleCardKeyDown(handleSelectSaas)}
92
- >
93
- <CardHeader>
94
- <Cloud className="size-10 text-primary mb-2" />
95
- <CardTitle>{m.desktop_onboarding_saas_title()}</CardTitle>
96
- <CardDescription>{m.desktop_onboarding_saas_desc()}</CardDescription>
97
- </CardHeader>
98
- <CardContent>
99
- <Button className="w-full" variant="default" tabIndex={-1}>{m.desktop_onboarding_saas_button()}</Button>
100
- </CardContent>
101
- </Card>
102
-
103
- <Card
104
- className="cursor-pointer hover:border-primary transition-colors"
105
- onClick={() => setSelfHostedDialogOpen(true)}
106
- role="button"
107
- tabIndex={0}
108
- onKeyDown={handleCardKeyDown(() => setSelfHostedDialogOpen(true))}
109
- >
110
- <CardHeader>
111
- <Server className="size-10 text-primary mb-2" />
112
- <CardTitle>{m.desktop_onboarding_selfHosted_title()}</CardTitle>
113
- <CardDescription>{m.desktop_onboarding_selfHosted_desc()}</CardDescription>
114
- </CardHeader>
115
- <CardContent>
116
- <Button className="w-full" variant="outline" tabIndex={-1}>{m.desktop_onboarding_selfHosted_button()}</Button>
117
- </CardContent>
118
- </Card>
119
-
120
- <Card
121
- className="cursor-pointer hover:border-primary transition-colors"
122
- onClick={handleSelectLocal}
123
- role="button"
124
- tabIndex={0}
125
- onKeyDown={handleCardKeyDown(handleSelectLocal)}
126
- >
127
- <CardHeader>
128
- <HardDrive className="size-10 text-primary mb-2" />
129
- <CardTitle>{m.desktop_onboarding_local_title()}</CardTitle>
130
- <CardDescription>{m.desktop_onboarding_local_desc()}</CardDescription>
131
- </CardHeader>
132
- <CardContent>
133
- <Button className="w-full" variant="outline" tabIndex={-1}>{m.desktop_onboarding_local_button()}</Button>
134
- </CardContent>
135
- </Card>
136
- </div>
137
- </div>
138
-
139
- <Dialog open={selfHostedDialogOpen} onOpenChange={setSelfHostedDialogOpen}>
140
- <DialogContent>
141
- <DialogHeader>
142
- <DialogTitle>{m.desktop_onboarding_selfHosted_dialogTitle()}</DialogTitle>
143
- </DialogHeader>
144
- <div className="space-y-4 py-4">
145
- <div className="space-y-2">
146
- <Label htmlFor="url">{m.desktop_onboarding_selfHosted_urlLabel()}</Label>
147
- <Input
148
- id="url"
149
- placeholder="https://prd.mycompany.com"
150
- value={selfHostedUrl}
151
- onChange={(e) => setSelfHostedUrl(e.target.value)}
152
- />
153
- </div>
154
- <div className="space-y-2">
155
- <Label htmlFor="label">{m.desktop_onboarding_selfHosted_labelField()}</Label>
156
- <Input
157
- id="label"
158
- placeholder={m.desktop_onboarding_selfHosted_labelPlaceholder()}
159
- value={selfHostedLabel}
160
- onChange={(e) => setSelfHostedLabel(e.target.value)}
161
- />
162
- </div>
163
- </div>
164
- <DialogFooter>
165
- <Button variant="outline" onClick={() => setSelfHostedDialogOpen(false)}>{m.desktop_onboarding_selfHosted_cancel()}</Button>
166
- <Button onClick={handleConfirmSelfHosted} disabled={testing || !selfHostedUrl.trim()}>
167
- {testing ? <Loader2 className="size-4 animate-spin mr-2" /> : null}
168
- {testing ? m.desktop_onboarding_selfHosted_connecting() : m.desktop_onboarding_selfHosted_connect()}
169
- </Button>
170
- </DialogFooter>
171
- </DialogContent>
172
- </Dialog>
173
- </div>
174
- )
175
- }
@@ -1,11 +0,0 @@
1
- import { createFileRoute, redirect } from '@tanstack/react-router'
2
-
3
- export const Route = createFileRoute('/__desktop/onboarding')({
4
- // web 生产部署时该路由不可访问(仅桌面端 sidecar)
5
- beforeLoad: () => {
6
- const isDesktopBuild = import.meta.env.VITE_DEPLOY_TARGET === 'desktop'
7
- if (!isDesktopBuild) {
8
- throw redirect({ to: '/' })
9
- }
10
- },
11
- })
@@ -1,64 +0,0 @@
1
- import { createLazyFileRoute } from '@tanstack/react-router'
2
- import { useState } from 'react'
3
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
4
- import { Button } from '@/components/ui/button'
5
- import { Badge } from '@/components/ui/badge'
6
- import { useConnection, ModeSwitchDialog } from '@desktop/connection-manager'
7
- import type { ConnectionMode } from '@desktop/connection-manager'
8
- import { m } from "@/paraglide/messages.js";
9
-
10
- export const Route = createLazyFileRoute('/_nav/settings/connection')({
11
- component: ConnectionSettingsPage,
12
- })
13
-
14
- const MODE_VARIANT: Record<ConnectionMode, 'default' | 'secondary' | 'outline'> = {
15
- saas: 'default',
16
- 'self-hosted': 'secondary',
17
- local: 'outline',
18
- }
19
-
20
- function ConnectionSettingsPage() {
21
- const { config, switchMode } = useConnection()
22
- const [dialogOpen, setDialogOpen] = useState(false)
23
-
24
- const mode = config?.mode ?? 'local'
25
- const MODE_LABELS: Record<string, () => string> = {
26
- local: m.settings_connection_local,
27
- cloud: m.settings_connection_cloud,
28
- selfHosted: m.settings_connection_selfHosted,
29
- }
30
- const modeLabel = MODE_LABELS[mode]?.() ?? mode
31
-
32
- return (
33
- <div className="space-y-6 p-6">
34
- <div>
35
- <h1 className="text-2xl font-bold">{m.settings_connection_mode()}</h1>
36
- <p className="text-muted-foreground mt-1">{m.settings_connection_modeDescription()}</p>
37
- </div>
38
-
39
- <Card>
40
- <CardHeader>
41
- <CardTitle className="flex items-center gap-2">
42
- {m.settings_connection_currentMode()}
43
- <Badge variant={MODE_VARIANT[mode]}>{modeLabel}</Badge>
44
- </CardTitle>
45
- <CardDescription>
46
- {mode === 'saas' && m.settings_connection_cloudDescription()}
47
- {mode === 'self-hosted' && m.settings_connection_selfHostedDescription({ label: (config?.label ?? config?.url) ?? "" })}
48
- {mode === 'local' && m.settings_connection_localDescription()}
49
- </CardDescription>
50
- </CardHeader>
51
- <CardContent>
52
- <Button onClick={() => setDialogOpen(true)}>{m.settings_connection_switchMode()}</Button>
53
- </CardContent>
54
- </Card>
55
-
56
- <ModeSwitchDialog
57
- open={dialogOpen}
58
- onOpenChange={setDialogOpen}
59
- currentMode={mode}
60
- onConfirm={switchMode}
61
- />
62
- </div>
63
- )
64
- }
@@ -1,10 +0,0 @@
1
- import { createFileRoute, redirect } from '@tanstack/react-router'
2
-
3
- export const Route = createFileRoute('/_nav/settings/connection')({
4
- beforeLoad: () => {
5
- // 仅桌面端可访问
6
- if (import.meta.env.VITE_DEPLOY_TARGET !== 'desktop') {
7
- throw redirect({ to: '/settings' })
8
- }
9
- },
10
- })