@undefineds.co/xpod 0.3.70 → 0.3.71-rc.13

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 (172) hide show
  1. package/config/cloud.json +3 -0
  2. package/config/local.json +3 -0
  3. package/config/resolver.json +7 -0
  4. package/config/xpod.base.json +20 -0
  5. package/dist/agents/ClaudeExecutor.js +1 -1
  6. package/dist/agents/ClaudeExecutor.js.map +1 -1
  7. package/dist/agents/types.d.ts +5 -5
  8. package/dist/agents/types.js.map +1 -1
  9. package/dist/api/ai-gateway/AiGatewayService.d.ts +5 -16
  10. package/dist/api/ai-gateway/AiGatewayService.js +13 -35
  11. package/dist/api/ai-gateway/AiGatewayService.js.map +1 -1
  12. package/dist/api/ai-gateway/auth/AiConnectionInvocationKeyIssuer.d.ts +3 -2
  13. package/dist/api/ai-gateway/auth/AiConnectionInvocationKeyIssuer.js +37 -12
  14. package/dist/api/ai-gateway/auth/AiConnectionInvocationKeyIssuer.js.map +1 -1
  15. package/dist/api/ai-gateway/auth/GatewayPrincipal.d.ts +2 -16
  16. package/dist/api/ai-gateway/auth/GatewayPrincipal.js +3 -33
  17. package/dist/api/ai-gateway/auth/GatewayPrincipal.js.map +1 -1
  18. package/dist/api/ai-gateway/auth/InvocationTokenAuthenticator.d.ts +23 -0
  19. package/dist/api/ai-gateway/auth/InvocationTokenAuthenticator.js +81 -0
  20. package/dist/api/ai-gateway/auth/InvocationTokenAuthenticator.js.map +1 -0
  21. package/dist/api/ai-gateway/auth/InvocationTokenCodec.d.ts +7 -4
  22. package/dist/api/ai-gateway/auth/InvocationTokenCodec.js.map +1 -1
  23. package/dist/api/ai-gateway/connect/index.d.ts +7 -26
  24. package/dist/api/ai-gateway/connect/index.js +40 -67
  25. package/dist/api/ai-gateway/connect/index.js.map +1 -1
  26. package/dist/api/ai-gateway/credentials/PlaintextCredentialPayload.d.ts +17 -0
  27. package/dist/api/ai-gateway/credentials/PlaintextCredentialPayload.js +65 -0
  28. package/dist/api/ai-gateway/credentials/PlaintextCredentialPayload.js.map +1 -0
  29. package/dist/api/ai-gateway/pod/HostedPodDataAccess.d.ts +24 -0
  30. package/dist/api/ai-gateway/pod/HostedPodDataAccess.js +149 -0
  31. package/dist/api/ai-gateway/pod/HostedPodDataAccess.js.map +1 -0
  32. package/dist/api/ai-gateway/quota/ProviderQuotaAdapter.d.ts +13 -5
  33. package/dist/api/ai-gateway/quota/ProviderQuotaAdapter.js +16 -19
  34. package/dist/api/ai-gateway/quota/ProviderQuotaAdapter.js.map +1 -1
  35. package/dist/api/ai-gateway/service-access/AiConnectionServiceAccess.d.ts +1 -1
  36. package/dist/api/ai-gateway/service-access/AiConnectionServiceAccess.js +0 -2
  37. package/dist/api/ai-gateway/service-access/AiConnectionServiceAccess.js.map +1 -1
  38. package/dist/api/auth/AuthContext.d.ts +1 -12
  39. package/dist/api/auth/AuthContext.js +0 -11
  40. package/dist/api/auth/AuthContext.js.map +1 -1
  41. package/dist/api/auth/ClientCredentialsAuthenticator.js +4 -11
  42. package/dist/api/auth/ClientCredentialsAuthenticator.js.map +1 -1
  43. package/dist/api/chatkit/default-agent.d.ts +2 -2
  44. package/dist/api/chatkit/default-agent.js +4 -4
  45. package/dist/api/chatkit/default-agent.js.map +1 -1
  46. package/dist/api/chatkit/runtime/AcpAgentRuntime.js +1 -1
  47. package/dist/api/chatkit/runtime/AcpAgentRuntime.js.map +1 -1
  48. package/dist/api/container/common.js +28 -69
  49. package/dist/api/container/common.js.map +1 -1
  50. package/dist/api/container/index.js +5 -6
  51. package/dist/api/container/index.js.map +1 -1
  52. package/dist/api/container/routes.js +2 -5
  53. package/dist/api/container/routes.js.map +1 -1
  54. package/dist/api/container/types.d.ts +8 -10
  55. package/dist/api/container/types.js.map +1 -1
  56. package/dist/api/handlers/AiClientConfigurationHandler.js +3 -3
  57. package/dist/api/handlers/AiClientConfigurationHandler.js.map +1 -1
  58. package/dist/api/handlers/AiGatewayHandler.js +2 -2
  59. package/dist/api/handlers/AiGatewayHandler.js.map +1 -1
  60. package/dist/api/handlers/AiGatewayManagementHandler.d.ts +2 -10
  61. package/dist/api/handlers/AiGatewayManagementHandler.js +4 -154
  62. package/dist/api/handlers/AiGatewayManagementHandler.js.map +1 -1
  63. package/dist/api/handlers/NetworkSettingsHandler.js +0 -3
  64. package/dist/api/handlers/NetworkSettingsHandler.js.map +1 -1
  65. package/dist/api/handlers/PodSettingsHandler.d.ts +5 -2
  66. package/dist/api/handlers/PodSettingsHandler.js +3 -3
  67. package/dist/api/handlers/PodSettingsHandler.js.map +1 -1
  68. package/dist/api/runs/AgentRuntimeTypes.d.ts +3 -3
  69. package/dist/api/runs/AgentRuntimeTypes.js +10 -10
  70. package/dist/api/runs/AgentRuntimeTypes.js.map +1 -1
  71. package/dist/api/runs/InngestRunExecutionBackend.d.ts +15 -9
  72. package/dist/api/runs/InngestRunExecutionBackend.js +25 -10
  73. package/dist/api/runs/InngestRunExecutionBackend.js.map +1 -1
  74. package/dist/api/runs/PiAgentRuntimeDriver.js +2 -2
  75. package/dist/api/runs/PiAgentRuntimeDriver.js.map +1 -1
  76. package/dist/api/runs/RunStateCenter.js +1 -1
  77. package/dist/api/runs/RunStateCenter.js.map +1 -1
  78. package/dist/api/service/AiClientConfigurationService.d.ts +2 -2
  79. package/dist/api/service/AiClientConfigurationService.js +11 -11
  80. package/dist/api/service/AiClientConfigurationService.js.map +1 -1
  81. package/dist/api/tasks/InngestTaskScheduler.d.ts +16 -10
  82. package/dist/api/tasks/InngestTaskScheduler.js +24 -12
  83. package/dist/api/tasks/InngestTaskScheduler.js.map +1 -1
  84. package/dist/cli/commands/start.js +7 -7
  85. package/dist/cli/commands/start.js.map +1 -1
  86. package/dist/components/components.jsonld +1 -0
  87. package/dist/components/context.jsonld +36 -0
  88. package/dist/http/InternalPodDataHttpHandler.d.ts +32 -0
  89. package/dist/http/InternalPodDataHttpHandler.js +258 -0
  90. package/dist/http/InternalPodDataHttpHandler.js.map +1 -0
  91. package/dist/http/InternalPodDataHttpHandler.jsonld +201 -0
  92. package/dist/index.d.ts +2 -1
  93. package/dist/index.js +4 -2
  94. package/dist/index.js.map +1 -1
  95. package/dist/main.js +13 -13
  96. package/dist/main.js.map +1 -1
  97. package/dist/runtime/GatewayAdminProxyAuth.d.ts +15 -1
  98. package/dist/runtime/GatewayAdminProxyAuth.js +57 -2
  99. package/dist/runtime/GatewayAdminProxyAuth.js.map +1 -1
  100. package/dist/runtime/bootstrap.js +3 -0
  101. package/dist/runtime/bootstrap.js.map +1 -1
  102. package/dist/runtime/css-process.d.ts +1 -1
  103. package/dist/runtime/css-process.js +4 -1
  104. package/dist/runtime/css-process.js.map +1 -1
  105. package/dist/runtime/runner/node/CommunitySolidServerCssRunner.d.ts +2 -0
  106. package/dist/runtime/runner/node/CommunitySolidServerCssRunner.js +50 -0
  107. package/dist/runtime/runner/node/CommunitySolidServerCssRunner.js.map +1 -1
  108. package/node_modules/@undefineds.co/ai-connection/dist/AiClientConfigurationSection.d.ts +5 -5
  109. package/node_modules/@undefineds.co/ai-connection/dist/AiClientConfigurationSection.js +7 -7
  110. package/node_modules/@undefineds.co/ai-connection/dist/AiConnectionMain.js +1 -1
  111. package/node_modules/@undefineds.co/ai-connection/dist/AiConnectionPanel.d.ts +3 -1
  112. package/node_modules/@undefineds.co/ai-connection/dist/AiConnectionPanel.js +74 -60
  113. package/node_modules/@undefineds.co/ai-connection/dist/ai-connection-client.d.ts +0 -21
  114. package/node_modules/@undefineds.co/ai-connection/dist/ai-connection-client.js +0 -41
  115. package/node_modules/@undefineds.co/ai-connection/dist/client-config/base-adapter.d.ts +1 -0
  116. package/node_modules/@undefineds.co/ai-connection/dist/client-config/base-adapter.js +7 -2
  117. package/node_modules/@undefineds.co/ai-connection/dist/client-config/codex.js +3 -3
  118. package/node_modules/@undefineds.co/ai-connection/dist/client-config/index.cjs +15 -9
  119. package/node_modules/@undefineds.co/ai-connection/dist/client-config/json-env-adapter.js +4 -4
  120. package/node_modules/@undefineds.co/ai-connection/dist/client-config/pi.js +3 -3
  121. package/node_modules/@undefineds.co/ai-connection/dist/client-config/types.d.ts +1 -1
  122. package/node_modules/@undefineds.co/ai-connection/dist/controller.d.ts +2 -1
  123. package/node_modules/@undefineds.co/ai-connection/dist/controller.js +1 -0
  124. package/node_modules/@undefineds.co/ai-connection/dist/service-access.js +0 -1
  125. package/node_modules/@undefineds.co/extension-sdk/dist/web.d.ts +22 -1
  126. package/package.json +3 -3
  127. package/static/app/assets/main.js +7 -7
  128. package/static/dashboard/assets/NetworkPage-CXjiUMR1.js +2 -0
  129. package/static/dashboard/assets/{PaneListHeader-57nvMwH6.js → PaneListHeader-BSipfl0W.js} +1 -1
  130. package/static/dashboard/assets/PodPage-Bd3Xhmp-.js +2 -0
  131. package/static/dashboard/assets/ServicesPage-CI3jWZGe.js +1 -0
  132. package/static/dashboard/assets/{card-B9Zjf_2p.js → card-z5BV5hTo.js} +1 -1
  133. package/static/dashboard/assets/{dashboard-8C6o666W.js → dashboard-CSrqAv2L.js} +2 -2
  134. package/static/dashboard/assets/{database-DPh9wDY9.js → database-Ct5ZPe6L.js} +1 -1
  135. package/static/dashboard/assets/{external-link-VbMQvVMz.js → external-link-B7wD1hkm.js} +1 -1
  136. package/static/dashboard/assets/index-tOBaUnM1.js +44 -0
  137. package/static/dashboard/assets/{refresh-cw-BxVxNxNr.js → refresh-cw-CFDZPaE8.js} +1 -1
  138. package/static/dashboard/assets/{services-status-context-DKmJ7ISr.js → services-status-context-DN1Icg4A.js} +1 -1
  139. package/static/dashboard/dashboard.html +1 -1
  140. package/static/settings/assets/ModelsPage-03NPWzoX.js +1 -0
  141. package/static/settings/assets/{PaneListHeader-Bgaze006.js → PaneListHeader-DMD8OJef.js} +1 -1
  142. package/static/settings/assets/PodPage-DT4EMYhL.js +2 -0
  143. package/static/settings/assets/ServicesPage-CJ0ub6IH.js +1 -0
  144. package/static/settings/assets/SettingsPage-BkR7ZK18.js +41 -0
  145. package/static/settings/assets/{card-B_bXSIn-.js → card-Cjsia0_V.js} +1 -1
  146. package/static/settings/assets/{services-status-context-DiX-AbrP.js → services-status-context-CkuKmH5h.js} +1 -1
  147. package/static/settings/assets/{settings-B30oMfZq.js → settings-Cyyv6-SD.js} +2 -2
  148. package/static/settings/assets/{workspace-layout-Dv1Yn9Ci.js → workspace-layout-B47luv6r.js} +1 -1
  149. package/static/settings/settings.html +1 -1
  150. package/dist/api/ai-gateway/auth/ClientCredentialsInternalPodAccessTokenProvider.d.ts +0 -25
  151. package/dist/api/ai-gateway/auth/ClientCredentialsInternalPodAccessTokenProvider.js +0 -91
  152. package/dist/api/ai-gateway/auth/ClientCredentialsInternalPodAccessTokenProvider.js.map +0 -1
  153. package/dist/api/ai-gateway/auth/GatewayApiKey.d.ts +0 -28
  154. package/dist/api/ai-gateway/auth/GatewayApiKey.js +0 -162
  155. package/dist/api/ai-gateway/auth/GatewayApiKey.js.map +0 -1
  156. package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.d.ts +0 -56
  157. package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.js +0 -143
  158. package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.js.map +0 -1
  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/GatewayKeyLocatorCodec.js.map +0 -1
  162. package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.d.ts +0 -54
  163. package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.js +0 -152
  164. package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.js.map +0 -1
  165. package/static/dashboard/assets/NetworkPage-Brt79fuQ.js +0 -2
  166. package/static/dashboard/assets/PodPage-CM6do1vB.js +0 -2
  167. package/static/dashboard/assets/ServicesPage-BUNxGJRz.js +0 -1
  168. package/static/dashboard/assets/index-RfiFckzb.js +0 -51
  169. package/static/settings/assets/ModelsPage-D_IH7AH3.js +0 -1
  170. package/static/settings/assets/PodPage-Cy1VNObw.js +0 -2
  171. package/static/settings/assets/ServicesPage-BsFaxVWC.js +0 -1
  172. package/static/settings/assets/SettingsPage-UafNWE_p.js +0 -48
