@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.
- package/config/cloud.json +3 -0
- package/config/local.json +3 -0
- package/config/resolver.json +7 -0
- package/config/xpod.base.json +20 -0
- package/dist/agents/ClaudeExecutor.js +1 -1
- package/dist/agents/ClaudeExecutor.js.map +1 -1
- package/dist/agents/types.d.ts +5 -5
- package/dist/agents/types.js.map +1 -1
- package/dist/api/ai-gateway/AiGatewayService.d.ts +5 -16
- package/dist/api/ai-gateway/AiGatewayService.js +13 -35
- package/dist/api/ai-gateway/AiGatewayService.js.map +1 -1
- package/dist/api/ai-gateway/auth/AiConnectionInvocationKeyIssuer.d.ts +3 -2
- package/dist/api/ai-gateway/auth/AiConnectionInvocationKeyIssuer.js +37 -12
- package/dist/api/ai-gateway/auth/AiConnectionInvocationKeyIssuer.js.map +1 -1
- package/dist/api/ai-gateway/auth/GatewayPrincipal.d.ts +2 -16
- package/dist/api/ai-gateway/auth/GatewayPrincipal.js +3 -33
- package/dist/api/ai-gateway/auth/GatewayPrincipal.js.map +1 -1
- package/dist/api/ai-gateway/auth/InvocationTokenAuthenticator.d.ts +23 -0
- package/dist/api/ai-gateway/auth/InvocationTokenAuthenticator.js +81 -0
- package/dist/api/ai-gateway/auth/InvocationTokenAuthenticator.js.map +1 -0
- package/dist/api/ai-gateway/auth/InvocationTokenCodec.d.ts +7 -4
- package/dist/api/ai-gateway/auth/InvocationTokenCodec.js.map +1 -1
- package/dist/api/ai-gateway/connect/index.d.ts +7 -26
- package/dist/api/ai-gateway/connect/index.js +40 -67
- package/dist/api/ai-gateway/connect/index.js.map +1 -1
- package/dist/api/ai-gateway/credentials/PlaintextCredentialPayload.d.ts +17 -0
- package/dist/api/ai-gateway/credentials/PlaintextCredentialPayload.js +65 -0
- package/dist/api/ai-gateway/credentials/PlaintextCredentialPayload.js.map +1 -0
- package/dist/api/ai-gateway/pod/HostedPodDataAccess.d.ts +24 -0
- package/dist/api/ai-gateway/pod/HostedPodDataAccess.js +149 -0
- package/dist/api/ai-gateway/pod/HostedPodDataAccess.js.map +1 -0
- package/dist/api/ai-gateway/quota/ProviderQuotaAdapter.d.ts +13 -5
- package/dist/api/ai-gateway/quota/ProviderQuotaAdapter.js +16 -19
- package/dist/api/ai-gateway/quota/ProviderQuotaAdapter.js.map +1 -1
- package/dist/api/ai-gateway/service-access/AiConnectionServiceAccess.d.ts +1 -1
- package/dist/api/ai-gateway/service-access/AiConnectionServiceAccess.js +0 -2
- package/dist/api/ai-gateway/service-access/AiConnectionServiceAccess.js.map +1 -1
- package/dist/api/auth/AuthContext.d.ts +1 -12
- package/dist/api/auth/AuthContext.js +0 -11
- package/dist/api/auth/AuthContext.js.map +1 -1
- package/dist/api/auth/ClientCredentialsAuthenticator.js +4 -11
- package/dist/api/auth/ClientCredentialsAuthenticator.js.map +1 -1
- package/dist/api/chatkit/default-agent.d.ts +2 -2
- package/dist/api/chatkit/default-agent.js +4 -4
- package/dist/api/chatkit/default-agent.js.map +1 -1
- package/dist/api/chatkit/runtime/AcpAgentRuntime.js +1 -1
- package/dist/api/chatkit/runtime/AcpAgentRuntime.js.map +1 -1
- package/dist/api/container/common.js +28 -69
- package/dist/api/container/common.js.map +1 -1
- package/dist/api/container/index.js +5 -6
- package/dist/api/container/index.js.map +1 -1
- package/dist/api/container/routes.js +2 -5
- package/dist/api/container/routes.js.map +1 -1
- package/dist/api/container/types.d.ts +8 -10
- package/dist/api/container/types.js.map +1 -1
- package/dist/api/handlers/AiClientConfigurationHandler.js +3 -3
- package/dist/api/handlers/AiClientConfigurationHandler.js.map +1 -1
- package/dist/api/handlers/AiGatewayHandler.js +2 -2
- package/dist/api/handlers/AiGatewayHandler.js.map +1 -1
- package/dist/api/handlers/AiGatewayManagementHandler.d.ts +2 -10
- package/dist/api/handlers/AiGatewayManagementHandler.js +4 -154
- package/dist/api/handlers/AiGatewayManagementHandler.js.map +1 -1
- package/dist/api/handlers/NetworkSettingsHandler.js +0 -3
- package/dist/api/handlers/NetworkSettingsHandler.js.map +1 -1
- package/dist/api/handlers/PodSettingsHandler.d.ts +5 -2
- package/dist/api/handlers/PodSettingsHandler.js +3 -3
- package/dist/api/handlers/PodSettingsHandler.js.map +1 -1
- package/dist/api/runs/AgentRuntimeTypes.d.ts +3 -3
- package/dist/api/runs/AgentRuntimeTypes.js +10 -10
- package/dist/api/runs/AgentRuntimeTypes.js.map +1 -1
- package/dist/api/runs/InngestRunExecutionBackend.d.ts +15 -9
- package/dist/api/runs/InngestRunExecutionBackend.js +25 -10
- package/dist/api/runs/InngestRunExecutionBackend.js.map +1 -1
- package/dist/api/runs/PiAgentRuntimeDriver.js +2 -2
- package/dist/api/runs/PiAgentRuntimeDriver.js.map +1 -1
- package/dist/api/runs/RunStateCenter.js +1 -1
- package/dist/api/runs/RunStateCenter.js.map +1 -1
- package/dist/api/service/AiClientConfigurationService.d.ts +2 -2
- package/dist/api/service/AiClientConfigurationService.js +11 -11
- package/dist/api/service/AiClientConfigurationService.js.map +1 -1
- package/dist/api/tasks/InngestTaskScheduler.d.ts +16 -10
- package/dist/api/tasks/InngestTaskScheduler.js +24 -12
- package/dist/api/tasks/InngestTaskScheduler.js.map +1 -1
- package/dist/cli/commands/start.js +7 -7
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/components/components.jsonld +1 -0
- package/dist/components/context.jsonld +36 -0
- package/dist/http/InternalPodDataHttpHandler.d.ts +32 -0
- package/dist/http/InternalPodDataHttpHandler.js +258 -0
- package/dist/http/InternalPodDataHttpHandler.js.map +1 -0
- package/dist/http/InternalPodDataHttpHandler.jsonld +201 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/main.js +13 -13
- package/dist/main.js.map +1 -1
- package/dist/runtime/GatewayAdminProxyAuth.d.ts +15 -1
- package/dist/runtime/GatewayAdminProxyAuth.js +57 -2
- package/dist/runtime/GatewayAdminProxyAuth.js.map +1 -1
- package/dist/runtime/bootstrap.js +3 -0
- package/dist/runtime/bootstrap.js.map +1 -1
- package/dist/runtime/css-process.d.ts +1 -1
- package/dist/runtime/css-process.js +4 -1
- package/dist/runtime/css-process.js.map +1 -1
- package/dist/runtime/runner/node/CommunitySolidServerCssRunner.d.ts +2 -0
- package/dist/runtime/runner/node/CommunitySolidServerCssRunner.js +50 -0
- package/dist/runtime/runner/node/CommunitySolidServerCssRunner.js.map +1 -1
- package/node_modules/@undefineds.co/ai-connection/dist/AiClientConfigurationSection.d.ts +5 -5
- package/node_modules/@undefineds.co/ai-connection/dist/AiClientConfigurationSection.js +7 -7
- package/node_modules/@undefineds.co/ai-connection/dist/AiConnectionMain.js +1 -1
- package/node_modules/@undefineds.co/ai-connection/dist/AiConnectionPanel.d.ts +3 -1
- package/node_modules/@undefineds.co/ai-connection/dist/AiConnectionPanel.js +74 -60
- package/node_modules/@undefineds.co/ai-connection/dist/ai-connection-client.d.ts +0 -21
- package/node_modules/@undefineds.co/ai-connection/dist/ai-connection-client.js +0 -41
- package/node_modules/@undefineds.co/ai-connection/dist/client-config/base-adapter.d.ts +1 -0
- package/node_modules/@undefineds.co/ai-connection/dist/client-config/base-adapter.js +7 -2
- package/node_modules/@undefineds.co/ai-connection/dist/client-config/codex.js +3 -3
- package/node_modules/@undefineds.co/ai-connection/dist/client-config/index.cjs +15 -9
- package/node_modules/@undefineds.co/ai-connection/dist/client-config/json-env-adapter.js +4 -4
- package/node_modules/@undefineds.co/ai-connection/dist/client-config/pi.js +3 -3
- package/node_modules/@undefineds.co/ai-connection/dist/client-config/types.d.ts +1 -1
- package/node_modules/@undefineds.co/ai-connection/dist/controller.d.ts +2 -1
- package/node_modules/@undefineds.co/ai-connection/dist/controller.js +1 -0
- package/node_modules/@undefineds.co/ai-connection/dist/service-access.js +0 -1
- package/node_modules/@undefineds.co/extension-sdk/dist/web.d.ts +22 -1
- package/package.json +3 -3
- package/static/app/assets/main.js +7 -7
- package/static/dashboard/assets/NetworkPage-CXjiUMR1.js +2 -0
- package/static/dashboard/assets/{PaneListHeader-57nvMwH6.js → PaneListHeader-BSipfl0W.js} +1 -1
- package/static/dashboard/assets/PodPage-Bd3Xhmp-.js +2 -0
- package/static/dashboard/assets/ServicesPage-CI3jWZGe.js +1 -0
- package/static/dashboard/assets/{card-B9Zjf_2p.js → card-z5BV5hTo.js} +1 -1
- package/static/dashboard/assets/{dashboard-8C6o666W.js → dashboard-CSrqAv2L.js} +2 -2
- package/static/dashboard/assets/{database-DPh9wDY9.js → database-Ct5ZPe6L.js} +1 -1
- package/static/dashboard/assets/{external-link-VbMQvVMz.js → external-link-B7wD1hkm.js} +1 -1
- package/static/dashboard/assets/index-tOBaUnM1.js +44 -0
- package/static/dashboard/assets/{refresh-cw-BxVxNxNr.js → refresh-cw-CFDZPaE8.js} +1 -1
- package/static/dashboard/assets/{services-status-context-DKmJ7ISr.js → services-status-context-DN1Icg4A.js} +1 -1
- package/static/dashboard/dashboard.html +1 -1
- package/static/settings/assets/ModelsPage-03NPWzoX.js +1 -0
- package/static/settings/assets/{PaneListHeader-Bgaze006.js → PaneListHeader-DMD8OJef.js} +1 -1
- package/static/settings/assets/PodPage-DT4EMYhL.js +2 -0
- package/static/settings/assets/ServicesPage-CJ0ub6IH.js +1 -0
- package/static/settings/assets/SettingsPage-BkR7ZK18.js +41 -0
- package/static/settings/assets/{card-B_bXSIn-.js → card-Cjsia0_V.js} +1 -1
- package/static/settings/assets/{services-status-context-DiX-AbrP.js → services-status-context-CkuKmH5h.js} +1 -1
- package/static/settings/assets/{settings-B30oMfZq.js → settings-Cyyv6-SD.js} +2 -2
- package/static/settings/assets/{workspace-layout-Dv1Yn9Ci.js → workspace-layout-B47luv6r.js} +1 -1
- package/static/settings/settings.html +1 -1
- package/dist/api/ai-gateway/auth/ClientCredentialsInternalPodAccessTokenProvider.d.ts +0 -25
- package/dist/api/ai-gateway/auth/ClientCredentialsInternalPodAccessTokenProvider.js +0 -91
- package/dist/api/ai-gateway/auth/ClientCredentialsInternalPodAccessTokenProvider.js.map +0 -1
- package/dist/api/ai-gateway/auth/GatewayApiKey.d.ts +0 -28
- package/dist/api/ai-gateway/auth/GatewayApiKey.js +0 -162
- package/dist/api/ai-gateway/auth/GatewayApiKey.js.map +0 -1
- package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.d.ts +0 -56
- package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.js +0 -143
- package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.js.map +0 -1
- package/dist/api/ai-gateway/auth/GatewayKeyLocatorCodec.d.ts +0 -26
- package/dist/api/ai-gateway/auth/GatewayKeyLocatorCodec.js +0 -97
- package/dist/api/ai-gateway/auth/GatewayKeyLocatorCodec.js.map +0 -1
- package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.d.ts +0 -54
- package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.js +0 -152
- package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.js.map +0 -1
- package/static/dashboard/assets/NetworkPage-Brt79fuQ.js +0 -2
- package/static/dashboard/assets/PodPage-CM6do1vB.js +0 -2
- package/static/dashboard/assets/ServicesPage-BUNxGJRz.js +0 -1
- package/static/dashboard/assets/index-RfiFckzb.js +0 -51
- package/static/settings/assets/ModelsPage-D_IH7AH3.js +0 -1
- package/static/settings/assets/PodPage-Cy1VNObw.js +0 -2
- package/static/settings/assets/ServicesPage-BsFaxVWC.js +0 -1
- package/static/settings/assets/SettingsPage-UafNWE_p.js +0 -48
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { type AiConnectionClient, type AiConnectionProvider, type AiProviderConnectionSummary } from './ai-connection-client';
|
|
2
|
+
import type { AiClientCredentialManager } from '@undefineds.co/extension-sdk/web';
|
|
2
3
|
import { type ProviderProductState } from './controller';
|
|
3
4
|
import { type AiClientConfigurationBridge } from './AiClientConfigurationSection';
|
|
4
5
|
export interface AiConnectionPanelProps {
|
|
5
6
|
client: AiConnectionClient;
|
|
6
7
|
openExternal?: (url: string) => void | Promise<void>;
|
|
7
8
|
clientConfigurationBridge?: AiClientConfigurationBridge;
|
|
9
|
+
clientCredentialManager?: AiClientCredentialManager;
|
|
8
10
|
selectedProvider?: AiConnectionProvider;
|
|
9
11
|
providerSummaries?: Partial<Record<AiConnectionProvider, AiProviderConnectionSummary>>;
|
|
10
12
|
providerLoadError?: string;
|
|
11
13
|
serviceAccessGranted?: boolean;
|
|
12
14
|
onProviderStateChange?: (provider: AiConnectionProvider, state: ProviderProductState) => void;
|
|
13
15
|
}
|
|
14
|
-
export declare function AiConnectionPanel({ client, openExternal, clientConfigurationBridge, selectedProvider, providerSummaries: providerSummariesInput, providerLoadError, serviceAccessGranted, onProviderStateChange, }: AiConnectionPanelProps): import("react").JSX.Element;
|
|
16
|
+
export declare function AiConnectionPanel({ client, openExternal, clientConfigurationBridge, clientCredentialManager, selectedProvider, providerSummaries: providerSummariesInput, providerLoadError, serviceAccessGranted, onProviderStateChange, }: AiConnectionPanelProps): import("react").JSX.Element;
|
|
@@ -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 [
|
|
12
|
-
const [
|
|
13
|
-
const [
|
|
14
|
-
const [
|
|
15
|
-
const [
|
|
16
|
-
const [
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
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
|
|
204
|
-
if (!serviceAccessGranted)
|
|
209
|
+
const createCredential = async () => {
|
|
210
|
+
if (!serviceAccessGranted || !clientCredentialManager)
|
|
205
211
|
return;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
212
|
+
setCreatingCredential(true);
|
|
213
|
+
setCredentialError(undefined);
|
|
214
|
+
setOneTimeCredential(undefined);
|
|
209
215
|
try {
|
|
210
|
-
const created = await
|
|
211
|
-
|
|
216
|
+
const created = await clientCredentialManager.create({
|
|
217
|
+
name: credentialName.trim() || 'AI Connection',
|
|
218
|
+
webId: client.webId,
|
|
212
219
|
});
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
232
|
+
setCredentialError(errorMessage(error));
|
|
219
233
|
}
|
|
220
234
|
finally {
|
|
221
|
-
|
|
235
|
+
setCreatingCredential(false);
|
|
222
236
|
}
|
|
223
237
|
};
|
|
224
|
-
const
|
|
225
|
-
if (!serviceAccessGranted)
|
|
238
|
+
const revokeCredential = async (credential) => {
|
|
239
|
+
if (!serviceAccessGranted || !clientCredentialManager)
|
|
226
240
|
return;
|
|
227
|
-
|
|
241
|
+
setCredentialError(undefined);
|
|
228
242
|
try {
|
|
229
|
-
|
|
230
|
-
|
|
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
|
-
|
|
247
|
+
setCredentialError(errorMessage(error));
|
|
236
248
|
}
|
|
237
249
|
};
|
|
238
|
-
const
|
|
239
|
-
if (!
|
|
250
|
+
const copyOneTimeApiKey = async () => {
|
|
251
|
+
if (!oneTimeCredential)
|
|
240
252
|
return;
|
|
241
|
-
await navigator.clipboard?.writeText(
|
|
253
|
+
await navigator.clipboard?.writeText(oneTimeCredential.apiKey);
|
|
242
254
|
setCopied(true);
|
|
243
255
|
};
|
|
244
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
252
|
-
created
|
|
253
|
-
...current.filter((record) => record.
|
|
267
|
+
setCredentials((current) => [
|
|
268
|
+
created,
|
|
269
|
+
...current.filter((record) => record.resourceUrl !== created.resourceUrl),
|
|
254
270
|
]);
|
|
255
271
|
return {
|
|
256
|
-
|
|
272
|
+
apiKey: created.apiKey,
|
|
257
273
|
revoke: async () => {
|
|
258
|
-
await
|
|
259
|
-
|
|
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,
|
|
269
|
-
|
|
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,
|
|
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.
|
|
162
|
-
throw new Error('AI Connection
|
|
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
|
|
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
|
|
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.
|
|
374
|
-
throw new Error("AI Connection
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,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
|
-
|
|
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>;
|