@undefineds.co/xpod 0.1.6 → 0.2.0-preview.2

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 (307) hide show
  1. package/README.md +141 -2
  2. package/config/cli.json +9 -71
  3. package/config/cloud.json +34 -7
  4. package/config/local.json +6 -2
  5. package/config/resolver.json +11 -49
  6. package/config/runtime-open.json +22 -0
  7. package/config/xpod.base.json +32 -0
  8. package/config/xpod.cluster.json +2 -44
  9. package/config/xpod.json +5 -2
  10. package/dist/api/ApiServer.js +1 -1
  11. package/dist/api/ApiServer.js.map +1 -1
  12. package/dist/api/auth/AuthContext.d.ts +12 -1
  13. package/dist/api/auth/AuthContext.js +18 -1
  14. package/dist/api/auth/AuthContext.js.map +1 -1
  15. package/dist/api/auth/ClientCredentialsAuthenticator.d.ts +0 -1
  16. package/dist/api/auth/ClientCredentialsAuthenticator.js.map +1 -1
  17. package/dist/api/auth/ServiceTokenAuthenticator.d.ts +18 -0
  18. package/dist/api/auth/ServiceTokenAuthenticator.js +50 -0
  19. package/dist/api/auth/ServiceTokenAuthenticator.js.map +1 -0
  20. package/dist/api/auth/index.d.ts +1 -0
  21. package/dist/api/auth/index.js +1 -0
  22. package/dist/api/auth/index.js.map +1 -1
  23. package/dist/api/chatkit/ai-provider.d.ts +0 -10
  24. package/dist/api/chatkit/ai-provider.js +11 -120
  25. package/dist/api/chatkit/ai-provider.js.map +1 -1
  26. package/dist/api/chatkit/default-agent.js +11 -8
  27. package/dist/api/chatkit/default-agent.js.map +1 -1
  28. package/dist/api/chatkit/pod-store.js +19 -3
  29. package/dist/api/chatkit/pod-store.js.map +1 -1
  30. package/dist/api/chatkit/schema.d.ts +9 -3
  31. package/dist/api/chatkit/schema.js +14 -6
  32. package/dist/api/chatkit/schema.js.map +1 -1
  33. package/dist/api/container/business-token.d.ts +9 -0
  34. package/dist/api/container/business-token.js +32 -0
  35. package/dist/api/container/business-token.js.map +1 -0
  36. package/dist/api/container/cloud.js +36 -12
  37. package/dist/api/container/cloud.js.map +1 -1
  38. package/dist/api/container/common.js +12 -5
  39. package/dist/api/container/common.js.map +1 -1
  40. package/dist/api/container/index.js +94 -14
  41. package/dist/api/container/index.js.map +1 -1
  42. package/dist/api/container/local.js +2 -1
  43. package/dist/api/container/local.js.map +1 -1
  44. package/dist/api/container/routes.js +81 -15
  45. package/dist/api/container/routes.js.map +1 -1
  46. package/dist/api/container/types.d.ts +8 -6
  47. package/dist/api/container/types.js.map +1 -1
  48. package/dist/api/handlers/AdminHandler.js +9 -9
  49. package/dist/api/handlers/AdminHandler.js.map +1 -1
  50. package/dist/api/handlers/ApiKeyHandler.js +0 -6
  51. package/dist/api/handlers/ApiKeyHandler.js.map +1 -1
  52. package/dist/api/handlers/EdgeNodeSignalHandler.d.ts +17 -0
  53. package/dist/api/handlers/EdgeNodeSignalHandler.js +171 -0
  54. package/dist/api/handlers/EdgeNodeSignalHandler.js.map +1 -0
  55. package/dist/api/handlers/PodManagementHandler.d.ts +5 -4
  56. package/dist/api/handlers/PodManagementHandler.js +11 -10
  57. package/dist/api/handlers/PodManagementHandler.js.map +1 -1
  58. package/dist/api/handlers/ProvisionHandler.d.ts +42 -0
  59. package/dist/api/handlers/ProvisionHandler.js +161 -0
  60. package/dist/api/handlers/ProvisionHandler.js.map +1 -0
  61. package/dist/api/handlers/QuotaHandler.d.ts +7 -7
  62. package/dist/api/handlers/QuotaHandler.js +143 -73
  63. package/dist/api/handlers/QuotaHandler.js.map +1 -1
  64. package/dist/api/handlers/SubdomainClientHandler.js +2 -2
  65. package/dist/api/handlers/SubdomainClientHandler.js.map +1 -1
  66. package/dist/api/handlers/SubdomainHandler.js +13 -8
  67. package/dist/api/handlers/SubdomainHandler.js.map +1 -1
  68. package/dist/api/handlers/UsageHandler.d.ts +14 -0
  69. package/dist/api/handlers/UsageHandler.js +123 -0
  70. package/dist/api/handlers/UsageHandler.js.map +1 -0
  71. package/dist/api/handlers/index.d.ts +3 -1
  72. package/dist/api/handlers/index.js +3 -1
  73. package/dist/api/handlers/index.js.map +1 -1
  74. package/dist/api/main.js +18 -0
  75. package/dist/api/main.js.map +1 -1
  76. package/dist/api/middleware/OpenAuthMiddleware.d.ts +12 -0
  77. package/dist/api/middleware/OpenAuthMiddleware.js +27 -0
  78. package/dist/api/middleware/OpenAuthMiddleware.js.map +1 -0
  79. package/dist/api/runtime.d.ts +15 -0
  80. package/dist/api/runtime.js +104 -0
  81. package/dist/api/runtime.js.map +1 -0
  82. package/dist/api/service/VercelChatService.d.ts +16 -7
  83. package/dist/api/service/VercelChatService.js +98 -178
  84. package/dist/api/service/VercelChatService.js.map +1 -1
  85. package/dist/api/store/DrizzleClientCredentialsStore.d.ts +6 -11
  86. package/dist/api/store/DrizzleClientCredentialsStore.js +9 -39
  87. package/dist/api/store/DrizzleClientCredentialsStore.js.map +1 -1
  88. package/dist/authorization/AuthModeSelector.d.ts +10 -0
  89. package/dist/authorization/AuthModeSelector.js +27 -0
  90. package/dist/authorization/AuthModeSelector.js.map +1 -0
  91. package/dist/authorization/AuthModeSelector.jsonld +81 -0
  92. package/dist/cli/commands/account.d.ts +6 -0
  93. package/dist/cli/commands/account.js +119 -0
  94. package/dist/cli/commands/account.js.map +1 -0
  95. package/dist/cli/commands/auth.js +20 -29
  96. package/dist/cli/commands/auth.js.map +1 -1
  97. package/dist/cli/commands/backup.d.ts +15 -0
  98. package/dist/cli/commands/backup.js +286 -0
  99. package/dist/cli/commands/backup.js.map +1 -0
  100. package/dist/cli/commands/config.d.ts +34 -3
  101. package/dist/cli/commands/config.js +195 -258
  102. package/dist/cli/commands/config.js.map +1 -1
  103. package/dist/cli/commands/doctor.d.ts +6 -0
  104. package/dist/cli/commands/doctor.js +94 -0
  105. package/dist/cli/commands/doctor.js.map +1 -0
  106. package/dist/cli/commands/pod.d.ts +6 -0
  107. package/dist/cli/commands/pod.js +124 -0
  108. package/dist/cli/commands/pod.js.map +1 -0
  109. package/dist/cli/commands/start.js +28 -5
  110. package/dist/cli/commands/start.js.map +1 -1
  111. package/dist/cli/index.js +9 -0
  112. package/dist/cli/index.js.map +1 -1
  113. package/dist/cli/lib/credentials-store.d.ts +17 -0
  114. package/dist/cli/lib/credentials-store.js +73 -0
  115. package/dist/cli/lib/credentials-store.js.map +1 -0
  116. package/dist/cli/lib/css-account.d.ts +17 -0
  117. package/dist/cli/lib/css-account.js +56 -0
  118. package/dist/cli/lib/css-account.js.map +1 -1
  119. package/dist/cli/lib/pod-thread-store.d.ts +57 -0
  120. package/dist/cli/lib/pod-thread-store.js +310 -0
  121. package/dist/cli/lib/pod-thread-store.js.map +1 -0
  122. package/dist/cli/lib/solid-auth.d.ts +20 -0
  123. package/dist/cli/lib/solid-auth.js +70 -0
  124. package/dist/cli/lib/solid-auth.js.map +1 -0
  125. package/dist/components/components.jsonld +5 -8
  126. package/dist/components/context.jsonld +114 -244
  127. package/dist/edge/EdgeNodeAgent.js +2 -2
  128. package/dist/edge/EdgeNodeAgent.js.map +1 -1
  129. package/dist/edge/EdgeNodeDnsCoordinator.d.ts +1 -7
  130. package/dist/edge/EdgeNodeDnsCoordinator.js +31 -41
  131. package/dist/edge/EdgeNodeDnsCoordinator.js.map +1 -1
  132. package/dist/edge/EdgeNodeDnsCoordinator.jsonld +1 -27
  133. package/dist/edge/EdgeNodeModeDetector.d.ts +1 -1
  134. package/dist/edge/EdgeNodeModeDetector.js +9 -11
  135. package/dist/edge/EdgeNodeModeDetector.js.map +1 -1
  136. package/dist/http/ClusterIngressRouter.js +3 -3
  137. package/dist/http/ClusterIngressRouter.js.map +1 -1
  138. package/dist/http/ClusterWebSocketConfigurator.js +2 -2
  139. package/dist/http/ClusterWebSocketConfigurator.js.map +1 -1
  140. package/dist/http/PodRoutingHttpHandler.js +2 -2
  141. package/dist/http/PodRoutingHttpHandler.js.map +1 -1
  142. package/dist/http/cluster/PodMigrationHttpHandler.d.ts +1 -1
  143. package/dist/http/cluster/PodMigrationHttpHandler.js +1 -1
  144. package/dist/http/cluster/PodMigrationHttpHandler.js.map +1 -1
  145. package/dist/identity/drizzle/EdgeNodeRepository.d.ts +37 -4
  146. package/dist/identity/drizzle/EdgeNodeRepository.js +120 -128
  147. package/dist/identity/drizzle/EdgeNodeRepository.js.map +1 -1
  148. package/dist/identity/drizzle/ServiceTokenRepository.d.ts +52 -0
  149. package/dist/identity/drizzle/ServiceTokenRepository.js +143 -0
  150. package/dist/identity/drizzle/ServiceTokenRepository.js.map +1 -0
  151. package/dist/identity/drizzle/db.d.ts +9 -0
  152. package/dist/identity/drizzle/db.js +208 -1
  153. package/dist/identity/drizzle/db.js.map +1 -1
  154. package/dist/identity/drizzle/schema.pg.d.ts +5 -0
  155. package/dist/identity/drizzle/schema.pg.js +49 -20
  156. package/dist/identity/drizzle/schema.pg.js.map +1 -1
  157. package/dist/identity/drizzle/schema.sqlite.d.ts +332 -57
  158. package/dist/identity/drizzle/schema.sqlite.js +48 -18
  159. package/dist/identity/drizzle/schema.sqlite.js.map +1 -1
  160. package/dist/identity/oidc/AutoDetectIdentityProviderHandler.js +6 -4
  161. package/dist/identity/oidc/AutoDetectIdentityProviderHandler.js.map +1 -1
  162. package/dist/index.d.ts +6 -9
  163. package/dist/index.js +12 -14
  164. package/dist/index.js.map +1 -1
  165. package/dist/main.js +25 -8
  166. package/dist/main.js.map +1 -1
  167. package/dist/provision/ProvisionCodeCodec.d.ts +39 -0
  168. package/dist/provision/ProvisionCodeCodec.js +65 -0
  169. package/dist/provision/ProvisionCodeCodec.js.map +1 -0
  170. package/dist/provision/ProvisionCodeCodec.jsonld +47 -0
  171. package/dist/provision/ProvisionPodCreator.d.ts +20 -0
  172. package/dist/provision/ProvisionPodCreator.js +84 -0
  173. package/dist/provision/ProvisionPodCreator.js.map +1 -0
  174. package/dist/provision/ProvisionPodCreator.jsonld +118 -0
  175. package/dist/quota/DrizzleQuotaService.d.ts +17 -3
  176. package/dist/quota/DrizzleQuotaService.js +108 -8
  177. package/dist/quota/DrizzleQuotaService.js.map +1 -1
  178. package/dist/quota/DrizzleQuotaService.jsonld +33 -22
  179. package/dist/quota/NoopQuotaService.d.ts +7 -1
  180. package/dist/quota/NoopQuotaService.js +12 -0
  181. package/dist/quota/NoopQuotaService.js.map +1 -1
  182. package/dist/quota/NoopQuotaService.jsonld +24 -0
  183. package/dist/quota/QuotaService.d.ts +17 -0
  184. package/dist/quota/QuotaService.js +5 -0
  185. package/dist/quota/QuotaService.js.map +1 -1
  186. package/dist/quota/QuotaService.jsonld +50 -0
  187. package/dist/runtime/Proxy.d.ts +22 -4
  188. package/dist/runtime/Proxy.js +154 -35
  189. package/dist/runtime/Proxy.js.map +1 -1
  190. package/dist/runtime/XpodRuntime.d.ts +49 -0
  191. package/dist/runtime/XpodRuntime.js +374 -0
  192. package/dist/runtime/XpodRuntime.js.map +1 -0
  193. package/dist/runtime/env-utils.d.ts +2 -0
  194. package/dist/runtime/env-utils.js +55 -0
  195. package/dist/runtime/env-utils.js.map +1 -0
  196. package/dist/runtime/index.d.ts +4 -0
  197. package/dist/runtime/index.js +8 -1
  198. package/dist/runtime/index.js.map +1 -1
  199. package/dist/runtime/socket-fetch.d.ts +1 -0
  200. package/dist/runtime/socket-fetch.js +72 -0
  201. package/dist/runtime/socket-fetch.js.map +1 -0
  202. package/dist/runtime/socket-http.d.ts +1 -0
  203. package/dist/runtime/socket-http.js +142 -0
  204. package/dist/runtime/socket-http.js.map +1 -0
  205. package/dist/runtime/socket-utils.d.ts +2 -0
  206. package/dist/runtime/socket-utils.js +34 -0
  207. package/dist/runtime/socket-utils.js.map +1 -0
  208. package/dist/service/{EdgeNodeHeartbeatService.d.ts → EdgeNodeSignalClient.d.ts} +3 -3
  209. package/dist/service/{EdgeNodeHeartbeatService.js → EdgeNodeSignalClient.js} +4 -4
  210. package/dist/service/EdgeNodeSignalClient.js.map +1 -0
  211. package/dist/service/PodMigrationService.d.ts +1 -2
  212. package/dist/service/PodMigrationService.js +1 -2
  213. package/dist/service/PodMigrationService.js.map +1 -1
  214. package/dist/storage/SparqlUpdateResourceStore.js +1 -1
  215. package/dist/storage/SparqlUpdateResourceStore.js.map +1 -1
  216. package/dist/storage/accessors/MinioDataAccessor.d.ts +6 -0
  217. package/dist/storage/accessors/MinioDataAccessor.js +10 -0
  218. package/dist/storage/accessors/MinioDataAccessor.js.map +1 -1
  219. package/dist/storage/accessors/MinioDataAccessor.jsonld +4 -0
  220. package/dist/storage/accessors/MixDataAccessor.d.ts +2 -1
  221. package/dist/storage/accessors/MixDataAccessor.js +12 -1
  222. package/dist/storage/accessors/MixDataAccessor.js.map +1 -1
  223. package/dist/storage/accessors/MixDataAccessor.jsonld +19 -0
  224. package/dist/storage/locking/UrlAwareRedisLocker.d.ts +18 -0
  225. package/dist/storage/locking/UrlAwareRedisLocker.js +60 -0
  226. package/dist/storage/locking/UrlAwareRedisLocker.js.map +1 -0
  227. package/dist/storage/locking/UrlAwareRedisLocker.jsonld +123 -0
  228. package/dist/storage/quota/UsageRepository.d.ts +41 -8
  229. package/dist/storage/quota/UsageRepository.js +252 -50
  230. package/dist/storage/quota/UsageRepository.js.map +1 -1
  231. package/dist/storage/sparql/ComunicaQuintEngine.d.ts +9 -0
  232. package/dist/storage/sparql/ComunicaQuintEngine.js +50 -9
  233. package/dist/storage/sparql/ComunicaQuintEngine.js.map +1 -1
  234. package/dist/storage/sparql/QueryOptimizer.js +13 -1
  235. package/dist/storage/sparql/QueryOptimizer.js.map +1 -1
  236. package/dist/storage/sparql/QuintQuerySource.d.ts +14 -0
  237. package/dist/storage/sparql/QuintQuerySource.js +152 -1
  238. package/dist/storage/sparql/QuintQuerySource.js.map +1 -1
  239. package/dist/storage/sparql/SubgraphQueryEngine.d.ts +1 -0
  240. package/dist/storage/sparql/SubgraphQueryEngine.js +6 -2
  241. package/dist/storage/sparql/SubgraphQueryEngine.js.map +1 -1
  242. package/dist/storage/sparql/SubgraphQueryEngine.jsonld +4 -0
  243. package/dist/subdomain/SubdomainClient.d.ts +3 -3
  244. package/dist/subdomain/SubdomainClient.js +1 -1
  245. package/dist/subdomain/SubdomainClient.js.map +1 -1
  246. package/dist/subdomain/SubdomainService.d.ts +15 -16
  247. package/dist/subdomain/SubdomainService.js +80 -54
  248. package/dist/subdomain/SubdomainService.js.map +1 -1
  249. package/dist/subdomain/SubdomainService.jsonld +22 -26
  250. package/dist/supervisor/Supervisor.d.ts +7 -2
  251. package/dist/supervisor/Supervisor.js +33 -1
  252. package/dist/supervisor/Supervisor.js.map +1 -1
  253. package/dist/test-utils/index.d.ts +4 -0
  254. package/dist/test-utils/index.js +8 -0
  255. package/dist/test-utils/index.js.map +1 -0
  256. package/dist/test-utils/no-auth-xpod.d.ts +11 -0
  257. package/dist/test-utils/no-auth-xpod.js +25 -0
  258. package/dist/test-utils/no-auth-xpod.js.map +1 -0
  259. package/dist/test-utils/seed-pod.d.ts +5 -0
  260. package/dist/test-utils/seed-pod.js +61 -0
  261. package/dist/test-utils/seed-pod.js.map +1 -0
  262. package/package.json +23 -5
  263. package/templates/identity/account/create-pod.html.ejs +110 -0
  264. package/templates/main.html.ejs +10 -0
  265. package/dist/api/handlers/DevHandler.d.ts +0 -18
  266. package/dist/api/handlers/DevHandler.js +0 -276
  267. package/dist/api/handlers/DevHandler.js.map +0 -1
  268. package/dist/api/handlers/SignalHandler.d.ts +0 -13
  269. package/dist/api/handlers/SignalHandler.js +0 -122
  270. package/dist/api/handlers/SignalHandler.js.map +0 -1
  271. package/dist/gateway/Proxy.d.ts +0 -24
  272. package/dist/gateway/Proxy.js +0 -209
  273. package/dist/gateway/Proxy.js.map +0 -1
  274. package/dist/gateway/Supervisor.d.ts +0 -2
  275. package/dist/gateway/Supervisor.js +0 -7
  276. package/dist/gateway/Supervisor.js.map +0 -1
  277. package/dist/gateway/port-finder.d.ts +0 -4
  278. package/dist/gateway/port-finder.js +0 -15
  279. package/dist/gateway/port-finder.js.map +0 -1
  280. package/dist/gateway/types.d.ts +0 -1
  281. package/dist/gateway/types.js +0 -3
  282. package/dist/gateway/types.js.map +0 -1
  283. package/dist/http/SignalInterceptHttpHandler.d.ts +0 -24
  284. package/dist/http/SignalInterceptHttpHandler.js +0 -47
  285. package/dist/http/SignalInterceptHttpHandler.js.map +0 -1
  286. package/dist/http/SignalInterceptHttpHandler.jsonld +0 -103
  287. package/dist/http/admin/EdgeNodeSignalHttpHandler.d.ts +0 -71
  288. package/dist/http/admin/EdgeNodeSignalHttpHandler.js +0 -674
  289. package/dist/http/admin/EdgeNodeSignalHttpHandler.js.map +0 -1
  290. package/dist/http/admin/EdgeNodeSignalHttpHandler.jsonld +0 -406
  291. package/dist/http/cluster/PodMigrationHttpHandler.jsonld +0 -169
  292. package/dist/quota/DefaultQuotaService.d.ts +0 -16
  293. package/dist/quota/DefaultQuotaService.js +0 -37
  294. package/dist/quota/DefaultQuotaService.js.map +0 -1
  295. package/dist/quota/DefaultQuotaService.jsonld +0 -85
  296. package/dist/service/EdgeNodeHeartbeatService.js.map +0 -1
  297. package/dist/service/PodMigrationService.jsonld +0 -76
  298. package/dist/storage/MigratableDataAccessor.d.ts +0 -63
  299. package/dist/storage/MigratableDataAccessor.js +0 -11
  300. package/dist/storage/MigratableDataAccessor.js.map +0 -1
  301. package/dist/storage/MigratableDataAccessor.jsonld +0 -60
  302. package/dist/storage/accessors/TieredMinioDataAccessor.d.ts +0 -150
  303. package/dist/storage/accessors/TieredMinioDataAccessor.js +0 -582
  304. package/dist/storage/accessors/TieredMinioDataAccessor.js.map +0 -1
  305. package/dist/storage/accessors/TieredMinioDataAccessor.jsonld +0 -333
  306. package/static/app/assets/index.css +0 -1
  307. package/static/app/assets/main.js +0 -11
