@csntgao/uni-base 0.1.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.
Files changed (90) hide show
  1. package/.prettierignore +4 -0
  2. package/.prettierrc.json +6 -0
  3. package/AGENTS.md +519 -0
  4. package/CLAUDE.md +512 -0
  5. package/README.md +734 -0
  6. package/docs//346/225/260/345/255/227/345/221/230/345/267/245/345/256/214/346/225/264/346/212/275/345/261/211/346/216/245/345/205/245/350/257/264/346/230/216.md +110 -0
  7. package/docs//347/273/204/347/273/207/345/221/230/345/267/245/345/215/225/351/200/211/347/273/204/344/273/266/346/216/245/345/205/245/350/257/264/346/230/216.md +79 -0
  8. package/docs//347/273/204/347/273/207/345/221/230/345/267/245/345/244/232/351/200/211/347/273/204/344/273/266/346/216/245/345/205/245/350/257/264/346/230/216.md +93 -0
  9. package/docs//350/264/246/346/210/267/344/270/216/347/231/273/345/275/225/347/273/204/344/273/266/345/212/250/346/200/201/346/216/245/345/205/245/350/257/264/346/230/216.md +201 -0
  10. package/eslint.config.js +30 -0
  11. package/examples/vanilla/index.html +33 -0
  12. package/examples/vanilla/main.js +247 -0
  13. package/examples/vanilla/package.json +15 -0
  14. package/examples/vanilla/public/runtime-config.js +10 -0
  15. package/examples/vanilla/style.css +99 -0
  16. package/package.json +31 -0
  17. package/packages/core/package.json +30 -0
  18. package/packages/core/src/client.ts +1132 -0
  19. package/packages/core/src/customer-support-client.ts +825 -0
  20. package/packages/core/src/customer-support-types.ts +123 -0
  21. package/packages/core/src/index.ts +79 -0
  22. package/packages/core/src/notification-center-client.ts +462 -0
  23. package/packages/core/src/notification-center-types.ts +154 -0
  24. package/packages/core/src/protocol.ts +237 -0
  25. package/packages/core/src/types.ts +170 -0
  26. package/packages/core/tests/client.test.ts +760 -0
  27. package/packages/core/tests/customer-support-client.test.ts +576 -0
  28. package/packages/core/tests/fake-websocket.ts +55 -0
  29. package/packages/core/tests/notification-center-client.test.ts +467 -0
  30. package/packages/core/tests/protocol.test.ts +124 -0
  31. package/packages/core/tsconfig.json +7 -0
  32. package/packages/core/tsup.config.ts +11 -0
  33. package/packages/web-components/package.json +35 -0
  34. package/packages/web-components/scripts/verify-runtime-build.mjs +111 -0
  35. package/packages/web-components/src/account-context.ts +1540 -0
  36. package/packages/web-components/src/application-switcher-transport.ts +243 -0
  37. package/packages/web-components/src/application-switcher.ts +1103 -0
  38. package/packages/web-components/src/chat.ts +1754 -0
  39. package/packages/web-components/src/customer-support-chat.ts +862 -0
  40. package/packages/web-components/src/customer-support-transport.ts +269 -0
  41. package/packages/web-components/src/index.ts +256 -0
  42. package/packages/web-components/src/notification-center.ts +1066 -0
  43. package/packages/web-components/src/notification-popover.ts +615 -0
  44. package/packages/web-components/src/organization-multi-employee-selector.ts +1564 -0
  45. package/packages/web-components/src/organization-onboarding-transport.ts +54 -0
  46. package/packages/web-components/src/organization-onboarding.ts +1216 -0
  47. package/packages/web-components/src/organization-selector.ts +534 -0
  48. package/packages/web-components/src/organization-single-employee-selector.ts +114 -0
  49. package/packages/web-components/src/prototype-icons.ts +242 -0
  50. package/packages/web-components/src/safe-image-url.ts +29 -0
  51. package/packages/web-components/src/safe-markdown.ts +192 -0
  52. package/packages/web-components/src/uniplat-base-account-context-transport.ts +625 -0
  53. package/packages/web-components/src/uniplat-base-application-switcher-transport.ts +418 -0
  54. package/packages/web-components/src/uniplat-base-application-ticket.ts +254 -0
  55. package/packages/web-components/src/uniplat-base-identity-ticket.ts +202 -0
  56. package/packages/web-components/src/uniplat-base-notification-center-transport.ts +378 -0
  57. package/packages/web-components/src/uniplat-base-organization-multi-employee-selector-transport.ts +394 -0
  58. package/packages/web-components/src/uniplat-base-organization-onboarding-transport.ts +183 -0
  59. package/packages/web-components/src/uniplat-base-organization-single-employee-selector-transport.ts +14 -0
  60. package/packages/web-components/src/user-login-transport.ts +563 -0
  61. package/packages/web-components/src/user-login.ts +2027 -0
  62. package/packages/web-components/src/user-menu.ts +880 -0
  63. package/packages/web-components/tests/account-context.test.ts +798 -0
  64. package/packages/web-components/tests/application-switcher.test.ts +562 -0
  65. package/packages/web-components/tests/chat.test.ts +1130 -0
  66. package/packages/web-components/tests/customer-support-chat.test.ts +428 -0
  67. package/packages/web-components/tests/customer-support-transport.test.ts +79 -0
  68. package/packages/web-components/tests/notification-center-transport.test.ts +271 -0
  69. package/packages/web-components/tests/notification-center.test.ts +429 -0
  70. package/packages/web-components/tests/notification-popover.test.ts +283 -0
  71. package/packages/web-components/tests/organization-multi-employee-selector-transport.test.ts +332 -0
  72. package/packages/web-components/tests/organization-multi-employee-selector.test.ts +225 -0
  73. package/packages/web-components/tests/organization-onboarding.test.ts +260 -0
  74. package/packages/web-components/tests/organization-selector.test.ts +127 -0
  75. package/packages/web-components/tests/organization-single-employee-selector.test.ts +145 -0
  76. package/packages/web-components/tests/uniplat-base-account-context-transport.test.ts +496 -0
  77. package/packages/web-components/tests/uniplat-base-application-switcher-transport.test.ts +598 -0
  78. package/packages/web-components/tests/uniplat-base-application-ticket.test.ts +233 -0
  79. package/packages/web-components/tests/uniplat-base-identity-ticket.test.ts +226 -0
  80. package/packages/web-components/tests/uniplat-base-organization-onboarding-transport.test.ts +227 -0
  81. package/packages/web-components/tests/user-login-transport.test.ts +443 -0
  82. package/packages/web-components/tests/user-login.test.ts +626 -0
  83. package/packages/web-components/tests/user-menu.test.ts +271 -0
  84. package/packages/web-components/tsconfig.json +8 -0
  85. package/packages/web-components/tsup.config.ts +12 -0
  86. package/packages/web-components/tsup.runtime.config.ts +37 -0
  87. package/pnpm-workspace.yaml +6 -0
  88. package/tsconfig.base.json +21 -0
  89. package/vitest.config.ts +20 -0
  90. package//344/272/244/346/216/245/346/226/207/346/241/243.md +254 -0
