@undefineds.co/xpod 0.3.69 → 0.3.71-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/config/cloud.json +3 -28
  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 +42 -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/dist/storage/rdf/PostgresRdfEngine.js +2 -0
  109. package/dist/storage/rdf/PostgresRdfEngine.js.map +1 -1
  110. package/dist/storage/rdf/PostgresRdfVectorIndex.d.ts +1 -0
  111. package/dist/storage/rdf/PostgresRdfVectorIndex.js +1 -1
  112. package/dist/storage/rdf/PostgresRdfVectorIndex.js.map +1 -1
  113. package/dist/storage/rdf/PostgresRdfVectorIndex.jsonld +29 -0
  114. package/node_modules/@undefineds.co/ai-connection/dist/AiClientConfigurationSection.d.ts +5 -5
  115. package/node_modules/@undefineds.co/ai-connection/dist/AiClientConfigurationSection.js +7 -7
  116. package/node_modules/@undefineds.co/ai-connection/dist/AiConnectionMain.js +1 -1
  117. package/node_modules/@undefineds.co/ai-connection/dist/AiConnectionPanel.d.ts +3 -1
  118. package/node_modules/@undefineds.co/ai-connection/dist/AiConnectionPanel.js +74 -60
  119. package/node_modules/@undefineds.co/ai-connection/dist/ai-connection-client.d.ts +0 -21
  120. package/node_modules/@undefineds.co/ai-connection/dist/ai-connection-client.js +0 -41
  121. package/node_modules/@undefineds.co/ai-connection/dist/client-config/base-adapter.d.ts +1 -0
  122. package/node_modules/@undefineds.co/ai-connection/dist/client-config/base-adapter.js +7 -2
  123. package/node_modules/@undefineds.co/ai-connection/dist/client-config/codex.js +3 -3
  124. package/node_modules/@undefineds.co/ai-connection/dist/client-config/index.cjs +15 -9
  125. package/node_modules/@undefineds.co/ai-connection/dist/client-config/json-env-adapter.js +4 -4
  126. package/node_modules/@undefineds.co/ai-connection/dist/client-config/pi.js +3 -3
  127. package/node_modules/@undefineds.co/ai-connection/dist/client-config/types.d.ts +1 -1
  128. package/node_modules/@undefineds.co/ai-connection/dist/controller.d.ts +2 -1
  129. package/node_modules/@undefineds.co/ai-connection/dist/controller.js +1 -0
  130. package/node_modules/@undefineds.co/ai-connection/dist/service-access.js +0 -1
  131. package/node_modules/@undefineds.co/extension-sdk/dist/web.d.ts +22 -1
  132. package/package.json +3 -3
  133. package/static/app/assets/main.js +7 -7
  134. package/static/dashboard/assets/NetworkPage-CXjiUMR1.js +2 -0
  135. package/static/dashboard/assets/{PaneListHeader-57nvMwH6.js → PaneListHeader-BSipfl0W.js} +1 -1
  136. package/static/dashboard/assets/PodPage-Bd3Xhmp-.js +2 -0
  137. package/static/dashboard/assets/ServicesPage-CI3jWZGe.js +1 -0
  138. package/static/dashboard/assets/{card-B9Zjf_2p.js → card-z5BV5hTo.js} +1 -1
  139. package/static/dashboard/assets/{dashboard-8C6o666W.js → dashboard-CSrqAv2L.js} +2 -2
  140. package/static/dashboard/assets/{database-DPh9wDY9.js → database-Ct5ZPe6L.js} +1 -1
  141. package/static/dashboard/assets/{external-link-VbMQvVMz.js → external-link-B7wD1hkm.js} +1 -1
  142. package/static/dashboard/assets/index-tOBaUnM1.js +44 -0
  143. package/static/dashboard/assets/{refresh-cw-BxVxNxNr.js → refresh-cw-CFDZPaE8.js} +1 -1
  144. package/static/dashboard/assets/{services-status-context-DKmJ7ISr.js → services-status-context-DN1Icg4A.js} +1 -1
  145. package/static/dashboard/dashboard.html +1 -1
  146. package/static/settings/assets/ModelsPage-D87RrLB0.js +1 -0
  147. package/static/settings/assets/{PaneListHeader-Bgaze006.js → PaneListHeader-BtON8Z5Q.js} +1 -1
  148. package/static/settings/assets/PodPage-9LGuOloQ.js +2 -0
  149. package/static/settings/assets/ServicesPage-ZoT9-B_0.js +1 -0
  150. package/static/settings/assets/SettingsPage-Bj4lB3mq.js +41 -0
  151. package/static/settings/assets/{card-B_bXSIn-.js → card-CCGngjas.js} +1 -1
  152. package/static/settings/assets/{services-status-context-DiX-AbrP.js → services-status-context-CBfImxL_.js} +1 -1
  153. package/static/settings/assets/{settings-B30oMfZq.js → settings-yUyFIJxi.js} +2 -2
  154. package/static/settings/assets/{workspace-layout-Dv1Yn9Ci.js → workspace-layout-B0wJpPze.js} +1 -1
  155. package/static/settings/settings.html +1 -1
  156. package/dist/api/ai-gateway/auth/ClientCredentialsInternalPodAccessTokenProvider.d.ts +0 -25
  157. package/dist/api/ai-gateway/auth/ClientCredentialsInternalPodAccessTokenProvider.js +0 -91
  158. package/dist/api/ai-gateway/auth/ClientCredentialsInternalPodAccessTokenProvider.js.map +0 -1
  159. package/dist/api/ai-gateway/auth/GatewayApiKey.d.ts +0 -28
  160. package/dist/api/ai-gateway/auth/GatewayApiKey.js +0 -162
  161. package/dist/api/ai-gateway/auth/GatewayApiKey.js.map +0 -1
  162. package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.d.ts +0 -56
  163. package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.js +0 -143
  164. package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.js.map +0 -1
  165. package/dist/api/ai-gateway/auth/GatewayKeyLocatorCodec.d.ts +0 -26
  166. package/dist/api/ai-gateway/auth/GatewayKeyLocatorCodec.js +0 -97
  167. package/dist/api/ai-gateway/auth/GatewayKeyLocatorCodec.js.map +0 -1
  168. package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.d.ts +0 -54
  169. package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.js +0 -152
  170. package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.js.map +0 -1
  171. package/static/dashboard/assets/NetworkPage-Brt79fuQ.js +0 -2
  172. package/static/dashboard/assets/PodPage-CM6do1vB.js +0 -2
  173. package/static/dashboard/assets/ServicesPage-BUNxGJRz.js +0 -1
  174. package/static/dashboard/assets/index-RfiFckzb.js +0 -51
  175. package/static/settings/assets/ModelsPage-D_IH7AH3.js +0 -1
  176. package/static/settings/assets/PodPage-Cy1VNObw.js +0 -2
  177. package/static/settings/assets/ServicesPage-BsFaxVWC.js +0 -1
  178. package/static/settings/assets/SettingsPage-UafNWE_p.js +0 -48
