@undefineds.co/xpod 0.4.28 → 0.4.30

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 (179) hide show
  1. package/dist/api/ApiServer.js +4 -0
  2. package/dist/api/ai-gateway/AiGatewayService.d.ts +55 -0
  3. package/dist/api/ai-gateway/AiGatewayService.js +145 -6
  4. package/dist/api/ai-gateway/auth/AiConnectionsInvocationKeyIssuer.js +5 -3
  5. package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.d.ts +16 -49
  6. package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.js +6 -71
  7. package/dist/api/ai-gateway/auth/InvocationTokenAuthenticator.js +1 -0
  8. package/dist/api/ai-gateway/auth/InvocationTokenCodec.d.ts +4 -0
  9. package/dist/api/ai-gateway/auth/InvocationTokenCodec.js +8 -1
  10. package/dist/api/ai-gateway/connect/FileSessionImportAdapter.d.ts +3 -0
  11. package/dist/api/ai-gateway/connect/FileSessionImportAdapter.js +23 -3
  12. package/dist/api/ai-gateway/connect/SessionImportProfiles.d.ts +9 -1
  13. package/dist/api/ai-gateway/connect/SessionImportProfiles.js +4 -3
  14. package/dist/api/ai-gateway/connect/index.d.ts +40 -0
  15. package/dist/api/ai-gateway/connect/index.js +194 -4
  16. package/dist/api/ai-gateway/models/PodModelSelectionRepository.js +1 -1
  17. package/dist/api/ai-gateway/pod/OwnerPodAccess.js +5 -0
  18. package/dist/api/ai-gateway/pod/PodBaseUrlResolver.d.ts +11 -2
  19. package/dist/api/ai-gateway/pod/PodBaseUrlResolver.js +72 -10
  20. package/dist/api/ai-gateway/quota/ProviderQuotaAdapter.js +1 -1
  21. package/dist/api/ai-gateway/routing/ModelRouter.d.ts +7 -0
  22. package/dist/api/ai-gateway/routing/ModelRouter.js +13 -10
  23. package/dist/api/ai-gateway/service-access/AiConnectionsServiceAccess.d.ts +1 -5
  24. package/dist/api/ai-gateway/service-access/AiConnectionsServiceAccess.js +8 -63
  25. package/dist/api/auth/AuthContext.d.ts +4 -0
  26. package/dist/api/auth/ClientCredentialsAuthenticator.js +11 -5
  27. package/dist/api/auth/SolidSessionFactory.d.ts +50 -4
  28. package/dist/api/auth/SolidSessionFactory.js +114 -4
  29. package/dist/api/chatkit/pod-store.d.ts +8 -1
  30. package/dist/api/chatkit/pod-store.js +54 -3
  31. package/dist/api/container/common.js +34 -60
  32. package/dist/api/container/index.js +0 -3
  33. package/dist/api/container/routes.js +1 -8
  34. package/dist/api/container/types.d.ts +2 -9
  35. package/dist/api/container/types.js +0 -5
  36. package/dist/api/handlers/AiGatewayManagementHandler.d.ts +4 -12
  37. package/dist/api/handlers/AiGatewayManagementHandler.js +18 -258
  38. package/dist/api/middleware/AuthMiddleware.js +29 -1
  39. package/dist/api/runs/PiAgentRuntimeDriver.js +6 -1
  40. package/dist/cli/index.js +2 -1
  41. package/dist/identity/oidc/PodOwnershipResolver.js +16 -6
  42. package/dist/runtime/ingress-port.d.ts +9 -2
  43. package/dist/runtime/ingress-port.js +7 -2
  44. package/dist/runtime/local-secret-file.d.ts +1 -1
  45. package/dist/runtime/runner/node/CommunitySolidServerCssRunner.d.ts +3 -1
  46. package/dist/runtime/runner/node/CommunitySolidServerCssRunner.js +31 -1
  47. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsPanel.d.ts +3 -1
  48. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsPanel.js +96 -67
  49. package/node_modules/@undefineds.co/ai-connections/dist/AiCredentialPoolSection.d.ts +2 -1
  50. package/node_modules/@undefineds.co/ai-connections/dist/AiCredentialPoolSection.js +2 -2
  51. package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeyRow.d.ts +1 -2
  52. package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeyRow.js +2 -2
  53. package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeysSection.js +81 -23
  54. package/node_modules/@undefineds.co/ai-connections/dist/AiOfferingDetails.d.ts +2 -1
  55. package/node_modules/@undefineds.co/ai-connections/dist/AiOfferingDetails.js +2 -2
  56. package/node_modules/@undefineds.co/ai-connections/dist/AiProviderCard.d.ts +2 -1
  57. package/node_modules/@undefineds.co/ai-connections/dist/AiProviderCard.js +2 -2
  58. package/node_modules/@undefineds.co/ai-connections/dist/AiProviderHeader.js +1 -1
  59. package/node_modules/@undefineds.co/ai-connections/dist/contract/client/fingerprint.d.ts +1 -0
  60. package/node_modules/@undefineds.co/ai-connections/dist/contract/client/fingerprint.js +7 -0
  61. package/node_modules/@undefineds.co/ai-connections/dist/contract/client/normalize.d.ts +1 -2
  62. package/node_modules/@undefineds.co/ai-connections/dist/contract/client/normalize.js +0 -33
  63. package/node_modules/@undefineds.co/ai-connections/dist/contract/client/request.d.ts +1 -1
  64. package/node_modules/@undefineds.co/ai-connections/dist/contract/client/request.js +68 -22
  65. package/node_modules/@undefineds.co/ai-connections/dist/contract/client/types.d.ts +37 -10
  66. package/node_modules/@undefineds.co/ai-connections/dist/controller.js +5 -54
  67. package/node_modules/@undefineds.co/ai-connections/dist/error-wording.js +7 -2
  68. package/node_modules/@undefineds.co/ai-connections/dist/service-access-resources.cjs +405 -0
  69. package/node_modules/@undefineds.co/ai-connections/dist/service-access-resources.d.ts +14 -0
  70. package/node_modules/@undefineds.co/ai-connections/dist/service-access-resources.js +36 -0
  71. package/node_modules/@undefineds.co/ai-connections/dist/service-access.js +5 -26
  72. package/node_modules/@undefineds.co/ai-connections/package.json +10 -2
  73. package/node_modules/@undefineds.co/extension-sdk/dist/solid-permissions.js +131 -13
  74. package/node_modules/@undefineds.co/extension-sdk/dist/web.d.ts +7 -1
  75. package/node_modules/@undefineds.co/extension-sdk/package.json +1 -1
  76. package/node_modules/@undefineds.co/pod-collections/dist/index.cjs +38 -5
  77. package/node_modules/@undefineds.co/pod-collections/dist/mapping.d.ts +8 -0
  78. package/node_modules/@undefineds.co/pod-collections/dist/mapping.js +34 -0
  79. package/node_modules/@undefineds.co/pod-collections/dist/mutations.d.ts +2 -1
  80. package/node_modules/@undefineds.co/pod-collections/dist/mutations.js +6 -4
  81. package/node_modules/@undefineds.co/pod-collections/dist/sync.js +4 -3
  82. package/node_modules/@undefineds.co/pod-collections/package.json +1 -1
  83. package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/IdpChrome.d.ts +1 -1
  84. package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/IdpChrome.js +2 -2
  85. package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/IdpViews.js +2 -2
  86. package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/PodSignInFrame.d.ts +3 -3
  87. package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/PodSignInFrame.js +5 -5
  88. package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/parts.d.ts +5 -5
  89. package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/parts.js +5 -5
  90. package/node_modules/@undefineds.co/shared-ui/dist/theme.css +32 -5
  91. package/node_modules/@undefineds.co/shared-ui/package.json +1 -1
  92. package/package.json +2 -2
  93. package/static/app/assets/main.css +1 -1
  94. package/static/app/assets/main.js +18 -18
  95. package/static/dashboard/assets/{AccountAuthBoundary-Dv52SZc6.js → AccountAuthBoundary-CuqXFEW4.js} +1 -1
  96. package/static/dashboard/assets/{DesktopOidcCallback-DaT_Rumf.js → DesktopOidcCallback-7i-yOvPW.js} +1 -1
  97. package/static/dashboard/assets/DevicePages-D1m_BNtX.js +2 -0
  98. package/static/dashboard/assets/ModelsPage-0qWGa5Y6.js +12 -0
  99. package/static/dashboard/assets/{PaneListHeader-DtvnRmFq.js → PaneListHeader-uWdPgJ0E.js} +1 -1
  100. package/static/dashboard/assets/{PodDeletionAuthorizationPanel-BJk_8rJq.js → PodDeletionAuthorizationPanel-C22bhZeD.js} +3 -3
  101. package/static/dashboard/assets/{PodPage-C4gzEspV.js → PodPage-Dyo3yCb-.js} +1 -1
  102. package/static/dashboard/assets/{SystemSettingsPage-HaNrP1H1.js → SystemSettingsPage-CeX-oG_a.js} +1 -1
  103. package/static/dashboard/assets/{TasksPage-BdO5K2L7.js → TasksPage-B0EoyRYT.js} +1 -1
  104. package/static/dashboard/assets/{WebDesktopEntry-Cj9_ZfJU.js → WebDesktopEntry-DT44RrTa.js} +1 -1
  105. package/static/dashboard/assets/XpodAiConnectionsPodStore-EXCqZPmb.js +3 -0
  106. package/static/dashboard/assets/XpodShellApp-BEzi4f1j.css +1 -0
  107. package/static/dashboard/assets/XpodShellApp-DAFsxamX.js +4 -0
  108. package/static/dashboard/assets/{account-session-DlcKNG7k.js → account-session-rRAOF7nF.js} +1 -1
  109. package/static/dashboard/assets/{button-wslF55uZ.js → button-WVnQUNG-.js} +1 -1
  110. package/static/dashboard/assets/{collection-runtime-uxxgEu13.js → collection-runtime-BAJQ8Qub.js} +1 -1
  111. package/static/dashboard/assets/{createLucideIcon-BJQl82Qk.js → createLucideIcon-BWelSi3L.js} +1 -1
  112. package/static/dashboard/assets/dashboard-hSOBxPcP.css +1 -0
  113. package/static/dashboard/assets/{dashboard-Or0gnkou.js → dashboard-vmne1ZE0.js} +2 -2
  114. package/static/dashboard/assets/{index-BqFeOML9.js → index-HDQQ8asz.js} +1 -1
  115. package/static/dashboard/assets/{index-DgKn1twH.js → index-gtv-MAHl.js} +1 -1
  116. package/static/dashboard/assets/{label-CS6Sx6Zb.js → label-tKuCTJm2.js} +1 -1
  117. package/static/dashboard/assets/{list-UyhlCFKk.js → list-CKq3KHke.js} +1 -1
  118. package/static/dashboard/assets/{nav-item-style-CW1Nnorq.js → nav-item-style-B2gI7c52.js} +1 -1
  119. package/static/dashboard/assets/pod-collections-host-BqOAkacM.js +8 -0
  120. package/static/dashboard/assets/{safe-continuation-ClWAhcma.js → safe-continuation-BsKjDpAA.js} +1 -1
  121. package/static/dashboard/assets/{section-header-CEIEcco-.js → section-header-DM0JU-wk.js} +1 -1
  122. package/static/dashboard/assets/{setting-row-CaYv3dfF.js → setting-row-XiH7UjWy.js} +1 -1
  123. package/static/dashboard/assets/{xpod-remembered-login-BUF9fin1.js → xpod-remembered-login-nv1g1sfC.js} +4 -4
  124. package/static/dashboard/dashboard.html +2 -2
  125. package/static/settings/assets/{AccountAuthBoundary-Chqd38oU.js → AccountAuthBoundary-DAUR0omy.js} +1 -1
  126. package/static/settings/assets/{DesktopOidcCallback-VUrOSM_q.js → DesktopOidcCallback-BNZAZMWY.js} +1 -1
  127. package/static/settings/assets/DevicePages-BO9SrXRp.js +2 -0
  128. package/static/settings/assets/ModelsPage-DREEXdf3.js +12 -0
  129. package/static/settings/assets/{PaneListHeader-B5RkRqr4.js → PaneListHeader-CeUH0JjQ.js} +1 -1
  130. package/static/settings/assets/{PodDeletionAuthorizationPanel-DPM-5Izt.js → PodDeletionAuthorizationPanel-Ch4--l4b.js} +3 -3
  131. package/static/settings/assets/{PodPage-CxND6wwc.js → PodPage-CqJBj3GJ.js} +1 -1
  132. package/static/settings/assets/{SystemSettingsPage-C8EdMkW7.js → SystemSettingsPage-2LSjpqUa.js} +1 -1
  133. package/static/settings/assets/{TasksPage-DuqptEm8.js → TasksPage-DA5IRxlP.js} +1 -1
  134. package/static/settings/assets/{WebDesktopEntry-BRcyktkv.js → WebDesktopEntry-BVelF1q0.js} +1 -1
  135. package/static/settings/assets/XpodAiConnectionsPodStore-O5-2qBsx.js +3 -0
  136. package/static/settings/assets/XpodShellApp-BEzi4f1j.css +1 -0
  137. package/static/settings/assets/XpodShellApp-D1b3kqD0.js +4 -0
  138. package/static/settings/assets/XpodThemeProvider-hSOBxPcP.css +1 -0
  139. package/static/settings/assets/{XpodThemeProvider-DK3pIdRG.js → XpodThemeProvider-rUdAw66p.js} +2 -2
  140. package/static/settings/assets/{account-session-DJOZ03xr.js → account-session-BD1q8D_o.js} +1 -1
  141. package/static/settings/assets/auth-callback-PWS7okI8.js +1 -0
  142. package/static/settings/assets/{button-D3yW-2o1.js → button-ZJYFB218.js} +1 -1
  143. package/static/settings/assets/{collection-runtime-DfGo25mh.js → collection-runtime-C2FHCCXJ.js} +1 -1
  144. package/static/settings/assets/{createLucideIcon-CacwLsmB.js → createLucideIcon-B2wXvYTD.js} +1 -1
  145. package/static/settings/assets/{index-r5KRYsKC.js → index-JFAkaKZH.js} +1 -1
  146. package/static/settings/assets/{label-MJjUmvzG.js → label-BLrTAmGy.js} +1 -1
  147. package/static/settings/assets/{list-Ce-5k1MX.js → list-Cm96vvJO.js} +1 -1
  148. package/static/settings/assets/{nav-item-style-ZUbL8KOZ.js → nav-item-style-C2upI7sr.js} +1 -1
  149. package/static/settings/assets/pod-collections-host-BqOAkacM.js +8 -0
  150. package/static/settings/assets/{safe-continuation-vlBZFSlU.js → safe-continuation-CYaRj3rj.js} +1 -1
  151. package/static/settings/assets/{section-header-DGJdEvQw.js → section-header-BviL5H8E.js} +1 -1
  152. package/static/settings/assets/{setting-row-C7tuFiho.js → setting-row-BYikj0YO.js} +1 -1
  153. package/static/settings/assets/{settings-CyQPih1O.js → settings-CpoDG_Qr.js} +1 -1
  154. package/static/settings/assets/{xpod-remembered-login-D-TzzT5K.js → xpod-remembered-login-BJ4QUSXy.js} +4 -4
  155. package/static/settings/auth-callback.html +3 -3
  156. package/static/settings/settings.html +3 -3
  157. package/dist/api/ai-gateway/auth/GatewayApiKey.d.ts +0 -28
  158. package/dist/api/ai-gateway/auth/GatewayApiKey.js +0 -162
  159. package/dist/api/ai-gateway/auth/GatewayKeyLocatorCodec.d.ts +0 -26
  160. package/dist/api/ai-gateway/auth/GatewayKeyLocatorCodec.js +0 -97
  161. package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.d.ts +0 -67
  162. package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.js +0 -340
  163. package/dist/runtime/gateway-locator-secret.d.ts +0 -5
  164. package/dist/runtime/gateway-locator-secret.js +0 -30
  165. package/static/dashboard/assets/DevicePages-HjclpIVm.js +0 -2
  166. package/static/dashboard/assets/ModelsPage-Be2TPd-x.js +0 -11
  167. package/static/dashboard/assets/XpodAiConnectionsPodStore-B7gmXBmC.js +0 -3
  168. package/static/dashboard/assets/XpodShellApp-Bwom2xL3.css +0 -1
  169. package/static/dashboard/assets/XpodShellApp-D_KY4nCW.js +0 -4
  170. package/static/dashboard/assets/dashboard-CbJ-JkrI.css +0 -1
  171. package/static/dashboard/assets/pod-collections-host-DjK2xf-b.js +0 -8
  172. package/static/settings/assets/DevicePages-C6qwvWwu.js +0 -2
  173. package/static/settings/assets/ModelsPage-Ry_dObRQ.js +0 -11
  174. package/static/settings/assets/XpodAiConnectionsPodStore-CyA_BJVC.js +0 -3
  175. package/static/settings/assets/XpodShellApp-Bwom2xL3.css +0 -1
  176. package/static/settings/assets/XpodShellApp-CsivHJl0.js +0 -4
  177. package/static/settings/assets/XpodThemeProvider-CbJ-JkrI.css +0 -1
  178. package/static/settings/assets/auth-callback-GIBYEz_7.js +0 -1
  179. package/static/settings/assets/pod-collections-host-DjK2xf-b.js +0 -8