@@ -107,6 +107,8 @@ exports.Thread = (0, drizzle_solid_1.podTable)('Thread', {
107
107
  chatId: (0, drizzle_solid_1.uri)('chatId').predicate(vocab_1.SIOC.has_parent).reference(exports.Chat),
108
108
  title: (0, drizzle_solid_1.string)('title'),
109
109
  status: (0, drizzle_solid_1.string)('status'),
110
+ /** 工作目录路径,可变(运行时可切换) */
111
+ workspace: (0, drizzle_solid_1.string)('workspace'),
110
112
  /**
111
113
  * JSON string for extended metadata (e.g., xpod runtime hints).
112
114
  * Note: drizzle-solid stores this as an RDF literal.
@@ -130,24 +132,30 @@ exports.Thread = (0, drizzle_solid_1.podTable)('Thread', {
130
132
  * 放在 Chat 下,通过 threadId 关联到 Thread。
131
133
  * 对应 ChatKit 的 ThreadItem 概念。
132
134
  *
133
- * 存储位置: /.data/chat/{chatId}/{id}.ttl#{id}
135
+ * 存储位置: /.data/chat/{chatId}/{yyyy}/{MM}/{dd}/messages.ttl#{id}
136
+ * 按日期分组存储,避免独立文件导致的 SPARQL OPTIONAL 查询失败问题
134
137
  */
135
138
  exports.Message = (0, drizzle_solid_1.podTable)('Message', {
136
139
  id: (0, drizzle_solid_1.string)('id').primaryKey(),
137
- // chatId 用于路径构建
138
- chatId: (0, drizzle_solid_1.string)('chatId'),
139
- // threadId 关联到 Thread (ChatKit thread) - 使用简单字符串便于查询
140
- threadId: (0, drizzle_solid_1.string)('threadId'),
140
+ // chatId 引用 Chat,同时用于路径构建(insert 时传入 bare ID)
141
+ chatId: (0, drizzle_solid_1.uri)('chatId').reference(exports.Chat),
142
+ // threadId 关联到 Thread,表达 RDF 关系
143
+ threadId: (0, drizzle_solid_1.uri)('threadId').predicate(vocab_1.SIOC.has_container).reference(exports.Thread),
141
144
  maker: (0, drizzle_solid_1.uri)('maker').predicate(vocab_1.FOAF.maker),
142
145
  role: (0, drizzle_solid_1.string)('role'),
143
146
  content: (0, drizzle_solid_1.string)('content').predicate(vocab_1.SIOC.content),
144
147
  status: (0, drizzle_solid_1.string)('status'),
145
148
  createdAt: (0, drizzle_solid_1.datetime)('createdAt'),
149
+ // 工具调用字段(方便 SPARQL 查询和索引)
150
+ toolName: (0, drizzle_solid_1.string)('toolName'),
151
+ toolCallId: (0, drizzle_solid_1.string)('toolCallId'),
152
+ // 详细信息(JSON 字符串,存储复杂数据)
153
+ metadata: (0, drizzle_solid_1.string)('metadata'),
146
154
  }, {
147
155
  base: '/.data/chat/',
148
156
  type: vocab_1.Meeting.Message,
149
157
  namespace: vocab_2.UDFS_NAMESPACE,
150
- subjectTemplate: '{chatId}/{id}.ttl#{id}',
158
+ subjectTemplate: '{chatId}/{yyyy}/{MM}/{dd}/messages.ttl#{id}',
151
159
  sparqlEndpoint: '/.data/chat/-/sparql',
152
160
  });
153
161
  exports.ChatStatus = {
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/api/chatkit/schema.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ChatKit Pod Schema\n *\n * 数据模型对齐:\n * - Chat (meeting:LongChat) - 对话容器(微信中间栏)\n * - Thread (sioc:Thread) - ChatKit thread,作为 Chat 的 fragment\n * - Message (meeting:Message) - ChatKit item,放在 Chat 下\n *\n * 存储结构:\n * /.data/chat/{chatId}/\n * index.ttl\n * #this # Chat 元数据\n * #{threadId} # Thread (fragment, ChatKit thread)\n * {yyyy}/{MM}/{dd}/messages.ttl\n * #{msgId} # Message (ChatKit item)\n *\n * 映射关系:\n * - 微信中间栏 Chat 列表 → Chat\n * - ChatKit Thread → Thread (Chat 的 fragment)\n * - ChatKit ThreadItem → Message\n *\n * 注意:Agent 配置(model, systemPrompt)不在 Chat 中,应该在单独的 Agent 表中\n *\n * RDF 示例:\n * ```turtle\n * # /.data/chat/workspace-1/index.ttl\n * @prefix meeting: <http://www.w3.org/ns/pim/meeting#> .\n * @prefix sioc: <http://rdfs.org/sioc/ns#> .\n * @prefix dc: <http://purl.org/dc/terms/> .\n * @prefix foaf: <http://xmlns.com/foaf/0.1/> .\n * @prefix udfs: <https://undefineds.co/ns#> .\n *\n * <#this> a meeting:LongChat ;\n * dc:title \"工作区\" ;\n * dc:author <https://user.pod/profile/card#me> ;\n * udfs:status \"active\" .\n *\n * <#thread-1> a sioc:Thread ;\n * sioc:has_parent <#this> ;\n * dc:title \"关于代码重构的讨论\" ;\n * udfs:status \"active\" ;\n * dc:created \"2024-01-15T10:00:00Z\"^^xsd:dateTime .\n *\n * # /.data/chat/workspace-1/2024/01/15/messages.ttl\n * <#msg-1> a meeting:Message ;\n * sioc:has_container <../../../index.ttl#thread-1> ;\n * foaf:maker <https://user.pod/profile/card#me> ;\n * udfs:role \"user\" ;\n * sioc:content \"Hello\" ;\n * dc:created \"2024-01-15T10:00:00Z\"^^xsd:dateTime .\n *\n * <#msg-2> a meeting:Message ;\n * sioc:has_container <../../../index.ttl#thread-1> ;\n * foaf:maker </.data/agents/claude.ttl#this> ;\n * udfs:role \"assistant\" ;\n * sioc:content \"Hi there!\" ;\n * udfs:status \"completed\" ;\n * dc:created \"2024-01-15T10:00:01Z\"^^xsd:dateTime .\n * ```\n */\n\nimport { podTable, string, datetime, uri } from 'drizzle-solid';\nimport { Meeting, SIOC, FOAF } from '../../vocab';\nimport { UDFS_NAMESPACE } from '../../vocab';\n\n// ============================================================================\n// Chat Schema (meeting:LongChat) - 对话容器\n// ============================================================================\n\n/**\n * Chat - 对话容器\n *\n * 对应微信中间栏的 Chat 列表,可以是群聊或私聊\n *\n * 注意:Agent 配置(model, systemPrompt)不在这里,应该在单独的 Agent 表中\n *\n * 存储位置: /.data/chat/{chatId}/index.ttl#this\n */\nexport const Chat = podTable(\n 'Chat',\n {\n id: string('id').primaryKey(),\n title: string('title'),\n author: uri('author'),\n participants: uri('participants').array(),\n status: string('status'),\n createdAt: datetime('createdAt'),\n updatedAt: datetime('updatedAt'),\n },\n {\n base: '/.data/chat/',\n type: Meeting.LongChat,\n namespace: UDFS_NAMESPACE,\n subjectTemplate: '{id}/index.ttl#this',\n sparqlEndpoint: '/.data/chat/-/sparql',\n },\n);\n\n// ============================================================================\n// Thread Schema (sioc:Thread) - ChatKit thread\n// ============================================================================\n\n/**\n * Thread - ChatKit 的 thread(对话线程)\n *\n * 作为 Chat 的 fragment,与 Chat 元数据存储在同一文件。\n * 对应 ChatKit 的 Thread 概念,是一次完整的对话。\n *\n * 存储位置: /.data/chat/{chatId}/index.ttl#{id}\n */\nexport const Thread = podTable(\n 'Thread',\n {\n id: string('id').primaryKey(),\n chatId: uri('chatId').predicate(SIOC.has_parent).reference(Chat),\n title: string('title'),\n status: string('status'),\n /**\n * JSON string for extended metadata (e.g., xpod runtime hints).\n * Note: drizzle-solid stores this as an RDF literal.\n */\n metadata: string('metadata'),\n createdAt: datetime('createdAt'),\n updatedAt: datetime('updatedAt'),\n },\n {\n base: '/.data/chat/',\n type: SIOC.Thread,\n namespace: UDFS_NAMESPACE,\n subjectTemplate: '{chatId}/index.ttl#{id}',\n sparqlEndpoint: '/.data/chat/-/sparql',\n },\n);\n\n// ============================================================================\n// Message Schema (meeting:Message) - ChatKit item\n// ============================================================================\n\n/**\n * Message - ChatKit 的 ThreadItem(单条消息)\n *\n * 放在 Chat 下,通过 threadId 关联到 Thread。\n * 对应 ChatKit 的 ThreadItem 概念。\n *\n * 存储位置: /.data/chat/{chatId}/{id}.ttl#{id}\n */\nexport const Message = podTable(\n 'Message',\n {\n id: string('id').primaryKey(),\n // chatId 用于路径构建\n chatId: string('chatId'),\n // threadId 关联到 Thread (ChatKit thread) - 使用简单字符串便于查询\n threadId: string('threadId'),\n maker: uri('maker').predicate(FOAF.maker),\n role: string('role'),\n content: string('content').predicate(SIOC.content),\n status: string('status'),\n createdAt: datetime('createdAt'),\n },\n {\n base: '/.data/chat/',\n type: Meeting.Message,\n namespace: UDFS_NAMESPACE,\n subjectTemplate: '{chatId}/{id}.ttl#{id}',\n sparqlEndpoint: '/.data/chat/-/sparql',\n },\n);\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type ChatRecord = typeof Chat.$inferSelect;\nexport type ThreadRecord = typeof Thread.$inferSelect;\nexport type MessageRecord = typeof Message.$inferSelect;\n\nexport const ChatStatus = {\n ACTIVE: 'active',\n ARCHIVED: 'archived',\n DELETED: 'deleted',\n} as const;\n\nexport type ChatStatusType = (typeof ChatStatus)[keyof typeof ChatStatus];\n\nexport const ThreadStatus = {\n ACTIVE: 'active',\n LOCKED: 'locked',\n CLOSED: 'closed',\n} as const;\n\nexport type ThreadStatusType = (typeof ThreadStatus)[keyof typeof ThreadStatus];\n\nexport const MessageRole = {\n USER: 'user',\n ASSISTANT: 'assistant',\n SYSTEM: 'system',\n} as const;\n\nexport type MessageRoleType = (typeof MessageRole)[keyof typeof MessageRole];\n\nexport const MessageStatus = {\n IN_PROGRESS: 'in_progress',\n COMPLETED: 'completed',\n INCOMPLETE: 'incomplete',\n} as const;\n\nexport type MessageStatusType = (typeof MessageStatus)[keyof typeof MessageStatus];\n"]}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/api/chatkit/schema.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ChatKit Pod Schema\n *\n * 数据模型对齐:\n * - Chat (meeting:LongChat) - 对话容器(微信中间栏)\n * - Thread (sioc:Thread) - ChatKit thread,作为 Chat 的 fragment\n * - Message (meeting:Message) - ChatKit item,放在 Chat 下\n *\n * 存储结构:\n * /.data/chat/{chatId}/\n * index.ttl\n * #this # Chat 元数据\n * #{threadId} # Thread (fragment, ChatKit thread)\n * {yyyy}/{MM}/{dd}/messages.ttl\n * #{msgId} # Message (ChatKit item)\n *\n * 映射关系:\n * - 微信中间栏 Chat 列表 → Chat\n * - ChatKit Thread → Thread (Chat 的 fragment)\n * - ChatKit ThreadItem → Message\n *\n * 注意:Agent 配置(model, systemPrompt)不在 Chat 中,应该在单独的 Agent 表中\n *\n * RDF 示例:\n * ```turtle\n * # /.data/chat/workspace-1/index.ttl\n * @prefix meeting: <http://www.w3.org/ns/pim/meeting#> .\n * @prefix sioc: <http://rdfs.org/sioc/ns#> .\n * @prefix dc: <http://purl.org/dc/terms/> .\n * @prefix foaf: <http://xmlns.com/foaf/0.1/> .\n * @prefix udfs: <https://undefineds.co/ns#> .\n *\n * <#this> a meeting:LongChat ;\n * dc:title \"工作区\" ;\n * dc:author <https://user.pod/profile/card#me> ;\n * udfs:status \"active\" .\n *\n * <#thread-1> a sioc:Thread ;\n * sioc:has_parent <#this> ;\n * dc:title \"关于代码重构的讨论\" ;\n * udfs:status \"active\" ;\n * dc:created \"2024-01-15T10:00:00Z\"^^xsd:dateTime .\n *\n * # /.data/chat/workspace-1/2024/01/15/messages.ttl\n * <#msg-1> a meeting:Message ;\n * sioc:has_container <../../../index.ttl#thread-1> ;\n * foaf:maker <https://user.pod/profile/card#me> ;\n * udfs:role \"user\" ;\n * sioc:content \"Hello\" ;\n * dc:created \"2024-01-15T10:00:00Z\"^^xsd:dateTime .\n *\n * <#msg-2> a meeting:Message ;\n * sioc:has_container <../../../index.ttl#thread-1> ;\n * foaf:maker </.data/agents/claude.ttl#this> ;\n * udfs:role \"assistant\" ;\n * sioc:content \"Hi there!\" ;\n * udfs:status \"completed\" ;\n * dc:created \"2024-01-15T10:00:01Z\"^^xsd:dateTime .\n * ```\n */\n\nimport { podTable, string, datetime, uri } from 'drizzle-solid';\nimport { Meeting, SIOC, FOAF } from '../../vocab';\nimport { UDFS_NAMESPACE } from '../../vocab';\n\n// ============================================================================\n// Chat Schema (meeting:LongChat) - 对话容器\n// ============================================================================\n\n/**\n * Chat - 对话容器\n *\n * 对应微信中间栏的 Chat 列表,可以是群聊或私聊\n *\n * 注意:Agent 配置(model, systemPrompt)不在这里,应该在单独的 Agent 表中\n *\n * 存储位置: /.data/chat/{chatId}/index.ttl#this\n */\nexport const Chat = podTable(\n 'Chat',\n {\n id: string('id').primaryKey(),\n title: string('title'),\n author: uri('author'),\n participants: uri('participants').array(),\n status: string('status'),\n createdAt: datetime('createdAt'),\n updatedAt: datetime('updatedAt'),\n },\n {\n base: '/.data/chat/',\n type: Meeting.LongChat,\n namespace: UDFS_NAMESPACE,\n subjectTemplate: '{id}/index.ttl#this',\n sparqlEndpoint: '/.data/chat/-/sparql',\n },\n);\n\n// ============================================================================\n// Thread Schema (sioc:Thread) - ChatKit thread\n// ============================================================================\n\n/**\n * Thread - ChatKit 的 thread(对话线程)\n *\n * 作为 Chat 的 fragment,与 Chat 元数据存储在同一文件。\n * 对应 ChatKit 的 Thread 概念,是一次完整的对话。\n *\n * 存储位置: /.data/chat/{chatId}/index.ttl#{id}\n */\nexport const Thread = podTable(\n 'Thread',\n {\n id: string('id').primaryKey(),\n chatId: uri('chatId').predicate(SIOC.has_parent).reference(Chat),\n title: string('title'),\n status: string('status'),\n /** 工作目录路径,可变(运行时可切换) */\n workspace: string('workspace'),\n /**\n * JSON string for extended metadata (e.g., xpod runtime hints).\n * Note: drizzle-solid stores this as an RDF literal.\n */\n metadata: string('metadata'),\n createdAt: datetime('createdAt'),\n updatedAt: datetime('updatedAt'),\n },\n {\n base: '/.data/chat/',\n type: SIOC.Thread,\n namespace: UDFS_NAMESPACE,\n subjectTemplate: '{chatId}/index.ttl#{id}',\n sparqlEndpoint: '/.data/chat/-/sparql',\n },\n);\n\n// ============================================================================\n// Message Schema (meeting:Message) - ChatKit item\n// ============================================================================\n\n/**\n * Message - ChatKit 的 ThreadItem(单条消息)\n *\n * 放在 Chat 下,通过 threadId 关联到 Thread。\n * 对应 ChatKit 的 ThreadItem 概念。\n *\n * 存储位置: /.data/chat/{chatId}/{yyyy}/{MM}/{dd}/messages.ttl#{id}\n * 按日期分组存储,避免独立文件导致的 SPARQL OPTIONAL 查询失败问题\n */\nexport const Message = podTable(\n 'Message',\n {\n id: string('id').primaryKey(),\n // chatId 引用 Chat,同时用于路径构建(insert 时传入 bare ID)\n chatId: uri('chatId').reference(Chat),\n // threadId 关联到 Thread,表达 RDF 关系\n threadId: uri('threadId').predicate(SIOC.has_container).reference(Thread),\n maker: uri('maker').predicate(FOAF.maker),\n role: string('role'),\n content: string('content').predicate(SIOC.content),\n status: string('status'),\n createdAt: datetime('createdAt'),\n\n // 工具调用字段(方便 SPARQL 查询和索引)\n toolName: string('toolName'),\n toolCallId: string('toolCallId'),\n\n // 详细信息(JSON 字符串,存储复杂数据)\n metadata: string('metadata'),\n },\n {\n base: '/.data/chat/',\n type: Meeting.Message,\n namespace: UDFS_NAMESPACE,\n subjectTemplate: '{chatId}/{yyyy}/{MM}/{dd}/messages.ttl#{id}',\n sparqlEndpoint: '/.data/chat/-/sparql',\n },\n);\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type ChatRecord = typeof Chat.$inferSelect;\nexport type ThreadRecord = typeof Thread.$inferSelect;\nexport type MessageRecord = typeof Message.$inferSelect;\n\nexport const ChatStatus = {\n ACTIVE: 'active',\n ARCHIVED: 'archived',\n DELETED: 'deleted',\n} as const;\n\nexport type ChatStatusType = (typeof ChatStatus)[keyof typeof ChatStatus];\n\nexport const ThreadStatus = {\n ACTIVE: 'active',\n LOCKED: 'locked',\n CLOSED: 'closed',\n} as const;\n\nexport type ThreadStatusType = (typeof ThreadStatus)[keyof typeof ThreadStatus];\n\nexport const MessageRole = {\n USER: 'user',\n ASSISTANT: 'assistant',\n SYSTEM: 'system',\n} as const;\n\nexport type MessageRoleType = (typeof MessageRole)[keyof typeof MessageRole];\n\nexport const MessageStatus = {\n IN_PROGRESS: 'in_progress',\n COMPLETED: 'completed',\n INCOMPLETE: 'incomplete',\n} as const;\n\nexport type MessageStatusType = (typeof MessageStatus)[keyof typeof MessageStatus];\n"]}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Business Token 自动注册
3
+ *
4
+ * 如果配置了 XPOD_BUSINESS_TOKEN 环境变量,
5
+ * 启动时自动注册到 service_token 表,赋予完整的 Business 权限。
6
+ */
7
+ import type { AwilixContainer } from 'awilix';
8
+ import type { ApiContainerCradle } from './types';
9
+ export declare function registerBusinessToken(container: AwilixContainer<ApiContainerCradle>): void;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ /**
3
+ * Business Token 自动注册
4
+ *
5
+ * 如果配置了 XPOD_BUSINESS_TOKEN 环境变量,
6
+ * 启动时自动注册到 service_token 表,赋予完整的 Business 权限。
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.registerBusinessToken = registerBusinessToken;
10
+ const BUSINESS_SCOPES = ['quota:write', 'usage:read', 'account:manage'];
11
+ function registerBusinessToken(container) {
12
+ const token = process.env.XPOD_BUSINESS_TOKEN;
13
+ if (!token) {
14
+ return;
15
+ }
16
+ // Defer registration to avoid blocking startup
17
+ setImmediate(async () => {
18
+ try {
19
+ const repo = container.resolve('serviceTokenRepo');
20
+ await repo.registerToken(token, {
21
+ serviceType: 'business',
22
+ serviceId: 'business-default',
23
+ scopes: BUSINESS_SCOPES,
24
+ });
25
+ console.log('[Business] Service token registered (XPOD_BUSINESS_TOKEN)');
26
+ }
27
+ catch (error) {
28
+ console.error(`[Business] Failed to register service token: ${error}`);
29
+ }
30
+ });
31
+ }
32
+ //# sourceMappingURL=business-token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"business-token.js","sourceRoot":"","sources":["../../../src/api/container/business-token.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAQH,sDAsBC;AAxBD,MAAM,eAAe,GAAG,CAAC,aAAa,EAAE,YAAY,EAAE,gBAAgB,CAAC,CAAC;AAExE,SAAgB,qBAAqB,CACnC,SAA8C;IAE9C,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;IAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;IACT,CAAC;IAED,+CAA+C;IAC/C,YAAY,CAAC,KAAK,IAAI,EAAE;QACtB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,kBAAkB,CAA2B,CAAC;YAC7E,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE;gBAC9B,WAAW,EAAE,UAAU;gBACvB,SAAS,EAAE,kBAAkB;gBAC7B,MAAM,EAAE,eAAe;aACxB,CAAC,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC;QAC3E,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,gDAAgD,KAAK,EAAE,CAAC,CAAC;QACzE,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * Business Token 自动注册\n *\n * 如果配置了 XPOD_BUSINESS_TOKEN 环境变量,\n * 启动时自动注册到 service_token 表,赋予完整的 Business 权限。\n */\n\nimport type { AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle } from './types';\nimport type { ServiceTokenRepository } from '../../identity/drizzle/ServiceTokenRepository';\n\nconst BUSINESS_SCOPES = ['quota:write', 'usage:read', 'account:manage'];\n\nexport function registerBusinessToken(\n container: AwilixContainer<ApiContainerCradle>,\n): void {\n const token = process.env.XPOD_BUSINESS_TOKEN;\n if (!token) {\n return;\n }\n\n // Defer registration to avoid blocking startup\n setImmediate(async () => {\n try {\n const repo = container.resolve('serviceTokenRepo') as ServiceTokenRepository;\n await repo.registerToken(token, {\n serviceType: 'business',\n serviceId: 'business-default',\n scopes: BUSINESS_SCOPES,\n });\n console.log('[Business] Service token registered (XPOD_BUSINESS_TOKEN)');\n } catch (error) {\n console.error(`[Business] Failed to register service token: ${error}`);\n }\n });\n}\n"]}
@@ -11,6 +11,8 @@ const awilix_1 = require("awilix");
11
11
  const TencentDnsProvider_1 = require("../../dns/tencent/TencentDnsProvider");
12
12
  const CloudflareTunnelProvider_1 = require("../../tunnel/CloudflareTunnelProvider");
13
13
  const SubdomainService_1 = require("../../subdomain/SubdomainService");
14
+ const EdgeNodeDnsCoordinator_1 = require("../../edge/EdgeNodeDnsCoordinator");
15
+ const EdgeNodeHealthProbeService_1 = require("../../edge/EdgeNodeHealthProbeService");
14
16
  const WebIdProfileRepository_1 = require("../../identity/drizzle/WebIdProfileRepository");
15
17
  const DdnsRepository_1 = require("../../identity/drizzle/DdnsRepository");
16
18
  const global_logger_factory_1 = require("global-logger-factory");
@@ -37,17 +39,13 @@ function registerCloudServices(container) {
37
39
  }).singleton(),
38
40
  });
39
41
  logger.info('DDNS repository registered');
40
- // 只有配置了子域名功能才注册 DNS/Tunnel 服务
41
- if (!config.subdomain?.enabled) {
42
- logger.info('Subdomain service disabled');
43
- return;
44
- }
45
- const { baseDomain, tencentDnsSecretId, tencentDnsSecretKey, cloudflareAccountId, cloudflareApiToken, } = config.subdomain;
46
- // 检查必要配置
47
- if (!baseDomain) {
48
- logger.warn('Subdomain enabled but missing baseDomain, skipping DNS/Tunnel services');
42
+ // 只有配置了 baseStorageDomain 才注册 DNS/Tunnel 服务
43
+ const baseStorageDomain = config.subdomain?.baseStorageDomain;
44
+ if (!baseStorageDomain) {
45
+ logger.info('Subdomain service disabled (no CSS_BASE_STORAGE_DOMAIN)');
49
46
  return;
50
47
  }
48
+ const { tencentDnsSecretId, tencentDnsSecretKey, cloudflareAccountId, cloudflareApiToken, } = config.subdomain;
51
49
  // DNS Provider (腾讯云或 Cloudflare)
52
50
  if (tencentDnsSecretId && tencentDnsSecretKey) {
53
51
  container.register({
@@ -67,7 +65,7 @@ function registerCloudServices(container) {
67
65
  return new CloudflareTunnelProvider_1.CloudflareTunnelProvider({
68
66
  accountId: cloudflareAccountId,
69
67
  apiToken: cloudflareApiToken,
70
- baseDomain: baseDomain,
68
+ baseDomain: baseStorageDomain,
71
69
  });
72
70
  }).singleton(),
73
71
  });
@@ -78,20 +76,46 @@ function registerCloudServices(container) {
78
76
  const dnsProvider = container.resolve('dnsProvider', { allowUnregistered: true });
79
77
  const tunnelProvider = container.resolve('tunnelProvider', { allowUnregistered: true });
80
78
  if (dnsProvider && tunnelProvider) {
79
+ const nodeRepo = container.resolve('nodeRepo');
81
80
  container.register({
82
81
  subdomainService: (0, awilix_1.asFunction)(() => {
83
82
  return new SubdomainService_1.SubdomainService({
84
- baseDomain: baseDomain,
83
+ baseDomain: baseStorageDomain,
85
84
  dnsProvider: dnsProvider,
86
85
  tunnelProvider: tunnelProvider,
86
+ edgeNodeRepo: nodeRepo,
87
87
  });
88
88
  }).singleton(),
89
89
  });
90
- logger.info(`Subdomain service registered for domain: ${baseDomain}`);
90
+ logger.info(`Subdomain service registered for domain: ${baseStorageDomain}`);
91
91
  }
92
92
  }
93
93
  catch {
94
94
  logger.warn('Subdomain service not registered (missing DNS or Tunnel provider)');
95
95
  }
96
+ // DNS Coordinator (心跳→DNS 同步,需要 dnsProvider)
97
+ const dnsProvider = container.resolve('dnsProvider', { allowUnregistered: true });
98
+ if (dnsProvider) {
99
+ container.register({
100
+ dnsCoordinator: (0, awilix_1.asFunction)(() => {
101
+ return new EdgeNodeDnsCoordinator_1.EdgeNodeDnsCoordinator({
102
+ provider: dnsProvider,
103
+ rootDomain: baseStorageDomain,
104
+ });
105
+ }).singleton(),
106
+ });
107
+ logger.info('DNS coordinator registered');
108
+ }
109
+ // Health Probe Service (心跳时探测节点可达性)
110
+ const nodeRepo = container.resolve('nodeRepo');
111
+ container.register({
112
+ healthProbeService: (0, awilix_1.asFunction)(() => {
113
+ return new EdgeNodeHealthProbeService_1.EdgeNodeHealthProbeService({
114
+ repository: nodeRepo,
115
+ enabled: true,
116
+ });
117
+ }).singleton(),
118
+ });
119
+ logger.info('Health probe service registered');
96
120
  }
97
121
  //# sourceMappingURL=cloud.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloud.js","sourceRoot":"","sources":["../../../src/api/container/cloud.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Cloud 模式服务注册\n *\n * Cloud 模式持有 DNS/Tunnel 密钥,直接操作子域名\n * 提供身份服务 (IdP) 和可选的托管存储 (SP)\n */\n\nimport { asFunction, type AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle, ApiContainerConfig } from './types';\n\nimport { TencentDnsProvider } from '../../dns/tencent/TencentDnsProvider';\nimport { CloudflareTunnelProvider } from '../../tunnel/CloudflareTunnelProvider';\nimport { SubdomainService } from '../../subdomain/SubdomainService';\nimport { WebIdProfileRepository } from '../../identity/drizzle/WebIdProfileRepository';\nimport { DdnsRepository } from '../../identity/drizzle/DdnsRepository';\nimport { getLoggerFor } from 'global-logger-factory';\n\nconst logger = getLoggerFor('CloudServices');\n\n/**\n * 注册 Cloud 模式专属服务\n */\nexport function registerCloudServices(\n container: AwilixContainer<ApiContainerCradle>,\n): void {\n const config = container.resolve('config') as ApiContainerConfig;\n const db = container.resolve('db');\n\n // 获取 baseUrl 用于 WebID Profile\n const baseUrl = process.env.CSS_BASE_URL || `http://localhost:${process.env.CSS_PORT || 3000}`;\n\n // 注册 WebID Profile Repository (始终注册,用于身份服务)\n container.register({\n webIdProfileRepo: asFunction(() => {\n return new WebIdProfileRepository(db, { baseUrl });\n }).singleton(),\n });\n logger.info('WebID Profile repository registered');\n\n // 注册 DDNS Repository (始终注册,用于 DDNS 服务)\n container.register({\n ddnsRepo: asFunction(() => {\n return new DdnsRepository(db);\n }).singleton(),\n });\n logger.info('DDNS repository registered');\n\n // 只有配置了子域名功能才注册 DNS/Tunnel 服务\n if (!config.subdomain?.enabled) {\n logger.info('Subdomain service disabled');\n return;\n }\n\n const {\n baseDomain,\n tencentDnsSecretId,\n tencentDnsSecretKey,\n cloudflareAccountId,\n cloudflareApiToken,\n } = config.subdomain;\n\n // 检查必要配置\n if (!baseDomain) {\n logger.warn('Subdomain enabled but missing baseDomain, skipping DNS/Tunnel services');\n return;\n }\n\n // DNS Provider (腾讯云或 Cloudflare)\n if (tencentDnsSecretId && tencentDnsSecretKey) {\n container.register({\n dnsProvider: asFunction(() => {\n return new TencentDnsProvider({\n tokenId: tencentDnsSecretId,\n token: tencentDnsSecretKey,\n });\n }).singleton(),\n });\n logger.info('Tencent DNS provider registered');\n }\n\n // Tunnel Provider (Cloudflare)\n if (cloudflareAccountId && cloudflareApiToken) {\n container.register({\n tunnelProvider: asFunction(() => {\n return new CloudflareTunnelProvider({\n accountId: cloudflareAccountId,\n apiToken: cloudflareApiToken,\n baseDomain: baseDomain!,\n });\n }).singleton(),\n });\n logger.info('Cloudflare Tunnel provider registered');\n }\n\n // Subdomain Service (需要 DNS 和 Tunnel Provider)\n try {\n const dnsProvider = container.resolve('dnsProvider', { allowUnregistered: true });\n const tunnelProvider = container.resolve('tunnelProvider', { allowUnregistered: true });\n\n if (dnsProvider && tunnelProvider) {\n container.register({\n subdomainService: asFunction(() => {\n return new SubdomainService({\n baseDomain: baseDomain!,\n dnsProvider: dnsProvider as any,\n tunnelProvider: tunnelProvider as any,\n });\n }).singleton(),\n });\n logger.info(`Subdomain service registered for domain: ${baseDomain}`);\n }\n } catch {\n logger.warn('Subdomain service not registered (missing DNS or Tunnel provider)');\n }\n}\n"]}
1
+ {"version":3,"file":"cloud.js","sourceRoot":"","sources":["../../../src/api/container/cloud.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAmBH,sDAkHC;AAnID,mCAA0D;AAG1D,6EAA0E;AAC1E,oFAAiF;AACjF,uEAAoE;AACpE,8EAA2E;AAC3E,sFAAmF;AACnF,0FAAuF;AACvF,0EAAuE;AACvE,iEAAqD;AAErD,MAAM,MAAM,GAAG,IAAA,oCAAY,EAAC,eAAe,CAAC,CAAC;AAE7C;;GAEG;AACH,SAAgB,qBAAqB,CACnC,SAA8C;IAE9C,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAuB,CAAC;IACjE,MAAM,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnC,8BAA8B;IAC9B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,oBAAoB,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,IAAI,EAAE,CAAC;IAE/F,4CAA4C;IAC5C,SAAS,CAAC,QAAQ,CAAC;QACjB,gBAAgB,EAAE,IAAA,mBAAU,EAAC,GAAG,EAAE;YAChC,OAAO,IAAI,+CAAsB,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC,SAAS,EAAE;KACf,CAAC,CAAC;IACH,MAAM,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;IAEnD,uCAAuC;IACvC,SAAS,CAAC,QAAQ,CAAC;QACjB,QAAQ,EAAE,IAAA,mBAAU,EAAC,GAAG,EAAE;YACxB,OAAO,IAAI,+BAAc,CAAC,EAAE,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC,SAAS,EAAE;KACf,CAAC,CAAC;IACH,MAAM,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;IAE1C,4CAA4C;IAC5C,MAAM,iBAAiB,GAAG,MAAM,CAAC,SAAS,EAAE,iBAAiB,CAAC;IAC9D,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,MAAM,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;QACvE,OAAO;IACT,CAAC;IAED,MAAM,EACJ,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,GACnB,GAAG,MAAM,CAAC,SAAU,CAAC;IAEtB,iCAAiC;IACjC,IAAI,kBAAkB,IAAI,mBAAmB,EAAE,CAAC;QAC9C,SAAS,CAAC,QAAQ,CAAC;YACjB,WAAW,EAAE,IAAA,mBAAU,EAAC,GAAG,EAAE;gBAC3B,OAAO,IAAI,uCAAkB,CAAC;oBAC5B,OAAO,EAAE,kBAAkB;oBAC3B,KAAK,EAAE,mBAAmB;iBAC3B,CAAC,CAAC;YACL,CAAC,CAAC,CAAC,SAAS,EAAE;SACf,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;IACjD,CAAC;IAED,+BAA+B;IAC/B,IAAI,mBAAmB,IAAI,kBAAkB,EAAE,CAAC;QAC9C,SAAS,CAAC,QAAQ,CAAC;YACjB,cAAc,EAAE,IAAA,mBAAU,EAAC,GAAG,EAAE;gBAC9B,OAAO,IAAI,mDAAwB,CAAC;oBAClC,SAAS,EAAE,mBAAmB;oBAC9B,QAAQ,EAAE,kBAAkB;oBAC5B,UAAU,EAAE,iBAAiB;iBAC9B,CAAC,CAAC;YACL,CAAC,CAAC,CAAC,SAAS,EAAE;SACf,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;IACvD,CAAC;IAED,+CAA+C;IAC/C,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,SAAS,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC;QAClF,MAAM,cAAc,GAAG,SAAS,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC;QAExF,IAAI,WAAW,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAC/C,SAAS,CAAC,QAAQ,CAAC;gBACjB,gBAAgB,EAAE,IAAA,mBAAU,EAAC,GAAG,EAAE;oBAChC,OAAO,IAAI,mCAAgB,CAAC;wBAC1B,UAAU,EAAE,iBAAiB;wBAC7B,WAAW,EAAE,WAAkB;wBAC/B,cAAc,EAAE,cAAqB;wBACrC,YAAY,EAAE,QAAQ;qBACvB,CAAC,CAAC;gBACL,CAAC,CAAC,CAAC,SAAS,EAAE;aACf,CAAC,CAAC;YACH,MAAM,CAAC,IAAI,CAAC,4CAA4C,iBAAiB,EAAE,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,CAAC,IAAI,CAAC,mEAAmE,CAAC,CAAC;IACnF,CAAC;IAED,6CAA6C;IAC7C,MAAM,WAAW,GAAG,SAAS,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC;IAClF,IAAI,WAAW,EAAE,CAAC;QAChB,SAAS,CAAC,QAAQ,CAAC;YACjB,cAAc,EAAE,IAAA,mBAAU,EAAC,GAAG,EAAE;gBAC9B,OAAO,IAAI,+CAAsB,CAAC;oBAChC,QAAQ,EAAE,WAAkB;oBAC5B,UAAU,EAAE,iBAAiB;iBAC9B,CAAC,CAAC;YACL,CAAC,CAAC,CAAC,SAAS,EAAE;SACf,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;IAC5C,CAAC;IAED,oCAAoC;IACpC,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/C,SAAS,CAAC,QAAQ,CAAC;QACjB,kBAAkB,EAAE,IAAA,mBAAU,EAAC,GAAG,EAAE;YAClC,OAAO,IAAI,uDAA0B,CAAC;gBACpC,UAAU,EAAE,QAAQ;gBACpB,OAAO,EAAE,IAAI;aACd,CAAC,CAAC;QACL,CAAC,CAAC,CAAC,SAAS,EAAE;KACf,CAAC,CAAC;IACH,MAAM,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;AACjD,CAAC","sourcesContent":["/**\n * Cloud 模式服务注册\n *\n * Cloud 模式持有 DNS/Tunnel 密钥,直接操作子域名\n * 提供身份服务 (IdP) 和可选的托管存储 (SP)\n */\n\nimport { asFunction, type AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle, ApiContainerConfig } from './types';\n\nimport { TencentDnsProvider } from '../../dns/tencent/TencentDnsProvider';\nimport { CloudflareTunnelProvider } from '../../tunnel/CloudflareTunnelProvider';\nimport { SubdomainService } from '../../subdomain/SubdomainService';\nimport { EdgeNodeDnsCoordinator } from '../../edge/EdgeNodeDnsCoordinator';\nimport { EdgeNodeHealthProbeService } from '../../edge/EdgeNodeHealthProbeService';\nimport { WebIdProfileRepository } from '../../identity/drizzle/WebIdProfileRepository';\nimport { DdnsRepository } from '../../identity/drizzle/DdnsRepository';\nimport { getLoggerFor } from 'global-logger-factory';\n\nconst logger = getLoggerFor('CloudServices');\n\n/**\n * 注册 Cloud 模式专属服务\n */\nexport function registerCloudServices(\n container: AwilixContainer<ApiContainerCradle>,\n): void {\n const config = container.resolve('config') as ApiContainerConfig;\n const db = container.resolve('db');\n\n // 获取 baseUrl 用于 WebID Profile\n const baseUrl = process.env.CSS_BASE_URL || `http://localhost:${process.env.CSS_PORT || 3000}`;\n\n // 注册 WebID Profile Repository (始终注册,用于身份服务)\n container.register({\n webIdProfileRepo: asFunction(() => {\n return new WebIdProfileRepository(db, { baseUrl });\n }).singleton(),\n });\n logger.info('WebID Profile repository registered');\n\n // 注册 DDNS Repository (始终注册,用于 DDNS 服务)\n container.register({\n ddnsRepo: asFunction(() => {\n return new DdnsRepository(db);\n }).singleton(),\n });\n logger.info('DDNS repository registered');\n\n // 只有配置了 baseStorageDomain 才注册 DNS/Tunnel 服务\n const baseStorageDomain = config.subdomain?.baseStorageDomain;\n if (!baseStorageDomain) {\n logger.info('Subdomain service disabled (no CSS_BASE_STORAGE_DOMAIN)');\n return;\n }\n\n const {\n tencentDnsSecretId,\n tencentDnsSecretKey,\n cloudflareAccountId,\n cloudflareApiToken,\n } = config.subdomain!;\n\n // DNS Provider (腾讯云或 Cloudflare)\n if (tencentDnsSecretId && tencentDnsSecretKey) {\n container.register({\n dnsProvider: asFunction(() => {\n return new TencentDnsProvider({\n tokenId: tencentDnsSecretId,\n token: tencentDnsSecretKey,\n });\n }).singleton(),\n });\n logger.info('Tencent DNS provider registered');\n }\n\n // Tunnel Provider (Cloudflare)\n if (cloudflareAccountId && cloudflareApiToken) {\n container.register({\n tunnelProvider: asFunction(() => {\n return new CloudflareTunnelProvider({\n accountId: cloudflareAccountId,\n apiToken: cloudflareApiToken,\n baseDomain: baseStorageDomain,\n });\n }).singleton(),\n });\n logger.info('Cloudflare Tunnel provider registered');\n }\n\n // Subdomain Service (需要 DNS 和 Tunnel Provider)\n try {\n const dnsProvider = container.resolve('dnsProvider', { allowUnregistered: true });\n const tunnelProvider = container.resolve('tunnelProvider', { allowUnregistered: true });\n\n if (dnsProvider && tunnelProvider) {\n const nodeRepo = container.resolve('nodeRepo');\n container.register({\n subdomainService: asFunction(() => {\n return new SubdomainService({\n baseDomain: baseStorageDomain,\n dnsProvider: dnsProvider as any,\n tunnelProvider: tunnelProvider as any,\n edgeNodeRepo: nodeRepo,\n });\n }).singleton(),\n });\n logger.info(`Subdomain service registered for domain: ${baseStorageDomain}`);\n }\n } catch {\n logger.warn('Subdomain service not registered (missing DNS or Tunnel provider)');\n }\n\n // DNS Coordinator (心跳→DNS 同步,需要 dnsProvider)\n const dnsProvider = container.resolve('dnsProvider', { allowUnregistered: true });\n if (dnsProvider) {\n container.register({\n dnsCoordinator: asFunction(() => {\n return new EdgeNodeDnsCoordinator({\n provider: dnsProvider as any,\n rootDomain: baseStorageDomain,\n });\n }).singleton(),\n });\n logger.info('DNS coordinator registered');\n }\n\n // Health Probe Service (心跳时探测节点可达性)\n const nodeRepo = container.resolve('nodeRepo');\n container.register({\n healthProbeService: asFunction(() => {\n return new EdgeNodeHealthProbeService({\n repository: nodeRepo,\n enabled: true,\n });\n }).singleton(),\n });\n logger.info('Health probe service registered');\n}\n"]}
@@ -9,10 +9,12 @@ exports.registerCommonServices = registerCommonServices;
9
9
  const awilix_1 = require("awilix");
10
10
  const db_1 = require("../../identity/drizzle/db");
11
11
  const EdgeNodeRepository_1 = require("../../identity/drizzle/EdgeNodeRepository");
12
+ const ServiceTokenRepository_1 = require("../../identity/drizzle/ServiceTokenRepository");
12
13
  const DrizzleClientCredentialsStore_1 = require("../store/DrizzleClientCredentialsStore");
13
14
  const SolidTokenAuthenticator_1 = require("../auth/SolidTokenAuthenticator");
14
15
  const ClientCredentialsAuthenticator_1 = require("../auth/ClientCredentialsAuthenticator");
15
16
  const NodeTokenAuthenticator_1 = require("../auth/NodeTokenAuthenticator");
17
+ const ServiceTokenAuthenticator_1 = require("../auth/ServiceTokenAuthenticator");
16
18
  const MultiAuthenticator_1 = require("../auth/MultiAuthenticator");
17
19
  const AuthMiddleware_1 = require("../middleware/AuthMiddleware");
18
20
  const VercelChatService_1 = require("../service/VercelChatService");
@@ -34,12 +36,14 @@ function registerCommonServices(container) {
34
36
  apiKeyStore: (0, awilix_1.asFunction)(({ db, config }) => {
35
37
  return new DrizzleClientCredentialsStore_1.DrizzleClientCredentialsStore({
36
38
  db,
37
- encryptionKey: config.encryptionKey,
38
39
  isSqlite: config.databaseUrl.startsWith('sqlite:'),
39
40
  });
40
41
  }).singleton(),
41
42
  // 认证
42
- authenticator: (0, awilix_1.asFunction)(({ apiKeyStore, nodeRepo, config }) => {
43
+ serviceTokenRepo: (0, awilix_1.asFunction)(({ db }) => {
44
+ return new ServiceTokenRepository_1.ServiceTokenRepository(db);
45
+ }).singleton(),
46
+ authenticator: (0, awilix_1.asFunction)(({ nodeRepo, serviceTokenRepo, config }) => {
43
47
  const solidAuthenticator = new SolidTokenAuthenticator_1.SolidTokenAuthenticator({
44
48
  resolveAccountId: async (webId) => webId,
45
49
  });
@@ -49,10 +53,13 @@ function registerCommonServices(container) {
49
53
  const nodeTokenAuthenticator = new NodeTokenAuthenticator_1.NodeTokenAuthenticator({
50
54
  repository: nodeRepo,
51
55
  });
56
+ const serviceTokenAuthenticator = new ServiceTokenAuthenticator_1.ServiceTokenAuthenticator({
57
+ repository: serviceTokenRepo,
58
+ });
52
59
  return new MultiAuthenticator_1.MultiAuthenticator({
53
- // NodeTokenAuthenticator 必须在 ClientCredentialsAuthenticator 之前
54
- // 因为两者都处理 Bearer token,但 Node Token X-Node-Id 头
55
- authenticators: [solidAuthenticator, nodeTokenAuthenticator, clientCredAuthenticator],
60
+ // Order: Solid DPoP → Service Token → Node Token → Client Credentials
61
+ // ServiceTokenAuthenticator handles 'svc-' prefix, so no ambiguity
62
+ authenticators: [solidAuthenticator, serviceTokenAuthenticator, nodeTokenAuthenticator, clientCredAuthenticator],
56
63
  });
57
64
  }).singleton(),
58
65
  authMiddleware: (0, awilix_1.asFunction)(({ authenticator }) => {
@@ -1 +1 @@
1
- {"version":3,"file":"common.js","sourceRoot":"","sources":["../../../src/api/container/common.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * 共享服务注册\n *\n * cloud 和 local 模式都需要的服务\n */\n\nimport { asFunction, type AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle } from './types';\n\nimport { getIdentityDatabase } from '../../identity/drizzle/db';\nimport { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\nimport { DrizzleClientCredentialsStore } from '../store/DrizzleClientCredentialsStore';\nimport { SolidTokenAuthenticator } from '../auth/SolidTokenAuthenticator';\nimport { ClientCredentialsAuthenticator } from '../auth/ClientCredentialsAuthenticator';\nimport { NodeTokenAuthenticator } from '../auth/NodeTokenAuthenticator';\nimport { MultiAuthenticator } from '../auth/MultiAuthenticator';\nimport { AuthMiddleware } from '../middleware/AuthMiddleware';\nimport { VercelChatService } from '../service/VercelChatService';\nimport { ApiServer } from '../ApiServer';\nimport { ChatKitService, PodChatKitStore, VercelAiProvider } from '../chatkit';\n\n/**\n * 注册共享服务到容器\n */\nexport function registerCommonServices(\n container: AwilixContainer<ApiContainerCradle>,\n): void {\n container.register({\n // 数据库\n db: asFunction(({ config }: ApiContainerCradle) => {\n return getIdentityDatabase(config.databaseUrl);\n }).singleton(),\n\n // 仓库\n nodeRepo: asFunction(({ db }: ApiContainerCradle) => {\n return new EdgeNodeRepository(db);\n }).singleton(),\n\n apiKeyStore: asFunction(({ db, config }: ApiContainerCradle) => {\n return new DrizzleClientCredentialsStore({\n db,\n encryptionKey: config.encryptionKey,\n isSqlite: config.databaseUrl.startsWith('sqlite:'),\n });\n }).singleton(),\n\n // 认证\n authenticator: asFunction(({ apiKeyStore, nodeRepo, config }: ApiContainerCradle) => {\n const solidAuthenticator = new SolidTokenAuthenticator({\n resolveAccountId: async (webId) => webId,\n });\n\n const clientCredAuthenticator = new ClientCredentialsAuthenticator({\n tokenEndpoint: config.cssTokenEndpoint,\n });\n\n const nodeTokenAuthenticator = new NodeTokenAuthenticator({\n repository: nodeRepo,\n });\n\n return new MultiAuthenticator({\n // NodeTokenAuthenticator 必须在 ClientCredentialsAuthenticator 之前\n // 因为两者都处理 Bearer token,但 Node Token X-Node-Id 头\n authenticators: [solidAuthenticator, nodeTokenAuthenticator, clientCredAuthenticator],\n });\n }).singleton(),\n\n authMiddleware: asFunction(({ authenticator }: ApiContainerCradle) => {\n return new AuthMiddleware({ authenticator });\n }).singleton(),\n\n // ChatKit 存储与服务\n chatKitStore: asFunction(({ config }: ApiContainerCradle) => {\n return new PodChatKitStore({\n tokenEndpoint: config.cssTokenEndpoint,\n });\n }).singleton(),\n\n chatKitAiProvider: asFunction(({ chatKitStore }: ApiContainerCradle) => {\n return new VercelAiProvider({ store: chatKitStore });\n }).singleton(),\n\n chatKitService: asFunction(({ chatKitStore, chatKitAiProvider, config }: ApiContainerCradle) => {\n return new ChatKitService({\n store: chatKitStore,\n aiProvider: chatKitAiProvider,\n enablePtyRuntime: config.edition === 'local',\n });\n }).singleton(),\n\n // 业务服务\n chatService: asFunction(({ chatKitStore }: ApiContainerCradle) => {\n return new VercelChatService(chatKitStore);\n }).singleton(),\n\n\n // API Server\n apiServer: asFunction(({ config, authMiddleware }: ApiContainerCradle) => {\n return new ApiServer({\n port: config.port,\n host: config.host,\n authMiddleware,\n corsOrigins: config.corsOrigins,\n });\n }).singleton(),\n });\n}\n"]}
1
+ {"version":3,"file":"common.js","sourceRoot":"","sources":["../../../src/api/container/common.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * 共享服务注册\n *\n * cloud 和 local 模式都需要的服务\n */\n\nimport { asFunction, type AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle } from './types';\n\nimport { getIdentityDatabase } from '../../identity/drizzle/db';\nimport { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\nimport { ServiceTokenRepository } from '../../identity/drizzle/ServiceTokenRepository';\nimport { DrizzleClientCredentialsStore } from '../store/DrizzleClientCredentialsStore';\nimport { SolidTokenAuthenticator } from '../auth/SolidTokenAuthenticator';\nimport { ClientCredentialsAuthenticator } from '../auth/ClientCredentialsAuthenticator';\nimport { NodeTokenAuthenticator } from '../auth/NodeTokenAuthenticator';\nimport { ServiceTokenAuthenticator } from '../auth/ServiceTokenAuthenticator';\nimport { MultiAuthenticator } from '../auth/MultiAuthenticator';\nimport { AuthMiddleware } from '../middleware/AuthMiddleware';\nimport { VercelChatService } from '../service/VercelChatService';\nimport { ApiServer } from '../ApiServer';\nimport { ChatKitService, PodChatKitStore, VercelAiProvider } from '../chatkit';\n\n/**\n * 注册共享服务到容器\n */\nexport function registerCommonServices(\n container: AwilixContainer<ApiContainerCradle>,\n): void {\n container.register({\n // 数据库\n db: asFunction(({ config }: ApiContainerCradle) => {\n return getIdentityDatabase(config.databaseUrl);\n }).singleton(),\n\n // 仓库\n nodeRepo: asFunction(({ db }: ApiContainerCradle) => {\n return new EdgeNodeRepository(db);\n }).singleton(),\n\n apiKeyStore: asFunction(({ db, config }: ApiContainerCradle) => {\n return new DrizzleClientCredentialsStore({\n db,\n isSqlite: config.databaseUrl.startsWith('sqlite:'),\n });\n }).singleton(),\n\n // 认证\n serviceTokenRepo: asFunction(({ db }: ApiContainerCradle) => {\n return new ServiceTokenRepository(db);\n }).singleton(),\n\n authenticator: asFunction(({ nodeRepo, serviceTokenRepo, config }: ApiContainerCradle) => {\n const solidAuthenticator = new SolidTokenAuthenticator({\n resolveAccountId: async (webId) => webId,\n });\n\n const clientCredAuthenticator = new ClientCredentialsAuthenticator({\n tokenEndpoint: config.cssTokenEndpoint,\n });\n\n const nodeTokenAuthenticator = new NodeTokenAuthenticator({\n repository: nodeRepo,\n });\n\n const serviceTokenAuthenticator = new ServiceTokenAuthenticator({\n repository: serviceTokenRepo,\n });\n\n return new MultiAuthenticator({\n // Order: Solid DPoP Service Token Node Token Client Credentials\n // ServiceTokenAuthenticator handles 'svc-' prefix, so no ambiguity\n authenticators: [solidAuthenticator, serviceTokenAuthenticator, nodeTokenAuthenticator, clientCredAuthenticator],\n });\n }).singleton(),\n\n authMiddleware: asFunction(({ authenticator }: ApiContainerCradle) => {\n return new AuthMiddleware({ authenticator });\n }).singleton(),\n\n // ChatKit 存储与服务\n chatKitStore: asFunction(({ config }: ApiContainerCradle) => {\n return new PodChatKitStore({\n tokenEndpoint: config.cssTokenEndpoint,\n });\n }).singleton(),\n\n chatKitAiProvider: asFunction(({ chatKitStore }: ApiContainerCradle) => {\n return new VercelAiProvider({ store: chatKitStore });\n }).singleton(),\n\n chatKitService: asFunction(({ chatKitStore, chatKitAiProvider, config }: ApiContainerCradle) => {\n return new ChatKitService({\n store: chatKitStore,\n aiProvider: chatKitAiProvider,\n enablePtyRuntime: config.edition === 'local',\n });\n }).singleton(),\n\n // 业务服务\n chatService: asFunction(({ chatKitStore }: ApiContainerCradle) => {\n return new VercelChatService(chatKitStore);\n }).singleton(),\n\n\n // API Server\n apiServer: asFunction(({ config, authMiddleware }: ApiContainerCradle) => {\n return new ApiServer({\n port: config.port,\n host: config.host,\n authMiddleware,\n corsOrigins: config.corsOrigins,\n });\n }).singleton(),\n });\n}\n"]}
@@ -4,13 +4,41 @@
4
4
  *
5
5
  * 使用 Awilix 进行依赖注入,根据 edition 注册不同服务
6
6
  */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || function (mod) {
24
+ if (mod && mod.__esModule) return mod;
25
+ var result = {};
26
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
27
+ __setModuleDefault(result, mod);
28
+ return result;
29
+ };
7
30
  Object.defineProperty(exports, "__esModule", { value: true });
8
31
  exports.createApiContainer = createApiContainer;
9
32
  exports.loadConfigFromEnv = loadConfigFromEnv;
10
33
  const awilix_1 = require("awilix");
34
+ const node_crypto_1 = require("node:crypto");
35
+ const fs = __importStar(require("node:fs"));
36
+ const os = __importStar(require("node:os"));
37
+ const path = __importStar(require("node:path"));
11
38
  const common_1 = require("./common");
12
39
  const cloud_1 = require("./cloud");
13
40
  const local_1 = require("./local");
41
+ const business_token_1 = require("./business-token");
14
42
  function ensureTrailingSlash(url) {
15
43
  return url.endsWith('/') ? url : `${url}/`;
16
44
  }
@@ -44,6 +72,8 @@ function createApiContainer(config) {
44
72
  else {
45
73
  (0, local_1.registerLocalServices)(container);
46
74
  }
75
+ // 注册 Business Token (如果配置了 XPOD_BUSINESS_TOKEN)
76
+ (0, business_token_1.registerBusinessToken)(container);
47
77
  return container;
48
78
  }
49
79
  /**
@@ -60,24 +90,13 @@ function loadConfigFromEnv() {
60
90
  edition,
61
91
  port: apiPort,
62
92
  host: process.env.API_HOST ?? '0.0.0.0',
93
+ socketPath: process.env.API_SOCKET_PATH,
63
94
  databaseUrl: process.env.CSS_IDENTITY_DB_URL ?? process.env.DATABASE_URL ?? '',
64
95
  corsOrigins: process.env.CORS_ORIGINS?.split(',').map(s => s.trim()) ?? ['*'],
65
- encryptionKey: process.env.XPOD_ENCRYPTION_KEY ?? 'default-dev-key-change-me',
66
96
  cssTokenEndpoint: resolveCssTokenEndpoint(),
67
97
  // 子域名配置 (cloud 模式)
68
98
  subdomain: {
69
- enabled: process.env.XPOD_SUBDOMAIN_ENABLED === 'true',
70
- baseDomain: (() => {
71
- if (!process.env.CSS_BASE_URL)
72
- return undefined;
73
- try {
74
- return new URL(process.env.CSS_BASE_URL).hostname;
75
- }
76
- catch {
77
- return undefined;
78
- }
79
- })(),
80
- ddnsDomain: process.env.XPOD_DDNS_DOMAIN || 'undefineds.xyz',
99
+ baseStorageDomain: process.env.CSS_BASE_STORAGE_DOMAIN,
81
100
  cloudflareAccountId: process.env.CLOUDFLARE_ACCOUNT_ID,
82
101
  cloudflareApiToken: process.env.CLOUDFLARE_API_TOKEN,
83
102
  tencentDnsSecretId: process.env.TENCENT_DNS_SECRET_ID,
@@ -85,7 +104,7 @@ function loadConfigFromEnv() {
85
104
  },
86
105
  // Local 托管式:连接 Cloud
87
106
  cloudApiEndpoint: process.env.XPOD_CLOUD_API_ENDPOINT,
88
- nodeId: process.env.XPOD_NODE_ID,
107
+ nodeId: loadOrGenerateDeviceId(process.env.XPOD_NODE_ID),
89
108
  nodeToken: process.env.XPOD_NODE_TOKEN,
90
109
  // OIDC Issuer (Local 托管式使用 Cloud IdP)
91
110
  // 如果配置了 XPOD_NODE_TOKEN,默认使用 Cloud IdP
@@ -100,4 +119,65 @@ function loadConfigFromEnv() {
100
119
  edgeNodesEnabled: process.env.XPOD_EDGE_NODES_ENABLED === 'true',
101
120
  };
102
121
  }
122
+ /**
123
+ * 获取设备首个非内部网卡的 MAC 地址。
124
+ * 返回小写冒号分隔格式,如 "aa:bb:cc:dd:ee:ff"。
125
+ * 容器/虚拟机中可能拿不到稳定 MAC,此时返回 undefined。
126
+ */
127
+ function getFirstMacAddress() {
128
+ const interfaces = os.networkInterfaces();
129
+ for (const name of Object.keys(interfaces)) {
130
+ for (const iface of interfaces[name] ?? []) {
131
+ if (!iface.internal && iface.mac && iface.mac !== '00:00:00:00:00:00') {
132
+ return iface.mac.toLowerCase();
133
+ }
134
+ }
135
+ }
136
+ return undefined;
137
+ }
138
+ /**
139
+ * 读取或生成设备 ID(持久化到 data/.device-id)。
140
+ *
141
+ * 优先级:
142
+ * 1. 环境变量 XPOD_NODE_ID
143
+ * 2. 已持久化的 data/.device-id
144
+ * 3. 基于 MAC 地址的 SHA-256 哈希(截取前 32 位 hex)
145
+ * 4. 随机 UUID(容器/虚拟机无稳定 MAC 时兜底)
146
+ *
147
+ * 生成后写入 data/.device-id,后续启动直接读取,保证同一设备 ID 稳定。
148
+ */
149
+ function loadOrGenerateDeviceId(envNodeId) {
150
+ if (envNodeId) {
151
+ return envNodeId;
152
+ }
153
+ const rootDir = process.env.CSS_ROOT_FILE_PATH || './data';
154
+ const deviceIdPath = path.join(rootDir, '.device-id');
155
+ // 尝试从文件读取
156
+ try {
157
+ if (fs.existsSync(deviceIdPath)) {
158
+ const content = fs.readFileSync(deviceIdPath, 'utf-8').trim();
159
+ if (content) {
160
+ return content;
161
+ }
162
+ }
163
+ }
164
+ catch {
165
+ // 读取失败,继续生成
166
+ }
167
+ // 优先用 MAC 哈希,拿不到则 UUID 兜底
168
+ const mac = getFirstMacAddress();
169
+ const deviceId = mac
170
+ ? (0, node_crypto_1.createHash)('sha256').update(mac).digest('hex').slice(0, 32)
171
+ : (0, node_crypto_1.randomUUID)();
172
+ try {
173
+ if (!fs.existsSync(rootDir)) {
174
+ fs.mkdirSync(rootDir, { recursive: true });
175
+ }
176
+ fs.writeFileSync(deviceIdPath, deviceId, 'utf-8');
177
+ }
178
+ catch {
179
+ // 写入失败不阻塞启动
180
+ }
181
+ return deviceId;
182
+ }
103
183
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/api/container/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * API Container 入口\n *\n * 使用 Awilix 进行依赖注入,根据 edition 注册不同服务\n */\n\nimport { createContainer, asValue, InjectionMode, type AwilixContainer } from 'awilix';\nimport type { ApiContainerCradle, ApiContainerConfig } from './types';\nimport { registerCommonServices } from './common';\nimport { registerCloudServices } from './cloud';\nimport { registerLocalServices } from './local';\n\nexport type { ApiContainerCradle, ApiContainerConfig } from './types';\n\nfunction ensureTrailingSlash(url: string): string {\n return url.endsWith('/') ? url : `${url}/`;\n}\n\nfunction resolveCssTokenEndpoint(): string {\n if (process.env.CSS_TOKEN_ENDPOINT) {\n return process.env.CSS_TOKEN_ENDPOINT;\n }\n\n if (process.env.CSS_BASE_URL) {\n return `${ensureTrailingSlash(process.env.CSS_BASE_URL)}.oidc/token`;\n }\n\n return 'http://localhost:3000/.oidc/token';\n}\n\n/**\n * 创建 API 容器\n */\nexport function createApiContainer(config: ApiContainerConfig): AwilixContainer<ApiContainerCradle> {\n const container = createContainer<ApiContainerCradle>({\n injectionMode: InjectionMode.PROXY,\n strict: true,\n });\n\n // 注册配置\n container.register({\n config: asValue(config),\n });\n\n // 注册共享服务\n registerCommonServices(container);\n\n // 根据 edition 注册专属服务\n if (config.edition === 'cloud') {\n registerCloudServices(container);\n } else {\n registerLocalServices(container);\n }\n\n return container;\n}\n\n/**\n * 从环境变量读取配置\n */\nexport function loadConfigFromEnv(): ApiContainerConfig {\n const edition = (process.env.XPOD_EDITION ?? 'local') as 'cloud' | 'local';\n\n // Port auto-increment: API_PORT = CSS_PORT + 1 if not explicitly set\n const cssPort = parseInt(process.env.CSS_PORT ?? '3000', 10);\n const apiPort = process.env.API_PORT\n ? parseInt(process.env.API_PORT, 10)\n : cssPort + 1;\n\n return {\n edition,\n port: apiPort,\n host: process.env.API_HOST ?? '0.0.0.0',\n databaseUrl: process.env.CSS_IDENTITY_DB_URL ?? process.env.DATABASE_URL ?? '',\n corsOrigins: process.env.CORS_ORIGINS?.split(',').map(s => s.trim()) ?? ['*'],\n encryptionKey: process.env.XPOD_ENCRYPTION_KEY ?? 'default-dev-key-change-me',\n cssTokenEndpoint: resolveCssTokenEndpoint(),\n\n // 子域名配置 (cloud 模式)\n subdomain: {\n enabled: process.env.XPOD_SUBDOMAIN_ENABLED === 'true',\n baseDomain: ((): string | undefined => {\n if (!process.env.CSS_BASE_URL) return undefined;\n try {\n return new URL(process.env.CSS_BASE_URL).hostname;\n } catch {\n return undefined;\n }\n })(),\n ddnsDomain: process.env.XPOD_DDNS_DOMAIN || 'undefineds.xyz',\n cloudflareAccountId: process.env.CLOUDFLARE_ACCOUNT_ID,\n cloudflareApiToken: process.env.CLOUDFLARE_API_TOKEN,\n tencentDnsSecretId: process.env.TENCENT_DNS_SECRET_ID,\n tencentDnsSecretKey: process.env.TENCENT_DNS_SECRET_KEY,\n },\n\n // Local 托管式:连接 Cloud\n cloudApiEndpoint: process.env.XPOD_CLOUD_API_ENDPOINT,\n nodeId: process.env.XPOD_NODE_ID,\n nodeToken: process.env.XPOD_NODE_TOKEN,\n\n // OIDC Issuer (Local 托管式使用 Cloud IdP)\n // 如果配置了 XPOD_NODE_TOKEN,默认使用 Cloud IdP\n oidcIssuer: process.env.XPOD_OIDC_ISSUER ?? process.env.CSS_OIDC_ISSUER ?? (\n process.env.XPOD_NODE_TOKEN\n ? (process.env.XPOD_CLOUD_API_ENDPOINT ?? 'https://id.undefineds.co')\n : undefined\n ),\n\n // 隧道配置\n cloudflareTunnelToken: process.env.CLOUDFLARE_TUNNEL_TOKEN,\n // Prefer SAKURA_TUNNEL_TOKEN; keep SAKURA_TOKEN for backward compatibility.\n sakuraTunnelToken: process.env.SAKURA_TUNNEL_TOKEN ?? process.env.SAKURA_TOKEN,\n\n // Edge 节点管理 (cloud 模式)\n edgeNodesEnabled: process.env.XPOD_EDGE_NODES_ENABLED === 'true',\n };\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/api/container/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * API Container 入口\n *\n * 使用 Awilix 进行依赖注入,根据 edition 注册不同服务\n */\n\nimport { createContainer, asValue, InjectionMode, type AwilixContainer } from 'awilix';\nimport { randomUUID, createHash } from 'node:crypto';\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport type { ApiContainerCradle, ApiContainerConfig } from './types';\nimport { registerCommonServices } from './common';\nimport { registerCloudServices } from './cloud';\nimport { registerLocalServices } from './local';\nimport { registerBusinessToken } from './business-token';\n\nexport type { ApiContainerCradle, ApiContainerConfig } from './types';\n\nfunction ensureTrailingSlash(url: string): string {\n return url.endsWith('/') ? url : `${url}/`;\n}\n\nfunction resolveCssTokenEndpoint(): string {\n if (process.env.CSS_TOKEN_ENDPOINT) {\n return process.env.CSS_TOKEN_ENDPOINT;\n }\n\n if (process.env.CSS_BASE_URL) {\n return `${ensureTrailingSlash(process.env.CSS_BASE_URL)}.oidc/token`;\n }\n\n return 'http://localhost:3000/.oidc/token';\n}\n\n/**\n * 创建 API 容器\n */\nexport function createApiContainer(config: ApiContainerConfig): AwilixContainer<ApiContainerCradle> {\n const container = createContainer<ApiContainerCradle>({\n injectionMode: InjectionMode.PROXY,\n strict: true,\n });\n\n // 注册配置\n container.register({\n config: asValue(config),\n });\n\n // 注册共享服务\n registerCommonServices(container);\n\n // 根据 edition 注册专属服务\n if (config.edition === 'cloud') {\n registerCloudServices(container);\n } else {\n registerLocalServices(container);\n }\n\n // 注册 Business Token (如果配置了 XPOD_BUSINESS_TOKEN)\n registerBusinessToken(container);\n\n return container;\n}\n\n/**\n * 从环境变量读取配置\n */\nexport function loadConfigFromEnv(): ApiContainerConfig {\n const edition = (process.env.XPOD_EDITION ?? 'local') as 'cloud' | 'local';\n\n // Port auto-increment: API_PORT = CSS_PORT + 1 if not explicitly set\n const cssPort = parseInt(process.env.CSS_PORT ?? '3000', 10);\n const apiPort = process.env.API_PORT\n ? parseInt(process.env.API_PORT, 10)\n : cssPort + 1;\n\n return {\n edition,\n port: apiPort,\n host: process.env.API_HOST ?? '0.0.0.0',\n socketPath: process.env.API_SOCKET_PATH,\n databaseUrl: process.env.CSS_IDENTITY_DB_URL ?? process.env.DATABASE_URL ?? '',\n corsOrigins: process.env.CORS_ORIGINS?.split(',').map(s => s.trim()) ?? ['*'],\n cssTokenEndpoint: resolveCssTokenEndpoint(),\n\n // 子域名配置 (cloud 模式)\n subdomain: {\n baseStorageDomain: process.env.CSS_BASE_STORAGE_DOMAIN,\n cloudflareAccountId: process.env.CLOUDFLARE_ACCOUNT_ID,\n cloudflareApiToken: process.env.CLOUDFLARE_API_TOKEN,\n tencentDnsSecretId: process.env.TENCENT_DNS_SECRET_ID,\n tencentDnsSecretKey: process.env.TENCENT_DNS_SECRET_KEY,\n },\n\n // Local 托管式:连接 Cloud\n cloudApiEndpoint: process.env.XPOD_CLOUD_API_ENDPOINT,\n nodeId: loadOrGenerateDeviceId(process.env.XPOD_NODE_ID),\n nodeToken: process.env.XPOD_NODE_TOKEN,\n\n // OIDC Issuer (Local 托管式使用 Cloud IdP)\n // 如果配置了 XPOD_NODE_TOKEN,默认使用 Cloud IdP\n oidcIssuer: process.env.XPOD_OIDC_ISSUER ?? process.env.CSS_OIDC_ISSUER ?? (\n process.env.XPOD_NODE_TOKEN\n ? (process.env.XPOD_CLOUD_API_ENDPOINT ?? 'https://id.undefineds.co')\n : undefined\n ),\n\n // 隧道配置\n cloudflareTunnelToken: process.env.CLOUDFLARE_TUNNEL_TOKEN,\n // Prefer SAKURA_TUNNEL_TOKEN; keep SAKURA_TOKEN for backward compatibility.\n sakuraTunnelToken: process.env.SAKURA_TUNNEL_TOKEN ?? process.env.SAKURA_TOKEN,\n\n // Edge 节点管理 (cloud 模式)\n edgeNodesEnabled: process.env.XPOD_EDGE_NODES_ENABLED === 'true',\n };\n}\n\n/**\n * 获取设备首个非内部网卡的 MAC 地址。\n * 返回小写冒号分隔格式,如 \"aa:bb:cc:dd:ee:ff\"。\n * 容器/虚拟机中可能拿不到稳定 MAC,此时返回 undefined。\n */\nfunction getFirstMacAddress(): string | undefined {\n const interfaces = os.networkInterfaces();\n for (const name of Object.keys(interfaces)) {\n for (const iface of interfaces[name] ?? []) {\n if (!iface.internal && iface.mac && iface.mac !== '00:00:00:00:00:00') {\n return iface.mac.toLowerCase();\n }\n }\n }\n return undefined;\n}\n\n/**\n * 读取或生成设备 ID(持久化到 data/.device-id)。\n *\n * 优先级:\n * 1. 环境变量 XPOD_NODE_ID\n * 2. 已持久化的 data/.device-id\n * 3. 基于 MAC 地址的 SHA-256 哈希(截取前 32 位 hex)\n * 4. 随机 UUID(容器/虚拟机无稳定 MAC 时兜底)\n *\n * 生成后写入 data/.device-id,后续启动直接读取,保证同一设备 ID 稳定。\n */\nfunction loadOrGenerateDeviceId(envNodeId?: string): string | undefined {\n if (envNodeId) {\n return envNodeId;\n }\n\n const rootDir = process.env.CSS_ROOT_FILE_PATH || './data';\n const deviceIdPath = path.join(rootDir, '.device-id');\n\n // 尝试从文件读取\n try {\n if (fs.existsSync(deviceIdPath)) {\n const content = fs.readFileSync(deviceIdPath, 'utf-8').trim();\n if (content) {\n return content;\n }\n }\n } catch {\n // 读取失败,继续生成\n }\n\n // 优先用 MAC 哈希,拿不到则 UUID 兜底\n const mac = getFirstMacAddress();\n const deviceId = mac\n ? createHash('sha256').update(mac).digest('hex').slice(0, 32)\n : randomUUID();\n\n try {\n if (!fs.existsSync(rootDir)) {\n fs.mkdirSync(rootDir, { recursive: true });\n }\n fs.writeFileSync(deviceIdPath, deviceId, 'utf-8');\n } catch {\n // 写入失败不阻塞启动\n }\n\n return deviceId;\n}\n"]}
@@ -96,7 +96,7 @@ function registerLocalServices(container) {
96
96
  });
97
97
  }).singleton(),
98
98
  // Subdomain Service (Keep for API support)
99
- subdomainService: (0, awilix_1.asFunction)(({ dnsProvider, localTunnelProvider }) => {
99
+ subdomainService: (0, awilix_1.asFunction)(({ dnsProvider, localTunnelProvider, nodeRepo }) => {
100
100
  // 如果没有配置 Tunnel Token,使用一个 Mock Provider
101
101
  const tunnelProvider = localTunnelProvider ?? {
102
102
  name: 'noop',
@@ -111,6 +111,7 @@ function registerLocalServices(container) {
111
111
  baseDomain: baseDomain,
112
112
  dnsProvider: dnsProvider,
113
113
  tunnelProvider,
114
+ edgeNodeRepo: nodeRepo,
114
115
  });
115
116
  }).singleton(),
116
117
  });