@@ -7,13 +7,13 @@ import { Check, Copy, KeyRound, Loader2, Trash2, } from 'lucide-react';
7
7
  import { AI_CLIENT_LABELS, AiClientConfigurationSection, } from './AiClientConfigurationSection.js';
8
8
  import { AiProviderCard, } from './AiProviderCard.js';
9
9
  const EMPTY_PROVIDER_SUMMARIES = {};
10
- export function AiConnectionPanel({ client, openExternal = openExternalUrl, clientConfigurationBridge, selectedProvider, providerSummaries: providerSummariesInput = EMPTY_PROVIDER_SUMMARIES, providerLoadError, serviceAccessGranted = false, onProviderStateChange, }) {
11
- const [keys, setKeys] = useState([]);
12
- const [keysLoading, setKeysLoading] = useState(true);
13
- const [keyName, setKeyName] = useState('');
14
- const [creatingKey, setCreatingKey] = useState(false);
15
- const [oneTimeKey, setOneTimeKey] = useState();
16
- const [keyError, setKeyError] = useState();
10
+ export function AiConnectionPanel({ client, openExternal = openExternalUrl, clientConfigurationBridge, clientCredentialManager, selectedProvider, providerSummaries: providerSummariesInput = EMPTY_PROVIDER_SUMMARIES, providerLoadError, serviceAccessGranted = false, onProviderStateChange, }) {
11
+ const [credentials, setCredentials] = useState([]);
12
+ const [credentialsLoading, setCredentialsLoading] = useState(Boolean(clientCredentialManager));
13
+ const [credentialName, setCredentialName] = useState('');
14
+ const [creatingCredential, setCreatingCredential] = useState(false);
15
+ const [oneTimeCredential, setOneTimeCredential] = useState();
16
+ const [credentialError, setCredentialError] = useState();
17
17
  const [connectionStates, setConnectionStates] = useState({});
18
18
  const [models, setModels] = useState([]);
19
19
  const [attempts, setAttempts] = useState({});
@@ -37,20 +37,26 @@ export function AiConnectionPanel({ client, openExternal = openExternalUrl, clie
37
37
  }, [providerSummariesInput]);
38
38
  useEffect(() => {
39
39
  let active = true;
40
- setKeysLoading(true);
41
- void client.listGatewayKeys()
42
- .then((records) => {
43
- if (active)
44
- setKeys(records);
45
- })
46
- .catch((error) => {
47
- if (active)
48
- setKeyError(errorMessage(error));
49
- })
50
- .finally(() => {
51
- if (active)
52
- setKeysLoading(false);
53
- });
40
+ if (clientCredentialManager) {
41
+ setCredentialsLoading(true);
42
+ void clientCredentialManager.list()
43
+ .then((records) => {
44
+ if (active)
45
+ setCredentials(records);
46
+ })
47
+ .catch((error) => {
48
+ if (active)
49
+ setCredentialError(errorMessage(error));
50
+ })
51
+ .finally(() => {
52
+ if (active)
53
+ setCredentialsLoading(false);
54
+ });
55
+ }
56
+ else {
57
+ setCredentialsLoading(false);
58
+ setCredentials([]);
59
+ }
54
60
  void client.listModels()
55
61
  .then((availableModels) => {
56
62
  if (active)
@@ -58,13 +64,13 @@ export function AiConnectionPanel({ client, openExternal = openExternalUrl, clie
58
64
  })
59
65
  .catch((error) => {
60
66
  if (active)
61
- setKeyError(errorMessage(error));
67
+ setCredentialError(errorMessage(error));
62
68
  });
63
69
  return () => {
64
70
  active = false;
65
71
  pollingGeneration.current += 1;
66
72
  };
67
- }, [client]);
73
+ }, [client, clientCredentialManager]);
68
74
  const setBusy = (provider, value) => {
69
75
  setBusyProviders((current) => ({ ...current, [provider]: value }));
70
76
  };
@@ -200,75 +206,83 @@ export function AiConnectionPanel({ client, openExternal = openExternalUrl, clie
200
206
  setBusy(provider, false);
201
207
  }
202
208
  };
203
- const createKey = async () => {
204
- if (!serviceAccessGranted)
209
+ const createCredential = async () => {
210
+ if (!serviceAccessGranted || !clientCredentialManager)
205
211
  return;
206
- setCreatingKey(true);
207
- setKeyError(undefined);
208
- setOneTimeKey(undefined);
212
+ setCreatingCredential(true);
213
+ setCredentialError(undefined);
214
+ setOneTimeCredential(undefined);
209
215
  try {
210
- const created = await client.createGatewayKey({
211
- ...(keyName.trim() ? { name: keyName.trim() } : {}),
216
+ const created = await clientCredentialManager.create({
217
+ name: credentialName.trim() || 'AI Connection',
218
+ webId: client.webId,
212
219
  });
213
- setOneTimeKey(created.plaintext);
214
- setKeys((current) => [created.record, ...current]);
215
- setKeyName('');
220
+ setOneTimeCredential({
221
+ clientId: created.clientId,
222
+ clientSecret: created.clientSecret,
223
+ apiKey: created.apiKey,
224
+ });
225
+ setCredentials((current) => [
226
+ created,
227
+ ...current.filter((record) => record.resourceUrl !== created.resourceUrl),
228
+ ]);
229
+ setCredentialName('');
216
230
  }
217
231
  catch (error) {
218
- setKeyError(errorMessage(error));
232
+ setCredentialError(errorMessage(error));
219
233
  }
220
234
  finally {
221
- setCreatingKey(false);
235
+ setCreatingCredential(false);
222
236
  }
223
237
  };
224
- const revokeKey = async (keyId) => {
225
- if (!serviceAccessGranted)
238
+ const revokeCredential = async (credential) => {
239
+ if (!serviceAccessGranted || !clientCredentialManager)
226
240
  return;
227
- setKeyError(undefined);
241
+ setCredentialError(undefined);
228
242
  try {
229
- const record = await client.revokeGatewayKey(keyId);
230
- setKeys((current) => current.map((item) => item.id === keyId
231
- ? (record ?? { ...item, revokedAt: new Date().toISOString() })
232
- : item));
243
+ await clientCredentialManager.revoke(credential.resourceUrl);
244
+ setCredentials((current) => current.filter((item) => item.resourceUrl !== credential.resourceUrl));
233
245
  }
234
246
  catch (error) {
235
- setKeyError(errorMessage(error));
247
+ setCredentialError(errorMessage(error));
236
248
  }
237
249
  };
238
- const copyOneTimeKey = async () => {
239
- if (!oneTimeKey)
250
+ const copyOneTimeApiKey = async () => {
251
+ if (!oneTimeCredential)
240
252
  return;
241
- await navigator.clipboard?.writeText(oneTimeKey);
253
+ await navigator.clipboard?.writeText(oneTimeCredential.apiKey);
242
254
  setCopied(true);
243
255
  };
244
- const createManagedGatewayKey = useCallback(async (targetClient) => {
256
+ const createManagedClientCredential = useCallback(async (targetClient) => {
245
257
  if (!serviceAccessGranted) {
246
258
  throw new Error('AI Connection service access is not granted');
247
259
  }
248
- const created = await client.createGatewayKey({
260
+ if (!clientCredentialManager) {
261
+ throw new Error('Client Credentials are not available in this host');
262
+ }
263
+ const created = await clientCredentialManager.create({
249
264
  name: `AI Connection · ${AI_CLIENT_LABELS[targetClient]}`,
265
+ webId: client.webId,
250
266
  });
251
- setKeys((current) => [
252
- created.record,
253
- ...current.filter((record) => record.id !== created.record.id),
267
+ setCredentials((current) => [
268
+ created,
269
+ ...current.filter((record) => record.resourceUrl !== created.resourceUrl),
254
270
  ]);
255
271
  return {
256
- gatewayKey: created.plaintext,
272
+ apiKey: created.apiKey,
257
273
  revoke: async () => {
258
- await client.revokeGatewayKey(created.record.id);
259
- setKeys((current) => current.map((record) => record.id === created.record.id
260
- ? { ...record, revokedAt: new Date().toISOString() }
261
- : record));
274
+ await clientCredentialManager.revoke(created.resourceUrl);
275
+ setCredentials((current) => current.filter((record) => record.resourceUrl !== created.resourceUrl));
262
276
  },
263
277
  };
264
- }, [client, serviceAccessGranted]);
278
+ }, [client.webId, clientCredentialManager, serviceAccessGranted]);
265
279
  return (_jsxs("div", { className: "mx-auto w-full max-w-3xl px-6 py-6", children: [_jsxs("section", { children: [providerLoadError ? (_jsxs("p", { className: "mb-4 rounded-md border border-destructive/30 px-3 py-2 text-sm text-destructive", children: ["Provider \u72B6\u6001\u8BFB\u53D6\u5931\u8D25\uFF1A", providerLoadError] })) : null, PROVIDERS.filter((definition) => definition.id === (selectedProvider ?? 'openai')).map((definition) => (_jsx(AiProviderCard, { definition: definition, status: connectionStates[definition.id] ?? 'unknown', accountLabel: providerSummariesInput[definition.id]?.accountLabel, attempt: attempts[definition.id], apiKey: apiKeyInputs[definition.id] ?? '', busy: Boolean(busyProviders[definition.id]), disabled: !serviceAccessGranted, error: providerErrors[definition.id], quota: quotas[definition.id], models: models.filter((model) => model.provider === definition.id), onApiKeyChange: (value) => setApiKeyInputs((current) => ({
266
280
  ...current,
267
281
  [definition.id]: value,
268
- })), onBeginApiKey: () => void beginApiKey(definition.id), onBeginBrowser: () => void beginBrowserConnect(definition), onSaveApiKey: () => void saveApiKey(definition), onDisconnect: () => void disconnect(definition.id), onRefreshQuota: () => void refreshQuota(definition.id) }, definition.id)))] }), _jsxs("details", { className: "border-t border-border/60 py-5", children: [_jsx("summary", { className: "cursor-pointer list-none text-sm font-medium", children: "\u5BA2\u6237\u7AEF\u63A5\u5165" }), _jsxs("div", { className: "mt-4 space-y-6", children: [_jsx(AiClientConfigurationSection, { bridge: clientConfigurationBridge, endpoint: client.apiBase, createGatewayKey: createManagedGatewayKey }), _jsxs("details", { className: "border-t border-border/60 pt-4", children: [_jsx("summary", { className: "cursor-pointer text-sm font-medium text-muted-foreground", children: "\u9AD8\u7EA7\uFF1AGateway Keys" }), _jsx("div", { className: "mt-3", children: _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs(CardTitle, { className: "flex items-center gap-2 text-base", children: [_jsx(KeyRound, { className: "h-4 w-4" }), "Gateway Keys"] }), _jsx(CardDescription, { children: "\u7F16\u7801\u5BA2\u6237\u7AEF\u53EA\u4F7F\u7528 Gateway Key\uFF1B\u65B0\u5BC6\u94A5\u660E\u6587\u4EC5\u5728\u521B\u5EFA\u540E\u663E\u793A\u4E00\u6B21\u3002" })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "flex gap-2", children: [_jsx(Input, { "aria-label": "Gateway Key \u540D\u79F0", placeholder: "\u540D\u79F0\uFF0C\u4F8B\u5982 Codex", value: keyName, onChange: (event) => setKeyName(event.target.value) }), _jsxs(Button, { onClick: () => void createKey(), disabled: creatingKey || !serviceAccessGranted, children: [creatingKey ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(KeyRound, { className: "mr-2 h-4 w-4" }), "\u521B\u5EFA Gateway Key"] })] }), oneTimeKey ? (_jsxs("div", { className: "space-y-3 rounded-lg border border-amber-500/30 bg-amber-500/5 p-4", children: [_jsx("p", { className: "text-sm font-medium", children: "\u8BF7\u7ACB\u5373\u4FDD\u5B58\uFF1B\u5173\u95ED\u540E\u65E0\u6CD5\u518D\u6B21\u67E5\u770B\u3002" }), _jsx("code", { className: "block overflow-x-auto rounded bg-background p-3 text-xs", children: oneTimeKey }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: () => void copyOneTimeKey(), children: [copied ? _jsx(Check, { className: "mr-2 h-4 w-4" }) : _jsx(Copy, { className: "mr-2 h-4 w-4" }), copied ? '已复制' : '复制'] }), _jsx(Button, { size: "sm", onClick: () => {
269
- setOneTimeKey(undefined);
282
+ })), onBeginApiKey: () => void beginApiKey(definition.id), onBeginBrowser: () => void beginBrowserConnect(definition), onSaveApiKey: () => void saveApiKey(definition), onDisconnect: () => void disconnect(definition.id), onRefreshQuota: () => void refreshQuota(definition.id) }, definition.id)))] }), _jsxs("details", { className: "border-t border-border/60 py-5", children: [_jsx("summary", { className: "cursor-pointer list-none text-sm font-medium", children: "\u5BA2\u6237\u7AEF\u63A5\u5165" }), _jsxs("div", { className: "mt-4 space-y-6", children: [_jsx(AiClientConfigurationSection, { bridge: clientConfigurationBridge, endpoint: client.apiBase, createClientCredential: createManagedClientCredential }), _jsxs("details", { className: "border-t border-border/60 pt-4", children: [_jsx("summary", { className: "cursor-pointer text-sm font-medium text-muted-foreground", children: "Client Credentials" }), _jsx("div", { className: "mt-3", children: _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs(CardTitle, { className: "flex items-center gap-2 text-base", children: [_jsx(KeyRound, { className: "h-4 w-4" }), "Client Credentials"] }), _jsxs(CardDescription, { children: ["\u4E3A Codex\u3001Claude Code\u3001Pi\u3001CodeBuddy \u521B\u5EFA Solid Client Credentials\u3002API Key \u4E3A ", _jsx("code", { children: "sk-base64(client_id:client_secret)" }), "\uFF0C\u53EA\u5728\u521B\u5EFA\u540E\u663E\u793A\u4E00\u6B21\u3002"] })] }), _jsxs(CardContent, { className: "space-y-4", children: [!clientCredentialManager ? (_jsxs("div", { className: "rounded-lg border border-amber-500/30 bg-amber-500/5 p-4 text-sm text-muted-foreground", children: ["\u5F53\u524D Host \u6CA1\u6709\u63D0\u4F9B account-controls Client Credentials capability\u3002", _jsx("a", { className: "ml-1 underline", href: "/.account/", children: "\u6253\u5F00 Account Developer Access" }), ' ', "\u624B\u52A8\u521B\u5EFA API Key\u3002"] })) : null, _jsxs("div", { className: "flex gap-2", children: [_jsx(Input, { "aria-label": "Client Credential \u540D\u79F0", placeholder: "\u540D\u79F0\uFF0C\u4F8B\u5982 Codex", value: credentialName, onChange: (event) => setCredentialName(event.target.value) }), _jsxs(Button, { onClick: () => void createCredential(), disabled: creatingCredential || !serviceAccessGranted || !clientCredentialManager, children: [creatingCredential ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(KeyRound, { className: "mr-2 h-4 w-4" }), "\u521B\u5EFA Client Credential"] })] }), oneTimeCredential ? (_jsxs("div", { className: "space-y-3 rounded-lg border border-amber-500/30 bg-amber-500/5 p-4", children: [_jsx("p", { className: "text-sm font-medium", children: "\u8BF7\u7ACB\u5373\u4FDD\u5B58\uFF1B\u5173\u95ED\u540E\u65E0\u6CD5\u518D\u6B21\u67E5\u770B\u3002" }), _jsxs("div", { className: "space-y-2 rounded bg-background p-3 text-xs", children: [_jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Client ID: " }), _jsx("code", { children: oneTimeCredential.clientId })] }), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Client Secret: " }), _jsx("code", { children: oneTimeCredential.clientSecret })] }), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "API Key: " }), _jsx("code", { children: oneTimeCredential.apiKey })] })] }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: () => void copyOneTimeApiKey(), children: [copied ? _jsx(Check, { className: "mr-2 h-4 w-4" }) : _jsx(Copy, { className: "mr-2 h-4 w-4" }), copied ? '已复制' : '复制 API Key'] }), _jsx(Button, { size: "sm", onClick: () => {
283
+ setOneTimeCredential(undefined);
270
284
  setCopied(false);
271
- }, children: "\u6211\u5DF2\u4FDD\u5B58\uFF0C\u9690\u85CF\u5BC6\u94A5" })] })] })) : null, keyError ? _jsx("p", { className: "text-sm text-destructive", children: keyError }) : null, keysLoading ? (_jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), "\u6B63\u5728\u8BFB\u53D6 Gateway Keys"] })) : keys.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "\u5C1A\u672A\u521B\u5EFA Gateway Key\u3002" })) : (_jsx("div", { className: "space-y-2", children: keys.map((key) => (_jsxs("div", { className: "flex items-center justify-between gap-3 rounded-lg border p-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate text-sm font-medium", children: key.name || key.id }), _jsxs("div", { className: "mt-1 text-xs text-muted-foreground", children: [key.scopes.join(' · '), key.revokedAt ? ' · 已撤销' : ''] })] }), _jsx(Button, { variant: "ghost", size: "icon", "aria-label": `撤销 ${key.name || key.id}`, disabled: Boolean(key.revokedAt) || !serviceAccessGranted, onClick: () => void revokeKey(key.id), children: _jsx(Trash2, { className: "h-4 w-4" }) })] }, key.id))) }))] })] }) })] })] })] })] }));
285
+ }, children: "\u6211\u5DF2\u4FDD\u5B58\uFF0C\u9690\u85CF\u5BC6\u94A5" })] })] })) : null, credentialError ? _jsx("p", { className: "text-sm text-destructive", children: credentialError }) : null, credentialsLoading ? (_jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), "\u6B63\u5728\u8BFB\u53D6 Client Credentials"] })) : credentials.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "\u5C1A\u672A\u521B\u5EFA Client Credential\u3002" })) : (_jsx("div", { className: "space-y-2", children: credentials.map((credential) => (_jsxs("div", { className: "flex items-center justify-between gap-3 rounded-lg border p-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate text-sm font-medium", children: credential.id }), _jsx("div", { className: "mt-1 text-xs text-muted-foreground", children: credential.webId ?? client.webId })] }), _jsx(Button, { variant: "ghost", size: "icon", "aria-label": `撤销 ${credential.id}`, disabled: !serviceAccessGranted || !clientCredentialManager, onClick: () => void revokeCredential(credential), children: _jsx(Trash2, { className: "h-4 w-4" }) })] }, credential.resourceUrl))) }))] })] }) })] })] })] })] }));
272
286
  }