@@ -1,340 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PodGatewayAccessKeyRepository = void 0;
4
- exports.clientCredentialProviderId = clientCredentialProviderId;
5
- const drizzle_solid_1 = require("@undefineds.co/drizzle-solid");
6
- const models_1 = require("@undefineds.co/models");
7
- const global_logger_factory_1 = require("global-logger-factory");
8
- const OwnerPodAccess_1 = require("../pod/OwnerPodAccess");
9
- const AiConnectionsServiceAccess_1 = require("../service-access/AiConnectionsServiceAccess");
10
- const PodBaseUrlResolver_1 = require("../pod/PodBaseUrlResolver");
11
- const GatewayKeyLocatorCodec_1 = require("./GatewayKeyLocatorCodec");
12
- class PodGatewayAccessKeyRepository {
13
- constructor(options) {
14
- this.locatorCodec = options.locatorCodec;
15
- this.podAccess = options.podAccess;
16
- this.podBaseUrlResolver = options.podBaseUrlResolver;
17
- this.usesDefaultDbFactory = options.dbFactory === undefined;
18
- this.dbFactory = options.dbFactory ?? createDefaultGatewayAccessKeyDb;
19
- }
20
- createKeyId(owner, deployment) {
21
- return (0, GatewayKeyLocatorCodec_1.createGatewayKeyLocator)(owner, deployment, this.locatorCodec);
22
- }
23
- async create(record, context) {
24
- const { db, resource, fetch, podUrl } = await this.dbForOwner(record.owner, context);
25
- if (record.kind === 'client-credentials') {
26
- if (!record.clientCredentialId) {
27
- throw new Error('client_credentials_registration_incomplete');
28
- }
29
- const locator = this.locatorCodec.decode(record.id);
30
- if (locator?.owner !== record.owner || locator.deployment !== record.deployment) {
31
- throw new Error('client_credentials_registration_owner_mismatch');
32
- }
33
- // The wrapper itself is never stored: CSS keeps the secret and the client
34
- // configuration holds the only copy the caller needs. This row records the
35
- // purpose and where the credential is in effect.
36
- await db.insert(models_1.credentialResource).values(toClientCredentialInsert(record)).execute();
37
- return { ...record, scopes: [], secretHash: '' };
38
- }
39
- const valid = models_1.aiGatewayRepository.validateAccessKey(toGatewayAccessKeyInsert(record));
40
- await db.insert(resource).values(valid).execute();
41
- const created = recordFromRow(valid);
42
- return record.plaintext ? { ...created, plaintext: record.plaintext } : created;
43
- }
44
- async findById(id, context) {
45
- const locator = this.locatorCodec.decode(id);
46
- if (!locator) {
47
- return undefined;
48
- }
49
- const { db, resource } = await this.dbForOwner(locator.owner, context);
50
- // Authentication uses only the legacy RDF verifier rows; issued client
51
- // credentials are owner-authorized management records.
52
- if (!context?.gatewayKeyVerification) {
53
- const credential = await db.findById(models_1.credentialResource, clientCredentialStorageId(id));
54
- if (credential)
55
- return clientCredentialRecord(id, locator, credential);
56
- }
57
- const row = await db.findById(resource, gatewayAccessKeyStorageId(id));
58
- return row ? recordFromRow(row) : undefined;
59
- }
60
- async listByOwner(owner, context) {
61
- const { db, listResource, credentialListResource } = await this.dbForOwner(owner, context);
62
- const rows = await db
63
- .select()
64
- .from(listResource)
65
- .where((0, drizzle_solid_1.eq)(listResource.owner, owner))
66
- .execute();
67
- const credentialRows = await db
68
- .select()
69
- .from(credentialListResource)
70
- .where((0, drizzle_solid_1.eq)(credentialListResource.provider, clientCredentialProviderId()))
71
- .execute();
72
- const credentials = credentialRows
73
- .map((row) => {
74
- const id = clientCredentialLocator(String(row.id));
75
- return id ? clientCredentialRecord(id, this.locatorCodec.decode(id), row) : undefined;
76
- })
77
- .filter((record) => Boolean(record))
78
- .filter((record) => this.locatorCodec.decode(record.id)?.owner === owner);
79
- return [...rows.map(recordFromRow), ...credentials]
80
- .filter((record) => !record.revokedAt)
81
- .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
82
- }
83
- async setEnabled(id, enabled, changedAt, context) {
84
- const locator = this.locatorCodec.decode(id);
85
- if (!locator) {
86
- return undefined;
87
- }
88
- const { db, resource } = await this.dbForOwner(locator.owner, context);
89
- if (await db.findById(models_1.credentialResource, clientCredentialStorageId(id))) {
90
- // Suspension is an application-layer fact (is the client configuration in
91
- // effect?); the issued credential itself can only be destroyed.
92
- throw new Error('client_credentials_suspension_unsupported');
93
- }
94
- const row = await db.updateById(resource, gatewayAccessKeyStorageId(id), { disabledAt: enabled ? null : changedAt });
95
- return row ? recordFromRow(row) : undefined;
96
- }
97
- async revoke(id, revokedAt, context) {
98
- const locator = this.locatorCodec.decode(id);
99
- if (!locator) {
100
- return undefined;
101
- }
102
- const { db, resource } = await this.dbForOwner(locator.owner, context);
103
- if (await db.findById(models_1.credentialResource, clientCredentialStorageId(id))) {
104
- throw new Error('client_credentials_revocation_requires_account');
105
- }
106
- const row = await db.updateById(resource, gatewayAccessKeyStorageId(id), { revokedAt });
107
- return row ? recordFromRow(row) : undefined;
108
- }
109
- async delete(id, context) {
110
- const locator = this.locatorCodec.decode(id);
111
- if (!locator) {
112
- return false;
113
- }
114
- const { db, resource } = await this.dbForOwner(locator.owner, context);
115
- const credentialStorageId = clientCredentialStorageId(id);
116
- if (await db.findById(models_1.credentialResource, credentialStorageId)) {
117
- // The Account host destroys the CSS credential; this removes the record of
118
- // where it was applied. Nothing secret is deleted here because none was kept.
119
- await db.deleteById?.(models_1.credentialResource, credentialStorageId);
120
- return true;
121
- }
122
- await db.updateById(resource, gatewayAccessKeyStorageId(id), { revokedAt: new Date() });
123
- return true;
124
- }
125
- async revealPlaintext(id, context) {
126
- const locator = this.locatorCodec.decode(id);
127
- if (!locator) {
128
- return undefined;
129
- }
130
- // Xpod never keeps a copy of an issued credential: it exists in the client
131
- // configuration that received it and nowhere else, so there is nothing to
132
- // reveal after the call that created it.
133
- return undefined;
134
- }
135
- async touchLastUsed(id, lastUsedAt, context) {
136
- const locator = this.locatorCodec.decode(id);
137
- if (!locator) {
138
- return;
139
- }
140
- const { db, resource } = await this.dbForOwner(locator.owner, context);
141
- const credentialStorageId = clientCredentialStorageId(id);
142
- if (await db.findById(models_1.credentialResource, credentialStorageId)) {
143
- await db.updateById(models_1.credentialResource, credentialStorageId, { lastUsedAt });
144
- return;
145
- }
146
- await db.updateById(resource, gatewayAccessKeyStorageId(id), { lastUsedAt });
147
- }
148
- async dbForOwner(owner, context) {
149
- const podUrl = await (0, PodBaseUrlResolver_1.resolveOwnerPodBaseUrl)(owner, this.podBaseUrlResolver);
150
- const trustedFetch = await this.resolveTrustedFetch(owner, podUrl, context);
151
- const resource = models_1.gatewayAccessKeyResource;
152
- const listResource = this.usesDefaultDbFactory
153
- ? createGatewayAccessKeyResource(owner, podUrl)
154
- : models_1.gatewayAccessKeyResource;
155
- const credentialListResource = this.usesDefaultDbFactory
156
- ? createClientCredentialListResource(podUrl)
157
- : models_1.credentialResource;
158
- const db = await this.dbFactory({
159
- owner,
160
- auth: context?.auth,
161
- fetch: trustedFetch,
162
- podUrl,
163
- resource,
164
- listResource,
165
- credentialListResource,
166
- });
167
- await db.init?.(resource, listResource, models_1.credentialResource, credentialListResource);
168
- return { db, resource, listResource, credentialListResource, fetch: trustedFetch, podUrl };
169
- }
170
- async resolveTrustedFetch(owner, podUrl, context) {
171
- const auth = context?.auth;
172
- const trustedFetch = await this.podAccess?.getPodFetch(owner, {
173
- ...(auth ? { auth } : {}),
174
- podBaseUrl: podUrl,
175
- });
176
- if (!trustedFetch) {
177
- const reason = (0, OwnerPodAccess_1.podAccessError)(owner, auth);
178
- // The reason decides what the user has to do, and it is invisible in the wire response.
179
- (0, global_logger_factory_1.getLoggerFor)('PodGatewayAccessKeyRepository').warn(`No Pod fetch for ${owner}: ${reason}`);
180
- throw new Error(reason);
181
- }
182
- return this.wrapPodFetch(trustedFetch, owner);
183
- }
184
- wrapPodFetch(trustedFetch, owner) {
185
- return async (input, init) => {
186
- // Comunica can inject a malformed content-length value; let the runtime recompute it.
187
- const headers = new Headers(input instanceof Request ? input.headers : undefined);
188
- if (init?.headers) {
189
- new Headers(init.headers).forEach((value, key) => headers.set(key, value));
190
- }
191
- headers.delete('content-length');
192
- const response = await trustedFetch(input, { ...init, headers });
193
- if (response.status === 403) {
194
- const url = input instanceof Request ? input.url : String(input);
195
- (0, global_logger_factory_1.getLoggerFor)('PodGatewayAccessKeyRepository').warn(`The Pod refused the caller's credential for ${owner}: ${url}`);
196
- throw new Error('service_access_missing');
197
- }
198
- return response;
199
- };
200
- }
201
- }
202
- exports.PodGatewayAccessKeyRepository = PodGatewayAccessKeyRepository;
203
- /** Derived provider id of the built-in Xpod gateway provider; no provider row is persisted. */
204
- function clientCredentialProviderId() {
205
- return models_1.aiProviderResource.buildId({ id: 'xpod-gateway.ttl#this' });
206
- }
207
- function clientCredentialStorageId(locator) {
208
- return models_1.credentialResource.buildId({ id: locator });
209
- }
210
- function clientCredentialLocator(storageId) {
211
- const decoded = decodeStorageId(storageId);
212
- const fragment = decoded.lastIndexOf('#');
213
- const locator = fragment >= 0 ? decoded.slice(fragment + 1) : decoded;
214
- return locator || undefined;
215
- }
216
- function clientCredentialRecord(id, locator, row) {
217
- const createdAt = toDate(row.createdAt);
218
- const clientCredentialId = typeof row.clientCredentialId === 'string' ? row.clientCredentialId : undefined;
219
- if (!createdAt || !clientCredentialId || !locator)
220
- return undefined;
221
- return {
222
- id,
223
- owner: locator.owner,
224
- kind: 'client-credentials',
225
- clientCredentialId,
226
- name: typeof row.label === 'string' ? row.label : undefined,
227
- status: typeof row.status === 'string' ? row.status : undefined,
228
- createdAt,
229
- deployment: locator.deployment,
230
- scopes: [],
231
- secretHash: '',
232
- lastUsedAt: toDate(row.lastUsedAt),
233
- appliedTo: typeof row.appliedTo === 'string' ? row.appliedTo : undefined,
234
- appliedOn: typeof row.appliedOn === 'string' ? row.appliedOn : undefined,
235
- appliedAt: toDate(row.appliedAt),
236
- };
237
- }
238
- function toClientCredentialInsert(record) {
239
- return {
240
- id: clientCredentialStorageId(record.id),
241
- provider: clientCredentialProviderId(),
242
- authMode: 'apiKey',
243
- service: 'xpod-gateway',
244
- status: record.status ?? 'active',
245
- label: record.name,
246
- clientCredentialId: record.clientCredentialId,
247
- createdAt: record.createdAt,
248
- lastUsedAt: record.lastUsedAt,
249
- appliedTo: record.appliedTo,
250
- appliedOn: record.appliedOn,
251
- appliedAt: record.appliedAt,
252
- };
253
- }
254
- function createClientCredentialListResource(podUrl) {
255
- const resource = (0, drizzle_solid_1.alias)(models_1.credentialResource, 'clientCredentialList');
256
- resource.setSparqlEndpoint(`${podUrl.replace(/\/$/u, '')}/settings/-/sparql`);
257
- return resource;
258
- }
259
- function createDefaultGatewayAccessKeyDb(input) {
260
- const resource = input.resource ?? models_1.gatewayAccessKeyResource;
261
- const listResource = input.listResource ?? resource;
262
- return Promise.resolve((0, drizzle_solid_1.drizzle)({
263
- fetch: input.fetch,
264
- info: { webId: input.owner, podUrl: input.podUrl, isLoggedIn: true },
265
- }, {
266
- podUrl: input.podUrl,
267
- resourcePreparation: 'off',
268
- schema: {
269
- gatewayAccessKey: resource,
270
- gatewayAccessKeyList: listResource,
271
- credential: models_1.credentialResource,
272
- credentialList: input.credentialListResource ?? models_1.credentialResource,
273
- // The credential `provider` column is a link into the provider document;
274
- // resolving the derived gateway provider id needs that table in scope.
275
- aiProvider: models_1.aiProviderResource,
276
- },
277
- }));
278
- }
279
- function createGatewayAccessKeyResource(owner, podUrl) {
280
- const resource = (0, drizzle_solid_1.alias)(models_1.gatewayAccessKeyResource, 'gatewayAccessKeyList');
281
- resource.setSparqlEndpoint((0, AiConnectionsServiceAccess_1.resolveGatewayAccessKeySparqlEndpoint)(owner, podUrl));
282
- return resource;
283
- }
284
- function toGatewayAccessKeyInsert(record) {
285
- return {
286
- id: gatewayAccessKeyStorageId(record.id),
287
- owner: record.owner,
288
- secretHash: record.secretHash,
289
- deployment: record.deployment,
290
- scopes: record.scopes,
291
- createdAt: record.createdAt,
292
- expiresAt: record.expiresAt,
293
- lastUsedAt: record.lastUsedAt,
294
- disabledAt: record.disabledAt,
295
- revokedAt: record.revokedAt,
296
- name: record.name,
297
- };
298
- }
299
- function recordFromRow(row) {
300
- return {
301
- id: gatewayAccessKeyLocatorFromStorageId(String(row.id)),
302
- owner: String(row.owner),
303
- secretHash: String(row.secretHash),
304
- deployment: row.deployment === 'local' ? 'local' : 'cloud',
305
- scopes: Array.isArray(row.scopes) ? row.scopes.map(String) : [],
306
- createdAt: toDate(row.createdAt) ?? new Date(0),
307
- expiresAt: toDate(row.expiresAt),
308
- lastUsedAt: toDate(row.lastUsedAt),
309
- disabledAt: toDate(row.disabledAt),
310
- revokedAt: toDate(row.revokedAt),
311
- name: typeof row.name === 'string' ? String(row.name) : undefined,
312
- };
313
- }
314
- function gatewayAccessKeyStorageId(locator) {
315
- return models_1.gatewayAccessKeyResource.buildId({ id: locator });
316
- }
317
- function gatewayAccessKeyLocatorFromStorageId(id) {
318
- const decoded = decodeStorageId(id);
319
- const fragment = decoded.lastIndexOf('#');
320
- return fragment >= 0 ? decoded.slice(fragment + 1) : decoded;
321
- }
322
- function decodeStorageId(id) {
323
- try {
324
- return decodeURIComponent(id);
325
- }
326
- catch {
327
- return id;
328
- }
329
- }
330
- function toDate(value) {
331
- if (value instanceof Date) {
332
- return value;
333
- }
334
- if (typeof value === 'string' || typeof value === 'number') {
335
- const parsed = new Date(value);
336
- return Number.isNaN(parsed.getTime()) ? undefined : parsed;
337
- }
338
- return undefined;
339
- }
340
- //# sourceMappingURL=PodGatewayAccessKeyRepository.js.map
@@ -1,5 +0,0 @@
1
- export declare function resolvePersistentGatewayLocatorSecret(options: {
2
- databaseUrl: string;
3
- edition: 'local' | 'cloud';
4
- }): string;
5
- export declare function secretPathForGatewayLocatorDatabase(databaseUrl: string): string | undefined;
@@ -1,30 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolvePersistentGatewayLocatorSecret = resolvePersistentGatewayLocatorSecret;
4
- exports.secretPathForGatewayLocatorDatabase = secretPathForGatewayLocatorDatabase;
5
- const database_url_1 = require("./database-url");
6
- const local_secret_file_1 = require("./local-secret-file");
7
- function resolvePersistentGatewayLocatorSecret(options) {
8
- if (options.edition === 'cloud') {
9
- throw new Error('XPOD_GATEWAY_LOCATOR_SECRET is required for Cloud Gateway API keys; configure one stable shared value across replicas.');
10
- }
11
- const databasePath = (0, database_url_1.sqliteDatabaseFilePath)(options.databaseUrl);
12
- if (!databasePath) {
13
- throw new Error('XPOD_GATEWAY_LOCATOR_SECRET is required when CSS_IDENTITY_DB_URL is not a file-backed SQLite database.');
14
- }
15
- return (0, local_secret_file_1.readOrCreateLocalSecret)({
16
- databasePath,
17
- purpose: 'gateway-locator-secret',
18
- label: 'Gateway locator secret',
19
- // Keep existing locator strings readable; only newly generated values are fixed at 32 bytes.
20
- isValid: value => /^[A-Za-z0-9_-]{32,}$/u.test(value),
21
- });
22
- }
23
- function secretPathForGatewayLocatorDatabase(databaseUrl) {
24
- const databasePath = (0, database_url_1.sqliteDatabaseFilePath)(databaseUrl);
25
- return databasePath ? secretPathForDatabase(databasePath) : undefined;
26
- }
27
- function secretPathForDatabase(databasePath) {
28
- return (0, local_secret_file_1.localSecretPathForDatabase)(databasePath, 'gateway-locator-secret');
29
- }
30
- //# sourceMappingURL=gateway-locator-secret.js.map
@@ -1,2 +0,0 @@
1
- import{b as De,r as l,j as e,N as Re}from"./dashboard-Or0gnkou.js";import{T as Pe,u as Te,h as Le,b as Ae,c as Ee,d as $e,f as Me,e as Ie,t as _e}from"./nav-item-style-CW1Nnorq.js";import{P as Oe}from"./PaneListHeader-DtvnRmFq.js";import{c as C}from"./createLucideIcon-BJQl82Qk.js";import{t as H,m as re,R as qe,g as se,S as ze}from"./XpodShellApp-D_KY4nCW.js";import{B as y}from"./button-wslF55uZ.js";import{f as P,C as T,b as L,d as A,e as E}from"./PodDeletionAuthorizationPanel-BJk_8rJq.js";import{B as q,S as me}from"./section-header-CEIEcco-.js";import{L as ne,a as xe}from"./list-UyhlCFKk.js";import{a as he}from"./setting-row-CaYv3dfF.js";import"./xpod-remembered-login-BUF9fin1.js";import"./account-session-DlcKNG7k.js";import"@comunica/query-sparql-solid";import"./safe-continuation-ClWAhcma.js";const Ue=[["path",{d:"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2",key:"169zse"}]],be=C("activity",Ue);const He=[["path",{d:"M17 19a1 1 0 0 1-1-1v-2a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2a1 1 0 0 1-1 1z",key:"trhst0"}],["path",{d:"M17 21v-2",key:"ds4u3f"}],["path",{d:"M19 14V6.5a1 1 0 0 0-7 0v11a1 1 0 0 1-7 0V10",key:"1mo9zo"}],["path",{d:"M21 21v-2",key:"eo0ou"}],["path",{d:"M3 5V3",key:"1k5hjh"}],["path",{d:"M4 10a2 2 0 0 1-2-2V6a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2a2 2 0 0 1-2 2z",key:"1dd30t"}],["path",{d:"M7 5V3",key:"1t1388"}]],Ve=C("cable",He);const Be=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],Fe=C("download",Be);const Xe=[["path",{d:"M21.54 15H17a2 2 0 0 0-2 2v4.54",key:"1djwo0"}],["path",{d:"M7 3.34V5a3 3 0 0 0 3 3a2 2 0 0 1 2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2c0-1.1.9-2 2-2h3.17",key:"1tzkfa"}],["path",{d:"M11 21.95V18a2 2 0 0 0-2-2a2 2 0 0 1-2-2v-1a2 2 0 0 0-2-2H2.05",key:"14pb5j"}],["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]],ye=C("earth",Xe);const Ke=[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]],we=C("external-link",Ke);const We=[["circle",{cx:"12",cy:"16",r:"1",key:"1au0dj"}],["rect",{x:"3",y:"10",width:"18",height:"12",rx:"2",key:"6s8ecr"}],["path",{d:"M7 10V7a5 5 0 0 1 10 0v3",key:"1pqi11"}]],Ge=C("lock-keyhole",We);const Je=[["rect",{x:"16",y:"16",width:"6",height:"6",rx:"1",key:"4q2zg0"}],["rect",{x:"2",y:"16",width:"6",height:"6",rx:"1",key:"8cvhb9"}],["rect",{x:"9",y:"2",width:"6",height:"6",rx:"1",key:"1egb70"}],["path",{d:"M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3",key:"1jsf9p"}],["path",{d:"M12 12V8",key:"2874zd"}]],Ne=C("network",Je);const Qe=[["path",{d:"M16.247 7.761a6 6 0 0 1 0 8.478",key:"1fwjs5"}],["path",{d:"M19.075 4.933a10 10 0 0 1 0 14.134",key:"ehdyv1"}],["path",{d:"M4.925 19.067a10 10 0 0 1 0-14.134",key:"1q22gi"}],["path",{d:"M7.753 16.239a6 6 0 0 1 0-8.478",key:"r2q7qm"}],["circle",{cx:"12",cy:"12",r:"2",key:"1c9p78"}]],Ye=C("radio",Qe);const Ze=[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}]],ea=C("rotate-ccw",Ze);const aa=[["circle",{cx:"6",cy:"19",r:"3",key:"1kj8tv"}],["path",{d:"M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15",key:"1d8sl"}],["circle",{cx:"18",cy:"5",r:"3",key:"gq8acd"}]],ta=C("route",aa);const sa=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],na=C("shield-check",sa);const ia=[["path",{d:"m10.586 5.414-5.172 5.172",key:"4mc350"}],["path",{d:"m18.586 13.414-5.172 5.172",key:"8c96vv"}],["path",{d:"M6 12h12",key:"8npq4p"}],["circle",{cx:"12",cy:"20",r:"2",key:"144qzu"}],["circle",{cx:"12",cy:"4",r:"2",key:"muu5ef"}],["circle",{cx:"20",cy:"12",r:"2",key:"1xzzfp"}],["circle",{cx:"4",cy:"12",r:"2",key:"1hvhnz"}]],ra=C("waypoints",ia);const la=[["path",{d:"M12 20h.01",key:"zekei9"}],["path",{d:"M2 8.82a15 15 0 0 1 20 0",key:"dnpr2z"}],["path",{d:"M5 12.859a10 10 0 0 1 14 0",key:"1x1e6c"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0",key:"1bycff"}]],oa=C("wifi",la);async function da({fetchImpl:a}){return F(a,B("/api/network/settings/status"),{method:"GET",credentials:"include",headers:{accept:"application/json"}})}async function ca({fetchImpl:a}){return F(a,B("/api/network/settings/diagnose"),{method:"POST",credentials:"include",headers:{accept:"application/json"}})}async function ke({fetchImpl:a}){return F(a,B("/api/network/settings/tunnel-clients"),{method:"GET",credentials:"include",headers:{accept:"application/json"}})}async function ua({fetchImpl:a,provider:t}){return F(a,B(`/api/network/settings/tunnel-clients/${encodeURIComponent(t)}/install`),{method:"POST",credentials:"include",headers:{accept:"application/json"}})}async function ma({fetchImpl:a}){await F(a,B("/api/network/settings/certificate/renew"),{method:"POST",credentials:"include",headers:{accept:"application/json"}})}async function xa({podUrl:a,fetchImpl:t,patch:s}){return F(t,ha("/api/network/settings/configuration",a),{method:"PUT",credentials:"include",headers:{accept:"application/json","content-type":"application/json"},body:JSON.stringify(s)})}function B(a){const t=typeof window>"u"?"http://localhost":window.location.origin;return new URL(a,t).toString()}function ha(a,t){return t?new URL(a,t).toString():B(a)}async function F(a,t,s){const i=await a(t,s);if(!i.ok)throw await i.arrayBuffer(),new Error("Network settings request failed. Please try again.");if(!i.headers.get("content-type")?.includes("application/json"))throw await i.arrayBuffer(),new Error("Network settings request failed. Please try again.");return await i.json()}const ie=[{id:"overview",label:"Overview",path:"",icon:Ne},{id:"endpoints",label:"Endpoints",path:"endpoints",icon:Ve},{id:"addresses",label:"Addresses",path:"addresses",icon:ye},{id:"domain-dns",label:"Domain & DNS",path:"domain-dns",icon:ta},{id:"https",label:"HTTPS",path:"https",icon:Ge},{id:"tunnel-profiles",label:"Tunnel Profiles",path:"tunnel-profiles",icon:ra},{id:"p2p",label:"P2P",path:"p2p",icon:Ye},{id:"diagnostics",label:"Diagnostics",path:"diagnostics",icon:be}];function pa(a){const t=["Xpod Network Diagnostics",`Generated: ${a.generatedAt}`,`Canonical URL: ${a.endpoint??"Unavailable"}`],s=a.checks.flatMap(i=>["",`[${i.status.toUpperCase()}] ${i.label}`,...i.detail?[i.detail]:[]]);return[...t,...s].join(`
2
- `)}function ga({embedded:a=!1}){const t=De(),[s,i]=l.useState(),[n,c]=l.useState([]),[o,g]=l.useState(),[f,d]=l.useState(),[m,v]=l.useState(!1),[b,$]=l.useState(!1),[x,N]=l.useState(!1),[u,r]=l.useState(!1),[h,D]=l.useState(),R=l.useRef(0),X=l.useRef(0),K=l.useRef(0),W=l.useRef(void 0),G=l.useRef(!0),J=typeof window>"u"?void 0:window.location.origin,M=J?`local-host:${J}`:void 0,_=globalThis.fetch;l.useEffect(()=>(G.current=!0,()=>{G.current=!1,R.current+=1,X.current+=1,K.current+=1}),[]);const Q=l.useCallback((p,j)=>G.current&&R.current===p&&W.current===j,[]),Y=l.useCallback((p,j)=>G.current&&X.current===p&&W.current===j,[]),Z=l.useCallback((p,j)=>G.current&&K.current===p&&W.current===j,[]),z=l.useCallback(async()=>{const p=M;if(!p)return;const j=R.current+1;R.current=j,v(!0),d(void 0);try{const I=await da({fetchImpl:_});Q(j,p)&&i(I)}catch{Q(j,p)&&d("网络设置请求失败,请重试。")}finally{Q(j,p)&&v(!1)}},[_,M,Q]);l.useEffect(()=>{let p=!1;return W.current=M,R.current+=1,X.current+=1,K.current+=1,queueMicrotask(()=>{p||W.current!==M||(i(void 0),c([]),g(void 0),d(void 0),v(!1),$(!1),N(!1),M&&z())}),()=>{p=!0}},[M,z]);const oe=l.useCallback(async()=>{const p=M;if(!p)return;const j=X.current+1;X.current=j,$(!0),d(void 0);try{const I=await ca({fetchImpl:_});Y(j,p)&&(c(I.checks),g(new Date),H({description:"诊断完成"}))}catch{Y(j,p)&&d("网络诊断失败,请重试。")}finally{Y(j,p)&&$(!1)}},[_,M,Y]),de=l.useCallback(async()=>{const p=M;if(!p)return;const j=K.current+1;K.current=j,N(!0),d(void 0);try{await ma({fetchImpl:_}),Z(j,p)&&(H({variant:"success",description:"证书续签成功"}),await z())}catch{Z(j,p)&&d("证书续签失败,请重试。")}finally{Z(j,p)&&N(!1)}},[_,M,Z,z]),O=l.useCallback(async p=>{if(J){r(!0),d(void 0);try{const j=await xa({fetchImpl:_,patch:p});i(I=>I&&{...I,configuration:j.configuration}),D(j.applyState),H({variant:"success",description:"Network configuration saved"})}catch{d("网络配置保存失败,请重试。")}finally{r(!1)}}},[_,J]),ce=l.useMemo(()=>[{key:"local",title:"本机",values:s?.addresses.local??[]},{key:"lan",title:"局域网",values:s?.addresses.lan??[]},{key:"public",title:"公网",values:s?.addresses.public??[]}],[s]),ue=t.pathname.split("/").filter(Boolean).at(-1),k=ie.some(p=>p.path===ue)?ue:"overview";return a?e.jsxs("section",{className:"space-y-5 p-6",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsx("p",{className:"text-sm leading-normal text-muted-foreground",children:"这台设备上的所有 Pod 共用"}),e.jsx(y,{variant:"outline",size:"sm",disabled:m,onClick:z,children:"刷新"})]}),f&&e.jsx("p",{role:"alert",className:"text-sm leading-normal text-destructive",children:f}),e.jsx("div",{className:"divide-y rounded-xl border border-border bg-card",children:ce.map(({key:p,values:j})=>e.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-3 p-4",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-sm leading-normal font-medium",children:p==="local"?"这台电脑":p==="lan"?"局域网":"其他网络"}),e.jsx("div",{className:"mt-1 break-all text-xs leading-normal text-muted-foreground",children:j.join(" · ")||"暂无地址"})]}),e.jsx("span",{className:"text-xs leading-normal text-muted-foreground",children:p==="local"&&s&&["localhost","127.0.0.1","[::1]"].includes(window.location.hostname)?"可访问":p==="public"&&n.some(I=>I.id==="endpoint-reachability"&&I.status==="ok")?"本机检测通过":j.length?"待检测":"未配置"})]},p))}),e.jsx(fe,{configuration:s?.configuration,providers:s?.providers??[],ingress:s?.ingress,saving:u,applyState:h,onSave:O}),e.jsx(je,{status:s,endpoint:s?.endpoint,diagnostics:n,diagnosing:b,renewing:x,onDiagnose:oe,onRenewCertificate:de}),e.jsxs("details",{children:[e.jsx("summary",{className:"cursor-pointer text-sm leading-normal text-muted-foreground",children:"开发者模式:网络配置"}),e.jsxs("div",{className:"mt-4 space-y-4",children:[e.jsx(pe,{configuration:s?.configuration,saving:u,applyState:h,onSave:O}),e.jsx(ge,{configuration:s?.configuration,saving:u,applyState:h,onSave:O}),e.jsx(ve,{configuration:s?.configuration,saving:u,applyState:h,onSave:O})]})]})]}):e.jsx(Pe,{mode:"auto",listHeader:e.jsx(Oe,{title:"Network"}),list:e.jsx(fa,{}),mainHeader:e.jsx(va,{title:ie.find(p=>(p.path||"overview")===k)?.label??"Overview",loading:m,stale:m&&!!s,onRefresh:z}),main:e.jsxs("section",{className:"flex min-h-full flex-col gap-4 bg-background p-6",children:[f?e.jsx("div",{role:"alert",className:"rounded-md border border-destructive/30 bg-destructive/10 px-4 py-3 text-sm leading-normal text-destructive",children:f}):null,(k==="overview"||k==="endpoints")&&e.jsx(Na,{endpoint:s?.endpoint,loading:m&&!s}),k==="overview"&&e.jsx(ja,{status:s}),(k==="overview"||k==="addresses")&&e.jsx(Ca,{sections:ce,loading:m&&!s,diagnostics:n,checkedAt:o}),k==="overview"&&e.jsx(Ta,{status:s}),k==="domain-dns"&&e.jsxs(e.Fragment,{children:[e.jsx(Ra,{status:s}),e.jsx(pe,{configuration:s?.configuration,saving:u,applyState:h,onSave:O})]}),k==="https"&&e.jsxs(e.Fragment,{children:[e.jsx(Pa,{status:s}),e.jsx(ge,{configuration:s?.configuration,saving:u,applyState:h,onSave:O})]}),k==="tunnel-profiles"&&e.jsxs(e.Fragment,{children:[e.jsx(ba,{title:"Observed tunnel",label:"Tunnel",capability:s?.tunnel}),e.jsx(fe,{configuration:s?.configuration,providers:s?.providers??[],ingress:s?.ingress,saving:u,applyState:h,onSave:O}),e.jsx(ya,{fetchImpl:_})]}),k==="p2p"&&e.jsx(ve,{configuration:s?.configuration,saving:u,applyState:h,onSave:O}),(k==="overview"||k==="diagnostics"||k==="https")&&e.jsx(je,{status:s,endpoint:s?.endpoint,diagnostics:n,diagnosing:b,renewing:x,onDiagnose:oe,onRenewCertificate:de})]}),className:"min-h-full"})}function fa(){const a=Te();return e.jsx("aside",{className:"h-full border-r border-border bg-muted/20 py-2",children:e.jsx("nav",{"aria-label":"Network sections","data-list-navigation":!0,children:ie.map(t=>{const s=t.icon,i=`/network${t.path?`/${t.path}`:""}`;return e.jsxs(Re,{to:i,onClick:()=>a.openMain(),onKeyDown:Le,className:({isActive:n})=>Ae(n,{compact:!1}),end:t.path==="",children:[e.jsx(s,{className:"h-4 w-4","aria-hidden":"true"}),t.label]},t.id)})})})}function va({title:a,loading:t,stale:s,onRefresh:i}){return e.jsxs("div",{className:"flex h-full min-w-0 items-center justify-between gap-4 px-4",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsxs("h1",{className:"text-sm leading-normal font-semibold text-foreground",children:["Network · ",a]}),e.jsx("div",{className:"truncate text-xs leading-normal text-muted-foreground","aria-live":"polite",children:s?"Refreshing · showing previous snapshot":"接入点、DNS、TLS、隧道与连通性诊断"})]}),e.jsx("div",{className:"flex items-center gap-2",children:e.jsxs(y,{type:"button",size:"sm",variant:"outline",onClick:i,disabled:t,children:[e.jsx(qe,{className:"mr-2 h-4 w-4","aria-hidden":"true"}),"刷新"]})})]})}function ja({status:a}){const t=a?.addresses.public[0]??a?.addresses.lan[0]??a?.addresses.local[0]??a?.endpoint,s=a?!a.dns.supported||a.dns.status==="unsupported"?"Configure Domain & DNS, then recheck observed DNS.":!a.tls.supported||!["valid","active","ready"].includes(a.tls.status)?"Review HTTPS configuration and certificate renewal evidence.":"No connectivity action is currently required.":"Refresh network status.",i=a?.tunnel.supported===!0;return e.jsxs(P,{children:[e.jsxs(T,{children:[e.jsx(L,{className:"text-base leading-normal",children:"Preferred access path"}),e.jsx(A,{className:"leading-normal",children:"First address this runtime has configured, in public → LAN → local order"})]}),e.jsxs(E,{className:"space-y-3 text-sm leading-normal",children:[e.jsx("div",{className:"break-words font-medium",children:t??"Not configured"}),e.jsxs("div",{className:"grid gap-2 sm:grid-cols-4",children:[e.jsxs(q,{variant:"outline",children:["Local ",a?.addresses.local.length?"configured":"not configured"]}),e.jsxs(q,{variant:"outline",children:["LAN ",a?.addresses.lan.length?"configured":"not configured"]}),e.jsxs(q,{variant:"outline",children:["Public ",a?.addresses.public.length?"configured":"not configured"]}),e.jsxs(q,{variant:"outline",children:["Tunnel ",i?a?.tunnel.status??"unknown":"not observed"]})]}),e.jsx("div",{className:"mt-1 text-xs leading-normal text-muted-foreground",children:"Configured addresses are not probe results; reachability is reported per route."}),e.jsxs("div",{className:"rounded-md border border-border bg-muted/30 p-3",children:[e.jsx("div",{className:"font-medium",children:"Next action"}),e.jsx("div",{className:"mt-1 text-muted-foreground",children:s})]})]})]})}function ba({title:a,label:t,capability:s,extra:i}){return e.jsxs(P,{children:[e.jsxs(T,{children:[e.jsx(L,{className:"text-base leading-normal",children:a}),e.jsx(A,{className:"leading-normal",children:"Observed runtime state"})]}),e.jsx(E,{children:e.jsx(V,{label:t,capability:s,extra:i})})]})}function ee({title:a}){return e.jsxs(P,{children:[e.jsxs(T,{children:[e.jsx(L,{className:"text-base leading-normal",children:a}),e.jsx(A,{className:"leading-normal",children:"设备配置"})]}),e.jsxs(E,{className:"text-sm leading-normal text-muted-foreground",children:["这台设备暂未提供",a,"配置。"]})]})}function pe({configuration:a,saving:t,applyState:s,onSave:i}){const[n,c]=l.useState(a?.domainDns),[o,g]=l.useState("");return l.useEffect(()=>{let f=!1;return queueMicrotask(()=>{f||c(a?.domainDns)}),()=>{f=!0}},[a]),n?e.jsxs(ae,{title:"Saved configuration",applyState:s,children:[e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsx(S,{name:"domain",label:"Domain",value:n.domain,onChange:f=>c({...n,domain:f})}),e.jsx(S,{name:"provider",label:"DNS provider",value:n.provider,onChange:f=>c({...n,provider:f})}),e.jsx(Ce,{name:"recordTtl",label:"Record TTL (seconds)",value:n.recordTtl,min:30,max:86400,onChange:f=>c({...n,recordTtl:f})}),e.jsx(S,{name:"dnsCredential",label:n.credentialConfigured?"更换凭据(已配置)":"凭据",value:o,type:"password",onChange:g})]}),e.jsx(le,{label:"Enable DDNS",checked:n.ddnsEnabled,onChange:f=>c({...n,ddnsEnabled:f})}),e.jsx(te,{label:"Save DNS configuration",saving:t,onClick:()=>i({domainDns:{domain:n.domain,ddnsEnabled:n.ddnsEnabled,provider:n.provider,recordTtl:n.recordTtl,...o?{credential:o}:{}}})})]}):e.jsx(ee,{title:"Domain & DNS"})}function ge({configuration:a,saving:t,applyState:s,onSave:i}){const[n,c]=l.useState(a?.https);return l.useEffect(()=>{let o=!1;return queueMicrotask(()=>{o||c(a?.https)}),()=>{o=!0}},[a]),n?e.jsxs(ae,{title:"Saved configuration",applyState:s,children:[e.jsx(le,{label:"Enable HTTPS",checked:n.enabled,onChange:o=>c({...n,enabled:o})}),e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsx(S,{name:"acmeEmail",label:"ACME email",value:n.acmeEmail,onChange:o=>c({...n,acmeEmail:o})}),e.jsx(S,{name:"acmeDomains",label:"Certificate domains",value:n.domains.join(", "),onChange:o=>c({...n,domains:o.split(",").map(g=>g.trim()).filter(Boolean)})}),e.jsx(S,{name:"certificatePath",label:"Certificate path",value:n.certificatePath??"",onChange:o=>c({...n,certificatePath:o})}),e.jsx(S,{name:"certificateKeyPath",label:"Private-key path",value:n.certificateKeyPath??"",onChange:o=>c({...n,certificateKeyPath:o})}),e.jsx(Ce,{name:"renewBeforeDays",label:"Renew before (days)",value:n.renewBeforeDays,min:1,max:90,onChange:o=>c({...n,renewBeforeDays:o})})]}),e.jsx(te,{label:"Save HTTPS configuration",saving:t,onClick:()=>i({https:n})})]}):e.jsx(ee,{title:"HTTPS"})}function ya({fetchImpl:a}){const[t,s]=l.useState(void 0),[i,n]=l.useState(void 0),[c,o]=l.useState(void 0),g=async()=>{n("inspect"),o(void 0);try{const d=await ke({fetchImpl:a});s(d.clients)}catch(d){o(d instanceof Error?d.message:String(d))}finally{n(void 0)}},f=async d=>{n(d),o(void 0);try{const m=await ua({fetchImpl:a,provider:d});s(m.clients)}catch(m){o(m instanceof Error?m.message:String(m))}finally{n(void 0)}};return e.jsxs(P,{children:[e.jsxs(T,{children:[e.jsx(L,{className:"text-base leading-normal",children:"Tunnel clients"}),e.jsx(A,{className:"leading-normal",children:"Clients are not bundled with this build: check where one comes from, or download it into the plugin directory."})]}),e.jsxs(E,{className:"space-y-3 text-sm leading-normal",children:[e.jsx(y,{type:"button",size:"sm",variant:"outline",onClick:g,disabled:i==="inspect",children:i==="inspect"?"Checking…":"Check clients"}),c&&e.jsx("div",{className:"text-destructive",children:c}),t&&e.jsx("div",{className:"space-y-2",children:t.map(d=>e.jsxs("div",{className:"flex flex-wrap items-center gap-2 rounded-md border border-border p-2",children:[e.jsx(q,{variant:d.state==="ready"?"default":"outline",children:d.state==="ready"?"Ready":"Missing"}),e.jsx("span",{className:"font-medium",children:d.label}),e.jsxs("span",{className:"text-xs leading-normal text-muted-foreground",children:[d.path??d.binary,d.version?` · ${d.version}`:"",d.source==="bundled"?" · plugin directory":""]}),d.state==="missing"&&e.jsx("span",{className:"text-xs leading-normal text-muted-foreground",children:d.installHint}),d.state==="missing"&&d.installable&&e.jsx(y,{type:"button",size:"sm",onClick:()=>f(d.provider),disabled:i===d.provider,children:i===d.provider?"Downloading…":"Download"}),d.state==="missing"&&!d.installable&&e.jsxs("span",{className:"text-xs leading-normal text-muted-foreground",children:["(",d.installableReason,")"]})]},d.provider))})]})]})}function fe({configuration:a,providers:t,ingress:s,saving:i,applyState:n,onSave:c}){const[o,g]=l.useState(a?.tunnelProfiles.activeProfileId??""),[f,d]=l.useState(a?.tunnelProfiles.profiles??[]),[m,v]=l.useState({});if(l.useEffect(()=>{let r=!1;return queueMicrotask(()=>{r||(g(a?.tunnelProfiles.activeProfileId??""),d(a?.tunnelProfiles.profiles??[]),v({}))}),()=>{r=!0}},[a]),!a)return e.jsx(ee,{title:"隧道"});const b=(r,h)=>d(D=>D.map(R=>R.id===r?{...R,...h}:R)),$=t.filter(r=>r.runtimeSupported),x=r=>t.find(h=>h.id===r),N=()=>{const r=`tunnel-${Date.now()}`,h=$[0]?.id??"ngrok";d(D=>[...D,{id:r,provider:h,label:"新隧道",credentialConfigured:!1,parameters:{}}])},u=r=>{d(h=>h.filter(D=>D.id!==r)),o===r&&g("none")};return e.jsxs(ae,{title:"隧道",applyState:n,children:[e.jsxs("label",{className:"block space-y-2 text-sm leading-normal font-medium",children:["主地址",e.jsxs("select",{value:o||"none",onChange:r=>g(r.target.value),className:"block h-10 w-full rounded-md border border-input bg-background px-3 sm:max-w-sm",children:[e.jsx("option",{value:"none",children:"停用"}),f.map(r=>e.jsxs("option",{value:r.id,children:[r.label," · ",r.provider]},r.id))]})]}),e.jsx("div",{className:"space-y-3",children:f.map(r=>e.jsxs("div",{className:"space-y-3 rounded-md border border-border p-3 text-sm leading-normal",children:[e.jsxs("div",{className:"grid gap-3 sm:grid-cols-2",children:[e.jsx(S,{name:`tunnel-label-${r.id}`,label:"名称",value:r.label,onChange:h=>b(r.id,{label:h})}),e.jsxs("label",{className:"block space-y-2 text-sm leading-normal font-medium",children:["隧道服务商",e.jsx("select",{value:r.provider,onChange:h=>b(r.id,{provider:h.target.value,parameters:{}}),className:"block h-10 w-full rounded-md border border-input bg-background px-3",children:t.map(h=>e.jsxs("option",{value:h.id,disabled:!h.runtimeSupported,children:[h.label,h.runtimeSupported?"":"(暂不支持)"]},h.id))})]}),x(r.provider)?.endpointSource==="declared"?e.jsx(S,{name:`tunnel-url-${r.id}`,label:"公网地址",value:r.publicUrl??"",onChange:h=>b(r.id,{publicUrl:h})}):e.jsx("p",{className:"text-xs leading-normal text-muted-foreground",children:"地址由隧道自动提供。"}),e.jsx(S,{name:`tunnel-credential-${r.id}`,label:r.credentialConfigured?"更换凭据(已配置)":"凭据",type:"password",value:m[r.id]??"",onChange:h=>v(D=>({...D,[r.id]:h}))})]}),e.jsxs("details",{children:[e.jsx("summary",{className:"cursor-pointer font-medium",children:"服务商参数"}),e.jsx("div",{className:"mt-3 grid gap-3 sm:grid-cols-2",children:(x(r.provider)?.parameterFields??[]).map(({key:h,label:D})=>e.jsx(S,{name:`tunnel-${h}-${r.id}`,label:D,value:r.parameters?.[h]??"",onChange:R=>b(r.id,{parameters:{...r.parameters,[h]:R}})},h))})]}),e.jsxs("div",{className:"flex justify-between",children:[e.jsxs("span",{className:"text-xs leading-normal text-muted-foreground",children:[o===r.id?"重启后启用":"已停用"," · ",r.credentialConfigured?"已配置凭据":"未配置凭据"]}),e.jsx(y,{type:"button",size:"sm",variant:"ghost",onClick:()=>u(r.id),children:"移除"})]})]},r.id))}),e.jsx(y,{type:"button",size:"sm",variant:"outline",onClick:N,children:"添加隧道"}),e.jsx(wa,{ingress:s,descriptor:x(f.find(r=>r.id===o)?.provider??"")}),e.jsx(te,{label:"保存隧道",saving:i,onClick:()=>c({tunnelProfiles:{activeProfileId:o,profiles:f.map(r=>({id:r.id,provider:r.provider,label:r.label,publicUrl:r.publicUrl,parameters:r.parameters,...m[r.id]?{credential:m[r.id]}:{}}))}})})]})}function wa({ingress:a,descriptor:t}){if(!a||t?.originOwner!=="console")return null;const s=async()=>{await window.navigator.clipboard.writeText(a.originUrl),H({description:"Tunnel origin copied"})};return e.jsxs("div",{className:"rounded-md border border-border bg-muted/30 p-3","data-testid":"ingress-origin",children:[e.jsx("div",{className:"text-xs leading-normal font-medium text-muted-foreground",children:"把隧道指向此地址"}),e.jsxs("div",{className:"mt-1 flex items-start justify-between gap-3",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"break-all font-mono text-sm leading-normal",children:a.originUrl}),e.jsx("div",{className:"mt-1 text-xs leading-normal text-muted-foreground",children:"在服务商控制台填写此地址。"})]}),e.jsxs("div",{className:"flex shrink-0 gap-1",children:[e.jsx(y,{type:"button",size:"icon",variant:"ghost","aria-label":"Copy tunnel origin",onClick:()=>{s()},children:e.jsx(re,{className:"h-4 w-4","aria-hidden":"true"})}),t.consoleUrl?e.jsx(y,{type:"button",size:"icon",variant:"ghost","aria-label":`Open ${t.label} console`,onClick:()=>window.open(t.consoleUrl,"_blank","noopener,noreferrer"),children:e.jsx(we,{className:"h-4 w-4","aria-hidden":"true"})}):null]})]})]})}function ve({configuration:a,saving:t,applyState:s,onSave:i}){const[n,c]=l.useState(a?.p2p);return l.useEffect(()=>{let o=!1;return queueMicrotask(()=>{o||c(a?.p2p)}),()=>{o=!0}},[a]),n?e.jsxs(ae,{title:"Saved P2P configuration",applyState:s,children:[e.jsx(le,{label:"Enable P2P fallback",checked:n.enabled,onChange:o=>c({...n,enabled:o})}),e.jsx(S,{name:"signalService",label:"Signal service",value:n.signalService,onChange:o=>c({...n,signalService:o})}),e.jsxs("label",{className:"block space-y-2 text-sm leading-normal font-medium",children:["Fallback policy",e.jsxs("select",{value:n.fallbackPolicy,onChange:o=>c({...n,fallbackPolicy:o.target.value}),className:"block h-10 w-full rounded-md border border-input bg-background px-3 sm:max-w-sm",children:[e.jsx("option",{value:"never",children:"Never"}),e.jsx("option",{value:"when-direct-unavailable",children:"When direct is unavailable"}),e.jsx("option",{value:"prefer-p2p",children:"Prefer P2P"})]})]}),e.jsx(te,{label:"Save P2P configuration",saving:t,onClick:()=>i({p2p:n})})]}):e.jsx(ee,{title:"P2P"})}function ae({title:a,applyState:t,children:s}){return e.jsxs(P,{children:[e.jsxs(T,{children:[e.jsx(L,{className:"text-base leading-normal",children:a}),e.jsx(A,{className:"leading-normal",children:"保存后重启 Xpod 生效"})]}),e.jsxs(E,{className:"space-y-4",children:[t==="restart-required"?e.jsx("div",{role:"status",className:"rounded-md border border-warning/40 bg-warning/10 px-3 py-2 text-sm leading-normal text-warning dark:bg-warning/20 dark:text-warning",children:"已保存,需要重启"}):null,s]})]})}function S({name:a,label:t,value:s,type:i="text",onChange:n}){return e.jsxs("label",{className:"block space-y-2 text-sm leading-normal font-medium",children:[t,e.jsx("input",{name:a,type:i,value:s,onChange:c=>n(c.target.value),className:"block h-10 w-full rounded-md border border-input bg-background px-3 text-sm leading-normal"})]})}function Ce({name:a,label:t,value:s,min:i,max:n,onChange:c}){return e.jsxs("label",{className:"block space-y-2 text-sm leading-normal font-medium",children:[t,e.jsx("input",{name:a,type:"number",value:s,min:i,max:n,onChange:o=>c(o.target.valueAsNumber),className:"block h-10 w-full rounded-md border border-input bg-background px-3 text-sm leading-normal"})]})}function le({label:a,checked:t,onChange:s}){return e.jsxs("label",{className:"flex items-center justify-between gap-3 rounded-md border border-border p-3 text-sm leading-normal font-medium",children:[a,e.jsx("input",{type:"checkbox",checked:t,onChange:i=>s(i.target.checked)})]})}function te({label:a,saving:t,onClick:s}){return e.jsx("div",{className:"flex justify-end",children:e.jsx(y,{type:"button",onClick:s,disabled:t,children:t?"正在保存…":a})})}function Na({endpoint:a,loading:t}){const s=[{label:"Canonical URL",value:a},{label:"API endpoint",value:a?ka(a,"/api/"):void 0}],i=async(n,c)=>{await window.navigator.clipboard.writeText(c),H({description:`${n} copied`})};return e.jsxs(P,{children:[e.jsxs(T,{children:[e.jsxs(L,{className:"flex items-center gap-2 text-base leading-normal",children:[e.jsx(ye,{className:"h-4 w-4","aria-hidden":"true"}),"接入点"]}),e.jsx(A,{className:"leading-normal",children:t?"正在解析接入点":"Observed endpoints · Currently effective route is the canonical URL"})]}),e.jsx(E,{className:"space-y-3",children:s.map(({label:n,value:c})=>e.jsxs("div",{className:"flex items-start justify-between gap-3 rounded-md border border-border p-3",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"text-xs leading-normal font-medium text-muted-foreground",children:n}),e.jsx("div",{className:"mt-1 break-words text-sm leading-normal text-foreground",children:c||"Unavailable"})]}),c?e.jsxs("div",{className:"flex shrink-0 gap-1",children:[e.jsx(y,{type:"button",size:"icon",variant:"ghost","aria-label":`Copy ${n}`,onClick:()=>{i(n,c)},children:e.jsx(re,{className:"h-4 w-4","aria-hidden":"true"})}),e.jsx(y,{type:"button",size:"icon",variant:"ghost","aria-label":`Open ${n}`,onClick:()=>window.open(c,"_blank","noopener,noreferrer"),children:e.jsx(we,{className:"h-4 w-4","aria-hidden":"true"})})]}):null]},n))})]})}function ka(a,t){try{return new URL(t,a).toString()}catch{return a}}function Ca({sections:a,loading:t,diagnostics:s,checkedAt:i}){return e.jsxs(P,{children:[e.jsxs(T,{children:[e.jsxs(L,{className:"flex items-center gap-2 text-base leading-normal",children:[e.jsx(oa,{className:"h-4 w-4","aria-hidden":"true"}),"网络地址"]}),e.jsx(A,{className:"leading-normal",children:t?"正在刷新地址":"由运行时网络能力上报"})]}),e.jsx(E,{className:"space-y-4",children:a.map(n=>e.jsxs("div",{children:[e.jsx("div",{className:"text-xs leading-normal font-medium text-muted-foreground",children:n.title}),n.values.length>0?e.jsx("div",{className:"mt-1 space-y-1",children:n.values.map(c=>e.jsx(Sa,{scope:n.key,value:c,diagnostics:s,checkedAt:i},c))}):e.jsx("div",{className:"mt-1 text-sm leading-normal text-muted-foreground",children:"未提供"})]},n.key))})]})}function Sa({scope:a,value:t,diagnostics:s,checkedAt:i}){const n=Da(t),c=s.find(o=>o.id==="address-configuration");return e.jsxs("div",{className:"rounded-md border border-border p-3 text-sm leading-normal",children:[e.jsx("div",{className:"break-words font-medium text-foreground",children:t}),e.jsxs("dl",{className:"mt-2 grid gap-x-4 gap-y-1 text-xs leading-normal sm:grid-cols-2",children:[e.jsx(w,{label:"Interface",value:a==="local"?"Loopback":a==="lan"?"LAN interface (name not reported)":"Public route"}),e.jsx(w,{label:"IP version",value:n.ipVersion}),e.jsx(w,{label:"Port",value:n.port}),e.jsx(w,{label:"Configured",value:c?c.status==="ok"?"Yes":"No":"Not checked"}),e.jsx(w,{label:"Reachability",value:"Not probed"}),e.jsx(w,{label:"Configuration checked",value:c?.checkedAt?U(c.checkedAt):i?i.toLocaleString():"Not checked"})]})]})}function w({label:a,value:t}){return e.jsxs("div",{className:"flex justify-between gap-3",children:[e.jsx("dt",{className:"text-muted-foreground",children:a}),e.jsx("dd",{className:"text-right text-foreground",children:t})]})}function Da(a){try{const t=new URL(a),s=t.hostname.replace(/^\[|\]$/g,"");return{ipVersion:s.includes(":")?"IPv6":/^\d{1,3}(?:\.\d{1,3}){3}$/.test(s)?"IPv4":"Resolved hostname",port:t.port||(t.protocol==="https:"?"443":t.protocol==="http:"?"80":"Not reported")}}catch{return{ipVersion:"Not reported",port:"Not reported"}}}function Ra({status:a}){const t=a?.configuration?.domainDns,s=(a?.addresses.public??[]).map(i=>{try{return new URL(i).hostname}catch{return i}});return e.jsxs(P,{children:[e.jsxs(T,{children:[e.jsx(L,{className:"text-base leading-normal",children:"Observed DNS"}),e.jsx(A,{className:"leading-normal",children:"Observed state is kept separate from desired DNS policy."})]}),e.jsxs(E,{className:"space-y-3",children:[e.jsx(V,{label:"Runtime status",capability:a?.dns}),e.jsxs("dl",{className:"grid gap-2 text-sm leading-normal sm:grid-cols-2",children:[e.jsx(w,{label:"Observed hostnames",value:s.join(", ")||"Not reported"}),e.jsx(w,{label:"Expected domain",value:t?.domain||"Not configured"}),e.jsx(w,{label:"Expected value",value:a?.addresses.public[0]||"Not reported"}),e.jsx(w,{label:"Provider",value:t?.provider||"Not configured"})]})]})]})}function Pa({status:a}){const t=a?.configuration?.https;return e.jsxs(P,{children:[e.jsxs(T,{children:[e.jsx(L,{className:"text-base leading-normal",children:"Observed HTTPS"}),e.jsx(A,{className:"leading-normal",children:"Certificate evidence reported by the active runtime."})]}),e.jsxs(E,{className:"space-y-3",children:[e.jsx(V,{label:"TLS",capability:a?.tls,extra:a?.tls.expiresAt?`Expires ${U(a.tls.expiresAt)}`:void 0}),e.jsxs("dl",{className:"grid gap-2 text-sm leading-normal sm:grid-cols-2",children:[e.jsx(w,{label:"Certificate domains",value:a?.tls.domains?.join(", ")||t?.domains.join(", ")||"Not reported"}),e.jsx(w,{label:"Issuer",value:a?.tls.issuer||"Not reported by runtime"}),e.jsx(w,{label:"Validity",value:a?.tls.validFrom?`${U(a.tls.validFrom)} — ${a.tls.expiresAt?U(a.tls.expiresAt):"open"}`:a?.tls.status??"Not reported"}),e.jsx(w,{label:"Expiry",value:a?.tls.expiresAt?U(a.tls.expiresAt):"Not reported"}),e.jsx(w,{label:"Renewal status",value:a?.tls.renewalStatus||(a?.actions.renewCertificate?"Renewal available":"Renewal unavailable")}),e.jsx(w,{label:"Renewal policy",value:t?`${t.renewBeforeDays} days before expiry`:"Not configured"})]})]})]})}function Ta({status:a}){return e.jsxs(P,{children:[e.jsxs(T,{children:[e.jsxs(L,{className:"flex items-center gap-2 text-base leading-normal",children:[e.jsx(Ne,{className:"h-4 w-4","aria-hidden":"true"}),"网络能力"]}),e.jsx(A,{className:"leading-normal",children:"服务端声明的网络支持"})]}),e.jsxs(E,{className:"grid gap-3 text-sm leading-normal",children:[e.jsx(V,{label:"TLS",capability:a?.tls,extra:a?.tls.expiresAt?`到期时间 ${U(a.tls.expiresAt)}`:void 0}),e.jsx(V,{label:"DNS",capability:a?.dns}),e.jsx(V,{label:"Tunnel",capability:a?.tunnel})]})]})}function je({status:a,endpoint:t,diagnostics:s,diagnosing:i,renewing:n,onDiagnose:c,onRenewCertificate:o}){const g=s.length>0?pa({generatedAt:new Date().toISOString(),endpoint:t,checks:s}):void 0,f=async()=>{g&&(await window.navigator.clipboard.writeText(g),H({description:"Diagnostic report copied"}))},d=()=>{if(!g)return;const m=URL.createObjectURL(new Blob([g],{type:"text/plain;charset=utf-8"})),v=document.createElement("a");v.href=m,v.download=`xpod-network-diagnostics-${new Date().toISOString().replace(/[:.]/g,"-")}.txt`,v.click(),URL.revokeObjectURL(m)};return e.jsxs(P,{children:[e.jsxs(T,{children:[e.jsxs(L,{className:"flex items-center gap-2 text-base leading-normal",children:[e.jsx(be,{className:"h-4 w-4","aria-hidden":"true"}),"操作"]}),e.jsx(A,{className:"leading-normal",children:"仅显示网络能力允许的操作"})]}),e.jsxs(E,{className:"space-y-4",children:[e.jsxs("div",{className:"flex flex-wrap gap-2",children:[a?.actions.diagnose?e.jsxs(y,{type:"button",size:"sm",variant:"outline",onClick:c,disabled:i,children:[e.jsx(na,{className:"mr-2 h-4 w-4","aria-hidden":"true"}),"连通性诊断"]}):null,a?.actions.renewCertificate?e.jsxs(y,{type:"button",size:"sm",variant:"subtle",onClick:o,disabled:n,children:[e.jsx(ea,{className:"mr-2 h-4 w-4","aria-hidden":"true"}),"续签证书"]}):null,e.jsxs(y,{type:"button",size:"sm",variant:"outline",onClick:()=>{f()},disabled:!g,children:[e.jsx(re,{className:"mr-2 h-4 w-4","aria-hidden":"true"}),"复制诊断"]}),e.jsxs(y,{type:"button",size:"sm",variant:"outline",onClick:d,disabled:!g,children:[e.jsx(Fe,{className:"mr-2 h-4 w-4","aria-hidden":"true"}),"导出诊断"]})]}),s.length>0?e.jsx("div",{className:"space-y-2",children:s.map(m=>e.jsxs("div",{className:"rounded-md border border-border bg-muted/30 p-3",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsx("div",{className:"text-sm leading-normal font-medium text-foreground",children:m.label}),e.jsx(q,{variant:m.status==="ok"?"secondary":"outline",children:m.status})]}),m.detail?e.jsx("div",{className:"mt-1 break-words text-xs leading-normal text-muted-foreground",children:m.detail}):null]},m.id))}):e.jsx("div",{className:"text-sm leading-normal text-muted-foreground",children:"尚未运行过诊断。"})]})]})}function V({label:a,capability:t,extra:s}){const i=t?.supported===!0;return e.jsxs("div",{className:"rounded-md border border-border bg-muted/30 p-3",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-sm leading-normal font-medium text-foreground",children:i?a:`${a} 不支持`}),e.jsxs("div",{className:"text-xs leading-normal text-muted-foreground",children:[t?.status??"读取中",t?.detail?` · ${t.detail}`:""]})]}),e.jsx(q,{variant:i?"secondary":"outline",children:i?"支持":"不支持"})]}),s?e.jsx("div",{className:"mt-2 text-xs leading-normal text-muted-foreground",children:s}):null]})}function U(a){if(!a)return"暂无记录";const t=new Date(a);return Number.isNaN(t.getTime())?"暂无记录":t.toLocaleString()}function Ka(){return e.jsx(ga,{embedded:!0})}const Se=[["gateway","入口网关"],["css","Solid 服务"],["api","API 服务"],["qlever","查询引擎(QLever)"],["inngest","任务调度(Inngest)"]],La={running:"运行中",stopped:"已停止",starting:"启动中",crashed:"运行异常",failed:"运行异常"};function Wa(){const[a,t]=l.useState(),[s,i]=l.useState([]),[n,c]=l.useState(),[o,g]=l.useState(!1),[f,d]=l.useState(""),m=l.useCallback(async()=>{g(!0),d("");try{const[x,N,u]=await Promise.all([Me(),ke({fetchImpl:fetch}).catch(()=>{}),globalThis.xpodDesktop?.deviceRuntime?.getRuntimeSettings?.()]);t(x),i(N?.clients??[]),c(u),!x.adminData&&!u&&d("暂时无法读取设备状态,请重试。")}catch{d("暂时无法读取设备状态,请重试。")}finally{g(!1)}},[]);l.useEffect(()=>{let x=!0;return queueMicrotask(()=>{x&&m()}),()=>{x=!1}},[m]);const v=async x=>{if(!(x!=="start"&&!window.confirm(`${x==="stop"?"停止":"重启"}这台设备上的 Xpod?正在进行的请求会中断。`))){g(!0),d("");try{if(globalThis.xpodDesktop?.deviceRuntime?.runtimeAction)await globalThis.xpodDesktop.deviceRuntime.runtimeAction(x);else if(x!=="restart"||!await _e())throw new Error;await m()}catch{d("操作未完成,请重试。")}finally{g(!1)}}},b=Array.from(new Map(s.map(x=>[x.binary,x])).values()),$=n?.state==="stopped"||n?.state==="failed";return e.jsxs("section",{className:"space-y-6 p-6",children:[e.jsxs("div",{className:"flex flex-wrap gap-2",children:[$?e.jsx(y,{className:"h-auto min-h-9 py-1 leading-normal",disabled:o,onClick:()=>{v("start")},children:"启动 Xpod"}):e.jsxs(e.Fragment,{children:[e.jsx(y,{className:"h-auto min-h-9 py-1 leading-normal",variant:"outline",disabled:o,onClick:()=>{v("restart")},children:"重启 Xpod"}),e.jsx(y,{className:"h-auto min-h-9 py-1 leading-normal",variant:"outline",disabled:o||n?.ownership!=="desktop",onClick:()=>{v("stop")},children:"停止 Xpod"})]}),e.jsx(y,{className:"h-auto min-h-9 py-1 leading-normal",variant:"ghost",disabled:o,onClick:m,children:"刷新"})]}),f&&e.jsx("p",{role:"alert",className:"text-sm leading-normal text-destructive",children:f}),e.jsxs("section",{children:[e.jsx(me,{level:2,title:"核心服务",className:"mb-2",titleClassName:"text-muted-foreground"}),e.jsx(ne,{children:Se.map(([x,N])=>{const u=a?.servicesData?.find(r=>r.name===x)?.status??(x==="gateway"&&a?.adminData?"running":void 0);return e.jsx(xe,{title:N,trailing:e.jsx("span",{className:u==="running"?"text-success":"text-muted-foreground",children:u?La[u]:"未报告"})},x)})})]}),e.jsxs("section",{children:[e.jsx(me,{level:2,title:"可选组件",className:"mb-2",titleClassName:"text-muted-foreground"}),e.jsxs(ne,{children:[b.map(x=>e.jsx(xe,{title:x.binary,trailing:e.jsx("span",{children:x.state==="missing"?"未安装":a?.servicesData?.some(N=>N.name===x.binary&&N.status==="running")?"运行中":"未使用"}),children:x.state==="missing"&&e.jsxs("details",{children:[e.jsx("summary",{className:"cursor-pointer text-primary",children:"安装方法"}),e.jsx("p",{className:"mt-2 break-words text-xs leading-normal text-muted-foreground",children:x.installHint})]})},x.binary)),!b.length&&e.jsx("p",{className:"p-4 text-sm leading-normal text-muted-foreground",children:"暂未读取到可选组件状态"})]})]}),e.jsx("p",{className:"text-xs leading-normal text-muted-foreground",children:"停止只影响这台设备;Xpod 云端和其他设备上的 Pod 不受影响。"})]})}function Ga(){const[a,t]=l.useState(),[s,i]=l.useState(""),[n,c]=l.useState(""),[o,g]=l.useState(""),[f,d]=l.useState("");l.useEffect(()=>{Promise.all([globalThis.xpodDesktop?.deviceRuntime?.getRuntimeSettings?.(),$e()]).then(([v,b])=>{t(v),i(v?.dataDirectory??b?.env.CSS_ROOT_FILE_PATH??""),c(b?.env.XPOD_EDITION??"")}).catch(()=>g("无法读取运行设置,请重试。"))},[]);const m=async()=>{g("");try{const v=await globalThis.xpodDesktop?.deviceRuntime?.selectDataDirectory?.();if(!v)return;if(!await Ie({CSS_ROOT_FILE_PATH:v}))throw new Error;i(v),d("已保存,重启后使用新位置;原有数据不会自动迁移。")}catch{g("无法更改数据位置,请重试。")}};return e.jsxs("section",{className:"space-y-5 p-6 text-sm leading-normal",children:[o&&e.jsx("p",{role:"alert",className:"text-destructive",children:o}),f&&e.jsx("p",{role:"status",children:f}),e.jsx(he,{label:"开机时启动 Xpod",checked:a?.launchAtLogin??!1,disabled:!a||!globalThis.xpodDesktop?.deviceRuntime?.setLaunchAtLogin,onCheckedChange:async v=>{try{await globalThis.xpodDesktop?.deviceRuntime?.setLaunchAtLogin?.(v),t(b=>b&&{...b,launchAtLogin:v})}catch{g("无法保存开机启动设置。")}}}),e.jsx(he,{label:"意外退出时自动重启",description:"你手动停止的不会被重启",checked:a?.autoRestart??!1,disabled:!a||!globalThis.xpodDesktop?.deviceRuntime?.setAutoRestart,onCheckedChange:async v=>{try{await globalThis.xpodDesktop?.deviceRuntime?.setAutoRestart(v),t(b=>b&&{...b,autoRestart:v})}catch{g("无法保存自动重启设置。")}}}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("div",{children:"数据位置"}),e.jsx("p",{className:"break-all text-muted-foreground",children:s||"未报告"}),e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsxs(y,{className:"h-auto min-h-9 py-1 leading-normal",variant:"outline",disabled:!globalThis.xpodDesktop?.deviceRuntime?.showDataDirectory,onClick:()=>{globalThis.xpodDesktop?.deviceRuntime?.showDataDirectory?.().catch(()=>g("无法打开数据位置。"))},children:["在",globalThis.xpodDesktop?.platform==="darwin"?"访达":"文件管理器","中显示"]}),e.jsx(y,{className:"h-auto min-h-9 py-1 leading-normal",variant:"outline",disabled:!globalThis.xpodDesktop?.deviceRuntime?.selectDataDirectory,onClick:()=>{m()},children:"更改"})]})]}),e.jsxs("div",{className:"flex justify-between border-t border-border pt-4",children:[e.jsx("span",{children:"运行方式"}),e.jsx("span",{children:n==="cloud"?"Xpod 云端":n==="local"?"这台设备":n==="standalone"?"Xpod 独立运行":"未报告"})]}),e.jsxs("details",{children:[e.jsx("summary",{className:"cursor-pointer text-muted-foreground",children:"开发者模式"}),e.jsxs("dl",{className:"mt-3 space-y-2",children:[e.jsxs("div",{children:["访问地址:",window.location.origin]}),e.jsxs("div",{children:["端口:",window.location.port||(window.location.protocol==="https:"?"443":"80")]})]})]})]})}function Ja(){const[a,t]=l.useState([]),[s,i]=l.useState(()=>new URLSearchParams(window.location.search).get("source")??"all"),[n,c]=l.useState("all"),[o,g]=l.useState("all"),[f,d]=l.useState(()=>Date.now()),[m,v]=l.useState(""),[b,$]=l.useState(!1),x=l.useCallback(async()=>{$(!0);try{t(await Ee({source:s,level:n,limit:500})),d(Date.now())}finally{$(!1)}},[s,n]);l.useEffect(()=>{let u=!0;return queueMicrotask(()=>{u&&x()}),()=>{u=!1}},[x]);const N=a.filter(u=>(o==="all"||new Date(u.timestamp).getTime()>=f-Number(o))&&`${u.source} ${u.message}`.toLowerCase().includes(m.toLowerCase()));return e.jsxs("section",{className:"space-y-4 p-6",children:[e.jsxs("div",{className:"flex flex-wrap gap-3 text-sm leading-normal",children:[e.jsxs("label",{className:"grid w-full min-w-0 grid-cols-[auto_minmax(0,1fr)] items-center gap-2 sm:w-auto",children:["来源",e.jsxs(se,{value:s,onChange:u=>i(u.target.value),children:[e.jsx("option",{value:"all",children:"全部来源"}),Se.map(([u,r])=>e.jsx("option",{value:u,children:r},u))]})]}),e.jsxs("label",{className:"grid w-full min-w-0 grid-cols-[auto_minmax(0,1fr)] items-center gap-2 sm:w-auto",children:["级别",e.jsxs(se,{value:n,onChange:u=>c(u.target.value),children:[e.jsx("option",{value:"all",children:"全部级别"}),e.jsx("option",{value:"error",children:"错误"}),e.jsx("option",{value:"warn",children:"警告"}),e.jsx("option",{value:"info",children:"信息"}),e.jsx("option",{value:"debug",children:"调试"})]})]}),e.jsxs("label",{className:"grid w-full min-w-0 grid-cols-[auto_minmax(0,1fr)] items-center gap-2 sm:w-auto",children:["时间",e.jsxs(se,{value:o,onChange:u=>g(u.target.value),children:[e.jsx("option",{value:"all",children:"全部时间"}),e.jsx("option",{value:"3600000",children:"最近一小时"}),e.jsx("option",{value:"86400000",children:"最近一天"}),e.jsx("option",{value:"604800000",children:"最近七天"})]})]}),e.jsx(ze,{"aria-label":"搜索日志",value:m,onChange:u=>v(u.target.value),placeholder:"搜索日志"}),e.jsx(y,{className:"h-auto min-h-9 py-1 leading-normal",variant:"outline",disabled:b,onClick:x,children:"刷新"})]}),e.jsxs(ne,{children:[N.map((u,r)=>e.jsxs("div",{className:"p-3 text-xs leading-normal",children:[e.jsxs("div",{className:"text-muted-foreground",children:[new Date(u.timestamp).toLocaleString()," · ",u.source," · ",u.level]}),e.jsx("pre",{className:"mt-1 whitespace-pre-wrap break-words font-mono leading-normal",children:u.message})]},`${u.timestamp}-${r}`)),!N.length&&e.jsx("p",{className:"p-4 text-sm leading-normal text-muted-foreground",children:b?"正在读取日志…":"没有匹配的日志"})]}),e.jsx("p",{className:"text-xs leading-normal text-muted-foreground",children:"显示最近 500 条日志中的匹配结果"})]})}export{Ja as DeviceLogsPage,Ka as DeviceNetworkPage,Ga as DeviceRuntimePage,Wa as DeviceServicesPage};