@@ -1,143 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GatewayApiKeyAuthenticator = exports.DEFAULT_GATEWAY_API_KEY_SCOPES = void 0;
4
- const node_crypto_1 = require("node:crypto");
5
- const GatewayApiKey_1 = require("./GatewayApiKey");
6
- const InvocationTokenCodec_1 = require("./InvocationTokenCodec");
7
- const INVALID_GATEWAY_API_KEY = 'Invalid gateway API key';
8
- exports.DEFAULT_GATEWAY_API_KEY_SCOPES = ['models:read', 'inference:write'];
9
- class GatewayApiKeyAuthenticator {
10
- constructor(options) {
11
- this.repository = options.repository;
12
- this.deployment = options.deployment;
13
- this.requiredScopes = options.requiredScopes ?? [...exports.DEFAULT_GATEWAY_API_KEY_SCOPES];
14
- this.invocationTokenCodec = options.invocationTokenCodec;
15
- this.invocationTokenAudience = options.invocationTokenAudience
16
- ? (0, InvocationTokenCodec_1.requireCanonicalOrigin)(options.invocationTokenAudience, 'audience')
17
- : undefined;
18
- this.invocationTokenIssuer = options.invocationTokenIssuer
19
- ? (0, InvocationTokenCodec_1.requireCanonicalOrigin)(options.invocationTokenIssuer, 'issuer')
20
- : this.invocationTokenAudience;
21
- this.now = options.now ?? (() => new Date());
22
- this.maxClockSkewMs = options.maxClockSkewMs ?? 5_000;
23
- if (!Number.isSafeInteger(this.maxClockSkewMs) || this.maxClockSkewMs < 0 || this.maxClockSkewMs > 30_000) {
24
- throw new Error('Gateway invocation token clock skew must be between 0 and 30000 milliseconds');
25
- }
26
- this.dummyHash = (0, GatewayApiKey_1.hashGatewayApiKeySecret)('xpod-gateway-missing-key-dummy-secret');
27
- }
28
- canAuthenticate(request) {
29
- const bearer = this.readBearer(request);
30
- return Boolean(bearer
31
- && (bearer.startsWith('xpod_inv_v1.') || (0, GatewayApiKey_1.parseGatewayApiKey)(bearer)));
32
- }
33
- async authenticate(request) {
34
- const bearer = this.readBearer(request);
35
- if (bearer?.startsWith('xpod_inv_v1.')) {
36
- return this.authenticateInvocationToken(bearer);
37
- }
38
- const parsed = bearer ? (0, GatewayApiKey_1.parseGatewayApiKey)(bearer) : undefined;
39
- if (!parsed) {
40
- return { success: false, error: INVALID_GATEWAY_API_KEY };
41
- }
42
- let record;
43
- try {
44
- record = await this.repository.findById(parsed.keyId);
45
- }
46
- catch (cause) {
47
- return infrastructureError(cause);
48
- }
49
- if (!record) {
50
- await (0, GatewayApiKey_1.verifyGatewayApiKeySecret)(parsed.secret, await this.dummyHash);
51
- return invalidGatewayApiKey();
52
- }
53
- const secretMatches = await (0, GatewayApiKey_1.verifyGatewayApiKeySecret)(parsed.secret, record.secretHash);
54
- if (!secretMatches
55
- || parsed.deployment !== this.deployment
56
- || record.deployment !== this.deployment
57
- || record.revokedAt
58
- || isExpired(record, this.now())
59
- || !hasRequiredScopes(record.scopes, this.requiredScopes)) {
60
- return invalidGatewayApiKey();
61
- }
62
- const lastUsedAt = this.now();
63
- try {
64
- await this.repository.touchLastUsed(record.id, lastUsedAt);
65
- }
66
- catch (cause) {
67
- return infrastructureError(cause);
68
- }
69
- const context = {
70
- type: 'solid',
71
- webId: record.owner,
72
- accountId: record.owner,
73
- viaGatewayApiKey: true,
74
- gatewayKeyId: record.id,
75
- gatewayKeyFingerprint: fingerprintGatewayBearer(bearer),
76
- scopes: record.scopes,
77
- tokenType: 'Bearer',
78
- };
79
- return { success: true, context };
80
- }
81
- authenticateInvocationToken(token) {
82
- const claims = this.invocationTokenCodec?.decode(token);
83
- if (!claims || !this.validInvocationClaims(claims)) {
84
- return invalidGatewayApiKey();
85
- }
86
- const context = {
87
- type: 'solid',
88
- webId: claims.webId,
89
- accountId: claims.webId,
90
- viaGatewayApiKey: true,
91
- internalInvocation: true,
92
- gatewayKeyId: claims.jti,
93
- gatewayKeyFingerprint: fingerprintGatewayBearer(token),
94
- scopes: claims.scopes,
95
- tokenType: 'Bearer',
96
- };
97
- return { success: true, context };
98
- }
99
- validInvocationClaims(claims) {
100
- const now = this.now().getTime();
101
- return (claims.deployment === this.deployment
102
- && (!this.invocationTokenAudience || claims.audience === this.invocationTokenAudience)
103
- && (!this.invocationTokenIssuer || claims.issuer === this.invocationTokenIssuer)
104
- && claims.issuedAt.getTime() <= now + this.maxClockSkewMs
105
- && claims.expiresAt.getTime() > now
106
- && hasRequiredScopes(claims.scopes, this.requiredScopes));
107
- }
108
- readBearer(request) {
109
- const authorization = request.headers.authorization;
110
- if (!authorization?.startsWith('Bearer ')) {
111
- return undefined;
112
- }
113
- return authorization.slice(7).trim();
114
- }
115
- }
116
- exports.GatewayApiKeyAuthenticator = GatewayApiKeyAuthenticator;
117
- function fingerprintGatewayBearer(bearer) {
118
- return `sha256:${(0, node_crypto_1.createHash)('sha256').update(bearer).digest('hex')}`;
119
- }
120
- function invalidGatewayApiKey() {
121
- return {
122
- success: false,
123
- error: INVALID_GATEWAY_API_KEY,
124
- category: 'invalid_credentials',
125
- statusCode: 401,
126
- };
127
- }
128
- function infrastructureError(cause) {
129
- return {
130
- success: false,
131
- error: 'Gateway API key authentication unavailable',
132
- category: 'service_unavailable',
133
- statusCode: 503,
134
- cause,
135
- };
136
- }
137
- function isExpired(record, now) {
138
- return Boolean(record.expiresAt && record.expiresAt.getTime() <= now.getTime());
139
- }
140
- function hasRequiredScopes(scopes, requiredScopes) {
141
- return requiredScopes.every((scope) => scopes.includes(scope));
142
- }
143
- //# sourceMappingURL=GatewayApiKeyAuthenticator.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"GatewayApiKeyAuthenticator.js","sourceRoot":"","sources":["../../../../src/api/ai-gateway/auth/GatewayApiKeyAuthenticator.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage } from 'node:http';\nimport { createHash } from 'node:crypto';\nimport type { Authenticator, AuthResult } from '../../auth/Authenticator';\nimport type { AuthContext, SolidAuthContext } from '../../auth/AuthContext';\nimport {\n type GatewayDeployment,\n hashGatewayApiKeySecret,\n parseGatewayApiKey,\n verifyGatewayApiKeySecret,\n} from './GatewayApiKey';\nimport type { InvocationTokenClaims, InvocationTokenCodec } from './InvocationTokenCodec';\nimport { requireCanonicalOrigin } from './InvocationTokenCodec';\n\nexport interface GatewayAccessKeyRecord {\n id: string;\n owner: string;\n secretHash: string;\n deployment: GatewayDeployment;\n scopes: string[];\n createdAt: Date;\n expiresAt?: Date;\n lastUsedAt?: Date;\n revokedAt?: Date;\n name?: string;\n}\n\nexport interface GatewayAccessKeyRepositoryContext {\n auth?: AuthContext;\n}\n\nexport interface GatewayAccessKeyRepository {\n createKeyId?(owner: string, deployment: GatewayDeployment): string;\n create(record: GatewayAccessKeyRecord, context?: GatewayAccessKeyRepositoryContext): Promise<GatewayAccessKeyRecord>;\n findById(id: string): Promise<GatewayAccessKeyRecord | undefined>;\n listByOwner(owner: string, context?: GatewayAccessKeyRepositoryContext): Promise<GatewayAccessKeyRecord[]>;\n revoke(id: string, revokedAt: Date, context?: GatewayAccessKeyRepositoryContext): Promise<GatewayAccessKeyRecord | undefined>;\n touchLastUsed(id: string, lastUsedAt: Date): Promise<void>;\n}\n\nexport interface GatewayApiKeyAuthenticatorOptions {\n repository: GatewayAccessKeyRepository;\n deployment: GatewayDeployment;\n requiredScopes?: string[];\n invocationTokenCodec?: InvocationTokenCodec;\n invocationTokenAudience?: string;\n invocationTokenIssuer?: string;\n now?: () => Date;\n maxClockSkewMs?: number;\n}\n\nconst INVALID_GATEWAY_API_KEY = 'Invalid gateway API key';\nexport const DEFAULT_GATEWAY_API_KEY_SCOPES = ['models:read', 'inference:write'] as const;\n\nexport class GatewayApiKeyAuthenticator implements Authenticator {\n private readonly repository: GatewayAccessKeyRepository;\n private readonly deployment: GatewayDeployment;\n private readonly requiredScopes: string[];\n private readonly invocationTokenCodec?: InvocationTokenCodec;\n private readonly invocationTokenAudience?: string;\n private readonly invocationTokenIssuer?: string;\n private readonly now: () => Date;\n private readonly maxClockSkewMs: number;\n private readonly dummyHash: Promise<string>;\n\n public constructor(options: GatewayApiKeyAuthenticatorOptions) {\n this.repository = options.repository;\n this.deployment = options.deployment;\n this.requiredScopes = options.requiredScopes ?? [...DEFAULT_GATEWAY_API_KEY_SCOPES];\n this.invocationTokenCodec = options.invocationTokenCodec;\n this.invocationTokenAudience = options.invocationTokenAudience\n ? requireCanonicalOrigin(options.invocationTokenAudience, 'audience')\n : undefined;\n this.invocationTokenIssuer = options.invocationTokenIssuer\n ? requireCanonicalOrigin(options.invocationTokenIssuer, 'issuer')\n : this.invocationTokenAudience;\n this.now = options.now ?? (() => new Date());\n this.maxClockSkewMs = options.maxClockSkewMs ?? 5_000;\n if (!Number.isSafeInteger(this.maxClockSkewMs) || this.maxClockSkewMs < 0 || this.maxClockSkewMs > 30_000) {\n throw new Error('Gateway invocation token clock skew must be between 0 and 30000 milliseconds');\n }\n this.dummyHash = hashGatewayApiKeySecret('xpod-gateway-missing-key-dummy-secret');\n }\n\n public canAuthenticate(request: IncomingMessage): boolean {\n const bearer = this.readBearer(request);\n return Boolean(\n bearer\n && (bearer.startsWith('xpod_inv_v1.') || parseGatewayApiKey(bearer)),\n );\n }\n\n public async authenticate(request: IncomingMessage): Promise<AuthResult> {\n const bearer = this.readBearer(request);\n if (bearer?.startsWith('xpod_inv_v1.')) {\n return this.authenticateInvocationToken(bearer);\n }\n const parsed = bearer ? parseGatewayApiKey(bearer) : undefined;\n if (!parsed) {\n return { success: false, error: INVALID_GATEWAY_API_KEY };\n }\n\n let record: GatewayAccessKeyRecord | undefined;\n try {\n record = await this.repository.findById(parsed.keyId);\n } catch (cause) {\n return infrastructureError(cause);\n }\n if (!record) {\n await verifyGatewayApiKeySecret(parsed.secret, await this.dummyHash);\n return invalidGatewayApiKey();\n }\n\n const secretMatches = await verifyGatewayApiKeySecret(parsed.secret, record.secretHash);\n if (\n !secretMatches\n || parsed.deployment !== this.deployment\n || record.deployment !== this.deployment\n || record.revokedAt\n || isExpired(record, this.now())\n || !hasRequiredScopes(record.scopes, this.requiredScopes)\n ) {\n return invalidGatewayApiKey();\n }\n\n const lastUsedAt = this.now();\n try {\n await this.repository.touchLastUsed(record.id, lastUsedAt);\n } catch (cause) {\n return infrastructureError(cause);\n }\n\n const context: SolidAuthContext = {\n type: 'solid',\n webId: record.owner,\n accountId: record.owner,\n viaGatewayApiKey: true,\n gatewayKeyId: record.id,\n gatewayKeyFingerprint: fingerprintGatewayBearer(bearer!),\n scopes: record.scopes,\n tokenType: 'Bearer',\n };\n return { success: true, context };\n }\n\n private authenticateInvocationToken(token: string): AuthResult {\n const claims = this.invocationTokenCodec?.decode(token);\n if (!claims || !this.validInvocationClaims(claims)) {\n return invalidGatewayApiKey();\n }\n const context: SolidAuthContext = {\n type: 'solid',\n webId: claims.webId,\n accountId: claims.webId,\n viaGatewayApiKey: true,\n internalInvocation: true,\n gatewayKeyId: claims.jti,\n gatewayKeyFingerprint: fingerprintGatewayBearer(token),\n scopes: claims.scopes,\n tokenType: 'Bearer',\n };\n return { success: true, context };\n }\n\n private validInvocationClaims(claims: InvocationTokenClaims): boolean {\n const now = this.now().getTime();\n return (\n claims.deployment === this.deployment\n && (!this.invocationTokenAudience || claims.audience === this.invocationTokenAudience)\n && (!this.invocationTokenIssuer || claims.issuer === this.invocationTokenIssuer)\n && claims.issuedAt.getTime() <= now + this.maxClockSkewMs\n && claims.expiresAt.getTime() > now\n && hasRequiredScopes(claims.scopes, this.requiredScopes)\n );\n }\n\n private readBearer(request: IncomingMessage): string | undefined {\n const authorization = request.headers.authorization;\n if (!authorization?.startsWith('Bearer ')) {\n return undefined;\n }\n return authorization.slice(7).trim();\n }\n}\n\nfunction fingerprintGatewayBearer(bearer: string): string {\n return `sha256:${createHash('sha256').update(bearer).digest('hex')}`;\n}\n\nfunction invalidGatewayApiKey(): AuthResult {\n return {\n success: false,\n error: INVALID_GATEWAY_API_KEY,\n category: 'invalid_credentials',\n statusCode: 401,\n };\n}\n\nfunction infrastructureError(cause: unknown): AuthResult {\n return {\n success: false,\n error: 'Gateway API key authentication unavailable',\n category: 'service_unavailable',\n statusCode: 503,\n cause,\n };\n}\n\nfunction isExpired(record: GatewayAccessKeyRecord, now: Date): boolean {\n return Boolean(record.expiresAt && record.expiresAt.getTime() <= now.getTime());\n}\n\nfunction hasRequiredScopes(scopes: string[], requiredScopes: string[]): boolean {\n return requiredScopes.every((scope) => scopes.includes(scope));\n}\n"]}
@@ -1,26 +0,0 @@
1
- import type { GatewayDeployment } from './GatewayApiKey';
2
- export interface GatewayKeyLocatorPayload {
3
- owner: string;
4
- keyId: string;
5
- deployment: GatewayDeployment;
6
- }
7
- export interface GatewayKeyLocatorCodec {
8
- encode(payload: GatewayKeyLocatorPayload): string;
9
- decode(locator: string): GatewayKeyLocatorPayload | undefined;
10
- }
11
- export interface GatewayKeyLocatorSecret {
12
- kid: string;
13
- secret: string;
14
- }
15
- export interface AesGatewayKeyLocatorCodecOptions {
16
- active: GatewayKeyLocatorSecret;
17
- previous?: GatewayKeyLocatorSecret[];
18
- }
19
- export declare class AesGatewayKeyLocatorCodec implements GatewayKeyLocatorCodec {
20
- private readonly active;
21
- private readonly ring;
22
- constructor(secretOrOptions: string | AesGatewayKeyLocatorCodecOptions);
23
- encode(payload: GatewayKeyLocatorPayload): string;
24
- decode(locator: string): GatewayKeyLocatorPayload | undefined;
25
- }
26
- export declare function createGatewayKeyLocator(owner: string, deployment: GatewayDeployment, codec: GatewayKeyLocatorCodec): string;
@@ -1,97 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AesGatewayKeyLocatorCodec = void 0;
4
- exports.createGatewayKeyLocator = createGatewayKeyLocator;
5
- const node_crypto_1 = require("node:crypto");
6
- const PREFIX = 'gakv1';
7
- const NONCE_BYTES = 12;
8
- const KEY_BYTES = 32;
9
- class AesGatewayKeyLocatorCodec {
10
- constructor(secretOrOptions) {
11
- this.ring = new Map();
12
- const options = typeof secretOrOptions === 'string'
13
- ? { active: { kid: 'default', secret: secretOrOptions } }
14
- : secretOrOptions;
15
- this.active = normalizeSecret(options.active);
16
- this.ring.set(this.active.kid, this.active.key);
17
- for (const previous of options.previous ?? []) {
18
- const normalized = normalizeSecret(previous);
19
- if (this.ring.has(normalized.kid)) {
20
- throw new Error(`Duplicate Gateway key locator key id: ${normalized.kid}`);
21
- }
22
- this.ring.set(normalized.kid, normalized.key);
23
- }
24
- }
25
- encode(payload) {
26
- const nonce = (0, node_crypto_1.randomBytes)(NONCE_BYTES);
27
- const aad = Buffer.from(`${PREFIX}.${this.active.kid}`);
28
- const cipher = (0, node_crypto_1.createCipheriv)('aes-256-gcm', this.active.key, nonce);
29
- cipher.setAAD(aad);
30
- const plaintext = Buffer.from(JSON.stringify(payload), 'utf8');
31
- const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
32
- const tag = cipher.getAuthTag();
33
- return [
34
- PREFIX,
35
- this.active.kid,
36
- nonce.toString('base64url'),
37
- ciphertext.toString('base64url'),
38
- tag.toString('base64url'),
39
- ].join('.');
40
- }
41
- decode(locator) {
42
- const parts = locator.split('.');
43
- if (parts.length !== 5 || parts[0] !== PREFIX) {
44
- return undefined;
45
- }
46
- const kid = parts[1];
47
- const key = this.ring.get(kid);
48
- if (!key) {
49
- return undefined;
50
- }
51
- try {
52
- const nonce = Buffer.from(parts[2], 'base64url');
53
- const ciphertext = Buffer.from(parts[3], 'base64url');
54
- const tag = Buffer.from(parts[4], 'base64url');
55
- const decipher = (0, node_crypto_1.createDecipheriv)('aes-256-gcm', key, nonce);
56
- decipher.setAAD(Buffer.from(`${PREFIX}.${kid}`));
57
- decipher.setAuthTag(tag);
58
- const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
59
- const parsed = JSON.parse(plaintext.toString('utf8'));
60
- if (typeof parsed.owner !== 'string'
61
- || typeof parsed.keyId !== 'string'
62
- || (parsed.deployment !== 'local' && parsed.deployment !== 'cloud')) {
63
- return undefined;
64
- }
65
- return {
66
- owner: parsed.owner,
67
- keyId: parsed.keyId,
68
- deployment: parsed.deployment,
69
- };
70
- }
71
- catch {
72
- return undefined;
73
- }
74
- }
75
- }
76
- exports.AesGatewayKeyLocatorCodec = AesGatewayKeyLocatorCodec;
77
- function normalizeSecret(input) {
78
- const kid = input.kid.trim();
79
- if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(kid)) {
80
- throw new Error('Gateway key locator key id is invalid');
81
- }
82
- if (!input.secret.trim()) {
83
- throw new Error('Gateway key locator secret is required');
84
- }
85
- return {
86
- kid,
87
- key: (0, node_crypto_1.createHash)('sha256').update(input.secret).digest().subarray(0, KEY_BYTES),
88
- };
89
- }
90
- function createGatewayKeyLocator(owner, deployment, codec) {
91
- return codec.encode({
92
- owner,
93
- deployment,
94
- keyId: `gak_${(0, node_crypto_1.randomBytes)(18).toString('base64url')}`,
95
- });
96
- }
97
- //# sourceMappingURL=GatewayKeyLocatorCodec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"GatewayKeyLocatorCodec.js","sourceRoot":"","sources":["../../../../src/api/ai-gateway/auth/GatewayKeyLocatorCodec.ts"],"names":[],"mappings":"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","sourcesContent":["import { createCipheriv, createDecipheriv, createHash, randomBytes } from 'node:crypto';\nimport type { GatewayDeployment } from './GatewayApiKey';\n\nexport interface GatewayKeyLocatorPayload {\n owner: string;\n keyId: string;\n deployment: GatewayDeployment;\n}\n\nexport interface GatewayKeyLocatorCodec {\n encode(payload: GatewayKeyLocatorPayload): string;\n decode(locator: string): GatewayKeyLocatorPayload | undefined;\n}\n\nexport interface GatewayKeyLocatorSecret {\n kid: string;\n secret: string;\n}\n\nexport interface AesGatewayKeyLocatorCodecOptions {\n active: GatewayKeyLocatorSecret;\n previous?: GatewayKeyLocatorSecret[];\n}\n\nconst PREFIX = 'gakv1';\nconst NONCE_BYTES = 12;\nconst KEY_BYTES = 32;\n\nexport class AesGatewayKeyLocatorCodec implements GatewayKeyLocatorCodec {\n private readonly active: { kid: string; key: Buffer };\n private readonly ring = new Map<string, Buffer>();\n\n public constructor(secretOrOptions: string | AesGatewayKeyLocatorCodecOptions) {\n const options = typeof secretOrOptions === 'string'\n ? { active: { kid: 'default', secret: secretOrOptions } }\n : secretOrOptions;\n this.active = normalizeSecret(options.active);\n this.ring.set(this.active.kid, this.active.key);\n for (const previous of options.previous ?? []) {\n const normalized = normalizeSecret(previous);\n if (this.ring.has(normalized.kid)) {\n throw new Error(`Duplicate Gateway key locator key id: ${normalized.kid}`);\n }\n this.ring.set(normalized.kid, normalized.key);\n }\n }\n\n public encode(payload: GatewayKeyLocatorPayload): string {\n const nonce = randomBytes(NONCE_BYTES);\n const aad = Buffer.from(`${PREFIX}.${this.active.kid}`);\n const cipher = createCipheriv('aes-256-gcm', this.active.key, nonce);\n cipher.setAAD(aad);\n const plaintext = Buffer.from(JSON.stringify(payload), 'utf8');\n const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);\n const tag = cipher.getAuthTag();\n return [\n PREFIX,\n this.active.kid,\n nonce.toString('base64url'),\n ciphertext.toString('base64url'),\n tag.toString('base64url'),\n ].join('.');\n }\n\n public decode(locator: string): GatewayKeyLocatorPayload | undefined {\n const parts = locator.split('.');\n if (parts.length !== 5 || parts[0] !== PREFIX) {\n return undefined;\n }\n const kid = parts[1];\n const key = this.ring.get(kid);\n if (!key) {\n return undefined;\n }\n try {\n const nonce = Buffer.from(parts[2], 'base64url');\n const ciphertext = Buffer.from(parts[3], 'base64url');\n const tag = Buffer.from(parts[4], 'base64url');\n const decipher = createDecipheriv('aes-256-gcm', key, nonce);\n decipher.setAAD(Buffer.from(`${PREFIX}.${kid}`));\n decipher.setAuthTag(tag);\n const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);\n const parsed = JSON.parse(plaintext.toString('utf8')) as Record<string, unknown>;\n if (\n typeof parsed.owner !== 'string'\n || typeof parsed.keyId !== 'string'\n || (parsed.deployment !== 'local' && parsed.deployment !== 'cloud')\n ) {\n return undefined;\n }\n return {\n owner: parsed.owner,\n keyId: parsed.keyId,\n deployment: parsed.deployment,\n };\n } catch {\n return undefined;\n }\n }\n}\n\nfunction normalizeSecret(input: GatewayKeyLocatorSecret): { kid: string; key: Buffer } {\n const kid = input.kid.trim();\n if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(kid)) {\n throw new Error('Gateway key locator key id is invalid');\n }\n if (!input.secret.trim()) {\n throw new Error('Gateway key locator secret is required');\n }\n return {\n kid,\n key: createHash('sha256').update(input.secret).digest().subarray(0, KEY_BYTES),\n };\n}\n\nexport function createGatewayKeyLocator(\n owner: string,\n deployment: GatewayDeployment,\n codec: GatewayKeyLocatorCodec,\n): string {\n return codec.encode({\n owner,\n deployment,\n keyId: `gak_${randomBytes(18).toString('base64url')}`,\n });\n}\n"]}
@@ -1,54 +0,0 @@
1
- import { gatewayAccessKeyResource, type GatewayAccessKeyRow } from '@undefineds.co/models';
2
- import type { AuthContext } from '../../auth/AuthContext';
3
- import { type GatewayKeyLocatorCodec } from './GatewayKeyLocatorCodec';
4
- import { type GatewayAccessKeyRecord, type GatewayAccessKeyRepository, type GatewayAccessKeyRepositoryContext } from './GatewayApiKeyAuthenticator';
5
- import type { GatewayDeployment } from './GatewayApiKey';
6
- type GatewayAccessKeyDb = {
7
- init?: (...resources: unknown[]) => Promise<void>;
8
- insert(resource: typeof gatewayAccessKeyResource): {
9
- values(value: unknown): {
10
- execute(): Promise<unknown[]>;
11
- };
12
- };
13
- select(): {
14
- from(resource: typeof gatewayAccessKeyResource): {
15
- where(condition: unknown): {
16
- execute(): Promise<GatewayAccessKeyRow[]>;
17
- };
18
- };
19
- };
20
- findById<TRow>(resource: typeof gatewayAccessKeyResource, id: string): Promise<TRow | null>;
21
- findByIri<TRow>(resource: typeof gatewayAccessKeyResource, iri: string): Promise<TRow | null>;
22
- updateById<TRow>(resource: typeof gatewayAccessKeyResource, id: string, patch: unknown): Promise<TRow | null>;
23
- };
24
- type GatewayAccessKeyResource = typeof gatewayAccessKeyResource;
25
- export interface PodGatewayAccessKeyRepositoryOptions {
26
- locatorCodec: GatewayKeyLocatorCodec;
27
- internalPodAccess?: InternalPodAccessTokenProvider;
28
- dbFactory?: (input: {
29
- owner: string;
30
- auth?: AuthContext;
31
- fetch: typeof fetch;
32
- resource?: GatewayAccessKeyResource;
33
- listResource?: GatewayAccessKeyResource;
34
- }) => Promise<GatewayAccessKeyDb>;
35
- }
36
- export interface InternalPodAccessTokenProvider {
37
- getTrustedFetch(owner: string): Promise<typeof fetch | undefined>;
38
- }
39
- export declare class PodGatewayAccessKeyRepository implements GatewayAccessKeyRepository {
40
- private readonly dbFactory;
41
- private readonly locatorCodec;
42
- private readonly internalPodAccess?;
43
- private readonly usesDefaultDbFactory;
44
- constructor(options: PodGatewayAccessKeyRepositoryOptions);
45
- createKeyId(owner: string, deployment: GatewayDeployment): string;
46
- create(record: GatewayAccessKeyRecord, context?: GatewayAccessKeyRepositoryContext): Promise<GatewayAccessKeyRecord>;
47
- findById(id: string): Promise<GatewayAccessKeyRecord | undefined>;
48
- listByOwner(owner: string, context?: GatewayAccessKeyRepositoryContext): Promise<GatewayAccessKeyRecord[]>;
49
- revoke(id: string, revokedAt: Date, context?: GatewayAccessKeyRepositoryContext): Promise<GatewayAccessKeyRecord | undefined>;
50
- touchLastUsed(id: string, lastUsedAt: Date): Promise<void>;
51
- private dbForOwner;
52
- private resolveTrustedFetch;
53
- }
54
- export {};
@@ -1,152 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PodGatewayAccessKeyRepository = void 0;
4
- const drizzle_solid_1 = require("@undefineds.co/drizzle-solid");
5
- const models_1 = require("@undefineds.co/models");
6
- const GatewayKeyLocatorCodec_1 = require("./GatewayKeyLocatorCodec");
7
- class PodGatewayAccessKeyRepository {
8
- constructor(options) {
9
- this.locatorCodec = options.locatorCodec;
10
- this.internalPodAccess = options.internalPodAccess;
11
- this.usesDefaultDbFactory = options.dbFactory === undefined;
12
- this.dbFactory = options.dbFactory ?? createDefaultGatewayAccessKeyDb;
13
- }
14
- createKeyId(owner, deployment) {
15
- return (0, GatewayKeyLocatorCodec_1.createGatewayKeyLocator)(owner, deployment, this.locatorCodec);
16
- }
17
- async create(record, context) {
18
- const { db, resource } = await this.dbForOwner(record.owner, context);
19
- const valid = models_1.aiGatewayRepository.validateAccessKey(toGatewayAccessKeyInsert(record));
20
- await db.insert(resource).values(valid).execute();
21
- return recordFromRow(valid);
22
- }
23
- async findById(id) {
24
- const locator = this.locatorCodec.decode(id);
25
- if (!locator) {
26
- return undefined;
27
- }
28
- const { db, resource } = await this.dbForOwner(locator.owner);
29
- const row = await db.findById(resource, gatewayAccessKeyStorageId(id));
30
- return row ? recordFromRow(row) : undefined;
31
- }
32
- async listByOwner(owner, context) {
33
- const { db, listResource } = await this.dbForOwner(owner, context);
34
- const rows = await db
35
- .select()
36
- .from(listResource)
37
- .where((0, drizzle_solid_1.eq)(listResource.owner, owner))
38
- .execute();
39
- return rows.map(recordFromRow).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
40
- }
41
- async revoke(id, revokedAt, context) {
42
- const locator = this.locatorCodec.decode(id);
43
- if (!locator) {
44
- return undefined;
45
- }
46
- const { db, resource } = await this.dbForOwner(locator.owner, context);
47
- const row = await db.updateById(resource, gatewayAccessKeyStorageId(id), { revokedAt });
48
- return row ? recordFromRow(row) : undefined;
49
- }
50
- async touchLastUsed(id, lastUsedAt) {
51
- const locator = this.locatorCodec.decode(id);
52
- if (!locator) {
53
- return;
54
- }
55
- const { db, resource } = await this.dbForOwner(locator.owner);
56
- await db.updateById(resource, gatewayAccessKeyStorageId(id), { lastUsedAt });
57
- }
58
- async dbForOwner(owner, context) {
59
- const trustedFetch = await this.resolveTrustedFetch(owner);
60
- const resource = models_1.gatewayAccessKeyResource;
61
- const listResource = this.usesDefaultDbFactory
62
- ? createGatewayAccessKeyResource(owner)
63
- : models_1.gatewayAccessKeyResource;
64
- const db = await this.dbFactory({
65
- owner,
66
- auth: context?.auth,
67
- fetch: trustedFetch,
68
- resource,
69
- listResource,
70
- });
71
- await db.init?.(resource, listResource);
72
- return { db, resource, listResource };
73
- }
74
- async resolveTrustedFetch(owner) {
75
- const trustedFetch = await this.internalPodAccess?.getTrustedFetch(owner);
76
- if (!trustedFetch) {
77
- throw new Error('AI Connection service identity is not configured');
78
- }
79
- return async (input, init) => {
80
- const response = await trustedFetch(input, init);
81
- if (response.status === 403) {
82
- throw new Error('service_access_missing');
83
- }
84
- return response;
85
- };
86
- }
87
- }
88
- exports.PodGatewayAccessKeyRepository = PodGatewayAccessKeyRepository;
89
- function createDefaultGatewayAccessKeyDb(input) {
90
- const resource = input.resource ?? models_1.gatewayAccessKeyResource;
91
- const listResource = input.listResource ?? resource;
92
- return Promise.resolve((0, drizzle_solid_1.drizzle)({
93
- fetch: input.fetch,
94
- info: { webId: input.owner, isLoggedIn: true },
95
- }, {
96
- schema: {
97
- gatewayAccessKey: resource,
98
- gatewayAccessKeyList: listResource,
99
- },
100
- }));
101
- }
102
- function createGatewayAccessKeyResource(owner) {
103
- const resource = (0, drizzle_solid_1.alias)(models_1.gatewayAccessKeyResource, 'gatewayAccessKeyList');
104
- resource.setSparqlEndpoint(`${(0, drizzle_solid_1.resolvePodBaseUrl)(owner).replace(/\/$/u, '')}/-/sparql`);
105
- return resource;
106
- }
107
- function toGatewayAccessKeyInsert(record) {
108
- return {
109
- id: gatewayAccessKeyStorageId(record.id),
110
- owner: record.owner,
111
- secretHash: record.secretHash,
112
- deployment: record.deployment,
113
- scopes: record.scopes,
114
- createdAt: record.createdAt,
115
- expiresAt: record.expiresAt,
116
- lastUsedAt: record.lastUsedAt,
117
- revokedAt: record.revokedAt,
118
- name: record.name,
119
- };
120
- }
121
- function recordFromRow(row) {
122
- return {
123
- id: gatewayAccessKeyLocatorFromStorageId(String(row.id)),
124
- owner: String(row.owner),
125
- secretHash: String(row.secretHash),
126
- deployment: row.deployment === 'local' ? 'local' : 'cloud',
127
- scopes: Array.isArray(row.scopes) ? row.scopes.map(String) : [],
128
- createdAt: toDate(row.createdAt) ?? new Date(0),
129
- expiresAt: toDate(row.expiresAt),
130
- lastUsedAt: toDate(row.lastUsedAt),
131
- revokedAt: toDate(row.revokedAt),
132
- name: typeof row.name === 'string' ? row.name : undefined,
133
- };
134
- }
135
- function gatewayAccessKeyStorageId(locator) {
136
- return models_1.gatewayAccessKeyResource.buildId({ id: locator });
137
- }
138
- function gatewayAccessKeyLocatorFromStorageId(id) {
139
- const fragment = id.lastIndexOf('#');
140
- return fragment >= 0 ? id.slice(fragment + 1) : id;
141
- }
142
- function toDate(value) {
143
- if (value instanceof Date) {
144
- return value;
145
- }
146
- if (typeof value === 'string' || typeof value === 'number') {
147
- const parsed = new Date(value);
148
- return Number.isNaN(parsed.getTime()) ? undefined : parsed;
149
- }
150
- return undefined;
151
- }
152
- //# sourceMappingURL=PodGatewayAccessKeyRepository.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PodGatewayAccessKeyRepository.js","sourceRoot":"","sources":["../../../../src/api/ai-gateway/auth/PodGatewayAccessKeyRepository.ts"],"names":[],"mappings":"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","sourcesContent":["import { alias, drizzle, eq, resolvePodBaseUrl } from '@undefineds.co/drizzle-solid';\nimport {\n aiGatewayRepository,\n gatewayAccessKeyResource,\n type GatewayAccessKeyRow,\n} from '@undefineds.co/models';\nimport type { AuthContext } from '../../auth/AuthContext';\nimport { createGatewayKeyLocator, type GatewayKeyLocatorCodec } from './GatewayKeyLocatorCodec';\nimport {\n type GatewayAccessKeyRecord,\n type GatewayAccessKeyRepository,\n type GatewayAccessKeyRepositoryContext,\n} from './GatewayApiKeyAuthenticator';\nimport type { GatewayDeployment } from './GatewayApiKey';\n\ntype GatewayAccessKeyDb = {\n init?: (...resources: unknown[]) => Promise<void>;\n insert(resource: typeof gatewayAccessKeyResource): {\n values(value: unknown): { execute(): Promise<unknown[]> };\n };\n select(): {\n from(resource: typeof gatewayAccessKeyResource): {\n where(condition: unknown): { execute(): Promise<GatewayAccessKeyRow[]> };\n };\n };\n findById<TRow>(resource: typeof gatewayAccessKeyResource, id: string): Promise<TRow | null>;\n findByIri<TRow>(resource: typeof gatewayAccessKeyResource, iri: string): Promise<TRow | null>;\n updateById<TRow>(resource: typeof gatewayAccessKeyResource, id: string, patch: unknown): Promise<TRow | null>;\n};\n\ntype GatewayAccessKeyResource = typeof gatewayAccessKeyResource;\n\nexport interface PodGatewayAccessKeyRepositoryOptions {\n locatorCodec: GatewayKeyLocatorCodec;\n internalPodAccess?: InternalPodAccessTokenProvider;\n dbFactory?: (input: {\n owner: string;\n auth?: AuthContext;\n fetch: typeof fetch;\n resource?: GatewayAccessKeyResource;\n listResource?: GatewayAccessKeyResource;\n }) => Promise<GatewayAccessKeyDb>;\n}\n\nexport interface InternalPodAccessTokenProvider {\n getTrustedFetch(owner: string): Promise<typeof fetch | undefined>;\n}\n\nexport class PodGatewayAccessKeyRepository implements GatewayAccessKeyRepository {\n private readonly dbFactory: NonNullable<PodGatewayAccessKeyRepositoryOptions['dbFactory']>;\n private readonly locatorCodec: GatewayKeyLocatorCodec;\n private readonly internalPodAccess?: InternalPodAccessTokenProvider;\n private readonly usesDefaultDbFactory: boolean;\n\n public constructor(options: PodGatewayAccessKeyRepositoryOptions) {\n this.locatorCodec = options.locatorCodec;\n this.internalPodAccess = options.internalPodAccess;\n this.usesDefaultDbFactory = options.dbFactory === undefined;\n this.dbFactory = options.dbFactory ?? createDefaultGatewayAccessKeyDb;\n }\n\n public createKeyId(owner: string, deployment: GatewayDeployment): string {\n return createGatewayKeyLocator(owner, deployment, this.locatorCodec);\n }\n\n public async create(\n record: GatewayAccessKeyRecord,\n context?: GatewayAccessKeyRepositoryContext,\n ): Promise<GatewayAccessKeyRecord> {\n const { db, resource } = await this.dbForOwner(record.owner, context);\n const valid = aiGatewayRepository.validateAccessKey(toGatewayAccessKeyInsert(record));\n await db.insert(resource).values(valid).execute();\n return recordFromRow(valid as GatewayAccessKeyRow);\n }\n\n public async findById(id: string): Promise<GatewayAccessKeyRecord | undefined> {\n const locator = this.locatorCodec.decode(id);\n if (!locator) {\n return undefined;\n }\n const { db, resource } = await this.dbForOwner(locator.owner);\n const row = await db.findById<GatewayAccessKeyRow>(resource, gatewayAccessKeyStorageId(id));\n return row ? recordFromRow(row) : undefined;\n }\n\n public async listByOwner(\n owner: string,\n context?: GatewayAccessKeyRepositoryContext,\n ): Promise<GatewayAccessKeyRecord[]> {\n const { db, listResource } = await this.dbForOwner(owner, context);\n const rows = await db\n .select()\n .from(listResource)\n .where(eq(listResource.owner, owner))\n .execute();\n return rows.map(recordFromRow).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n }\n\n public async revoke(\n id: string,\n revokedAt: Date,\n context?: GatewayAccessKeyRepositoryContext,\n ): Promise<GatewayAccessKeyRecord | undefined> {\n const locator = this.locatorCodec.decode(id);\n if (!locator) {\n return undefined;\n }\n const { db, resource } = await this.dbForOwner(locator.owner, context);\n const row = await db.updateById<GatewayAccessKeyRow>(\n resource,\n gatewayAccessKeyStorageId(id),\n { revokedAt },\n );\n return row ? recordFromRow(row) : undefined;\n }\n\n public async touchLastUsed(id: string, lastUsedAt: Date): Promise<void> {\n const locator = this.locatorCodec.decode(id);\n if (!locator) {\n return;\n }\n const { db, resource } = await this.dbForOwner(locator.owner);\n await db.updateById(resource, gatewayAccessKeyStorageId(id), { lastUsedAt });\n }\n\n private async dbForOwner(\n owner: string,\n context?: GatewayAccessKeyRepositoryContext,\n ): Promise<{\n db: GatewayAccessKeyDb;\n resource: GatewayAccessKeyResource;\n listResource: GatewayAccessKeyResource;\n }> {\n const trustedFetch = await this.resolveTrustedFetch(owner);\n const resource = gatewayAccessKeyResource;\n const listResource = this.usesDefaultDbFactory\n ? createGatewayAccessKeyResource(owner)\n : gatewayAccessKeyResource;\n const db = await this.dbFactory({\n owner,\n auth: context?.auth,\n fetch: trustedFetch,\n resource,\n listResource,\n });\n await db.init?.(resource, listResource);\n return { db, resource, listResource };\n }\n\n private async resolveTrustedFetch(owner: string): Promise<typeof fetch> {\n const trustedFetch = await this.internalPodAccess?.getTrustedFetch(owner);\n if (!trustedFetch) {\n throw new Error('AI Connection service identity is not configured');\n }\n return async (input, init) => {\n const response = await trustedFetch(input, init);\n if (response.status === 403) {\n throw new Error('service_access_missing');\n }\n return response;\n };\n }\n}\n\nfunction createDefaultGatewayAccessKeyDb(input: {\n owner: string;\n auth?: AuthContext;\n fetch: typeof fetch;\n resource?: GatewayAccessKeyResource;\n listResource?: GatewayAccessKeyResource;\n}): Promise<GatewayAccessKeyDb> {\n const resource = input.resource ?? gatewayAccessKeyResource;\n const listResource = input.listResource ?? resource;\n return Promise.resolve(drizzle(\n {\n fetch: input.fetch,\n info: { webId: input.owner, isLoggedIn: true },\n } as any,\n {\n schema: {\n gatewayAccessKey: resource,\n gatewayAccessKeyList: listResource,\n },\n },\n ) as unknown as GatewayAccessKeyDb);\n}\n\nfunction createGatewayAccessKeyResource(owner: string): GatewayAccessKeyResource {\n const resource = alias(gatewayAccessKeyResource, 'gatewayAccessKeyList');\n resource.setSparqlEndpoint(`${resolvePodBaseUrl(owner).replace(/\\/$/u, '')}/-/sparql`);\n return resource;\n}\n\nfunction toGatewayAccessKeyInsert(record: GatewayAccessKeyRecord): Record<string, unknown> {\n return {\n id: gatewayAccessKeyStorageId(record.id),\n owner: record.owner,\n secretHash: record.secretHash,\n deployment: record.deployment,\n scopes: record.scopes,\n createdAt: record.createdAt,\n expiresAt: record.expiresAt,\n lastUsedAt: record.lastUsedAt,\n revokedAt: record.revokedAt,\n name: record.name,\n };\n}\n\nfunction recordFromRow(row: GatewayAccessKeyRow): GatewayAccessKeyRecord {\n return {\n id: gatewayAccessKeyLocatorFromStorageId(String(row.id)),\n owner: String(row.owner),\n secretHash: String(row.secretHash),\n deployment: row.deployment === 'local' ? 'local' : 'cloud',\n scopes: Array.isArray(row.scopes) ? row.scopes.map(String) : [],\n createdAt: toDate(row.createdAt) ?? new Date(0),\n expiresAt: toDate(row.expiresAt),\n lastUsedAt: toDate(row.lastUsedAt),\n revokedAt: toDate(row.revokedAt),\n name: typeof (row as any).name === 'string' ? (row as any).name : undefined,\n };\n}\n\nfunction gatewayAccessKeyStorageId(locator: string): string {\n return gatewayAccessKeyResource.buildId({ id: locator });\n}\n\nfunction gatewayAccessKeyLocatorFromStorageId(id: string): string {\n const fragment = id.lastIndexOf('#');\n return fragment >= 0 ? id.slice(fragment + 1) : id;\n}\n\nfunction toDate(value: unknown): Date | undefined {\n if (value instanceof Date) {\n return value;\n }\n if (typeof value === 'string' || typeof value === 'number') {\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? undefined : parsed;\n }\n return undefined;\n}\n"]}
@@ -1,2 +0,0 @@
1
- import{c as D,u as $,r as a,j as e,f as K,A as F,B as P}from"./dashboard-8C6o666W.js";import{T as O,P as V}from"./PaneListHeader-57nvMwH6.js";import{C as N,a as v,b as k,c as C,d as R,S as G,B as q}from"./card-B9Zjf_2p.js";import{R as J}from"./refresh-cw-BxVxNxNr.js";const W=[["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"}]],X=D("earth",W);const Q=[["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"}]],Y=D("rotate-ccw",Q);const Z=[["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"}]],ee=D("wifi",Z);async function te({podUrl:t,authenticatedFetch:s}){return T(s,new URL("/api/network/settings/status",t).toString(),{method:"GET",credentials:"include",headers:{accept:"application/json"}})}async function se({podUrl:t,authenticatedFetch:s}){return T(s,new URL("/api/network/settings/diagnose",t).toString(),{method:"POST",credentials:"include",headers:{accept:"application/json"}})}async function re({podUrl:t,authenticatedFetch:s}){await T(s,new URL("/api/network/settings/certificate/renew",t).toString(),{method:"POST",credentials:"include",headers:{accept:"application/json"}})}async function T(t,s,i){const n=await t(s,i);if(!n.ok)throw await n.arrayBuffer(),new Error("Network settings request failed. Please try again.");if(!n.headers.get("content-type")?.includes("application/json"))throw await n.arrayBuffer(),new Error("Network settings request failed. Please try again.");return await n.json()}function he(){const t=$(),[s,i]=a.useState(),[n,p]=a.useState([]),[g,c]=a.useState(),[S,U]=a.useState(!1),[M,L]=a.useState(!1),[B,A]=a.useState(!1),u=a.useRef(0),f=a.useRef(0),x=a.useRef(0),j=a.useRef(void 0),m=a.useRef(!0),l=t.state.status==="authenticated"&&!!(t.webId&&t.podUrl)?`${t.webId}
2
- ${t.podUrl}`:void 0;a.useEffect(()=>(m.current=!0,()=>{m.current=!1,u.current+=1,f.current+=1,x.current+=1}),[]);const y=a.useCallback((d,r)=>m.current&&u.current===d&&j.current===r,[]),w=a.useCallback((d,r)=>m.current&&f.current===d&&j.current===r,[]),b=a.useCallback((d,r)=>m.current&&x.current===d&&j.current===r,[]),h=a.useCallback(async()=>{const d=t.podUrl,r=l;if(!d||!r)return;const o=u.current+1;u.current=o,U(!0),c(void 0);try{const I=await te({podUrl:d,authenticatedFetch:t.fetch});y(o,r)&&i(I)}catch{y(o,r)&&c("Network settings request failed. Please try again.")}finally{y(o,r)&&U(!1)}},[l,y,t.fetch,t.podUrl]);a.useEffect(()=>{j.current=l,u.current+=1,f.current+=1,x.current+=1,i(void 0),p([]),c(void 0),U(!1),L(!1),A(!1),l&&h()},[l,h]);const H=a.useCallback(async()=>{const d=t.podUrl,r=l;if(!d||!r)return;const o=f.current+1;f.current=o,L(!0),c(void 0);try{const I=await se({podUrl:d,authenticatedFetch:t.fetch});w(o,r)&&p(I.checks)}catch{w(o,r)&&c("Network diagnostics failed. Please try again.")}finally{w(o,r)&&L(!1)}},[l,w,t.fetch,t.podUrl]),z=a.useCallback(async()=>{const d=t.podUrl,r=l;if(!d||!r)return;const o=x.current+1;x.current=o,A(!0),c(void 0);try{await re({podUrl:d,authenticatedFetch:t.fetch}),b(o,r)&&await h()}catch{b(o,r)&&c("Certificate renewal failed. Please try again.")}finally{b(o,r)&&A(!1)}},[l,b,h,t.fetch,t.podUrl]),_=a.useMemo(()=>[{key:"local",title:"Local",values:s?.addresses.local??[]},{key:"lan",title:"LAN",values:s?.addresses.lan??[]},{key:"public",title:"Public",values:s?.addresses.public??[]}],[s]);return e.jsx(O,{mode:"auto",listHeader:e.jsx(V,{title:"Network"}),list:e.jsxs("aside",{className:"flex h-full flex-col gap-3 border-r border-border bg-muted/30 p-4",children:[e.jsx(ne,{endpoint:s?.endpoint,loading:S&&!s}),e.jsx(ie,{sections:_,loading:S&&!s})]}),mainHeader:e.jsx(ae,{loading:S,onRefresh:h}),main:e.jsxs("section",{className:"flex min-h-full flex-col gap-4 bg-background p-6",children:[g?e.jsx("div",{role:"alert",className:"rounded-md border border-destructive/30 bg-destructive/10 px-4 py-3 text-sm text-destructive",children:g}):null,e.jsx(ce,{status:s}),e.jsx(de,{status:s,diagnostics:n,diagnosing:M,renewing:B,onDiagnose:H,onRenewCertificate:z})]}),className:"min-h-full"})}function ae({loading:t,onRefresh:s}){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.jsx("div",{className:"text-sm font-semibold text-foreground",children:"Network"}),e.jsx("div",{className:"truncate text-xs text-muted-foreground",children:"Endpoint, DNS, TLS, tunnel, and connectivity diagnostics"})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("a",{className:"inline-flex h-9 items-center justify-center rounded-md border border-input bg-background px-3 text-sm font-medium text-foreground hover:bg-accent",href:"/settings/network",children:"Configure"}),e.jsxs(P,{type:"button",size:"sm",variant:"outline",onClick:s,disabled:t,children:[e.jsx(J,{className:"mr-2 h-4 w-4","aria-hidden":"true"}),"Refresh"]})]})]})}function ne({endpoint:t,loading:s}){return e.jsxs(N,{children:[e.jsxs(v,{children:[e.jsxs(k,{className:"flex items-center gap-2 text-base",children:[e.jsx(X,{className:"h-4 w-4","aria-hidden":"true"}),"Endpoint"]}),e.jsx(C,{children:s?"Resolving endpoint":"Current reachable API origin"})]}),e.jsx(R,{children:e.jsx("div",{className:"break-words text-sm text-foreground",children:t||"Unsupported"})})]})}function ie({sections:t,loading:s}){return e.jsxs(N,{children:[e.jsxs(v,{children:[e.jsxs(k,{className:"flex items-center gap-2 text-base",children:[e.jsx(ee,{className:"h-4 w-4","aria-hidden":"true"}),"Addresses"]}),e.jsx(C,{children:s?"Refreshing addresses":"Reported by runtime network capability"})]}),e.jsx(R,{className:"space-y-4",children:t.map(i=>e.jsxs("div",{children:[e.jsx("div",{className:"text-xs font-medium text-muted-foreground",children:i.title}),i.values.length>0?e.jsx("div",{className:"mt-1 space-y-1",children:i.values.map(n=>e.jsx("div",{className:"break-words text-sm text-foreground",children:n},n))}):e.jsx("div",{className:"mt-1 text-sm text-muted-foreground",children:"Unsupported"})]},i.key))})]})}function ce({status:t}){return e.jsxs(N,{children:[e.jsxs(v,{children:[e.jsxs(k,{className:"flex items-center gap-2 text-base",children:[e.jsx(K,{className:"h-4 w-4","aria-hidden":"true"}),"Capabilities"]}),e.jsx(C,{children:"Server-declared network support"})]}),e.jsxs(R,{className:"grid gap-3 text-sm",children:[e.jsx(E,{label:"TLS",capability:t?.tls,extra:t?.tls.expiresAt?`Expires ${oe(t.tls.expiresAt)}`:void 0}),e.jsx(E,{label:"DNS",capability:t?.dns}),e.jsx(E,{label:"Tunnel",capability:t?.tunnel})]})]})}function de({status:t,diagnostics:s,diagnosing:i,renewing:n,onDiagnose:p,onRenewCertificate:g}){return e.jsxs(N,{children:[e.jsxs(v,{children:[e.jsxs(k,{className:"flex items-center gap-2 text-base",children:[e.jsx(F,{className:"h-4 w-4","aria-hidden":"true"}),"Actions"]}),e.jsx(C,{children:"Only actions allowed by the Network capability are shown"})]}),e.jsxs(R,{className:"space-y-4",children:[e.jsxs("div",{className:"flex flex-wrap gap-2",children:[t?.actions.diagnose?e.jsxs(P,{type:"button",size:"sm",variant:"outline",onClick:p,disabled:i,children:[e.jsx(G,{className:"mr-2 h-4 w-4","aria-hidden":"true"}),"Diagnose"]}):null,t?.actions.renewCertificate?e.jsxs(P,{type:"button",size:"sm",variant:"subtle",onClick:g,disabled:n,children:[e.jsx(Y,{className:"mr-2 h-4 w-4","aria-hidden":"true"}),"Renew certificate"]}):null]}),s.length>0?e.jsx("div",{className:"space-y-2",children:s.map(c=>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 font-medium text-foreground",children:c.label}),e.jsx(q,{variant:c.status==="ok"?"secondary":"outline",children:c.status})]}),c.detail?e.jsx("div",{className:"mt-1 break-words text-xs text-muted-foreground",children:c.detail}):null]},c.id))}):e.jsx("div",{className:"text-sm text-muted-foreground",children:"No diagnostics run yet."})]})]})}function E({label:t,capability:s,extra:i}){const n=s?.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 font-medium text-foreground",children:n?t:`${t} unsupported`}),e.jsx("div",{className:"text-xs text-muted-foreground",children:s?.status??"loading"})]}),e.jsx(q,{variant:n?"secondary":"outline",children:n?"Supported":"Unsupported"})]}),i?e.jsx("div",{className:"mt-2 text-xs text-muted-foreground",children:i}):null]})}function oe(t){if(!t)return"Not observed";const s=new Date(t);return Number.isNaN(s.getTime())?"Not observed":s.toLocaleString()}export{he as default};
@@ -1,2 +0,0 @@
1
- import{c as U,u as M,r as d,j as e,B as g}from"./dashboard-8C6o666W.js";import{T as $,P as q}from"./PaneListHeader-57nvMwH6.js";import{C as f,a as b,b as j,S as D,c as p,d as v,B as H}from"./card-B9Zjf_2p.js";import{E as T}from"./external-link-VbMQvVMz.js";import{D as _}from"./database-DPh9wDY9.js";import{R as z}from"./refresh-cw-BxVxNxNr.js";const K=[["path",{d:"m10 17 5-5-5-5",key:"1bsop3"}],["path",{d:"M15 12H3",key:"6jk70r"}],["path",{d:"M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4",key:"u53s6r"}]],F=U("log-in",K);const O=[["path",{d:"m16 17 5-5-5-5",key:"1bji2h"}],["path",{d:"M21 12H9",key:"dn1m92"}],["path",{d:"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4",key:"1uf3rs"}]],V=U("log-out",O);async function G({webId:t,podUrl:s,authenticatedFetch:a}){const n=new URL("/",s).origin,i=await a(`${n}/api/pod/settings/status`,{method:"GET",credentials:"include",headers:{accept:"application/json"}});if(!i.ok)throw await i.arrayBuffer(),new Error("Pod settings request failed. Please try again.");if(!i.headers.get("content-type")?.includes("application/json"))throw await i.arrayBuffer(),new Error("Pod settings request failed. Please try again.");const o=await i.json();if(o.identity?.webId!==t)throw new Error("Pod settings response does not match the current Solid session.");return o}function ne({view:t="combined"}){const s=M(),[a,n]=d.useState(),[i,o]=d.useState(),[l,c]=d.useState(!1),u=d.useRef(0),I=d.useRef(void 0),y=d.useRef(!0),m=s.state.status==="authenticated"&&!!(s.webId&&s.podUrl)?`${s.webId}
2
- ${s.podUrl}`:void 0;d.useEffect(()=>(y.current=!0,()=>{y.current=!1,u.current+=1}),[]);const N=d.useCallback(async()=>{const P=s.webId,B=s.podUrl,L=m;if(!P||!B||!L)return;const S=u.current+1;u.current=S;const w=()=>y.current&&u.current===S&&I.current===L;c(!0),o(void 0);try{const A=await G({webId:P,podUrl:B,authenticatedFetch:s.fetch});w()&&n(A)}catch{w()&&o("Pod settings request failed. Please try again.")}finally{w()&&c(!1)}},[m,s.fetch,s.podUrl,s.webId]);d.useEffect(()=>{I.current=m,u.current+=1,n(void 0),o(void 0),c(!1),m&&N()},[m,N]);const x=d.useMemo(()=>({webId:s.webId??a?.identity.webId,podUrl:s.podUrl??a?.identity.podUrl,issuer:s.issuer}),[s.issuer,s.podUrl,s.webId,a]),R=()=>{x.podUrl&&window.open(x.podUrl,"_blank","noopener,noreferrer")},E=()=>{s.issuer&&s.login(s.issuer)};return e.jsx($,{mode:"auto",listHeader:e.jsx(q,{title:t==="usage"?"Usage":"Pod"}),list:e.jsxs("aside",{className:"flex h-full flex-col gap-3 border-r border-border bg-muted/30 p-4",children:[t!=="usage"?e.jsx(W,{webId:x.webId,podUrl:x.podUrl,issuer:x.issuer,sessionStatus:s.state.status,onOpenPod:R,onLogout:()=>{s.logout()},onLoginAgain:E,canLoginAgain:!!s.issuer}):null,t!=="settings"?e.jsx(X,{storage:a?.storage,loading:l&&!a}):null]}),mainHeader:e.jsx(Q,{title:t==="usage"?"Usage":"Pod",loading:l,onRefresh:N}),main:e.jsxs("section",{className:"flex min-h-full flex-col gap-4 bg-background p-6",children:[i?e.jsx("div",{role:"alert",className:"rounded-md border border-destructive/30 bg-destructive/10 px-4 py-3 text-sm text-destructive",children:i}):null,e.jsx(J,{aiConnection:a?.aiConnection,loading:l&&!a}),e.jsxs(f,{children:[e.jsxs(b,{children:[e.jsxs(j,{className:"flex items-center gap-2 text-base",children:[e.jsx(D,{className:"h-4 w-4","aria-hidden":"true"}),"Access boundary"]}),e.jsx(p,{children:"This page reads the authenticated Solid session and current Pod only."})]}),e.jsxs(v,{className:"grid gap-3 text-sm text-muted-foreground",children:[e.jsx(r,{label:"Session",value:s.state.status}),e.jsx(r,{label:"Status generated",value:C(a?.generatedAt)})]})]})]}),className:"min-h-full"})}function W({webId:t,podUrl:s,issuer:a,sessionStatus:n,onOpenPod:i,onLogout:o,onLoginAgain:l,canLoginAgain:c}){return e.jsxs(f,{children:[e.jsxs(b,{children:[e.jsx(j,{className:"text-base",children:"Identity"}),e.jsx(p,{children:"Current Solid session"})]}),e.jsxs(v,{className:"space-y-4",children:[e.jsxs("div",{className:"space-y-3 text-sm",children:[e.jsx(r,{label:"WebID",value:t??"Not signed in"}),e.jsx(r,{label:"Pod",value:s??"Discovering"}),e.jsx(r,{label:"Issuer",value:a??"Unknown"}),e.jsx(r,{label:"Status",value:n})]}),e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsxs(g,{type:"button",size:"sm",variant:"outline",onClick:i,disabled:!s,children:[e.jsx(T,{className:"mr-2 h-4 w-4","aria-hidden":"true"}),"Open Pod"]}),e.jsxs(g,{type:"button",size:"sm",variant:"subtle",onClick:o,children:[e.jsx(V,{className:"mr-2 h-4 w-4","aria-hidden":"true"}),"Logout"]}),e.jsxs(g,{type:"button",size:"sm",variant:"ghost",onClick:l,disabled:!c,children:[e.jsx(F,{className:"mr-2 h-4 w-4","aria-hidden":"true"}),"Login again"]})]})]})]})}function X({storage:t,loading:s}){const a=t?.status==="available",n=a&&t.limits.storageLimitBytes?Math.min(100,Math.round(t.usage.storageBytes/t.limits.storageLimitBytes*100)):void 0;return e.jsxs(f,{children:[e.jsxs(b,{children:[e.jsxs(j,{className:"flex items-center gap-2 text-base",children:[e.jsx(_,{className:"h-4 w-4","aria-hidden":"true"}),"Storage usage"]}),e.jsx(p,{children:s?"Refreshing usage":"Current Pod quota view"})]}),e.jsx(v,{className:"space-y-4",children:a?e.jsxs(e.Fragment,{children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-2xl font-semibold text-foreground",children:h(t.usage.storageBytes)}),e.jsx("div",{className:"text-sm text-muted-foreground",children:t.limits.storageLimitBytes==null?"No storage limit":`${h(t.limits.storageLimitBytes)} limit`})]}),e.jsx("div",{className:"h-2 overflow-hidden rounded-full bg-muted","aria-label":"Storage usage meter",children:e.jsx("div",{className:"h-full bg-primary",style:{width:`${n??0}%`}})}),e.jsxs("div",{className:"grid gap-2 text-sm text-muted-foreground",children:[e.jsx(r,{label:"Ingress",value:h(t.usage.ingressBytes)}),e.jsx(r,{label:"Egress",value:h(t.usage.egressBytes)}),e.jsx(r,{label:"Bandwidth",value:t.limits.bandwidthLimitBps==null?"No limit":`${h(t.limits.bandwidthLimitBps)}/s`})]})]}):e.jsx(k,{title:t?.status==="error"?"Usage unavailable":"Usage unsupported",detail:t?.reason??"usage_not_available"})})]})}function J({aiConnection:t,loading:s}){const a=t?.status==="available";return e.jsxs(f,{children:[e.jsx(b,{children:e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx(j,{className:"text-base",children:"AI Connection"}),e.jsx(p,{children:"Pod-backed provider configuration status"})]}),e.jsx(H,{variant:a?"secondary":"outline",children:a?"Available":s?"Loading":"Partial"})]})}),e.jsx(v,{className:"space-y-4",children:a?e.jsxs("div",{className:"grid gap-3 text-sm",children:[e.jsx(r,{label:"Providers",value:`${t.configuredProviders} providers`}),e.jsx(r,{label:"Data container",value:t.containerUrl??"Not declared"}),e.jsx(r,{label:"Last sync",value:C(t.lastSyncAt)}),e.jsx(r,{label:"Source",value:t.source??"drizzle-solid"})]}):e.jsx(k,{title:t?.status==="error"?"AI Connection unavailable":"AI Connection unsupported",detail:t?.reason??"not_configured"})})]})}function Q({title:t,loading:s,onRefresh:a}){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.jsx("div",{className:"text-sm font-semibold text-foreground",children:t}),e.jsx("div",{className:"truncate text-xs text-muted-foreground",children:"Identity, storage, and applet data status"})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("a",{className:"inline-flex h-9 items-center justify-center rounded-md border border-input bg-background px-3 text-sm font-medium text-foreground hover:bg-accent",href:t==="Usage"?"/settings/pod":"/dashboard/usage",children:t==="Usage"?"Configure Pod":"View usage"}),e.jsxs(g,{type:"button",size:"sm",variant:"outline",onClick:a,disabled:s,children:[e.jsx(z,{className:"mr-2 h-4 w-4","aria-hidden":"true"}),"Refresh"]})]})]})}function r({label:t,value:s}){return e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"text-xs font-medium text-muted-foreground",children:t}),e.jsx("div",{className:"break-words text-sm text-foreground",children:s??"Unknown"})]})}function k({title:t,detail:s}){return e.jsxs("div",{className:"rounded-md border border-border bg-muted/40 p-3",children:[e.jsx("div",{className:"text-sm font-medium text-foreground",children:t}),e.jsx("div",{className:"mt-1 text-xs text-muted-foreground",children:s})]})}function h(t){if(!Number.isFinite(t))return"Unknown";const s=["B","KB","MB","GB","TB"];let a=Math.max(0,t),n=0;for(;a>=1024&&n<s.length-1;)a/=1024,n+=1;return`${Number.isInteger(a)?a:a.toFixed(a>=10?1:2)} ${s[n]}`}function C(t){if(!t)return"Not observed";const s=new Date(t);return Number.isNaN(s.getTime())?"Not observed":s.toLocaleString()}export{W as IdentityCard,X as PodUsageCard,ne as default};
@@ -1 +0,0 @@
1
- import{c as S,A as k,S as C,r as n,j as e,O as L,d as w,e as y,N as I}from"./dashboard-8C6o666W.js";import{f as P,S as D}from"./services-status-context-DKmJ7ISr.js";import{D as A}from"./database-DPh9wDY9.js";import{T as E,P as q,u as R}from"./PaneListHeader-57nvMwH6.js";import{R as _}from"./refresh-cw-BxVxNxNr.js";const z=[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2",key:"ngkwjq"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2",key:"iecqi9"}],["line",{x1:"6",x2:"6.01",y1:"6",y2:"6",key:"16zg32"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18",key:"nzw8ys"}]],M=S("server",z);const H=[["path",{d:"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915",key:"1i5ecw"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]],$=S("settings",H),F=[{id:"runtime",label:"Runtime",path:"/services/runtime",description:"Xpod runtime, Solid server, AI Gateway, storage, and worker health.",icon:k},{id:"logs",label:"Logs",path:"/services/logs",description:"Runtime logs and sanitized diagnostics export.",icon:C},{id:"rdf",label:"RDF",path:"/services/rdf",description:"RDF indexing, storage stats, cache health, and slow query evidence.",icon:A},{id:"configuration",label:"Configuration",path:"/services/configuration",description:"Advanced local runtime configuration and restart-controlled changes.",icon:$}],N={supported:!1,reason:"capability_not_reported"};function K({mode:a="auto",product:s="legacy"}){const[r,i]=n.useState(null),[c,l]=n.useState(!0),[m,f]=n.useState(!1),[t,p]=n.useState(),o=n.useRef(0),d=n.useRef(null),x=n.useRef(!1),h=n.useCallback(async(u=!1)=>{const b=o.current+1;o.current=b,d.current?.abort();const g=new AbortController;d.current=g,u?l(!0):f(!0),p(void 0);try{const v=await P({signal:g.signal});x.current&&o.current===b&&!g.signal.aborted&&i(v)}catch(v){x.current&&o.current===b&&!g.signal.aborted&&!(v instanceof DOMException&&v.name==="AbortError")&&p("Services status request failed. Please try again.")}finally{x.current&&o.current===b&&!g.signal.aborted&&(l(!1),f(!1))}},[]);n.useEffect(()=>{x.current=!0,h(!0);const u=setInterval(()=>{h(!1)},1e4);return()=>{x.current=!1,o.current+=1,d.current?.abort(),d.current=null,clearInterval(u)}},[h]);const j=n.useMemo(()=>{const u=r?.adminData?.capabilities?.services;return{snapshot:r,loading:c,refreshing:m,error:t,refresh:()=>{h(!1)},restartCapability:u?.lifecycle?.restart??N,configurationWriteCapability:u?.configuration?.write??N}},[t,h,c,m,r]);return e.jsx(D.Provider,{value:j,children:e.jsx(E,{mode:a,listHeader:e.jsx(q,{title:"Services"}),list:e.jsx(W,{snapshot:r,loading:c&&!r,showSections:s==="legacy"}),mainHeader:e.jsx(T,{product:s,loading:c||m,onRefresh:j.refresh}),main:e.jsxs("section",{className:"min-h-full min-w-0 bg-background",children:[e.jsx(O,{}),t?e.jsx("div",{role:"alert",className:"mx-6 mt-6 rounded-md border border-destructive/30 bg-destructive/10 px-4 py-3 text-sm text-destructive",children:t}):null,e.jsx(L,{})]}),className:"min-h-full"})})}function O(){const a=w(),s=R(),r=n.useRef(null),i=a.pathname.replace(/\/+$/,"")||"/services";return n.useEffect(()=>{if(r.current!==i){if(r.current=i,i==="/services"){s.openList();return}s.openMain()}},[i,s]),null}function T({product:a,loading:s,onRefresh:r}){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.jsx("div",{className:"text-sm font-semibold text-foreground",children:"Services"}),e.jsx("div",{className:"truncate text-xs text-muted-foreground",children:"Runtime health, logs, RDF indexing, and configuration"})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[a!=="legacy"?e.jsx("a",{className:"inline-flex h-9 items-center justify-center rounded-md border border-input bg-background px-3 text-sm font-medium text-foreground hover:bg-accent",href:a==="dashboard"?"/settings/services":"/dashboard/runtime",children:a==="dashboard"?"Configure":"View runtime"}):null,e.jsxs("button",{type:"button",onClick:r,disabled:s,className:"inline-flex h-9 items-center justify-center rounded-md border border-input bg-background px-3 text-sm font-medium text-foreground transition-colors hover:bg-accent disabled:pointer-events-none disabled:opacity-50",children:[e.jsx(_,{className:y("mr-2 h-4 w-4",s&&"animate-spin"),"aria-hidden":"true"}),"Refresh"]})]})]})}function W({snapshot:a,loading:s,showSections:r}){const i=w(),c=R(),l=a?.servicesData??[],m=l.filter(t=>t.status==="running").length,f=l.length;return e.jsxs("aside",{className:"flex h-full flex-col gap-4 border-r border-border bg-muted/30 p-4",children:[e.jsxs("div",{className:"rounded-md border border-border bg-card",children:[e.jsxs("div",{className:"px-4 pt-4",children:[e.jsxs("div",{className:"flex items-center gap-2 text-base font-semibold text-foreground",children:[e.jsx(M,{className:"h-4 w-4","aria-hidden":"true"}),"Service health"]}),e.jsx("div",{className:"mt-1 text-sm text-muted-foreground",children:s?"Loading runtime status":`${m}/${f||0} services running`})]}),e.jsx("div",{className:"space-y-2 px-4 pb-4 pt-3",children:f>0?l.map(t=>e.jsxs("div",{className:"flex items-center justify-between gap-3 text-sm",children:[e.jsx("span",{className:"font-medium text-foreground",children:t.name}),e.jsx("span",{className:y("rounded-md px-2 py-0.5 text-xs font-medium",t.status==="running"?"bg-primary text-primary-foreground":"bg-muted text-muted-foreground"),children:t.status})]},t.name)):e.jsx("div",{className:"text-sm text-muted-foreground",children:s?"Waiting for status":"No service status reported"})})]}),r?e.jsx("nav",{"aria-label":"Services sections",className:"space-y-1",children:F.map(t=>{const p=t.icon,o=t.id==="runtime"&&i.pathname==="/services";return e.jsxs(I,{to:t.path,onClick:()=>c.openMain(),className:({isActive:d})=>y("flex gap-3 rounded-md px-3 py-2 text-sm transition-colors","focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",d||o?"bg-accent text-accent-foreground":"text-muted-foreground hover:bg-accent hover:text-accent-foreground"),children:[e.jsx(p,{className:"mt-0.5 h-4 w-4 shrink-0","aria-hidden":"true"}),e.jsxs("span",{className:"min-w-0",children:[e.jsx("span",{className:"block font-medium",children:t.label}),e.jsx("span",{className:"mt-0.5 block text-xs opacity-80",children:t.description})]})]},t.id)})}):null]})}export{K as default};