@talex-touch/utils 1.0.42 → 1.0.45

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 (234) hide show
  1. package/.eslintcache +1 -0
  2. package/__tests__/cloud-sync-sdk.test.ts +442 -0
  3. package/__tests__/icons/icons.test.ts +84 -0
  4. package/__tests__/plugin-sdk-lifecycle.test.ts +130 -0
  5. package/__tests__/power-sdk.test.ts +143 -0
  6. package/__tests__/preset-export-types.test.ts +108 -0
  7. package/__tests__/search/fuzzy-match.test.ts +137 -0
  8. package/__tests__/transport/port-policy.test.ts +44 -0
  9. package/__tests__/transport-domain-sdks.test.ts +152 -0
  10. package/__tests__/types/update.test.ts +67 -0
  11. package/account/account-sdk.ts +915 -0
  12. package/account/index.ts +2 -0
  13. package/account/types.ts +321 -0
  14. package/analytics/client.ts +136 -0
  15. package/analytics/index.ts +2 -0
  16. package/analytics/types.ts +156 -0
  17. package/animation/auto-resize.ts +322 -0
  18. package/animation/window-node.ts +26 -19
  19. package/auth/clerk-types.ts +12 -30
  20. package/auth/index.ts +0 -2
  21. package/auth/useAuthState.ts +6 -14
  22. package/base/index.ts +2 -0
  23. package/base/log-level.ts +105 -0
  24. package/channel/index.ts +170 -69
  25. package/cloud-sync/cloud-sync-sdk.ts +450 -0
  26. package/cloud-sync/index.ts +1 -0
  27. package/common/file-scan-utils.ts +17 -9
  28. package/common/index.ts +4 -0
  29. package/common/logger/index.ts +46 -0
  30. package/common/logger/logger-manager.ts +303 -0
  31. package/common/logger/module-logger.ts +270 -0
  32. package/common/logger/transport-logger.ts +234 -0
  33. package/common/logger/types.ts +93 -0
  34. package/common/search/gather.ts +48 -6
  35. package/common/search/index.ts +8 -0
  36. package/common/storage/constants.ts +13 -0
  37. package/common/storage/entity/app-settings.ts +245 -0
  38. package/common/storage/entity/index.ts +3 -0
  39. package/common/storage/entity/layout-atom-types.ts +147 -0
  40. package/common/storage/entity/openers.ts +1 -0
  41. package/common/storage/entity/preset-cloud-api.ts +132 -0
  42. package/common/storage/entity/preset-export-types.ts +256 -0
  43. package/common/storage/entity/shortcut-settings.ts +1 -0
  44. package/common/storage/shortcut-storage.ts +11 -0
  45. package/common/utils/clone-diagnostics.ts +105 -0
  46. package/common/utils/file.ts +16 -8
  47. package/common/utils/index.ts +6 -2
  48. package/common/utils/payload-preview.ts +173 -0
  49. package/common/utils/polling.ts +167 -13
  50. package/common/utils/safe-path.ts +103 -0
  51. package/common/utils/safe-shell.ts +115 -0
  52. package/common/utils/task-queue.ts +4 -1
  53. package/core-box/builder/tuff-builder.ts +0 -1
  54. package/core-box/index.ts +1 -1
  55. package/core-box/recommendation.ts +38 -1
  56. package/core-box/tuff/tuff-dsl.ts +32 -0
  57. package/electron/download-manager.ts +10 -7
  58. package/electron/env-tool.ts +42 -40
  59. package/electron/index.ts +0 -1
  60. package/env/index.ts +156 -0
  61. package/eslint.config.js +55 -0
  62. package/i18n/index.ts +62 -0
  63. package/i18n/locales/en.json +226 -0
  64. package/i18n/locales/zh.json +226 -0
  65. package/i18n/message-keys.ts +236 -0
  66. package/i18n/resolver.ts +181 -0
  67. package/icons/index.ts +257 -0
  68. package/icons/svg.ts +69 -0
  69. package/index.ts +9 -1
  70. package/intelligence/client.ts +72 -42
  71. package/market/constants.ts +9 -5
  72. package/market/index.ts +1 -1
  73. package/market/types.ts +19 -4
  74. package/package.json +15 -5
  75. package/permission/index.ts +143 -46
  76. package/permission/legacy.ts +26 -0
  77. package/permission/registry.ts +304 -0
  78. package/permission/types.ts +164 -0
  79. package/plugin/channel.ts +68 -39
  80. package/plugin/index.ts +80 -7
  81. package/plugin/install.ts +3 -0
  82. package/plugin/log/types.ts +22 -5
  83. package/plugin/node/logger-manager.ts +11 -3
  84. package/plugin/node/logger.ts +24 -17
  85. package/plugin/preload.ts +25 -2
  86. package/plugin/providers/index.ts +4 -4
  87. package/plugin/providers/market-client.ts +6 -3
  88. package/plugin/providers/npm-provider.ts +22 -7
  89. package/plugin/providers/tpex-provider.ts +22 -8
  90. package/plugin/sdk/box-items.ts +14 -0
  91. package/plugin/sdk/box-sdk.ts +64 -0
  92. package/plugin/sdk/channel.ts +119 -4
  93. package/plugin/sdk/clipboard.ts +26 -12
  94. package/plugin/sdk/cloud-sync.ts +113 -0
  95. package/plugin/sdk/common.ts +19 -11
  96. package/plugin/sdk/core-box.ts +6 -15
  97. package/plugin/sdk/division-box.ts +160 -65
  98. package/plugin/sdk/examples/storage-onDidChange-example.js +5 -2
  99. package/plugin/sdk/feature-sdk.ts +111 -76
  100. package/plugin/sdk/flow.ts +146 -45
  101. package/plugin/sdk/hooks/bridge.ts +13 -6
  102. package/plugin/sdk/hooks/life-cycle.ts +35 -16
  103. package/plugin/sdk/index.ts +14 -3
  104. package/plugin/sdk/intelligence.ts +87 -0
  105. package/plugin/sdk/meta/README.md +179 -0
  106. package/plugin/sdk/meta-sdk.ts +244 -0
  107. package/plugin/sdk/notification.ts +9 -0
  108. package/plugin/sdk/plugin-info.ts +64 -0
  109. package/plugin/sdk/power.ts +155 -0
  110. package/plugin/sdk/recommend.ts +21 -0
  111. package/plugin/sdk/service/index.ts +12 -8
  112. package/plugin/sdk/sqlite.ts +141 -0
  113. package/plugin/sdk/storage.ts +2 -6
  114. package/plugin/sdk/system.ts +2 -9
  115. package/plugin/sdk/temp-files.ts +41 -0
  116. package/plugin/sdk/touch-sdk.ts +18 -0
  117. package/plugin/sdk/types.ts +44 -4
  118. package/plugin/sdk/window/index.ts +12 -9
  119. package/plugin/sdk-version.ts +231 -0
  120. package/preload/renderer.ts +3 -2
  121. package/renderer/hooks/arg-mapper.ts +16 -2
  122. package/renderer/hooks/index.ts +13 -0
  123. package/renderer/hooks/initialize.ts +2 -1
  124. package/renderer/hooks/use-agent-market-sdk.ts +7 -0
  125. package/renderer/hooks/use-agent-market.ts +106 -0
  126. package/renderer/hooks/use-agents-sdk.ts +7 -0
  127. package/renderer/hooks/use-app-sdk.ts +7 -0
  128. package/renderer/hooks/use-channel.ts +33 -4
  129. package/renderer/hooks/use-download-sdk.ts +21 -0
  130. package/renderer/hooks/use-intelligence-sdk.ts +7 -0
  131. package/renderer/hooks/use-intelligence-stats.ts +290 -0
  132. package/renderer/hooks/use-intelligence.ts +55 -214
  133. package/renderer/hooks/use-market-sdk.ts +16 -0
  134. package/renderer/hooks/use-notification-sdk.ts +7 -0
  135. package/renderer/hooks/use-permission-sdk.ts +7 -0
  136. package/renderer/hooks/use-permission.ts +325 -0
  137. package/renderer/hooks/use-platform-sdk.ts +7 -0
  138. package/renderer/hooks/use-plugin-sdk.ts +16 -0
  139. package/renderer/hooks/use-settings-sdk.ts +7 -0
  140. package/renderer/hooks/use-update-sdk.ts +21 -0
  141. package/renderer/index.ts +1 -0
  142. package/renderer/ref.ts +19 -10
  143. package/renderer/shared/components/SharedPluginDetailContent.vue +84 -0
  144. package/renderer/shared/components/SharedPluginDetailHeader.vue +116 -0
  145. package/renderer/shared/components/SharedPluginDetailMetaList.vue +39 -0
  146. package/renderer/shared/components/SharedPluginDetailReadme.vue +45 -0
  147. package/renderer/shared/components/SharedPluginDetailVersions.vue +98 -0
  148. package/renderer/shared/components/index.ts +5 -0
  149. package/renderer/shared/components/shims-vue.d.ts +5 -0
  150. package/renderer/shared/index.ts +2 -0
  151. package/renderer/shared/plugin-detail.ts +62 -0
  152. package/renderer/storage/app-settings.ts +3 -1
  153. package/renderer/storage/base-storage.ts +508 -82
  154. package/renderer/storage/intelligence-storage.ts +31 -40
  155. package/renderer/storage/openers.ts +3 -1
  156. package/renderer/storage/storage-subscription.ts +126 -42
  157. package/renderer/touch-sdk/env.ts +10 -10
  158. package/renderer/touch-sdk/index.ts +114 -18
  159. package/renderer/touch-sdk/terminal.ts +24 -13
  160. package/search/feature-matcher.ts +279 -0
  161. package/search/fuzzy-match.ts +64 -34
  162. package/search/index.ts +10 -0
  163. package/search/levenshtein-utils.ts +17 -11
  164. package/transport/errors.ts +310 -0
  165. package/transport/event/builder.ts +378 -0
  166. package/transport/event/index.ts +7 -0
  167. package/transport/event/types.ts +292 -0
  168. package/transport/events/index.ts +2690 -0
  169. package/transport/events/meta-overlay.ts +79 -0
  170. package/transport/events/types/agents.ts +177 -0
  171. package/transport/events/types/app-index.ts +20 -0
  172. package/transport/events/types/app.ts +475 -0
  173. package/transport/events/types/box-item.ts +222 -0
  174. package/transport/events/types/clipboard.ts +80 -0
  175. package/transport/events/types/core-box.ts +534 -0
  176. package/transport/events/types/device-idle.ts +7 -0
  177. package/transport/events/types/division-box.ts +99 -0
  178. package/transport/events/types/download.ts +115 -0
  179. package/transport/events/types/file-index.ts +84 -0
  180. package/transport/events/types/flow.ts +149 -0
  181. package/transport/events/types/index.ts +70 -0
  182. package/transport/events/types/market.ts +39 -0
  183. package/transport/events/types/meta-overlay.ts +184 -0
  184. package/transport/events/types/notification.ts +140 -0
  185. package/transport/events/types/permission.ts +90 -0
  186. package/transport/events/types/platform.ts +8 -0
  187. package/transport/events/types/plugin.ts +631 -0
  188. package/transport/events/types/sentry.ts +20 -0
  189. package/transport/events/types/storage.ts +208 -0
  190. package/transport/events/types/transport.ts +60 -0
  191. package/transport/events/types/tray.ts +16 -0
  192. package/transport/events/types/update.ts +78 -0
  193. package/transport/index.ts +141 -0
  194. package/transport/main.ts +2 -0
  195. package/transport/prelude.ts +208 -0
  196. package/transport/sdk/constants.ts +29 -0
  197. package/transport/sdk/domains/agents-market.ts +47 -0
  198. package/transport/sdk/domains/agents.ts +62 -0
  199. package/transport/sdk/domains/app.ts +48 -0
  200. package/transport/sdk/domains/disposable.ts +35 -0
  201. package/transport/sdk/domains/download.ts +139 -0
  202. package/transport/sdk/domains/index.ts +13 -0
  203. package/transport/sdk/domains/intelligence.ts +616 -0
  204. package/transport/sdk/domains/market.ts +35 -0
  205. package/transport/sdk/domains/notification.ts +62 -0
  206. package/transport/sdk/domains/permission.ts +85 -0
  207. package/transport/sdk/domains/platform.ts +19 -0
  208. package/transport/sdk/domains/plugin.ts +144 -0
  209. package/transport/sdk/domains/settings.ts +102 -0
  210. package/transport/sdk/domains/update.ts +64 -0
  211. package/transport/sdk/index.ts +60 -0
  212. package/transport/sdk/main-transport.ts +710 -0
  213. package/transport/sdk/main.ts +9 -0
  214. package/transport/sdk/plugin-transport.ts +654 -0
  215. package/transport/sdk/port-policy.ts +38 -0
  216. package/transport/sdk/renderer-transport.ts +1165 -0
  217. package/transport/types.ts +605 -0
  218. package/types/agent.ts +399 -0
  219. package/types/cloud-sync.ts +157 -0
  220. package/types/division-box.ts +31 -31
  221. package/types/download.ts +1 -0
  222. package/types/flow.ts +63 -12
  223. package/types/icon.ts +2 -1
  224. package/types/index.ts +5 -0
  225. package/types/intelligence.ts +166 -173
  226. package/types/modules/base.ts +2 -0
  227. package/types/path-browserify.d.ts +5 -0
  228. package/types/platform.ts +12 -0
  229. package/types/startup-info.ts +32 -0
  230. package/types/touch-app-core.ts +8 -8
  231. package/types/update.ts +94 -1
  232. package/vitest.config.ts +25 -0
  233. package/auth/useClerkConfig.ts +0 -40
  234. package/auth/useClerkProvider.ts +0 -52