@@ -0,0 +1,247 @@
1
+ import {
2
+ createMockApplicationSwitcherTransport,
3
+ createMockOrganizationOnboardingTransport,
4
+ createUniplatBaseOrganizationOnboardingTransport,
5
+ } from '@uniplat/ai-employee-web-components'
6
+
7
+ const config = window.AI_EMPLOYEE_EXAMPLE_CONFIG
8
+ const chat = document.querySelector('#employee-chat')
9
+ const openChatButton = document.querySelector('#open-employee-chat')
10
+ const userLogin = document.querySelector('#user-login')
11
+ const organizationSelector = document.querySelector('#organization-selector')
12
+ const accountContext = document.querySelector('#account-context')
13
+
14
+ function apiUrl(path) {
15
+ return `${config.hostApiBaseUrl.replace(/\/$/, '')}${path}`
16
+ }
17
+
18
+ async function request(url, options) {
19
+ const response = await fetch(url, {
20
+ credentials: 'include',
21
+ headers: { 'content-type': 'application/json' },
22
+ ...options,
23
+ })
24
+ if (!response.ok) throw new Error('Host transport request failed')
25
+ return response.json()
26
+ }
27
+
28
+ async function getAccessToken() {
29
+ const result = await request(config.accessTokenUrl)
30
+ return result.accessToken
31
+ }
32
+
33
+ const transport = {
34
+ async createSessionWithFirstMessage(input) {
35
+ const result = await request(apiUrl('/sessions'), {
36
+ method: 'POST',
37
+ body: JSON.stringify({
38
+ request_id: input.requestId,
39
+ ae_code: input.aeCode,
40
+ agent_code: input.agentCode,
41
+ content: input.content,
42
+ trusted_context: input.trustedContext,
43
+ attachments: input.attachments.map((attachment) => ({
44
+ transaction_id: attachment.transactionId,
45
+ fspath: attachment.fspath,
46
+ })),
47
+ }),
48
+ })
49
+ return {
50
+ sessionId: result.session_id,
51
+ messageId: result.message_id,
52
+ executionId: result.execution_id,
53
+ status: result.status,
54
+ startedAt: result.started_at,
55
+ aeCode: result.ae_code,
56
+ agentCode: result.agent_code,
57
+ conversationType: result.conversation_type,
58
+ title: result.title,
59
+ createdAt: result.created_at,
60
+ }
61
+ },
62
+
63
+ async listMessages(sessionId) {
64
+ const result = await request(apiUrl(`/sessions/${encodeURIComponent(sessionId)}/messages`))
65
+ return result.messages
66
+ },
67
+
68
+ async sendMessage(sessionId, content) {
69
+ await request(apiUrl(`/sessions/${encodeURIComponent(sessionId)}/messages`), {
70
+ method: 'POST',
71
+ body: JSON.stringify({ content }),
72
+ })
73
+ },
74
+
75
+ getAccessToken,
76
+ }
77
+
78
+ const demoLoginIcon = `data:image/svg+xml,${encodeURIComponent(`
79
+ <svg xmlns="http://www.w3.org/2000/svg" width="42" height="42" viewBox="0 0 42 42">
80
+ <rect width="42" height="42" rx="12" fill="#4a74ea"/>
81
+ <path d="M21 10l9 4v7c0 6-3.8 10.2-9 12-5.2-1.8-9-6-9-12v-7l9-4z" fill="none" stroke="#fff" stroke-width="2.2"/>
82
+ <path d="M17.5 21l2.3 2.4 4.8-5" fill="none" stroke="#fff" stroke-width="2.2" stroke-linecap="round"/>
83
+ </svg>
84
+ `)}`
85
+ const demoQrCode = `data:image/svg+xml,${encodeURIComponent(`
86
+ <svg xmlns="http://www.w3.org/2000/svg" width="150" height="150" viewBox="0 0 150 150">
87
+ <rect width="150" height="150" fill="#fff"/>
88
+ <path d="M10 10h42v42H10zm88 0h42v42H98zM10 98h42v42H10zm58-30h18v18H68zm30 0h12v30H98zm-30 30h30v12H68zm42 12h30v30h-30z" fill="#233043"/>
89
+ <path d="M20 20h22v22H20zm88 0h22v22h-22zM20 108h22v22H20z" fill="#fff"/>
90
+ </svg>
91
+ `)}`
92
+
93
+ // 登录示例使用无凭据的本地演示 transport。生产环境请改用
94
+ // createUniplatBaseUserLoginTransport({ apiBaseUrl, setAccessToken })。
95
+ userLogin.configure({
96
+ applicationName: 'HrSaas',
97
+ headerTitle: '登录企业工作台',
98
+ headerSubtitle: '手机号验证后进入企业服务平台',
99
+ headerIconUrl: demoLoginIcon,
100
+ securityHint: '登录凭证仅用于企业工作台认证,请勿向他人透露验证码',
101
+ transport: {
102
+ async sendSmsCode() {},
103
+ async login() {
104
+ return { applicationName: 'HrSaas', isNewUser: true }
105
+ },
106
+ async loginWithPassword() {
107
+ return { applicationName: 'HrSaas' }
108
+ },
109
+ async createQrChallenge() {
110
+ return { challengeId: crypto.randomUUID(), imageUrl: demoQrCode }
111
+ },
112
+ async pollQrChallenge() {
113
+ return { status: 'pending' }
114
+ },
115
+ },
116
+ })
117
+
118
+ const demoOrganizations = [
119
+ {
120
+ id: 'company-beijing',
121
+ name: '北京星河科技有限公司',
122
+ type: 'company',
123
+ avatarText: '星',
124
+ employeeDisplayName: '企业员工张伟',
125
+ employeeAvatarText: '张',
126
+ role: '超管',
127
+ memberCount: 128,
128
+ },
129
+ {
130
+ id: 'company-shanghai',
131
+ name: '星河(上海)信息服务分公司',
132
+ type: 'company',
133
+ avatarText: '蓝',
134
+ employeeDisplayName: '上海分公司张伟',
135
+ employeeAvatarText: '张',
136
+ role: '管理员',
137
+ memberCount: 46,
138
+ },
139
+ {
140
+ id: 'team-growth',
141
+ name: '星河增长项目组',
142
+ type: 'team',
143
+ avatarText: '增',
144
+ employeeDisplayName: '增长组成员张伟',
145
+ employeeAvatarText: '张',
146
+ role: '团队负责人',
147
+ memberCount: 9,
148
+ },
149
+ {
150
+ id: 'team-campus',
151
+ name: '2026 校招筹备团队',
152
+ type: 'team',
153
+ avatarText: '校',
154
+ employeeDisplayName: '校招组成员张伟',
155
+ employeeAvatarText: '张',
156
+ role: '成员',
157
+ memberCount: 5,
158
+ },
159
+ ]
160
+
161
+ const organizationOnboardingTransport = config.uniplatBaseServiceBaseUrl
162
+ ? createUniplatBaseOrganizationOnboardingTransport({
163
+ serviceBaseUrl: config.uniplatBaseServiceBaseUrl,
164
+ getAccessToken,
165
+ })
166
+ : createMockOrganizationOnboardingTransport()
167
+
168
+ organizationSelector.configure({
169
+ selectedOrganizationId: 'company-beijing',
170
+ organizations: demoOrganizations.map((organization) => ({
171
+ ...organization,
172
+ type: organization.type === 'company' ? 'enterprise' : 'team',
173
+ verificationStatus: organization.type === 'company' ? 'verified' : undefined,
174
+ })),
175
+ })
176
+
177
+ let demoCurrentOrganizationId = 'company-beijing'
178
+
179
+ function demoAccountProjection() {
180
+ const organizations = demoOrganizations.map((organization) => ({
181
+ ...organization,
182
+ type: organization.type === 'company' ? 'enterprise' : 'team',
183
+ verificationStatus: organization.type === 'company' ? 'verified' : undefined,
184
+ }))
185
+ return {
186
+ applicationName: 'HrSaas',
187
+ user: { displayName: '张伟', maskedMobile: '138****6688', avatarText: '张' },
188
+ currentOrganizationId: demoCurrentOrganizationId,
189
+ currentOrganization: organizations.find(({ id }) => id === demoCurrentOrganizationId) ?? null,
190
+ organizations,
191
+ }
192
+ }
193
+
194
+ const demoAccountTransport = {
195
+ async load() {
196
+ return demoAccountProjection()
197
+ },
198
+ async switchOrganization(_applicationName, organizationId) {
199
+ demoCurrentOrganizationId = organizationId
200
+ return demoAccountProjection()
201
+ },
202
+ }
203
+
204
+ accountContext.configure({
205
+ applicationName: 'HrSaas',
206
+ transport: demoAccountTransport,
207
+ workbench: {
208
+ getAccessToken,
209
+ transport: createMockApplicationSwitcherTransport(),
210
+ },
211
+ organizationOnboarding: { transport: organizationOnboardingTransport },
212
+ })
213
+
214
+ userLogin.addEventListener('onboarding-requested', () => {
215
+ accountContext.startOrganizationOnboarding()
216
+ })
217
+
218
+ accountContext.addEventListener('join-organization-requested', () => {
219
+ console.info('[organization-onboarding] Host should open its invitation flow')
220
+ })
221
+
222
+ organizationSelector.addEventListener('organization-enter-requested', (event) => {
223
+ void accountContext.switchOrganization(event.detail.organizationId)
224
+ })
225
+
226
+ accountContext.addEventListener('organization-selected', (event) => {
227
+ // Host 决定是否响应切换请求;组件负责调用已配置 transport 并自行重绘。
228
+ void accountContext.switchOrganization(event.detail.organizationId)
229
+ })
230
+
231
+ accountContext.addEventListener('logout-requested', () => {
232
+ console.info('[account-context] Host should clear its login state')
233
+ })
234
+
235
+ chat.configure({
236
+ realtimeUrl: config.realtimeUrl,
237
+ aeCode: config.aeCode,
238
+ agentCode: config.agentCode,
239
+ transport,
240
+ })
241
+
242
+ openChatButton.addEventListener('click', () => chat.open())
243
+
244
+ chat.addEventListener('error', (event) => {
245
+ // Only the SDK's sanitized code is recorded. Do not log transport responses or tokens.
246
+ console.warn('[ai-employee-chat]', event.detail.code)
247
+ })
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@uniplat/ai-employee-vanilla-example",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "@uniplat/ai-employee-client": "workspace:*",
13
+ "@uniplat/ai-employee-web-components": "workspace:*"
14
+ }
15
+ }
@@ -0,0 +1,10 @@
1
+ window.AI_EMPLOYEE_EXAMPLE_CONFIG = {
2
+ // 生产环境必须使用不带 query/hash 的 wss: 地址。
3
+ realtimeUrl: 'wss://kanban.example.com/api/user-realtime/ws',
4
+ hostApiBaseUrl: 'https://hr-saas.example.com/api/embed/ai-employee',
5
+ accessTokenUrl: 'https://hr-saas.example.com/api/auth/access-token',
6
+ // 填写 Gateway service 根地址后,Organization Onboarding 使用真实 UniplatBase 接口。
7
+ uniplatBaseServiceBaseUrl: '',
8
+ aeCode: 'recruit',
9
+ agentCode: 'default',
10
+ }
@@ -0,0 +1,99 @@
1
+ :root {
2
+ color: #172033;
3
+ background: #edf2f7;
4
+ font-family:
5
+ Inter,
6
+ ui-sans-serif,
7
+ system-ui,
8
+ -apple-system,
9
+ BlinkMacSystemFont,
10
+ 'Segoe UI',
11
+ sans-serif;
12
+ font-synthesis: none;
13
+ }
14
+
15
+ body {
16
+ min-width: 320px;
17
+ min-height: 100vh;
18
+ margin: 0;
19
+ }
20
+
21
+ .page {
22
+ display: grid;
23
+ grid-template-columns: minmax(0, 500px) minmax(320px, 400px);
24
+ gap: clamp(32px, 7vw, 100px);
25
+ align-items: center;
26
+ justify-content: center;
27
+ min-height: 100vh;
28
+ padding: 32px;
29
+ }
30
+
31
+ .introduction h1 {
32
+ max-width: 10ch;
33
+ margin: 10px 0 18px;
34
+ font-size: clamp(40px, 7vw, 72px);
35
+ line-height: 0.98;
36
+ letter-spacing: -0.055em;
37
+ }
38
+
39
+ .introduction p {
40
+ max-width: 55ch;
41
+ color: #526071;
42
+ font-size: 16px;
43
+ line-height: 1.75;
44
+ }
45
+
46
+ .introduction .eyebrow {
47
+ color: #2563eb;
48
+ font-size: 12px;
49
+ font-weight: 700;
50
+ letter-spacing: 0.12em;
51
+ text-transform: uppercase;
52
+ }
53
+
54
+ .component-showcase {
55
+ display: grid;
56
+ gap: 32px;
57
+ justify-items: center;
58
+ width: min(100%, 460px);
59
+ }
60
+
61
+ code {
62
+ padding: 2px 5px;
63
+ border-radius: 5px;
64
+ background: #dce5ef;
65
+ color: #344054;
66
+ }
67
+
68
+ #open-employee-chat {
69
+ padding: 12px 18px;
70
+ border: 0;
71
+ border-radius: 10px;
72
+ background: #2e6bf5;
73
+ color: white;
74
+ cursor: pointer;
75
+ font: inherit;
76
+ font-weight: 700;
77
+ }
78
+
79
+ uniplat-user-login {
80
+ width: 100%;
81
+ }
82
+
83
+ uniplat-organization-selector {
84
+ width: 100%;
85
+ }
86
+
87
+ uniplat-account-context {
88
+ max-width: 100%;
89
+ }
90
+
91
+ @media (max-width: 860px) {
92
+ .page {
93
+ grid-template-columns: minmax(0, 520px);
94
+ }
95
+
96
+ .introduction h1 {
97
+ max-width: 12ch;
98
+ }
99
+ }
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@csntgao/uni-base",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "packageManager": "pnpm@11.3.0",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "scripts": {
10
+ "build": "pnpm --filter @uniplat/ai-employee-client build && pnpm --filter @uniplat/ai-employee-web-components build && pnpm --filter @uniplat/ai-employee-vanilla-example build",
11
+ "dev": "pnpm --filter @uniplat/ai-employee-client build && pnpm --filter @uniplat/ai-employee-web-components build && pnpm --filter @uniplat/ai-employee-vanilla-example dev",
12
+ "lint": "eslint . --max-warnings 0",
13
+ "format": "prettier --write .",
14
+ "format:check": "prettier --check .",
15
+ "test": "vitest run",
16
+ "test:watch": "vitest",
17
+ "typecheck": "pnpm --filter @uniplat/ai-employee-client typecheck && pnpm --filter @uniplat/ai-employee-web-components typecheck"
18
+ },
19
+ "devDependencies": {
20
+ "@eslint/js": "^9.32.0",
21
+ "eslint": "^9.32.0",
22
+ "globals": "^16.3.0",
23
+ "happy-dom": "^18.0.1",
24
+ "prettier": "^3.6.2",
25
+ "tsup": "^8.5.0",
26
+ "typescript": "^5.9.2",
27
+ "typescript-eslint": "^8.39.0",
28
+ "vite": "^7.1.0",
29
+ "vitest": "^3.2.4"
30
+ }
31
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@uniplat/ai-employee-client",
3
+ "version": "0.14.0",
4
+ "description": "Browser client for Uniplat AI Employee sessions and realtime messages",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "main": "./dist/index.js",
11
+ "module": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js"
17
+ }
18
+ },
19
+ "scripts": {
20
+ "build": "tsup",
21
+ "typecheck": "tsc --noEmit",
22
+ "test": "vitest run"
23
+ },
24
+ "engines": {
25
+ "node": ">=20"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ }
30
+ }