273
287
  async function pollDeviceConnect(client, provider, initial, generation, generationRef, callbacks) {
274
288
  let attempt = initial;
@@ -49,16 +49,6 @@ export interface AiQuotaSnapshot {
49
49
  source: string;
50
50
  stale?: boolean;
51
51
  }
52
- export interface GatewayKeyRecord {
53
- id: string;
54
- owner: string;
55
- scopes: string[];
56
- createdAt: string;
57
- expiresAt?: string;
58
- lastUsedAt?: string;
59
- revokedAt?: string;
60
- name?: string;
61
- }
62
52
  export interface AiGatewayModel {
63
53
  id: string;
64
54
  provider: AiConnectionProvider;
@@ -66,10 +56,6 @@ export interface AiGatewayModel {
66
56
  contextWindow?: number;
67
57
  protocols?: string[];
68
58
  }
69
- export interface CreatedGatewayKey {
70
- plaintext: string;
71
- record: GatewayKeyRecord;
72
- }
73
59
  export interface AiProviderConnectionSummary {
74
60
  provider: AiConnectionProvider;
75
61
  status: 'connected' | 'disconnected' | 'reauthRequired';
@@ -91,13 +77,6 @@ export interface AiConnectionClient {
91
77
  getServiceAccess(): Promise<unknown>;
92
78
  listProviders(): Promise<AiProviderConnectionSummary[]>;
93
79
  listModels(): Promise<AiGatewayModel[]>;
94
- listGatewayKeys(): Promise<GatewayKeyRecord[]>;
95
- createGatewayKey(input: {
96
- name?: string;
97
- scopes?: string[];
98
- expiresAt?: string;
99
- }): Promise<CreatedGatewayKey>;
100
- revokeGatewayKey(keyId: string): Promise<GatewayKeyRecord | undefined>;
101
80
  beginConnect(provider: AiConnectionProvider, mode: AiConnectionMode): Promise<AiConnectAttempt>;
102
81
  connectStatus(provider: AiConnectionProvider, attempt: Pick<AiConnectAttempt, 'attemptId' | 'state' | 'signature'>): Promise<AiConnectAttempt>;
103
82
  completeApiKey(provider: AiConnectionProvider, attempt: Pick<AiConnectAttempt, 'attemptId' | 'state' | 'signature'>, apiKey: string, accountLabel?: string): Promise<AiConnectAttempt>;
@@ -58,27 +58,6 @@ export function createAiConnectionClient({ webId, podBaseUrl, authenticatedFetch
58
58
  ? payload.data.map(parseGatewayModel).filter(isDefined)
59
59
  : [];
60
60
  },
61
- async listGatewayKeys() {
62
- const payload = await request('/api/ai/gateway/keys', 'GET');
63
- return Array.isArray(payload.data)
64
- ? payload.data.map(parseGatewayKeyRecord).filter(isDefined)
65
- : [];
66
- },
67
- async createGatewayKey(input) {
68
- const payload = await request('/api/ai/gateway/keys', 'POST', compactObject(input));
69
- if (typeof payload.key !== 'string' || !payload.key) {
70
- throw new Error('AI Connection did not return the one-time Gateway key');
71
- }
72
- const record = parseGatewayKeyRecord(payload.record);
73
- if (!record) {
74
- throw new Error('AI Connection returned an invalid Gateway key record');
75
- }
76
- return { plaintext: payload.key, record };
77
- },
78
- async revokeGatewayKey(keyId) {
79
- const payload = await request(`/api/ai/gateway/keys/${encodeURIComponent(keyId)}`, 'DELETE');
80
- return parseGatewayKeyRecord(payload.record);
81
- },
82
61
  async beginConnect(provider, mode) {
83
62
  return await requestConnect(provider, '/connect/begin', 'POST', { mode });
84
63
  },
@@ -229,26 +208,6 @@ function assertProvider(provider) {
229
208
  throw new Error(`Unsupported AI provider: ${provider}`);
230
209
  }
231
210
  }
232
- function parseGatewayKeyRecord(value) {
233
- if (!isRecord(value)
234
- || typeof value.id !== 'string'
235
- || typeof value.owner !== 'string'
236
- || !Array.isArray(value.scopes)
237
- || !value.scopes.every((scope) => typeof scope === 'string')
238
- || typeof value.createdAt !== 'string') {
239
- return undefined;
240
- }
241
- return compactObject({
242
- id: value.id,
243
- owner: value.owner,
244
- scopes: value.scopes,
245
- createdAt: value.createdAt,
246
- expiresAt: stringValue(value.expiresAt),
247
- lastUsedAt: stringValue(value.lastUsedAt),
248
- revokedAt: stringValue(value.revokedAt),
249
- name: stringValue(value.name),
250
- });
251
- }
252
211
  function parseGatewayModel(value) {
253
212
  if (!isRecord(value) || typeof value.id !== 'string')
254
213
  return undefined;
@@ -6,6 +6,7 @@ export declare function normalizeMessagesEndpoint(endpoint: string): string;
6
6
  export declare function parseJsonObject(content: string | undefined, label: string): Record<string, unknown>;
7
7
  export declare function stringifyJson(value: unknown): string;
8
8
  export declare function looksLikePreviousXpodValue(value: unknown): boolean;
9
+ export declare function profileApiKey(profile: AiConnectionClientProfile): string;
9
10
  export declare function stripLegacyXpodObject(value: Record<string, unknown>): void;
10
11
  export declare abstract class BaseAiClientConfigAdapter implements AiClientConfigAdapter {
11
12
  protected readonly client: string;
@@ -35,6 +35,11 @@ export function looksLikePreviousXpodValue(value) {
35
35
  value.includes('/api/ai') ||
36
36
  value.includes('xpod.'));
37
37
  }
38
+ export function profileApiKey(profile) {
39
+ const legacyField = ['gateway', 'Key'].join('');
40
+ const value = profile.apiKey ?? profile[legacyField];
41
+ return typeof value === 'string' ? value : '';
42
+ }
38
43
  export function stripLegacyXpodObject(value) {
39
44
  const legacy = value.xpod;
40
45
  if (legacy && typeof legacy === 'object' && !Array.isArray(legacy)) {
@@ -158,8 +163,8 @@ export class BaseAiClientConfigAdapter {
158
163
  validateProfile(profile) {
159
164
  if (!profile.endpoint.trim())
160
165
  throw new Error('AI Connection endpoint is required');
161
- if (!profile.gatewayKey.trim())
162
- throw new Error('AI Connection Gateway key is required');
166
+ if (!profileApiKey(profile).trim())
167
+ throw new Error('AI Connection API key is required');
163
168
  if (!profile.webId.trim())
164
169
  throw new Error('Current WebID is required');
165
170
  }
@@ -1,7 +1,7 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as os from 'node:os';
3
3
  import * as path from 'node:path';
4
- import { BaseAiClientConfigAdapter, looksLikePreviousXpodValue, normalizeV1Endpoint, parseJsonObject, stringifyJson, } from './base-adapter.js';
4
+ import { BaseAiClientConfigAdapter, looksLikePreviousXpodValue, normalizeV1Endpoint, parseJsonObject, profileApiKey, stringifyJson, } from './base-adapter.js';
5
5
  const START = '# >>> xpod-ai-connection managed';
6
6
  const END = '# <<< xpod-ai-connection managed';
7
7
  export class CodexConfigAdapter extends BaseAiClientConfigAdapter {
@@ -34,7 +34,7 @@ export class CodexConfigAdapter extends BaseAiClientConfigAdapter {
34
34
  '',
35
35
  ].join('\n');
36
36
  const auth = parseJsonObject(current.get(this.authPath), 'Codex auth.json');
37
- auth.OPENAI_API_KEY = profile.gatewayKey;
37
+ auth.OPENAI_API_KEY = profileApiKey(profile);
38
38
  return new Map([
39
39
  [this.configPath, `${config}${config ? '\n\n' : ''}${block}`],
40
40
  [this.authPath, stringifyJson(auth)],
@@ -46,7 +46,7 @@ export class CodexConfigAdapter extends BaseAiClientConfigAdapter {
46
46
  const auth = parseJsonObject(await fs.promises.readFile(this.authPath, 'utf8'), 'Codex auth.json');
47
47
  const ok = config.includes('model_provider = "xpod"') &&
48
48
  config.includes(`base_url = ${JSON.stringify(normalizeV1Endpoint(profile.endpoint))}`) &&
49
- auth.OPENAI_API_KEY === profile.gatewayKey;
49
+ auth.OPENAI_API_KEY === profileApiKey(profile);
50
50
  return ok ? { ok: true } : { ok: false, reason: 'Codex projection differs from the requested connection' };
51
51
  }
52
52
  catch (error) {
@@ -44,6 +44,7 @@ var exports_client_config = {};
44
44
  __export(exports_client_config, {
45
45
  stripLegacyXpodObject: () => stripLegacyXpodObject,
46
46
  stringifyJson: () => stringifyJson,
47
+ profileApiKey: () => profileApiKey,
47
48
  parseJsonObject: () => parseJsonObject,
48
49
  normalizeV1Endpoint: () => normalizeV1Endpoint,
49
50
  normalizeMessagesEndpoint: () => normalizeMessagesEndpoint,
@@ -242,6 +243,11 @@ function stringifyJson(value) {
242
243
  function looksLikePreviousXpodValue(value) {
243
244
  return typeof value === "string" && (value.includes("xpod") || value.includes("/api/ai") || value.includes("xpod."));
244
245
  }
246
+ function profileApiKey(profile) {
247
+ const legacyField = ["gateway", "Key"].join("");
248
+ const value = profile.apiKey ?? profile[legacyField];
249
+ return typeof value === "string" ? value : "";
250
+ }
245
251
  function stripLegacyXpodObject(value) {
246
252
  const legacy = value.xpod;
247
253
  if (legacy && typeof legacy === "object" && !Array.isArray(legacy)) {
@@ -370,8 +376,8 @@ class BaseAiClientConfigAdapter {
370
376
  validateProfile(profile) {
371
377
  if (!profile.endpoint.trim())
372
378
  throw new Error("AI Connection endpoint is required");
373
- if (!profile.gatewayKey.trim())
374
- throw new Error("AI Connection Gateway key is required");
379
+ if (!profileApiKey(profile).trim())
380
+ throw new Error("AI Connection API key is required");
375
381
  if (!profile.webId.trim())
376
382
  throw new Error("Current WebID is required");
377
383
  }
@@ -454,7 +460,7 @@ class CodexConfigAdapter extends BaseAiClientConfigAdapter {
454
460
  ].join(`
455
461
  `);
456
462
  const auth = parseJsonObject(current.get(this.authPath), "Codex auth.json");
457
- auth.OPENAI_API_KEY = profile.gatewayKey;
463
+ auth.OPENAI_API_KEY = profileApiKey(profile);
458
464
  return new Map([
459
465
  [this.configPath, `${config}${config ? `
460
466
 
@@ -466,7 +472,7 @@ class CodexConfigAdapter extends BaseAiClientConfigAdapter {
466
472
  try {
467
473
  const config = await fs3.promises.readFile(this.configPath, "utf8");
468
474
  const auth = parseJsonObject(await fs3.promises.readFile(this.authPath, "utf8"), "Codex auth.json");
469
- const ok = config.includes('model_provider = "xpod"') && config.includes(`base_url = ${JSON.stringify(normalizeV1Endpoint(profile.endpoint))}`) && auth.OPENAI_API_KEY === profile.gatewayKey;
475
+ const ok = config.includes('model_provider = "xpod"') && config.includes(`base_url = ${JSON.stringify(normalizeV1Endpoint(profile.endpoint))}`) && auth.OPENAI_API_KEY === profileApiKey(profile);
470
476
  return ok ? { ok: true } : { ok: false, reason: "Codex projection differs from the requested connection" };
471
477
  } catch (error) {
472
478
  return { ok: false, reason: String(error) };
@@ -549,7 +555,7 @@ class JsonEnvAdapter extends BaseAiClientConfigAdapter {
549
555
  const beforeEnv = isObject(before.env) ? before.env : {};
550
556
  for (const key of Object.keys(this.envProjection({
551
557
  endpoint: "https://owned.invalid",
552
- gatewayKey: "owned",
558
+ apiKey: "owned",
553
559
  webId: "https://owned.invalid/profile#me"
554
560
  }))) {
555
561
  if (Object.prototype.hasOwnProperty.call(beforeEnv, key) && !looksLikePreviousXpodValue(beforeEnv[key])) {
@@ -575,7 +581,7 @@ class ClaudeCodeConfigAdapter extends JsonEnvAdapter {
575
581
  const settingsPath = path4.join(options.homeDir ?? os2.homedir(), ".claude", "settings.json");
576
582
  super("claude-code", settingsPath, (profile) => ({
577
583
  ANTHROPIC_BASE_URL: normalizeMessagesEndpoint(profile.endpoint),
578
- ANTHROPIC_AUTH_TOKEN: profile.gatewayKey
584
+ ANTHROPIC_AUTH_TOKEN: profileApiKey(profile)
579
585
  }));
580
586
  }
581
587
  }
@@ -585,7 +591,7 @@ class CodeBuddyConfigAdapter extends JsonEnvAdapter {
585
591
  const settingsPath = path4.join(options.homeDir ?? os2.homedir(), ".codebuddy", "settings.json");
586
592
  super("codebuddy", settingsPath, (profile) => ({
587
593
  CODEBUDDY_BASE_URL: normalizeV1Endpoint(profile.endpoint),
588
- CODEBUDDY_API_KEY: profile.gatewayKey
594
+ CODEBUDDY_API_KEY: profileApiKey(profile)
589
595
  }));
590
596
  }
591
597
  }
@@ -613,7 +619,7 @@ class PiConfigAdapter extends BaseAiClientConfigAdapter {
613
619
  settings.defaultModel = model;
614
620
  providers.xpod = {
615
621
  baseUrl: normalizeV1Endpoint(profile.endpoint),
616
- apiKey: profile.gatewayKey,
622
+ apiKey: profileApiKey(profile),
617
623
  authHeader: true,
618
624
  api: "openai-responses",
619
625
  models: [{ id: model, name: model }]
@@ -628,7 +634,7 @@ class PiConfigAdapter extends BaseAiClientConfigAdapter {
628
634
  try {
629
635
  const models = parseJsonObject(await fs5.promises.readFile(this.modelsPath, "utf8"), "Pi models.json");
630
636
  const xpod = models.providers?.xpod;
631
- const ok = xpod?.baseUrl === normalizeV1Endpoint(profile.endpoint) && xpod.apiKey === profile.gatewayKey;
637
+ const ok = xpod?.baseUrl === normalizeV1Endpoint(profile.endpoint) && xpod.apiKey === profileApiKey(profile);
632
638
  return ok ? { ok: true } : { ok: false, reason: "Pi projection differs from the requested connection" };
633
639
  } catch (error) {
634
640
  return { ok: false, reason: String(error) };
@@ -1,7 +1,7 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as os from 'node:os';
3
3
  import * as path from 'node:path';
4
- import { BaseAiClientConfigAdapter, looksLikePreviousXpodValue, normalizeMessagesEndpoint, normalizeV1Endpoint, parseJsonObject, stringifyJson, stripLegacyXpodObject, } from './base-adapter.js';
4
+ import { BaseAiClientConfigAdapter, looksLikePreviousXpodValue, normalizeMessagesEndpoint, normalizeV1Endpoint, parseJsonObject, profileApiKey, stringifyJson, stripLegacyXpodObject, } from './base-adapter.js';
5
5
  class JsonEnvAdapter extends BaseAiClientConfigAdapter {
6
6
  constructor(client, settingsPath, envProjection) {
7
7
  super(client, client === 'claude-code' ? 'claude' : 'codebuddy', [settingsPath], path.dirname(settingsPath));
@@ -40,7 +40,7 @@ class JsonEnvAdapter extends BaseAiClientConfigAdapter {
40
40
  const beforeEnv = isObject(before.env) ? before.env : {};
41
41
  for (const key of Object.keys(this.envProjection({
42
42
  endpoint: 'https://owned.invalid',
43
- gatewayKey: 'owned',
43
+ apiKey: 'owned',
44
44
  webId: 'https://owned.invalid/profile#me',
45
45
  }))) {
46
46
  if (Object.prototype.hasOwnProperty.call(beforeEnv, key) && !looksLikePreviousXpodValue(beforeEnv[key])) {
@@ -67,7 +67,7 @@ export class ClaudeCodeConfigAdapter extends JsonEnvAdapter {
67
67
  const settingsPath = path.join(options.homeDir ?? os.homedir(), '.claude', 'settings.json');
68
68
  super('claude-code', settingsPath, (profile) => ({
69
69
  ANTHROPIC_BASE_URL: normalizeMessagesEndpoint(profile.endpoint),
70
- ANTHROPIC_AUTH_TOKEN: profile.gatewayKey,
70
+ ANTHROPIC_AUTH_TOKEN: profileApiKey(profile),
71
71
  }));
72
72
  }
73
73
  }
@@ -76,7 +76,7 @@ export class CodeBuddyConfigAdapter extends JsonEnvAdapter {
76
76
  const settingsPath = path.join(options.homeDir ?? os.homedir(), '.codebuddy', 'settings.json');
77
77
  super('codebuddy', settingsPath, (profile) => ({
78
78
  CODEBUDDY_BASE_URL: normalizeV1Endpoint(profile.endpoint),
79
- CODEBUDDY_API_KEY: profile.gatewayKey,
79
+ CODEBUDDY_API_KEY: profileApiKey(profile),
80
80
  }));
81
81
  }
82
82
  }
@@ -1,7 +1,7 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as os from 'node:os';
3
3
  import * as path from 'node:path';
4
- import { BaseAiClientConfigAdapter, looksLikePreviousXpodValue, normalizeV1Endpoint, parseJsonObject, stringifyJson, stripLegacyXpodObject, } from './base-adapter.js';
4
+ import { BaseAiClientConfigAdapter, looksLikePreviousXpodValue, normalizeV1Endpoint, parseJsonObject, profileApiKey, stringifyJson, stripLegacyXpodObject, } from './base-adapter.js';
5
5
  export class PiConfigAdapter extends BaseAiClientConfigAdapter {
6
6
  constructor(options = {}) {
7
7
  const dir = path.join(options.homeDir ?? os.homedir(), '.pi', 'agent');
@@ -22,7 +22,7 @@ export class PiConfigAdapter extends BaseAiClientConfigAdapter {
22
22
  settings.defaultModel = model;
23
23
  providers.xpod = {
24
24
  baseUrl: normalizeV1Endpoint(profile.endpoint),
25
- apiKey: profile.gatewayKey,
25
+ apiKey: profileApiKey(profile),
26
26
  authHeader: true,
27
27
  api: 'openai-responses',
28
28
  models: [{ id: model, name: model }],
@@ -38,7 +38,7 @@ export class PiConfigAdapter extends BaseAiClientConfigAdapter {
38
38
  const models = parseJsonObject(await fs.promises.readFile(this.modelsPath, 'utf8'), 'Pi models.json');
39
39
  const xpod = models.providers?.xpod;
40
40
  const ok = xpod?.baseUrl === normalizeV1Endpoint(profile.endpoint) &&
41
- xpod.apiKey === profile.gatewayKey;
41
+ xpod.apiKey === profileApiKey(profile);
42
42
  return ok ? { ok: true } : { ok: false, reason: 'Pi projection differs from the requested connection' };
43
43
  }
44
44
  catch (error) {
@@ -1,6 +1,6 @@
1
1
  export interface AiConnectionClientProfile {
2
2
  endpoint: string;
3
- gatewayKey: string;
3
+ apiKey?: string;
4
4
  webId: string;
5
5
  model?: string;
6
6
  }
@@ -1,4 +1,4 @@
1
- import type { WebExtensionHost, WebExtensionSessionStatus, WebExtensionSolidPodStatus } from '@undefineds.co/extension-sdk/web';
1
+ import type { AiClientCredentialManager, WebExtensionHost, WebExtensionSessionStatus, WebExtensionSolidPodStatus } from '@undefineds.co/extension-sdk/web';
2
2
  import { type AiConnectionClient, type AiConnectionProvider, type AiProviderConnectionSummary } from './ai-connection-client';
3
3
  import type { AiClientConfigurationBridge } from './AiClientConfigurationSection';
4
4
  export interface AiProviderDefinition {
@@ -18,6 +18,7 @@ export interface AiConnectionController {
18
18
  readonly login: () => Promise<void>;
19
19
  readonly openExternal: (url: string) => Promise<void>;
20
20
  readonly clientConfigurationBridge?: AiClientConfigurationBridge;
21
+ readonly clientCredentialManager?: AiClientCredentialManager;
21
22
  readonly selectedProvider: AiConnectionProvider;
22
23
  readonly searchQuery: string;
23
24
  readonly providerStates: Partial<Record<AiConnectionProvider, ProviderProductState>>;
@@ -47,6 +47,7 @@ export function createAiConnectionController(host) {
47
47
  login: host.solid.requireLogin,
48
48
  openExternal: host.navigation.openExternal,
49
49
  clientConfigurationBridge: host.capabilities.aiClientConfiguration,
50
+ clientCredentialManager: host.capabilities.aiClientCredentials,
50
51
  get selectedProvider() {
51
52
  return selectedProvider;
52
53
  },
@@ -2,7 +2,6 @@ export const AI_CONNECTION_APPLET_ID = 'co.undefineds.ai-connection';
2
2
  const KNOWN_RESOURCE_IDS = new Set([
3
3
  'providerCredentials',
4
4
  'providerDefinitions',
5
- 'gatewayAccessKeys',
6
5
  'quotaSnapshots',
7
6
  ]);
8
7
  export function parseAiConnectionServiceAccess(value, currentPodUrl) {
@@ -38,7 +38,7 @@ export interface AiClientConfigurationCapability {
38
38
  apply(input: {
39
39
  client: AiClientId;
40
40
  planId: string;
41
- gatewayKey: string;
41
+ apiKey: string;
42
42
  confirmation?: {
43
43
  token: string;
44
44
  targetHash: string;
@@ -52,6 +52,26 @@ export interface AiClientConfigurationCapability {
52
52
  }): Promise<AiClientConfigurationStatus>;
53
53
  restore(client: AiClientId): Promise<AiClientConfigurationStatus>;
54
54
  }
55
+ export interface AiClientCredentialRecord {
56
+ id: string;
57
+ resourceUrl: string;
58
+ webId?: string;
59
+ }
60
+ export interface CreatedAiClientCredential extends AiClientCredentialRecord {
61
+ clientId: string;
62
+ clientSecret: string;
63
+ apiKey: string;
64
+ }
65
+ export interface AiClientCredentialManager {
66
+ readonly available?: boolean;
67
+ readonly accountUrl?: string;
68
+ list(): Promise<AiClientCredentialRecord[]>;
69
+ create(input: {
70
+ name: string;
71
+ webId: string;
72
+ }): Promise<CreatedAiClientCredential>;
73
+ revoke(resourceUrl: string): Promise<void>;
74
+ }
55
75
  export type WebExtensionSolidPodStatus = 'unavailable' | 'opening' | 'ready' | 'error';
56
76
  export type WebExtensionSolidSession = Readonly<Pick<SolidSessionRuntime, 'fetch' | 'getSnapshot' | 'subscribe'>>;
57
77
  export type WebExtensionSolidPod<Database = unknown> = {
@@ -105,6 +125,7 @@ export interface WebExtensionNavigationCapability {
105
125
  }
106
126
  export interface WebExtensionHostCapabilities {
107
127
  aiClientConfiguration?: AiClientConfigurationCapability;
128
+ aiClientCredentials?: AiClientCredentialManager;
108
129
  }
109
130
  export interface WebExtensionHost<Database = unknown> {
110
131
  readonly solid: WebExtensionSolidCapability<Database>;