@@ -0,0 +1,915 @@
1
+ import type {
2
+ AccountInfo,
3
+ AccountSDKConfig,
4
+ DeviceSession,
5
+ FeatureFlag,
6
+ PlanComparisonItem,
7
+ PlanQuota,
8
+ QuotaCheckResult,
9
+ SocialConnection,
10
+ Subscription,
11
+ Team,
12
+ UpgradeOption,
13
+ UsageStats,
14
+ UserProfile,
15
+ } from './types'
16
+ import {
17
+ BillingCycle,
18
+ SubscriptionPlan,
19
+ SubscriptionStatus,
20
+ TeamRole,
21
+ VerificationStatus,
22
+ } from './types'
23
+
24
+ /**
25
+ * Default plan quotas
26
+ */
27
+ export const DEFAULT_PLAN_QUOTAS: Record<SubscriptionPlan, PlanQuota> = {
28
+ [SubscriptionPlan.FREE]: {
29
+ aiRequestsPerDay: 50,
30
+ aiTokensPerMonth: 100000,
31
+ maxPlugins: 5,
32
+ maxStorageBytes: 100 * 1024 * 1024, // 100MB
33
+ maxWorkspaces: 1,
34
+ customModelAccess: false,
35
+ prioritySupport: false,
36
+ apiAccess: false,
37
+ advancedAnalytics: false,
38
+ },
39
+ [SubscriptionPlan.PRO]: {
40
+ aiRequestsPerDay: 500,
41
+ aiTokensPerMonth: 1000000,
42
+ maxPlugins: 20,
43
+ maxStorageBytes: 1024 * 1024 * 1024, // 1GB
44
+ maxWorkspaces: 5,
45
+ customModelAccess: true,
46
+ prioritySupport: false,
47
+ apiAccess: true,
48
+ advancedAnalytics: false,
49
+ },
50
+ [SubscriptionPlan.PLUS]: {
51
+ aiRequestsPerDay: 2000,
52
+ aiTokensPerMonth: 5000000,
53
+ maxPlugins: 50,
54
+ maxStorageBytes: 5 * 1024 * 1024 * 1024, // 5GB
55
+ maxWorkspaces: 20,
56
+ customModelAccess: true,
57
+ prioritySupport: true,
58
+ apiAccess: true,
59
+ advancedAnalytics: true,
60
+ },
61
+ [SubscriptionPlan.TEAM]: {
62
+ aiRequestsPerDay: 10000,
63
+ aiTokensPerMonth: 20000000,
64
+ maxPlugins: 100,
65
+ maxStorageBytes: 20 * 1024 * 1024 * 1024, // 20GB
66
+ maxTeamMembers: 10,
67
+ maxWorkspaces: 50,
68
+ customModelAccess: true,
69
+ prioritySupport: true,
70
+ apiAccess: true,
71
+ advancedAnalytics: true,
72
+ },
73
+ [SubscriptionPlan.ENTERPRISE]: {
74
+ aiRequestsPerDay: -1, // Unlimited
75
+ aiTokensPerMonth: -1, // Unlimited
76
+ maxPlugins: -1, // Unlimited
77
+ maxStorageBytes: -1, // Unlimited
78
+ maxTeamMembers: -1, // Unlimited
79
+ maxWorkspaces: -1, // Unlimited
80
+ customModelAccess: true,
81
+ prioritySupport: true,
82
+ apiAccess: true,
83
+ advancedAnalytics: true,
84
+ },
85
+ }
86
+
87
+ /**
88
+ * Upgrade options with pricing
89
+ */
90
+ export const UPGRADE_OPTIONS: UpgradeOption[] = [
91
+ {
92
+ plan: SubscriptionPlan.PRO,
93
+ name: 'Pro',
94
+ description: 'For power users who need more AI capabilities',
95
+ priceMonthly: 9.99,
96
+ priceYearly: 99.99,
97
+ currency: 'USD',
98
+ features: [
99
+ '500 AI requests/day',
100
+ '1M tokens/month',
101
+ '20 plugins',
102
+ '1GB storage',
103
+ 'Custom model access',
104
+ 'API access',
105
+ ],
106
+ },
107
+ {
108
+ plan: SubscriptionPlan.PLUS,
109
+ name: 'Plus',
110
+ description: 'For professionals who demand the best',
111
+ priceMonthly: 19.99,
112
+ priceYearly: 199.99,
113
+ currency: 'USD',
114
+ features: [
115
+ '2000 AI requests/day',
116
+ '5M tokens/month',
117
+ '50 plugins',
118
+ '5GB storage',
119
+ 'Priority support',
120
+ 'Advanced analytics',
121
+ ],
122
+ recommended: true,
123
+ },
124
+ {
125
+ plan: SubscriptionPlan.TEAM,
126
+ name: 'Team',
127
+ description: 'For teams that collaborate',
128
+ priceMonthly: 49.99,
129
+ priceYearly: 499.99,
130
+ currency: 'USD',
131
+ features: [
132
+ '10000 AI requests/day',
133
+ '20M tokens/month',
134
+ 'Unlimited plugins',
135
+ '20GB storage',
136
+ 'Up to 10 team members',
137
+ 'Team management',
138
+ ],
139
+ },
140
+ ]
141
+
142
+ /**
143
+ * Account SDK for plugins and Prelude scripts
144
+ */
145
+ export class AccountSDK {
146
+ private config: AccountSDKConfig
147
+ private cache: Map<string, { data: any, timestamp: number }> = new Map()
148
+ private channelSend: ((event: string, data?: any) => Promise<any>) | null = null
149
+
150
+ constructor(config: AccountSDKConfig = {}) {
151
+ this.config = {
152
+ cacheTTL: 60000, // 1 minute default
153
+ offlineMode: false,
154
+ ...config,
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Set the channel send function for IPC communication
160
+ */
161
+ setChannelSend(send: (event: string, data?: any) => Promise<any>): void {
162
+ this.channelSend = send
163
+ }
164
+
165
+ private async send<T>(event: string, data?: any): Promise<T> {
166
+ if (!this.channelSend) {
167
+ throw new Error('[AccountSDK] Channel not initialized. Call setChannelSend first.')
168
+ }
169
+ return this.channelSend(event, data)
170
+ }
171
+
172
+ private getCached<T>(key: string): T | null {
173
+ const cached = this.cache.get(key)
174
+ if (!cached)
175
+ return null
176
+
177
+ const ttl = this.config.cacheTTL || 60000
178
+ if (Date.now() - cached.timestamp > ttl) {
179
+ this.cache.delete(key)
180
+ return null
181
+ }
182
+
183
+ return cached.data as T
184
+ }
185
+
186
+ private setCache(key: string, data: any): void {
187
+ this.cache.set(key, { data, timestamp: Date.now() })
188
+ }
189
+
190
+ /**
191
+ * Clear all cached data
192
+ */
193
+ clearCache(): void {
194
+ this.cache.clear()
195
+ }
196
+
197
+ // ============================================================================
198
+ // Auth Token & Device
199
+ // ============================================================================
200
+
201
+ /**
202
+ * Get auth token for API requests
203
+ */
204
+ async getAuthToken(): Promise<string | null> {
205
+ const cached = this.getCached<string>('authToken')
206
+ if (cached)
207
+ return cached
208
+
209
+ try {
210
+ const token = await this.send<string | null>('account:get-auth-token')
211
+ if (token) {
212
+ this.setCache('authToken', token)
213
+ }
214
+ return token
215
+ }
216
+ catch {
217
+ return null
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Get current device id
223
+ */
224
+ async getDeviceId(): Promise<string | null> {
225
+ const cached = this.getCached<string>('deviceId')
226
+ if (cached)
227
+ return cached
228
+
229
+ try {
230
+ const deviceId = await this.send<string | null>('account:get-device-id')
231
+ if (deviceId) {
232
+ this.setCache('deviceId', deviceId)
233
+ }
234
+ return deviceId
235
+ }
236
+ catch {
237
+ return null
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Get user sync preference switch from host app.
243
+ * Defaults to true for backward compatibility when channel is not implemented.
244
+ */
245
+ async getSyncEnabled(): Promise<boolean> {
246
+ try {
247
+ const enabled = await this.send<boolean | null>('account:get-sync-enabled')
248
+ return typeof enabled === 'boolean' ? enabled : true
249
+ }
250
+ catch {
251
+ return true
252
+ }
253
+ }
254
+
255
+ /**
256
+ * Report sync activity to host app for status display.
257
+ */
258
+ async recordSyncActivity(kind: 'push' | 'pull'): Promise<void> {
259
+ try {
260
+ await this.send('account:record-sync-activity', { kind })
261
+ }
262
+ catch {
263
+ // ignore, best-effort telemetry
264
+ }
265
+ }
266
+
267
+ // ============================================================================
268
+ // User Profile
269
+ // ============================================================================
270
+
271
+ /**
272
+ * Get current user profile
273
+ */
274
+ async getProfile(): Promise<UserProfile | null> {
275
+ const cached = this.getCached<UserProfile>('profile')
276
+ if (cached)
277
+ return cached
278
+
279
+ try {
280
+ const profile = await this.send<UserProfile | null>('account:get-profile')
281
+ if (profile) {
282
+ this.setCache('profile', profile)
283
+ }
284
+ return profile
285
+ }
286
+ catch {
287
+ return null
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Check if user is logged in
293
+ */
294
+ async isLoggedIn(): Promise<boolean> {
295
+ const profile = await this.getProfile()
296
+ return profile !== null
297
+ }
298
+
299
+ /**
300
+ * Get user ID
301
+ */
302
+ async getUserId(): Promise<string | null> {
303
+ const profile = await this.getProfile()
304
+ return profile?.id ?? null
305
+ }
306
+
307
+ /**
308
+ * Get display name
309
+ */
310
+ async getDisplayName(): Promise<string> {
311
+ const profile = await this.getProfile()
312
+ if (!profile)
313
+ return ''
314
+ const email = profile.email ?? ''
315
+ const emailName = email ? email.split('@')[0] : ''
316
+ return profile.displayName ?? profile.username ?? emailName
317
+ }
318
+
319
+ /**
320
+ * Get user email
321
+ */
322
+ async getEmail(): Promise<string | null> {
323
+ const profile = await this.getProfile()
324
+ return profile?.email ?? null
325
+ }
326
+
327
+ /**
328
+ * Get avatar URL
329
+ */
330
+ async getAvatarUrl(): Promise<string | null> {
331
+ const profile = await this.getProfile()
332
+ return profile?.avatarUrl ?? null
333
+ }
334
+
335
+ // ============================================================================
336
+ // Subscription & Plan
337
+ // ============================================================================
338
+
339
+ /**
340
+ * Get current subscription
341
+ */
342
+ async getSubscription(): Promise<Subscription | null> {
343
+ const cached = this.getCached<Subscription>('subscription')
344
+ if (cached)
345
+ return cached
346
+
347
+ try {
348
+ const subscription = await this.send<Subscription | null>('account:get-subscription')
349
+ if (subscription) {
350
+ this.setCache('subscription', subscription)
351
+ }
352
+ return subscription
353
+ }
354
+ catch {
355
+ return null
356
+ }
357
+ }
358
+
359
+ /**
360
+ * Get current plan
361
+ */
362
+ async getPlan(): Promise<SubscriptionPlan> {
363
+ const subscription = await this.getSubscription()
364
+ return subscription?.plan ?? SubscriptionPlan.FREE
365
+ }
366
+
367
+ /**
368
+ * Check if user has a paid plan
369
+ */
370
+ async isPaidUser(): Promise<boolean> {
371
+ const plan = await this.getPlan()
372
+ return plan !== SubscriptionPlan.FREE
373
+ }
374
+
375
+ /**
376
+ * Check if user is Pro or above
377
+ */
378
+ async isProOrAbove(): Promise<boolean> {
379
+ const plan = await this.getPlan()
380
+ return [
381
+ SubscriptionPlan.PRO,
382
+ SubscriptionPlan.PLUS,
383
+ SubscriptionPlan.TEAM,
384
+ SubscriptionPlan.ENTERPRISE,
385
+ ].includes(plan)
386
+ }
387
+
388
+ /**
389
+ * Check if user is Plus or above
390
+ */
391
+ async isPlusOrAbove(): Promise<boolean> {
392
+ const plan = await this.getPlan()
393
+ return [
394
+ SubscriptionPlan.PLUS,
395
+ SubscriptionPlan.TEAM,
396
+ SubscriptionPlan.ENTERPRISE,
397
+ ].includes(plan)
398
+ }
399
+
400
+ /**
401
+ * Check if user is Team or above
402
+ */
403
+ async isTeamOrAbove(): Promise<boolean> {
404
+ const plan = await this.getPlan()
405
+ return [
406
+ SubscriptionPlan.TEAM,
407
+ SubscriptionPlan.ENTERPRISE,
408
+ ].includes(plan)
409
+ }
410
+
411
+ /**
412
+ * Check if user is Enterprise
413
+ */
414
+ async isEnterprise(): Promise<boolean> {
415
+ const plan = await this.getPlan()
416
+ return plan === SubscriptionPlan.ENTERPRISE
417
+ }
418
+
419
+ /**
420
+ * Check if subscription is active
421
+ */
422
+ async isSubscriptionActive(): Promise<boolean> {
423
+ const subscription = await this.getSubscription()
424
+ if (!subscription)
425
+ return false
426
+ return [
427
+ SubscriptionStatus.ACTIVE,
428
+ SubscriptionStatus.TRIALING,
429
+ ].includes(subscription.status)
430
+ }
431
+
432
+ /**
433
+ * Check if user is in trial period
434
+ */
435
+ async isTrialing(): Promise<boolean> {
436
+ const subscription = await this.getSubscription()
437
+ return subscription?.status === SubscriptionStatus.TRIALING
438
+ }
439
+
440
+ /**
441
+ * Get days remaining in current period
442
+ */
443
+ async getDaysRemaining(): Promise<number> {
444
+ const subscription = await this.getSubscription()
445
+ if (!subscription)
446
+ return 0
447
+
448
+ const now = Date.now()
449
+ const end = subscription.currentPeriodEnd
450
+ const days = Math.ceil((end - now) / (1000 * 60 * 60 * 24))
451
+ return Math.max(0, days)
452
+ }
453
+
454
+ /**
455
+ * Get trial days remaining
456
+ */
457
+ async getTrialDaysRemaining(): Promise<number> {
458
+ const subscription = await this.getSubscription()
459
+ if (!subscription?.trialEndAt)
460
+ return 0
461
+
462
+ const now = Date.now()
463
+ const days = Math.ceil((subscription.trialEndAt - now) / (1000 * 60 * 60 * 24))
464
+ return Math.max(0, days)
465
+ }
466
+
467
+ // ============================================================================
468
+ // Quota & Usage
469
+ // ============================================================================
470
+
471
+ /**
472
+ * Get plan quota limits
473
+ */
474
+ async getQuota(): Promise<PlanQuota> {
475
+ const subscription = await this.getSubscription()
476
+ return subscription?.quota ?? DEFAULT_PLAN_QUOTAS[SubscriptionPlan.FREE]
477
+ }
478
+
479
+ /**
480
+ * Get current usage stats
481
+ */
482
+ async getUsage(): Promise<UsageStats> {
483
+ const cached = this.getCached<UsageStats>('usage')
484
+ if (cached)
485
+ return cached
486
+
487
+ try {
488
+ const usage = await this.send<UsageStats>('account:get-usage')
489
+ this.setCache('usage', usage)
490
+ return usage
491
+ }
492
+ catch {
493
+ return {
494
+ aiRequestsToday: 0,
495
+ aiRequestsThisMonth: 0,
496
+ aiTokensThisMonth: 0,
497
+ storageUsedBytes: 0,
498
+ pluginsInstalled: 0,
499
+ }
500
+ }
501
+ }
502
+
503
+ /**
504
+ * Check if AI requests quota is available
505
+ */
506
+ async checkAiRequestQuota(): Promise<QuotaCheckResult> {
507
+ const [quota, usage] = await Promise.all([this.getQuota(), this.getUsage()])
508
+
509
+ if (quota.aiRequestsPerDay === -1) {
510
+ return { allowed: true, remaining: -1 }
511
+ }
512
+
513
+ const remaining = quota.aiRequestsPerDay - usage.aiRequestsToday
514
+ if (remaining <= 0) {
515
+ const tomorrow = new Date()
516
+ tomorrow.setHours(24, 0, 0, 0)
517
+ return {
518
+ allowed: false,
519
+ reason: 'Daily AI request limit reached',
520
+ remaining: 0,
521
+ resetAt: tomorrow.getTime(),
522
+ }
523
+ }
524
+
525
+ return { allowed: true, remaining }
526
+ }
527
+
528
+ /**
529
+ * Check if AI tokens quota is available
530
+ */
531
+ async checkAiTokenQuota(estimatedTokens: number = 0): Promise<QuotaCheckResult> {
532
+ const [quota, usage] = await Promise.all([this.getQuota(), this.getUsage()])
533
+
534
+ if (quota.aiTokensPerMonth === -1) {
535
+ return { allowed: true, remaining: -1 }
536
+ }
537
+
538
+ const remaining = quota.aiTokensPerMonth - usage.aiTokensThisMonth
539
+ if (remaining < estimatedTokens) {
540
+ const nextMonth = new Date()
541
+ nextMonth.setMonth(nextMonth.getMonth() + 1, 1)
542
+ nextMonth.setHours(0, 0, 0, 0)
543
+ return {
544
+ allowed: false,
545
+ reason: 'Monthly AI token limit reached',
546
+ remaining: Math.max(0, remaining),
547
+ resetAt: nextMonth.getTime(),
548
+ }
549
+ }
550
+
551
+ return { allowed: true, remaining }
552
+ }
553
+
554
+ /**
555
+ * Check if storage quota is available
556
+ */
557
+ async checkStorageQuota(additionalBytes: number = 0): Promise<QuotaCheckResult> {
558
+ const [quota, usage] = await Promise.all([this.getQuota(), this.getUsage()])
559
+
560
+ if (quota.maxStorageBytes === -1) {
561
+ return { allowed: true, remaining: -1 }
562
+ }
563
+
564
+ const remaining = quota.maxStorageBytes - usage.storageUsedBytes
565
+ if (remaining < additionalBytes) {
566
+ return {
567
+ allowed: false,
568
+ reason: 'Storage limit reached',
569
+ remaining: Math.max(0, remaining),
570
+ }
571
+ }
572
+
573
+ return { allowed: true, remaining }
574
+ }
575
+
576
+ /**
577
+ * Check if plugin install quota is available
578
+ */
579
+ async checkPluginQuota(): Promise<QuotaCheckResult> {
580
+ const [quota, usage] = await Promise.all([this.getQuota(), this.getUsage()])
581
+
582
+ if (quota.maxPlugins === -1) {
583
+ return { allowed: true, remaining: -1 }
584
+ }
585
+
586
+ const remaining = quota.maxPlugins - usage.pluginsInstalled
587
+ if (remaining <= 0) {
588
+ return {
589
+ allowed: false,
590
+ reason: 'Plugin limit reached',
591
+ remaining: 0,
592
+ }
593
+ }
594
+
595
+ return { allowed: true, remaining }
596
+ }
597
+
598
+ /**
599
+ * Get usage percentage (0-100)
600
+ */
601
+ async getUsagePercentage(type: 'aiRequests' | 'aiTokens' | 'storage' | 'plugins'): Promise<number> {
602
+ const [quota, usage] = await Promise.all([this.getQuota(), this.getUsage()])
603
+
604
+ switch (type) {
605
+ case 'aiRequests':
606
+ if (quota.aiRequestsPerDay === -1)
607
+ return 0
608
+ return Math.min(100, (usage.aiRequestsToday / quota.aiRequestsPerDay) * 100)
609
+ case 'aiTokens':
610
+ if (quota.aiTokensPerMonth === -1)
611
+ return 0
612
+ return Math.min(100, (usage.aiTokensThisMonth / quota.aiTokensPerMonth) * 100)
613
+ case 'storage':
614
+ if (quota.maxStorageBytes === -1)
615
+ return 0
616
+ return Math.min(100, (usage.storageUsedBytes / quota.maxStorageBytes) * 100)
617
+ case 'plugins':
618
+ if (quota.maxPlugins === -1)
619
+ return 0
620
+ return Math.min(100, (usage.pluginsInstalled / quota.maxPlugins) * 100)
621
+ default:
622
+ return 0
623
+ }
624
+ }
625
+
626
+ // ============================================================================
627
+ // Features & Permissions
628
+ // ============================================================================
629
+
630
+ /**
631
+ * Check if a feature is enabled for user
632
+ */
633
+ async hasFeature(featureId: string): Promise<boolean> {
634
+ try {
635
+ const features = await this.send<FeatureFlag[]>('account:get-features')
636
+ const feature = features.find(f => f.id === featureId)
637
+ return feature?.enabled ?? false
638
+ }
639
+ catch {
640
+ return false
641
+ }
642
+ }
643
+
644
+ /**
645
+ * Get feature value
646
+ */
647
+ async getFeatureValue<T = any>(featureId: string): Promise<T | null> {
648
+ try {
649
+ const features = await this.send<FeatureFlag[]>('account:get-features')
650
+ const feature = features.find(f => f.id === featureId)
651
+ return (feature?.value as T) ?? null
652
+ }
653
+ catch {
654
+ return null
655
+ }
656
+ }
657
+
658
+ /**
659
+ * Check if user has API access
660
+ */
661
+ async hasApiAccess(): Promise<boolean> {
662
+ const quota = await this.getQuota()
663
+ return quota.apiAccess
664
+ }
665
+
666
+ /**
667
+ * Check if user has custom model access
668
+ */
669
+ async hasCustomModelAccess(): Promise<boolean> {
670
+ const quota = await this.getQuota()
671
+ return quota.customModelAccess
672
+ }
673
+
674
+ /**
675
+ * Check if user has priority support
676
+ */
677
+ async hasPrioritySupport(): Promise<boolean> {
678
+ const quota = await this.getQuota()
679
+ return quota.prioritySupport
680
+ }
681
+
682
+ /**
683
+ * Check if user has advanced analytics
684
+ */
685
+ async hasAdvancedAnalytics(): Promise<boolean> {
686
+ const quota = await this.getQuota()
687
+ return quota.advancedAnalytics
688
+ }
689
+
690
+ // ============================================================================
691
+ // Team
692
+ // ============================================================================
693
+
694
+ /**
695
+ * Get user's teams
696
+ */
697
+ async getTeams(): Promise<Team[]> {
698
+ try {
699
+ return await this.send<Team[]>('account:get-teams')
700
+ }
701
+ catch {
702
+ return []
703
+ }
704
+ }
705
+
706
+ /**
707
+ * Check if user is in any team
708
+ */
709
+ async isInTeam(): Promise<boolean> {
710
+ const teams = await this.getTeams()
711
+ return teams.length > 0
712
+ }
713
+
714
+ /**
715
+ * Check if user is team owner
716
+ */
717
+ async isTeamOwner(teamId?: string): Promise<boolean> {
718
+ const teams = await this.getTeams()
719
+ if (teamId) {
720
+ const team = teams.find(t => t.id === teamId)
721
+ return team?.role === TeamRole.OWNER
722
+ }
723
+ return teams.some(t => t.role === TeamRole.OWNER)
724
+ }
725
+
726
+ /**
727
+ * Check if user is team admin
728
+ */
729
+ async isTeamAdmin(teamId?: string): Promise<boolean> {
730
+ const teams = await this.getTeams()
731
+ if (teamId) {
732
+ const team = teams.find(t => t.id === teamId)
733
+ return team?.role === TeamRole.OWNER || team?.role === TeamRole.ADMIN
734
+ }
735
+ return teams.some(t => t.role === TeamRole.OWNER || t.role === TeamRole.ADMIN)
736
+ }
737
+
738
+ // ============================================================================
739
+ // Sessions
740
+ // ============================================================================
741
+
742
+ /**
743
+ * Get active sessions
744
+ */
745
+ async getSessions(): Promise<DeviceSession[]> {
746
+ try {
747
+ return await this.send<DeviceSession[]>('account:get-sessions')
748
+ }
749
+ catch {
750
+ return []
751
+ }
752
+ }
753
+
754
+ /**
755
+ * Get current session
756
+ */
757
+ async getCurrentSession(): Promise<DeviceSession | null> {
758
+ const sessions = await this.getSessions()
759
+ return sessions.find(s => s.isCurrent) ?? null
760
+ }
761
+
762
+ // ============================================================================
763
+ // Full Account Info
764
+ // ============================================================================
765
+
766
+ /**
767
+ * Get complete account info
768
+ */
769
+ async getAccountInfo(): Promise<AccountInfo | null> {
770
+ const cached = this.getCached<AccountInfo>('accountInfo')
771
+ if (cached)
772
+ return cached
773
+
774
+ try {
775
+ const info = await this.send<AccountInfo | null>('account:get-info')
776
+ if (info) {
777
+ this.setCache('accountInfo', info)
778
+ }
779
+ return info
780
+ }
781
+ catch {
782
+ return null
783
+ }
784
+ }
785
+
786
+ /**
787
+ * Check if user is beta tester
788
+ */
789
+ async isBetaTester(): Promise<boolean> {
790
+ const info = await this.getAccountInfo()
791
+ return info?.isBetaTester ?? false
792
+ }
793
+
794
+ /**
795
+ * Get referral code
796
+ */
797
+ async getReferralCode(): Promise<string | null> {
798
+ const info = await this.getAccountInfo()
799
+ return info?.referralCode ?? null
800
+ }
801
+
802
+ /**
803
+ * Get referral count
804
+ */
805
+ async getReferralCount(): Promise<number> {
806
+ const info = await this.getAccountInfo()
807
+ return info?.referralCount ?? 0
808
+ }
809
+
810
+ // ============================================================================
811
+ // Upgrade & Billing
812
+ // ============================================================================
813
+
814
+ /**
815
+ * Get available upgrade options
816
+ */
817
+ getUpgradeOptions(): UpgradeOption[] {
818
+ return UPGRADE_OPTIONS
819
+ }
820
+
821
+ /**
822
+ * Get plan comparison table
823
+ */
824
+ getPlanComparison(): PlanComparisonItem[] {
825
+ return [
826
+ { feature: 'AI Requests/Day', free: 50, pro: 500, plus: 2000, team: 10000, enterprise: 'Unlimited' },
827
+ { feature: 'AI Tokens/Month', free: '100K', pro: '1M', plus: '5M', team: '20M', enterprise: 'Unlimited' },
828
+ { feature: 'Plugins', free: 5, pro: 20, plus: 50, team: 100, enterprise: 'Unlimited' },
829
+ { feature: 'Storage', free: '100MB', pro: '1GB', plus: '5GB', team: '20GB', enterprise: 'Unlimited' },
830
+ { feature: 'Workspaces', free: 1, pro: 5, plus: 20, team: 50, enterprise: 'Unlimited' },
831
+ { feature: 'Team Members', free: false, pro: false, plus: false, team: 10, enterprise: 'Unlimited' },
832
+ { feature: 'Custom Models', free: false, pro: true, plus: true, team: true, enterprise: true },
833
+ { feature: 'API Access', free: false, pro: true, plus: true, team: true, enterprise: true },
834
+ { feature: 'Priority Support', free: false, pro: false, plus: true, team: true, enterprise: true },
835
+ { feature: 'Advanced Analytics', free: false, pro: false, plus: true, team: true, enterprise: true },
836
+ ]
837
+ }
838
+
839
+ /**
840
+ * Open upgrade page
841
+ */
842
+ async openUpgradePage(plan?: SubscriptionPlan): Promise<void> {
843
+ await this.send('account:open-upgrade', { plan })
844
+ }
845
+
846
+ /**
847
+ * Open billing management page
848
+ */
849
+ async openBillingPage(): Promise<void> {
850
+ await this.send('account:open-billing')
851
+ }
852
+
853
+ // ============================================================================
854
+ // Account Actions
855
+ // ============================================================================
856
+
857
+ /**
858
+ * Open account settings
859
+ */
860
+ async openAccountSettings(): Promise<void> {
861
+ await this.send('account:open-settings')
862
+ }
863
+
864
+ /**
865
+ * Open profile editor
866
+ */
867
+ async openProfileEditor(): Promise<void> {
868
+ await this.send('account:open-profile')
869
+ }
870
+
871
+ /**
872
+ * Request login (opens login dialog)
873
+ */
874
+ async requestLogin(): Promise<boolean> {
875
+ return this.send<boolean>('account:request-login')
876
+ }
877
+
878
+ /**
879
+ * Logout current user
880
+ */
881
+ async logout(): Promise<void> {
882
+ this.clearCache()
883
+ await this.send('account:logout')
884
+ }
885
+ }
886
+
887
+ /**
888
+ * Singleton instance for easy import
889
+ */
890
+ export const accountSDK = new AccountSDK()
891
+
892
+ // Re-export types and enums
893
+ export {
894
+ BillingCycle,
895
+ SubscriptionPlan,
896
+ SubscriptionStatus,
897
+ TeamRole,
898
+ VerificationStatus,
899
+ }
900
+
901
+ export type {
902
+ AccountInfo,
903
+ AccountSDKConfig,
904
+ DeviceSession,
905
+ FeatureFlag,
906
+ PlanComparisonItem,
907
+ PlanQuota,
908
+ QuotaCheckResult,
909
+ SocialConnection,
910
+ Subscription,
911
+ Team,
912
+ UpgradeOption,
913
+ UsageStats,
914
+ UserProfile,
915
+ }