@allbluecn/web-app 0.10.3 → 0.11.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/package.json +21 -11
- package/public/favicon.ico +0 -0
- package/public/logo/allblue-logo-app-icon.svg +7 -0
- package/public/logo/allblue-logo-dark-square.svg +4 -0
- package/public/logo/allblue-logo-dark.svg +4 -0
- package/public/logo/allblue-logo-light-square.svg +4 -0
- package/public/logo/allblue-logo-light.svg +4 -0
- package/src/__tests__/bench/baselines/format-timestamp.json +44 -0
- package/src/__tests__/bench/core-hot-path.bench.ts +71 -0
- package/src/__tests__/integration/db-crud.integration.test.ts +79 -0
- package/src/__tests__/rejects-tothrow-regression.test.ts +31 -0
- package/src/__tests__/security/ai/prompt-injection.test.ts +105 -0
- package/src/__tests__/security/ssrf-url-whitelist.test.ts +54 -0
- package/src/__tests__/security/xss-sanitize.test.ts +99 -0
- package/src/__tests__/setup.ts +9 -0
- package/src/__tests__/visual/__screenshots__/theme-surface.vrt.test.tsx/theme-surface-dark-baseline-1-chromium-darwin.png +0 -0
- package/src/__tests__/visual/__screenshots__/theme-surface.vrt.test.tsx/theme-surface-light-baseline-1-chromium-darwin.png +0 -0
- package/src/__tests__/visual/__screenshots__/user-avatar.vrt.test.tsx/user-avatar-initial-fallback-baseline-1-chromium-darwin.png +0 -0
- package/src/__tests__/visual/theme-surface.vrt.test.tsx +54 -0
- package/src/__tests__/visual/user-avatar.vrt.test.tsx +32 -0
- package/src/components/error/global-error-boundary.tsx +2 -2
- package/src/components/knowledge/general-groups.ts +15 -0
- package/src/components/knowledge/kb-filter-popover.tsx +143 -0
- package/src/components/knowledge-public/legal-compliance-editor.tsx +4 -2
- package/src/components/layout/sidebar-03/__tests__/nav-workspace.test.tsx +9 -6
- package/src/components/layout/sidebar-03/nav-knowledge.tsx +19 -17
- package/src/components/layout/sidebar-03/nav-workspace.tsx +20 -22
- package/src/components/shared-kit/avatars/notion-avatar.tsx +2 -1
- package/src/components/shared-kit/icons/__tests__/lucide-curated-icons.test.ts +2 -2
- package/src/components/shared-kit/icons/__tests__/lucide-icon-picker.test.tsx +3 -2
- package/src/components/shared-kit/icons/lucide-icon-picker/curated-icons.ts +5 -10
- package/src/components/shared-kit/icons/lucide-icon-picker/lucide-icon-by-name.tsx +3 -3
- package/src/components/shared-kit/theme/theme-surface.tsx +52 -0
- package/src/components/story/relations/general-groups.ts +1 -15
- package/src/components/story/relations/knowledge-select-dialog.tsx +13 -87
- package/src/components/story/story-list-view.tsx +7 -1
- package/src/components/tiptap/node/table-node/ui/table-move-row-column-button/use-table-move-row-column.ts +0 -2
- package/src/components/workspace/__tests__/activity/workspace-activity-card.test.tsx +36 -0
- package/src/components/workspace/__tests__/contribution/workspace-contribution-card.test.tsx +17 -0
- package/src/components/workspace/__tests__/dialogs/create-workspace-dialog.test.tsx +63 -0
- package/src/components/workspace/__tests__/dialogs/delete-workspace-options.test.tsx +89 -0
- package/src/components/workspace/__tests__/dialogs/edit-workspace-dialog.test.tsx +68 -0
- package/src/components/workspace/__tests__/header/workspace-header.test.tsx +40 -0
- package/src/components/workspace/__tests__/kb/workspace-kb-card.test.tsx +154 -0
- package/src/components/workspace/__tests__/members/workspace-member-item.test.tsx +24 -0
- package/src/components/workspace/__tests__/members/workspace-members-manager.test.tsx +51 -0
- package/src/components/workspace/__tests__/projects/workspace-project-list.test.tsx +46 -0
- package/src/components/workspace/{create-workspace-dialog.tsx → dialogs/create-workspace-dialog.tsx} +1 -1
- package/src/components/workspace/{edit-workspace-dialog.tsx → dialogs/edit-workspace-dialog.tsx} +1 -1
- package/src/components/workspace/kb/workspace-kb-card.tsx +594 -0
- package/src/components/workspace/kb/workspace-kb-manage-dialog.tsx +174 -0
- package/src/components/workspace/kb/workspace-quick-doc-dialog.tsx +431 -0
- package/src/components/workspace/{workspace-members-manager.tsx → members/workspace-members-manager.tsx} +78 -40
- package/src/lib/__tests__/msw-sample.test.ts +68 -0
- package/src/lib/__tests__/pgvector.test.ts +2 -2
- package/src/lib/changelog/sdk-versions.ts +21 -21
- package/src/lib/collections/knowledge/knowledge-bases-collection.ts +3 -1
- package/src/lib/collections/reference/workspaces-collection.ts +3 -1
- package/src/lib/fetch-json.ts +23 -0
- package/src/{components/workspace/workspace-kb-card.tsx → lib/i18n/resolver.ts} +6 -12
- package/src/lib/sanitize.ts +14 -1
- package/src/lib/session.ts +9 -1
- package/src/lib/vector-store/__tests__/factory.test.ts +1 -1
- package/src/lib/vector-store/__tests__/sqlite-vec-store.test.ts +2 -2
- package/src/plugins/core/tags/__tests__/tags.test.ts +10 -10
- package/src/routes/__tests__/-edition-routing.test.tsx +4 -4
- package/src/routes/__tests__/route-tree-structure.test.ts +65 -0
- package/src/routes/_nav/knowledge/compliance/index.lazy.tsx +5 -2
- package/src/routes/_nav/knowledge/index.lazy.tsx +1 -1
- package/src/routes/_nav/knowledge/laws/index.lazy.tsx +5 -2
- package/src/routes/_nav/route.tsx +21 -0
- package/src/routes/_nav/workspaces/$workspaceId/route.lazy.tsx +13 -11
- package/src/routes/_nav/workspaces/$workspaceId/route.tsx +4 -2
- package/src/routes/_nav/workspaces/$workspaceId/settings/route.lazy.tsx +1 -1
- package/src/routes/_nav/workspaces/index.lazy.tsx +3 -3
- package/src/server/__tests__/agent-crud.test.ts +4 -4
- package/src/server/__tests__/chat-conversations.test.ts +1 -3
- package/src/server/__tests__/fetch-guard.test.ts +99 -0
- package/src/server/__tests__/permissions.test.ts +1 -3
- package/src/server/__tests__/resources.team-seats.test.ts +2 -0
- package/src/server/__tests__/skill-crud.test.ts +3 -3
- package/src/server/ai-gateway/__tests__/ai-output-guard.test.ts +95 -0
- package/src/server/ai-gateway/__tests__/kb-context-guard.test.ts +136 -0
- package/src/server/ai-gateway/__tests__/knowledge-search-fence.test.ts +240 -0
- package/src/server/ai-gateway/__tests__/llm-fence.test.ts +125 -0
- package/src/server/ai-gateway/__tests__/run-store.test.ts +52 -0
- package/src/server/ai-gateway/ai-output-guard.ts +77 -0
- package/src/server/ai-gateway/index.ts +5 -0
- package/src/server/ai-gateway/kb-context-guard.ts +46 -0
- package/src/server/ai-gateway/llm-fence.ts +82 -0
- package/src/server/ai-gateway/persistence/run-store.ts +19 -0
- package/src/server/ai-gateway/story-prompt.ts +4 -2
- package/src/server/ai-gateway/tools/definitions.ts +12 -6
- package/src/server/ai-gateway/tools/server.ts +97 -54
- package/src/server/builtin-project.ts +21 -0
- package/src/server/email/sender.ts +8 -2
- package/src/server/fetch-guard.ts +170 -0
- package/src/server/middlewares/security-headers.ts +113 -0
- package/src/server/serverFns/__tests__/billing.test.ts +263 -0
- package/src/server/serverFns/__tests__/link-preview.test.ts +239 -0
- package/src/server/serverFns/__tests__/project-kb.test.ts +3 -3
- package/src/server/serverFns/__tests__/project-team-assign.test.ts +1 -1
- package/src/server/serverFns/__tests__/story-comments.test.ts +265 -0
- package/src/server/serverFns/__tests__/story-crud-archive-detail.test.ts +266 -0
- package/src/server/serverFns/__tests__/story-crud-labels-users.test.ts +227 -0
- package/src/server/serverFns/__tests__/story-crud.test.ts +526 -0
- package/src/server/serverFns/__tests__/story-links.test.ts +262 -0
- package/src/server/serverFns/__tests__/story-list-query.test.ts +259 -0
- package/src/server/serverFns/__tests__/story-reactions.test.ts +230 -0
- package/src/server/serverFns/__tests__/story-tree.test.ts +317 -0
- package/src/server/serverFns/__tests__/story-types.test.ts +294 -0
- package/src/server/serverFns/__tests__/team-accept.test.ts +6 -6
- package/src/server/serverFns/__tests__/team-invitation.test.ts +3 -3
- package/src/server/serverFns/__tests__/team-join-request.test.ts +8 -8
- package/src/server/serverFns/__tests__/update-seats.test.ts +4 -4
- package/src/server/serverFns/__tests__/workspace-kb-idor.test.ts +160 -0
- package/src/server/serverFns/__tests__/workspace-kb-utils.test.ts +54 -0
- package/src/server/serverFns/__tests__/workspace-kb.test.ts +348 -0
- package/src/server/serverFns/__tests__/workspace.test.ts +28 -8
- package/src/server/serverFns/iteration/__tests__/iteration-crud.test.ts +3 -5
- package/src/server/serverFns/iteration/__tests__/iteration-stories.test.ts +1 -1
- package/src/server/serverFns/iteration/__tests__/iteration-transfer.test.ts +2 -2
- package/src/server/serverFns/kb-doc-search.ts +111 -0
- package/src/server/serverFns/knowledge-public/compliance/create.ts +2 -1
- package/src/server/serverFns/knowledge-public/legal/create.ts +2 -1
- package/src/server/serverFns/story/__tests__/bulk.test.ts +1 -1
- package/src/server/serverFns/story/__tests__/label-suggest-fn.test.ts +2 -6
- package/src/server/serverFns/story/__tests__/story-assoc.test.ts +2 -4
- package/src/server/serverFns/story/__tests__/story-dependencies.test.ts +2 -2
- package/src/server/serverFns/story/__tests__/story-groups.test.ts +2 -2
- package/src/server/serverFns/story/kb-general-consts.ts +39 -0
- package/src/server/serverFns/story/kb-general.ts +30 -17
- package/src/server/serverFns/story/story-assoc.ts +13 -103
- package/src/server/serverFns/story/story-crud.ts +2 -5
- package/src/server/serverFns/story/story-groups.ts +4 -13
- package/src/server/serverFns/team.ts +1 -1
- package/src/server/serverFns/workspace-kb-utils.ts +138 -0
- package/src/server/serverFns/workspace-kb.ts +403 -0
- package/src/server/serverFns/workspace.ts +4 -1
- package/src/shared/ai-cli/__tests__/adapter.test.ts +67 -0
- package/src/shared/ai-cli/__tests__/cli-detector.test.ts +99 -0
- package/src/shared/ai-cli/adapter.ts +2 -2
- package/src/shared/ai-cli/cli-detector.ts +18 -9
- package/src/start.ts +2 -1
- package/src/styles/globals.css +114 -0
- package/src/types/jest-dom-vitest.d.ts +8 -7
- package/vite.shared.ts +18 -1
- package/public/file.svg +0 -1
- package/public/globe.svg +0 -1
- package/public/window.svg +0 -1
- package/src/lib/__tests__/cli-detector.test.ts +0 -80
- /package/src/components/workspace/{workspace-activity-card.tsx → activity/workspace-activity-card.tsx} +0 -0
- /package/src/components/workspace/{workspace-contribution-card.tsx → contribution/workspace-contribution-card.tsx} +0 -0
- /package/src/components/workspace/{delete-workspace-options.tsx → dialogs/delete-workspace-options.tsx} +0 -0
- /package/src/components/workspace/{workspace-header.tsx → header/workspace-header.tsx} +0 -0
- /package/src/components/workspace/{workspace-member-item.tsx → members/workspace-member-item.tsx} +0 -0
- /package/src/components/workspace/{workspace-project-list.tsx → projects/workspace-project-list.tsx} +0 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { describe, expect, it, vi, beforeEach } from 'vitest'
|
|
2
|
+
|
|
3
|
+
const execFileSyncMock = vi.hoisted(() => vi.fn())
|
|
4
|
+
const execSyncMock = vi.hoisted(() => vi.fn())
|
|
5
|
+
|
|
6
|
+
vi.mock('child_process', async (importOriginal) => {
|
|
7
|
+
const actual = await importOriginal<typeof import('child_process')>()
|
|
8
|
+
return {
|
|
9
|
+
...actual,
|
|
10
|
+
execFileSync: execFileSyncMock,
|
|
11
|
+
execSync: execSyncMock,
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
import { detectAgents, getApiAgent } from '../cli-detector'
|
|
16
|
+
|
|
17
|
+
describe('getApiAgent', () => {
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
vi.resetModules()
|
|
20
|
+
delete process.env.OPENAI_API_KEY
|
|
21
|
+
delete process.env.AI_MODEL
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('未设置 API key 时返回 undefined', async () => {
|
|
25
|
+
const { getApiAgent: fn } = await import('../cli-detector')
|
|
26
|
+
expect(fn()).toBeUndefined()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('API key 为占位符时返回 undefined', async () => {
|
|
30
|
+
process.env.OPENAI_API_KEY = 'sk-your-api-key-here'
|
|
31
|
+
const { getApiAgent: fn } = await import('../cli-detector')
|
|
32
|
+
expect(fn()).toBeUndefined()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('API key 有效时返回 agent 信息', async () => {
|
|
36
|
+
process.env.OPENAI_API_KEY = 'sk-real-key'
|
|
37
|
+
const { getApiAgent: fn } = await import('../cli-detector')
|
|
38
|
+
const agent = fn()
|
|
39
|
+
expect(agent).toBeDefined()
|
|
40
|
+
expect(agent?.id).toBe('openai-api')
|
|
41
|
+
expect(agent?.available).toBe(true)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('使用 AI_MODEL 环境变量显示模型名', async () => {
|
|
45
|
+
process.env.OPENAI_API_KEY = 'sk-real-key'
|
|
46
|
+
process.env.AI_MODEL = 'gpt-4'
|
|
47
|
+
const { getApiAgent: fn } = await import('../cli-detector')
|
|
48
|
+
expect(fn()?.name).toContain('gpt-4')
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
describe('detectAgents 安全探测', () => {
|
|
53
|
+
beforeEach(() => {
|
|
54
|
+
execFileSyncMock.mockReset()
|
|
55
|
+
execSyncMock.mockReset()
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('用 execFileSync 数组参数探测版本(无 shell 拼接)', async () => {
|
|
59
|
+
execFileSyncMock.mockReturnValueOnce('v1.2.3')
|
|
60
|
+
const agents = await detectAgents()
|
|
61
|
+
const claude = agents.find((a) => a.id === 'claude-code')!
|
|
62
|
+
expect(claude.available).toBe(true)
|
|
63
|
+
expect(claude.version).toBe('v1.2.3')
|
|
64
|
+
expect(execFileSyncMock).toHaveBeenCalledWith(
|
|
65
|
+
expect.any(String),
|
|
66
|
+
expect.arrayContaining(['--version']),
|
|
67
|
+
expect.objectContaining({ timeout: expect.any(Number), encoding: 'utf-8' })
|
|
68
|
+
)
|
|
69
|
+
expect(execSyncMock).not.toHaveBeenCalled()
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('--version 失败时回退 version 子命令(两步探测,无 shell ||)', async () => {
|
|
73
|
+
execFileSyncMock
|
|
74
|
+
.mockImplementationOnce(() => { throw new Error('not found') })
|
|
75
|
+
.mockReturnValueOnce('2.0.0')
|
|
76
|
+
const agents = await detectAgents()
|
|
77
|
+
const first = agents[0]!
|
|
78
|
+
expect(first.available).toBe(true)
|
|
79
|
+
expect(first.version).toBe('2.0.0')
|
|
80
|
+
const calls = execFileSyncMock.mock.calls
|
|
81
|
+
expect(calls[0]?.[1]).toEqual(['--version'])
|
|
82
|
+
expect(calls[1]?.[1]).toEqual(['version'])
|
|
83
|
+
expect(calls[0]![0]).toBe(calls[1]![0])
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('两步都失败标记不可用且不抛错', async () => {
|
|
87
|
+
execFileSyncMock.mockImplementation(() => { throw new Error('not found') })
|
|
88
|
+
const agents = await detectAgents()
|
|
89
|
+
expect(agents).toHaveLength(16)
|
|
90
|
+
expect(agents.every((a) => a.available === false)).toBe(true)
|
|
91
|
+
expect(agents.every((a) => a.version === '')).toBe(true)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('版本串截断到 50 字符且去除首尾空白', async () => {
|
|
95
|
+
execFileSyncMock.mockReturnValue(` ${'x'.repeat(80)} \n`)
|
|
96
|
+
const agents = await detectAgents()
|
|
97
|
+
expect(agents[0]!.version).toBe('x'.repeat(50))
|
|
98
|
+
})
|
|
99
|
+
})
|
|
@@ -50,8 +50,8 @@ export async function executeViaAgent(
|
|
|
50
50
|
return data.choices[0]?.message?.content || ""
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
const {
|
|
54
|
-
const result =
|
|
53
|
+
const { execFileSync } = await import("child_process")
|
|
54
|
+
const result = execFileSync(agent.binary, ["-p", prompt], {
|
|
55
55
|
timeout: 60000,
|
|
56
56
|
encoding: "utf-8",
|
|
57
57
|
})
|
|
@@ -36,18 +36,27 @@ const KNOWN_AGENTS: { id: string; name: string; binary: string }[] = [
|
|
|
36
36
|
{ id: "mcp-cli", name: "MCP CLI", binary: "mcp" },
|
|
37
37
|
]
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
function probeVersion(
|
|
40
|
+
execFileSync: (file: string, args: string[], options: { timeout: number; encoding: "utf-8" }) => string,
|
|
41
|
+
binary: string,
|
|
42
|
+
): string {
|
|
43
|
+
const opts = { timeout: 3000, encoding: "utf-8" } as const
|
|
44
|
+
try {
|
|
45
|
+
return execFileSync(binary, ["--version"], opts).trim().slice(0, 50)
|
|
46
|
+
} catch {
|
|
42
47
|
try {
|
|
43
|
-
|
|
44
|
-
timeout: 3000,
|
|
45
|
-
encoding: "utf-8",
|
|
46
|
-
}).trim().slice(0, 50)
|
|
47
|
-
return { id, name, binary, version, available: true }
|
|
48
|
+
return execFileSync(binary, ["version"], opts).trim().slice(0, 50)
|
|
48
49
|
} catch {
|
|
49
|
-
return
|
|
50
|
+
return ""
|
|
50
51
|
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function detectAgents(): Promise<CLIAgentInfo[]> {
|
|
56
|
+
const { execFileSync } = await import("child_process")
|
|
57
|
+
return KNOWN_AGENTS.map(({ id, name, binary }) => {
|
|
58
|
+
const version = probeVersion(execFileSync, binary)
|
|
59
|
+
return { id, name, binary, version, available: version !== "" }
|
|
51
60
|
})
|
|
52
61
|
}
|
|
53
62
|
|
package/src/start.ts
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
18
|
import { createStart, createCsrfMiddleware, createMiddleware } from '@tanstack/react-start'
|
|
19
|
+
import { securityHeadersMiddleware } from '@/server/middlewares/security-headers'
|
|
19
20
|
import { requestContextMiddleware } from '@/server/middlewares/request'
|
|
20
21
|
import { auditRequestMiddleware } from '@/server/middlewares/audit'
|
|
21
22
|
import { neonKeepaliveMiddleware } from '@/server/keepalive/middleware'
|
|
@@ -97,6 +98,6 @@ export const startInstance = createStart(async () => {
|
|
|
97
98
|
} as any)
|
|
98
99
|
}
|
|
99
100
|
return {
|
|
100
|
-
requestMiddleware: [requestContextMiddleware, neonKeepaliveMiddleware, aiPortsMiddleware, apiCorsMiddleware, csrfMiddleware, auditRequestMiddleware],
|
|
101
|
+
requestMiddleware: [securityHeadersMiddleware, requestContextMiddleware, neonKeepaliveMiddleware, aiPortsMiddleware, apiCorsMiddleware, csrfMiddleware, auditRequestMiddleware],
|
|
101
102
|
}
|
|
102
103
|
})
|
package/src/styles/globals.css
CHANGED
|
@@ -247,6 +247,120 @@
|
|
|
247
247
|
--color-5: oklch(90.7% 0.231 133);
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
+
/* 知识库快捷文档标签莫兰迪色板(LABEL_COLORS 消费,light 原色 / dark 从种子色派生) */
|
|
251
|
+
:root {
|
|
252
|
+
--kb-row-mix: 45%;
|
|
253
|
+
--kb-seed-mrd: #9CAF97;
|
|
254
|
+
--kb-label-mrd-bg: #E8EFE5;
|
|
255
|
+
--kb-label-mrd-border: #9CAF97;
|
|
256
|
+
--kb-label-mrd-text: #5C705A;
|
|
257
|
+
--kb-seed-brd: #9FB2C4;
|
|
258
|
+
--kb-label-brd-bg: #E5EBF1;
|
|
259
|
+
--kb-label-brd-border: #9FB2C4;
|
|
260
|
+
--kb-label-brd-text: #54687A;
|
|
261
|
+
--kb-seed-feasibility: #C4B896;
|
|
262
|
+
--kb-label-feasibility-bg: #F1EDDF;
|
|
263
|
+
--kb-label-feasibility-border: #C4B896;
|
|
264
|
+
--kb-label-feasibility-text: #7A6E4C;
|
|
265
|
+
--kb-seed-competitor: #C4A48E;
|
|
266
|
+
--kb-label-competitor-bg: #F1E8E2;
|
|
267
|
+
--kb-label-competitor-border: #C4A48E;
|
|
268
|
+
--kb-label-competitor-text: #7A5F4C;
|
|
269
|
+
--kb-seed-charter: #B0A0B8;
|
|
270
|
+
--kb-label-charter-bg: #EDE7F0;
|
|
271
|
+
--kb-label-charter-border: #B0A0B8;
|
|
272
|
+
--kb-label-charter-text: #6A5A72;
|
|
273
|
+
--kb-seed-infoArchitecture: #96B8B0;
|
|
274
|
+
--kb-label-infoArchitecture-bg: #E2EFEC;
|
|
275
|
+
--kb-label-infoArchitecture-border: #96B8B0;
|
|
276
|
+
--kb-label-infoArchitecture-text: #4E6E68;
|
|
277
|
+
--kb-seed-techArchitecture: #8FA3AD;
|
|
278
|
+
--kb-label-techArchitecture-bg: #E1E8EB;
|
|
279
|
+
--kb-label-techArchitecture-border: #8FA3AD;
|
|
280
|
+
--kb-label-techArchitecture-text: #4E626C;
|
|
281
|
+
--kb-seed-dataStructure: #C09898;
|
|
282
|
+
--kb-label-dataStructure-bg: #F0E4E4;
|
|
283
|
+
--kb-label-dataStructure-border: #C09898;
|
|
284
|
+
--kb-label-dataStructure-text: #755050;
|
|
285
|
+
--kb-seed-integration: #B3A48E;
|
|
286
|
+
--kb-label-integration-bg: #EEE9E0;
|
|
287
|
+
--kb-label-integration-border: #B3A48E;
|
|
288
|
+
--kb-label-integration-text: #6E6046;
|
|
289
|
+
--kb-seed-copywriting: #C4A0A8;
|
|
290
|
+
--kb-label-copywriting-bg: #F2E7E9;
|
|
291
|
+
--kb-label-copywriting-border: #C4A0A8;
|
|
292
|
+
--kb-label-copywriting-text: #77545C;
|
|
293
|
+
--kb-seed-dictionary: #A8A88E;
|
|
294
|
+
--kb-label-dictionary-bg: #EBEBE0;
|
|
295
|
+
--kb-label-dictionary-border: #A8A88E;
|
|
296
|
+
--kb-label-dictionary-text: #68684C;
|
|
297
|
+
--kb-seed-legal: #8FA8B3;
|
|
298
|
+
--kb-label-legal-bg: #DCE8EC;
|
|
299
|
+
--kb-label-legal-border: #8FA8B3;
|
|
300
|
+
--kb-label-legal-text: #4C626C;
|
|
301
|
+
--kb-seed-compliance: #9FA88C;
|
|
302
|
+
--kb-label-compliance-bg: #E4E8DC;
|
|
303
|
+
--kb-label-compliance-border: #9FA88C;
|
|
304
|
+
--kb-label-compliance-text: #5E664E;
|
|
305
|
+
--kb-seed-kbDictionary: #B09A8C;
|
|
306
|
+
--kb-label-kbDictionary-bg: #EAE4DC;
|
|
307
|
+
--kb-label-kbDictionary-border: #B09A8C;
|
|
308
|
+
--kb-label-kbDictionary-text: #6E5A4C;
|
|
309
|
+
--kb-seed-website: #98A4B8;
|
|
310
|
+
--kb-label-website-bg: #E0E4EA;
|
|
311
|
+
--kb-label-website-border: #98A4B8;
|
|
312
|
+
--kb-label-website-text: #545E70;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.dark {
|
|
316
|
+
--kb-row-mix: 65%;
|
|
317
|
+
--kb-label-mrd-bg: color-mix(in srgb, var(--kb-seed-mrd) 20%, #1d1f1c);
|
|
318
|
+
--kb-label-mrd-border: color-mix(in srgb, var(--kb-seed-mrd) 45%, #1d1f1c);
|
|
319
|
+
--kb-label-mrd-text: color-mix(in srgb, var(--kb-seed-mrd) 60%, #dfe3dc);
|
|
320
|
+
--kb-label-brd-bg: color-mix(in srgb, var(--kb-seed-brd) 20%, #1c1e21);
|
|
321
|
+
--kb-label-brd-border: color-mix(in srgb, var(--kb-seed-brd) 45%, #1c1e21);
|
|
322
|
+
--kb-label-brd-text: color-mix(in srgb, var(--kb-seed-brd) 60%, #dce1e7);
|
|
323
|
+
--kb-label-feasibility-bg: color-mix(in srgb, var(--kb-seed-feasibility) 20%, #1f1d16);
|
|
324
|
+
--kb-label-feasibility-border: color-mix(in srgb, var(--kb-seed-feasibility) 45%, #1f1d16);
|
|
325
|
+
--kb-label-feasibility-text: color-mix(in srgb, var(--kb-seed-feasibility) 60%, #e6e2d5);
|
|
326
|
+
--kb-label-competitor-bg: color-mix(in srgb, var(--kb-seed-competitor) 20%, #1f1b18);
|
|
327
|
+
--kb-label-competitor-border: color-mix(in srgb, var(--kb-seed-competitor) 45%, #1f1b18);
|
|
328
|
+
--kb-label-competitor-text: color-mix(in srgb, var(--kb-seed-competitor) 60%, #e6ddd6);
|
|
329
|
+
--kb-label-charter-bg: color-mix(in srgb, var(--kb-seed-charter) 20%, #1d1b1f);
|
|
330
|
+
--kb-label-charter-border: color-mix(in srgb, var(--kb-seed-charter) 45%, #1d1b1f);
|
|
331
|
+
--kb-label-charter-text: color-mix(in srgb, var(--kb-seed-charter) 60%, #e2dde6);
|
|
332
|
+
--kb-label-infoArchitecture-bg: color-mix(in srgb, var(--kb-seed-infoArchitecture) 20%, #1b1e1d);
|
|
333
|
+
--kb-label-infoArchitecture-border: color-mix(in srgb, var(--kb-seed-infoArchitecture) 45%, #1b1e1d);
|
|
334
|
+
--kb-label-infoArchitecture-text: color-mix(in srgb, var(--kb-seed-infoArchitecture) 60%, #dbe4e1);
|
|
335
|
+
--kb-label-techArchitecture-bg: color-mix(in srgb, var(--kb-seed-techArchitecture) 20%, #1b1d1f);
|
|
336
|
+
--kb-label-techArchitecture-border: color-mix(in srgb, var(--kb-seed-techArchitecture) 45%, #1b1d1f);
|
|
337
|
+
--kb-label-techArchitecture-text: color-mix(in srgb, var(--kb-seed-techArchitecture) 60%, #dbe1e5);
|
|
338
|
+
--kb-label-dataStructure-bg: color-mix(in srgb, var(--kb-seed-dataStructure) 20%, #1f1b1b);
|
|
339
|
+
--kb-label-dataStructure-border: color-mix(in srgb, var(--kb-seed-dataStructure) 45%, #1f1b1b);
|
|
340
|
+
--kb-label-dataStructure-text: color-mix(in srgb, var(--kb-seed-dataStructure) 60%, #e6dcdc);
|
|
341
|
+
--kb-label-integration-bg: color-mix(in srgb, var(--kb-seed-integration) 20%, #1e1c18);
|
|
342
|
+
--kb-label-integration-border: color-mix(in srgb, var(--kb-seed-integration) 45%, #1e1c18);
|
|
343
|
+
--kb-label-integration-text: color-mix(in srgb, var(--kb-seed-integration) 60%, #e3ded4);
|
|
344
|
+
--kb-label-copywriting-bg: color-mix(in srgb, var(--kb-seed-copywriting) 20%, #1f1b1c);
|
|
345
|
+
--kb-label-copywriting-border: color-mix(in srgb, var(--kb-seed-copywriting) 45%, #1f1b1c);
|
|
346
|
+
--kb-label-copywriting-text: color-mix(in srgb, var(--kb-seed-copywriting) 60%, #e6dce0);
|
|
347
|
+
--kb-label-dictionary-bg: color-mix(in srgb, var(--kb-seed-dictionary) 20%, #1d1d19);
|
|
348
|
+
--kb-label-dictionary-border: color-mix(in srgb, var(--kb-seed-dictionary) 45%, #1d1d19);
|
|
349
|
+
--kb-label-dictionary-text: color-mix(in srgb, var(--kb-seed-dictionary) 60%, #e2e2da);
|
|
350
|
+
--kb-label-legal-bg: color-mix(in srgb, var(--kb-seed-legal) 20%, #1a1e20);
|
|
351
|
+
--kb-label-legal-border: color-mix(in srgb, var(--kb-seed-legal) 45%, #1a1e20);
|
|
352
|
+
--kb-label-legal-text: color-mix(in srgb, var(--kb-seed-legal) 60%, #d9e2e7);
|
|
353
|
+
--kb-label-compliance-bg: color-mix(in srgb, var(--kb-seed-compliance) 20%, #1c1e1a);
|
|
354
|
+
--kb-label-compliance-border: color-mix(in srgb, var(--kb-seed-compliance) 45%, #1c1e1a);
|
|
355
|
+
--kb-label-compliance-text: color-mix(in srgb, var(--kb-seed-compliance) 60%, #dee2d6);
|
|
356
|
+
--kb-label-kbDictionary-bg: color-mix(in srgb, var(--kb-seed-kbDictionary) 20%, #1e1c1a);
|
|
357
|
+
--kb-label-kbDictionary-border: color-mix(in srgb, var(--kb-seed-kbDictionary) 45%, #1e1c1a);
|
|
358
|
+
--kb-label-kbDictionary-text: color-mix(in srgb, var(--kb-seed-kbDictionary) 60%, #e3ddd6);
|
|
359
|
+
--kb-label-website-bg: color-mix(in srgb, var(--kb-seed-website) 20%, #1b1d21);
|
|
360
|
+
--kb-label-website-border: color-mix(in srgb, var(--kb-seed-website) 45%, #1b1d21);
|
|
361
|
+
--kb-label-website-text: color-mix(in srgb, var(--kb-seed-website) 60%, #dbe0e8);
|
|
362
|
+
}
|
|
363
|
+
|
|
250
364
|
@layer base {
|
|
251
365
|
* {
|
|
252
366
|
border-color: var(--color-border);
|
|
@@ -15,17 +15,18 @@
|
|
|
15
15
|
// You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
|
-
// Vitest
|
|
19
|
-
//
|
|
20
|
-
// `Assertion
|
|
21
|
-
//
|
|
18
|
+
// Vitest 5 类型兼容层:Vitest 5 将 expect 断言内联进主包(不再依赖 @vitest/expect),
|
|
19
|
+
// `declare module 'vitest'` 的接口增强直接生效。但 Vitest 5 的 `Assertion` 接口
|
|
20
|
+
// 使用双类型参数 `Assertion<R, T>`(R = matcher 返回类型,T = received 值类型),
|
|
21
|
+
// 而 @testing-library/jest-dom 官方 types/vitest.d.ts 仍声明单参数 `Assertion<T = any>`,
|
|
22
|
+
// 泛型参数个数不一致导致声明合并报 TS2428。这里以正确的双参数签名重写增强,
|
|
22
23
|
// 使 `toBeInTheDocument` / `toBeDisabled` 等 jest-dom matcher 类型可用。
|
|
23
24
|
// 运行时 matcher 注册在 src/__tests__/setup.ts 的 `import '@testing-library/jest-dom/vitest'`。
|
|
24
25
|
import type { TestingLibraryMatchers } from '@testing-library/jest-dom/matchers'
|
|
25
26
|
|
|
26
|
-
declare module '
|
|
27
|
-
interface Assertion<T =
|
|
28
|
-
extends TestingLibraryMatchers<
|
|
27
|
+
declare module 'vitest' {
|
|
28
|
+
interface Assertion<R extends void | Promise<void> = void, T = unknown>
|
|
29
|
+
extends TestingLibraryMatchers<R, T> {}
|
|
29
30
|
interface AsymmetricMatchersContaining
|
|
30
31
|
extends TestingLibraryMatchers<any, any> {}
|
|
31
32
|
}
|
package/vite.shared.ts
CHANGED
|
@@ -139,8 +139,25 @@ export function createWebViteConfig(opts: WebViteConfigOptions) {
|
|
|
139
139
|
},
|
|
140
140
|
server: {
|
|
141
141
|
port: 3000,
|
|
142
|
-
|
|
142
|
+
host: true,
|
|
143
|
+
// host.docker.internal:容器内安全扫描(ZAP/Nuclei,见
|
|
144
|
+
// scripts/security/)经该地址访问宿主 dev server;Vite 默认拒绝
|
|
145
|
+
// 非白名单 Host 头(返回 403 "Blocked request")。仅 dev server 生效。
|
|
146
|
+
allowedHosts: ['localhost', 'host.docker.internal'],
|
|
143
147
|
cors: { origin: false },
|
|
148
|
+
// Vite 内部中间件(/src/*、/@fs/*、.map)的响应绕过 TanStack Start
|
|
149
|
+
// requestMiddleware,故在此层补基础安全头;CSP/HSTS 仍由 middleware
|
|
150
|
+
// 统一注入(apps/web/src/server/middlewares/security-headers.ts),
|
|
151
|
+
// 生产构建无 Vite dev server,middleware 即唯一来源。
|
|
152
|
+
headers: {
|
|
153
|
+
'X-Content-Type-Options': 'nosniff',
|
|
154
|
+
'X-Frame-Options': 'DENY',
|
|
155
|
+
'Cross-Origin-Resource-Policy': 'same-origin',
|
|
156
|
+
'Cross-Origin-Opener-Policy': 'same-origin',
|
|
157
|
+
'Permissions-Policy':
|
|
158
|
+
'camera=(), microphone=(), geolocation=(), payment=(), usb=(), fullscreen=(self)',
|
|
159
|
+
'Referrer-Policy': 'strict-origin-when-cross-origin',
|
|
160
|
+
},
|
|
144
161
|
},
|
|
145
162
|
plugins,
|
|
146
163
|
resolve: {
|
package/public/file.svg
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
package/public/globe.svg
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
package/public/window.svg
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
-
// AllBlue - 理想之海
|
|
3
|
-
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
-
//
|
|
5
|
-
// This program is free software: you can redistribute it and/or modify
|
|
6
|
-
// it under the terms of the GNU Affero General Public License as published by
|
|
7
|
-
// the Free Software Foundation, either version 3 of the License, or
|
|
8
|
-
// (at your option) any later version.
|
|
9
|
-
//
|
|
10
|
-
// This program is distributed in the hope that it will be useful,
|
|
11
|
-
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
-
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
-
// GNU Affero General Public License for more details.
|
|
14
|
-
//
|
|
15
|
-
// You should have received a copy of the GNU Affero General Public License
|
|
16
|
-
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
-
|
|
18
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
19
|
-
|
|
20
|
-
describe('getApiAgent', () => {
|
|
21
|
-
beforeEach(() => {
|
|
22
|
-
vi.resetModules()
|
|
23
|
-
delete process.env.OPENAI_API_KEY
|
|
24
|
-
delete process.env.AI_MODEL
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
it('returns undefined when no API key set', async () => {
|
|
28
|
-
const { getApiAgent } = await import('@/shared/ai-cli/cli-detector')
|
|
29
|
-
expect(getApiAgent()).toBeUndefined()
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
it('returns undefined when API key is placeholder', async () => {
|
|
33
|
-
process.env.OPENAI_API_KEY = 'sk-your-api-key-here'
|
|
34
|
-
const { getApiAgent } = await import('@/shared/ai-cli/cli-detector')
|
|
35
|
-
expect(getApiAgent()).toBeUndefined()
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
it('returns agent info when API key is valid', async () => {
|
|
39
|
-
process.env.OPENAI_API_KEY = 'sk-real-key'
|
|
40
|
-
const { getApiAgent } = await import('@/shared/ai-cli/cli-detector')
|
|
41
|
-
const agent = getApiAgent()
|
|
42
|
-
expect(agent).toBeDefined()
|
|
43
|
-
expect(agent?.id).toBe('openai-api')
|
|
44
|
-
expect(agent?.available).toBe(true)
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
it('uses AI_MODEL env in display name', async () => {
|
|
48
|
-
process.env.OPENAI_API_KEY = 'sk-real-key'
|
|
49
|
-
process.env.AI_MODEL = 'gpt-4'
|
|
50
|
-
const { getApiAgent } = await import('@/shared/ai-cli/cli-detector')
|
|
51
|
-
expect(getApiAgent()?.name).toContain('gpt-4')
|
|
52
|
-
})
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
describe('detectAgents', () => {
|
|
56
|
-
beforeEach(() => {
|
|
57
|
-
vi.resetModules()
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('returns 16 known agents with executable check', async () => {
|
|
61
|
-
const { detectAgents } = await import('@/shared/ai-cli/cli-detector')
|
|
62
|
-
const agents = await detectAgents()
|
|
63
|
-
expect(agents).toHaveLength(16)
|
|
64
|
-
agents.forEach(a => {
|
|
65
|
-
expect(a.id).toBeDefined()
|
|
66
|
-
expect(a.binary).toBeDefined()
|
|
67
|
-
})
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it('handles all agents failing gracefully', async () => {
|
|
71
|
-
vi.doMock('child_process', () => ({
|
|
72
|
-
execSync: vi.fn(() => { throw new Error('not found') }),
|
|
73
|
-
}))
|
|
74
|
-
|
|
75
|
-
const { detectAgents } = await import('@/shared/ai-cli/cli-detector')
|
|
76
|
-
const agents = await detectAgents()
|
|
77
|
-
expect(agents).toHaveLength(16)
|
|
78
|
-
agents.forEach(a => expect(a.available).toBe(false))
|
|
79
|
-
})
|
|
80
|
-
})
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/src/components/workspace/{workspace-member-item.tsx → members/workspace-member-item.tsx}
RENAMED
|
File without changes
|
/package/src/components/workspace/{workspace-project-list.tsx → projects/workspace-project-list.tsx}
RENAMED
|
File without changes
|