Q88: a sign-in is saved only if a run can use and renew it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4668396f25
commit
7eef3d459f
38 files changed
+127
-398
No files matched your search
@@ -235,14 +235,6 @@ export function registerIpcHandlers(deps: IpcDeps): void {
|
||||
return fail("provider_model_check_failed", error instanceof Error ? error.message : String(error));
|
||||
}
|
||||
});
|
||||
ipcMain.handle(IPC_CHANNELS.launchProviderSignIn, (_event, input) => {
|
||||
try {
|
||||
providers.launchSignInWindow(input);
|
||||
return ok(null);
|
||||
} catch (error) {
|
||||
return fail("provider_sign_in_failed", error instanceof Error ? error.message : String(error));
|
||||
}
|
||||
});
|
||||
ipcMain.handle(IPC_CHANNELS.startProviderOAuth, async (_event, input) => {
|
||||
const emit = (event: Record<string, unknown>) =>
|
||||
deps.mainWindow.webContents.send(IPC_CHANNELS.rendererEvent, { type: "provider-oauth", providerKey: input.providerKey, ...event });
|
||||
@@ -264,7 +256,6 @@ export function registerIpcHandlers(deps: IpcDeps): void {
|
||||
providerId: input.providerId,
|
||||
secret,
|
||||
model: input.model ?? null,
|
||||
employeeId: input.employeeId,
|
||||
});
|
||||
emit({ phase: "active" });
|
||||
notifyDataChanged();
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { PROVIDER_CATALOG } from "../../../shared/provider-catalog";
|
||||
|
||||
/** A provider sign-in as stored: the token, the means to renew it, its expiry, and the extras its backend needs. */
|
||||
export interface StoredOAuth {
|
||||
access: string;
|
||||
refresh: string;
|
||||
expires: number;
|
||||
accountId?: string;
|
||||
projectId?: string;
|
||||
enterpriseUrl?: string | null;
|
||||
}
|
||||
|
||||
/** Extras a provider's turns cannot run without, with the words used when one is missing. */
|
||||
const REQUIRED_EXTRAS: Record<string, { field: "accountId" | "projectId"; label: string }> = {
|
||||
"gpt-account": { field: "accountId", label: "ChatGPT account id" },
|
||||
"google-antigravity": { field: "projectId", label: "Google Cloud project id" },
|
||||
};
|
||||
|
||||
/** The one check of a stored sign-in, used both when it is saved and when a run uses it. */
|
||||
export function parseStoredOAuth(providerKey: string, secret: string): StoredOAuth {
|
||||
const entry = PROVIDER_CATALOG.find((candidate) => candidate.key === providerKey);
|
||||
if (!entry) throw new Error(`${providerKey} is not a known provider.`);
|
||||
const label = entry.label;
|
||||
const value = secret.trim();
|
||||
if (!value.startsWith("{")) throw new Error(`Sign in to ${label}; a pasted token cannot be renewed when it expires.`);
|
||||
let parsed: unknown;
|
||||
try {
|
||||
parsed = JSON.parse(value);
|
||||
} catch {
|
||||
throw new Error(`The stored ${label} sign-in is not valid JSON; sign in again.`);
|
||||
}
|
||||
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error(`The stored ${label} sign-in is not an object; sign in again.`);
|
||||
const creds = parsed as Record<string, unknown>;
|
||||
const missing = (what: string) => new Error(`The ${label} sign-in is missing its ${what}; sign in again.`);
|
||||
if (typeof creds.access !== "string" || !creds.access.trim()) throw missing("access token");
|
||||
if (typeof creds.refresh !== "string" || !creds.refresh.trim()) throw missing("refresh token");
|
||||
if (typeof creds.expires !== "number" || !Number.isFinite(creds.expires)) throw missing("expiry");
|
||||
const extra = REQUIRED_EXTRAS[providerKey];
|
||||
if (extra && (typeof creds[extra.field] !== "string" || !(creds[extra.field] as string).trim())) throw missing(extra.label);
|
||||
return creds as unknown as StoredOAuth;
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
import { PROVIDER_AUTH_TYPE, type ProviderAuthType } from "../../shared/domain";
|
||||
import { listProviderModels, type ProviderModelEntry } from "../../shared/provider-models";
|
||||
import {
|
||||
parseAccessToken,
|
||||
parseAccountToken,
|
||||
oauthProfileParser,
|
||||
parseApiKey,
|
||||
parseAnthropicCompatProfile,
|
||||
parseAzureProfile,
|
||||
@@ -408,7 +407,7 @@ function responseMessages(instructions: string | null, prompt: string): Array<{
|
||||
}
|
||||
|
||||
function browserTokenAdapter(providerKey: string, authType: ProviderAuthType): ProviderAdapter {
|
||||
const parseAuthProfile = authType === PROVIDER_AUTH_TYPE.ACCOUNT_SUBSCRIPTION ? parseAccountToken : parseAccessToken;
|
||||
const parseAuthProfile = oauthProfileParser(providerKey, authType === PROVIDER_AUTH_TYPE.ACCOUNT_SUBSCRIPTION ? "account_token" : "access_token");
|
||||
const assertProfile = authType === PROVIDER_AUTH_TYPE.ACCOUNT_SUBSCRIPTION ? requireAccountTokenProfile : requireAccessTokenProfile;
|
||||
return {
|
||||
providerKey,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { JsonRecord } from "./provider-http";
|
||||
import { isRecord } from "./provider-http";
|
||||
import { parseStoredOAuth } from "./oauth/stored-oauth";
|
||||
|
||||
export type ProviderAuthProfile =
|
||||
| { type: "api_key"; apiKey: string }
|
||||
@@ -15,31 +16,9 @@ export function parseApiKey(secret: string): ProviderAuthProfile {
|
||||
return { type: "api_key", apiKey: nonEmptySecret(secret, "API key") };
|
||||
}
|
||||
|
||||
export function parseAccessToken(secret: string): ProviderAuthProfile {
|
||||
return { type: "access_token", accessToken: oauthAccessToken(secret, "Access token") };
|
||||
}
|
||||
|
||||
export function parseAccountToken(secret: string): ProviderAuthProfile {
|
||||
return { type: "account_token", accessToken: oauthAccessToken(secret, "Account token") };
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts either a raw token string or the JSON credential object produced by
|
||||
* the native OAuth login flow, returning the usable access token.
|
||||
*/
|
||||
function oauthAccessToken(secret: string, label: string): string {
|
||||
const value = nonEmptySecret(secret, label);
|
||||
if (!value.startsWith("{")) return value;
|
||||
let parsed: unknown;
|
||||
try {
|
||||
parsed = JSON.parse(value);
|
||||
} catch {
|
||||
throw new Error(`${label} credential must be a token or JSON.`);
|
||||
}
|
||||
if (isRecord(parsed) && typeof parsed.access === "string" && parsed.access.trim()) {
|
||||
return parsed.access.trim();
|
||||
}
|
||||
throw new Error(`${label} credential is missing an access token.`);
|
||||
/** A sign-in provider's parser: it accepts only what a run can use and renew (see parseStoredOAuth). */
|
||||
export function oauthProfileParser(providerKey: string, type: "access_token" | "account_token"): (secret: string) => ProviderAuthProfile {
|
||||
return (secret) => ({ type, accessToken: parseStoredOAuth(providerKey, secret).access });
|
||||
}
|
||||
|
||||
export function parseAzureProfile(secret: string): ProviderAuthProfile {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { DatabaseSync } from "node:sqlite";
|
||||
import { createHash, randomBytes } from "node:crypto";
|
||||
import type { CancelProviderResponseInput, LaunchProviderSignInInput, ListProviderModelsInput, RevokeProviderCredentialInput, SaveProviderCredentialInput, StreamProviderResponseInput, UpdateProviderPolicyInput, SetProviderPricesInput } from "../../shared/commands";
|
||||
import type { CancelProviderResponseInput, ListProviderModelsInput, RevokeProviderCredentialInput, SaveProviderCredentialInput, StreamProviderResponseInput, UpdateProviderPolicyInput, SetProviderPricesInput } from "../../shared/commands";
|
||||
import type { ProviderModelEntry } from "../../shared/provider-models";
|
||||
import { PROVIDER_AUTH_TYPE, PROVIDER_DATA_POLICY, type ProviderAuthType } from "../../shared/domain";
|
||||
import type { ProviderStreamRendererEvent } from "../../shared/provider-request-domain";
|
||||
@@ -15,7 +14,6 @@ import { ProviderRateLimitRegistry } from "./provider-rate-limit-registry";
|
||||
import { ProviderResponseObserver } from "./provider-response-observer";
|
||||
import { ProviderRequestStore } from "./provider-request-store";
|
||||
import { ProviderResponseRunner, type ProviderResponseCreateInput } from "./provider-response-runner";
|
||||
import { launchProviderSignInWindow, type LaunchProviderSignInWindowInput } from "./provider-sign-in-window";
|
||||
import type { ToolTurnFn } from "./provider-tool-turn";
|
||||
import { stopRun } from "./run-control";
|
||||
import type { ModelCheckResult } from "../../shared/desktop-api";
|
||||
@@ -35,8 +33,6 @@ import {
|
||||
|
||||
export type { ProviderResponseCreateInput };
|
||||
|
||||
const AUTH_SESSION_TTL_MS = 15 * 60 * 1000;
|
||||
|
||||
/** Room for instructions, the output reserve, and some history. */
|
||||
const MIN_CONTEXT_WINDOW_TOKENS = 24_000;
|
||||
|
||||
@@ -55,7 +51,6 @@ export class ProviderService {
|
||||
private readonly db: DatabaseSync,
|
||||
private readonly adapters: ProviderAdapterRegistry = providerAdapters,
|
||||
vault: ProviderSecretVault = new SecretVault(),
|
||||
private readonly launchSignIn: (input: LaunchProviderSignInWindowInput) => void = launchProviderSignInWindow,
|
||||
toolTurn?: ToolTurnFn,
|
||||
) {
|
||||
this.audit = new AuditService(db);
|
||||
@@ -106,14 +101,7 @@ export class ProviderService {
|
||||
const adapter = requireAdapter(this.adapters, provider.provider_key, provider.auth_type);
|
||||
adapter.parseAuthProfile(secret);
|
||||
const catalogEntry = requireCatalogEntry(provider.provider_key);
|
||||
const source = this.resolveCredentialSource({
|
||||
orgId: input.orgId,
|
||||
providerId: input.providerId,
|
||||
providerKey: provider.provider_key,
|
||||
authType: provider.auth_type,
|
||||
employeeId: input.employeeId ?? null,
|
||||
secret,
|
||||
});
|
||||
const source = this.resolveCredentialSource(provider.auth_type);
|
||||
const authProfile = this.authBroker.createProviderProfile({
|
||||
orgId: input.orgId,
|
||||
providerId: input.providerId,
|
||||
@@ -223,44 +211,6 @@ export class ProviderService {
|
||||
}
|
||||
|
||||
|
||||
launchSignInWindow(input: LaunchProviderSignInInput): void {
|
||||
this.assertEmployee(input.orgId, input.employeeId);
|
||||
const provider = this.db.prepare(`
|
||||
select id, provider_key, label, auth_type
|
||||
from providers
|
||||
where id = ? and org_id = ?
|
||||
`).get(input.providerId, input.orgId) as { id: string; provider_key: string; label: string; auth_type: string } | undefined;
|
||||
if (!provider) throw new Error("Provider was not found.");
|
||||
const catalogEntry = requireCatalogEntry(provider.provider_key);
|
||||
if (!catalogEntry.signInUrl) throw new Error(`${provider.label} does not support browser sign-in.`);
|
||||
const session = this.createAuthSession({
|
||||
orgId: input.orgId,
|
||||
providerId: provider.id,
|
||||
providerKey: provider.provider_key,
|
||||
employeeId: input.employeeId,
|
||||
signInUrl: catalogEntry.signInUrl,
|
||||
});
|
||||
this.launchSignIn({ providerId: provider.id, label: provider.label, signInUrl: catalogEntry.signInUrl, partition: session.partition });
|
||||
|
||||
this.audit.record({
|
||||
orgId: input.orgId,
|
||||
entityType: "provider",
|
||||
entityId: provider.id,
|
||||
actorType: "employee",
|
||||
actorId: input.employeeId,
|
||||
action: "sign_in_window_opened",
|
||||
payload: {
|
||||
providerKey: provider.provider_key,
|
||||
authType: provider.auth_type,
|
||||
authSessionId: session.id,
|
||||
state: session.state,
|
||||
nonce: session.nonce,
|
||||
codeVerifierSha256: session.codeVerifierSha256,
|
||||
redirectUri: session.redirectUri,
|
||||
partition: session.partition,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** Sets the per-million-token prices that turn recorded usage into spend. */
|
||||
setPrices(input: SetProviderPricesInput): void {
|
||||
@@ -322,101 +272,12 @@ export class ProviderService {
|
||||
});
|
||||
}
|
||||
|
||||
private assertEmployee(orgId: string, employeeId: string): void {
|
||||
const employee = this.db.prepare("select id from employees where id = ? and org_id = ?")
|
||||
.get(employeeId, orgId) as { id: string } | undefined;
|
||||
if (!employee) throw new Error("Provider sign-in employee was not found.");
|
||||
}
|
||||
|
||||
private resolveCredentialSource(input: {
|
||||
orgId: string;
|
||||
providerId: string;
|
||||
providerKey: string;
|
||||
authType: ProviderAuthType;
|
||||
employeeId: string | null;
|
||||
secret: string;
|
||||
}): { sourceType: string; sourceRef: string | null } {
|
||||
if (input.authType !== PROVIDER_AUTH_TYPE.OAUTH_PKCE && input.authType !== PROVIDER_AUTH_TYPE.ACCOUNT_SUBSCRIPTION) {
|
||||
private resolveCredentialSource(authType: ProviderAuthType): { sourceType: string; sourceRef: null } {
|
||||
if (authType !== PROVIDER_AUTH_TYPE.OAUTH_PKCE && authType !== PROVIDER_AUTH_TYPE.ACCOUNT_SUBSCRIPTION) {
|
||||
return { sourceType: "manual_secret", sourceRef: null };
|
||||
}
|
||||
// Credentials obtained through the native OAuth login arrive as a JSON
|
||||
// object; they are authenticated by the flow itself and need no paste session.
|
||||
if (input.secret.trim().startsWith("{")) {
|
||||
return { sourceType: "provider_oauth_login", sourceRef: null };
|
||||
}
|
||||
if (!input.employeeId) throw new Error("Provider sign-in employee is required.");
|
||||
const session = this.db.prepare(`
|
||||
select id
|
||||
from provider_auth_sessions
|
||||
where org_id = ?
|
||||
and provider_id = ?
|
||||
and provider_key = ?
|
||||
and employee_id = ?
|
||||
and completed_at is null
|
||||
and expires_at > ?
|
||||
order by created_at desc
|
||||
limit 1
|
||||
`).get(input.orgId, input.providerId, input.providerKey, input.employeeId, new Date().toISOString()) as { id: string } | undefined;
|
||||
if (!session) throw new Error("Provider sign-in session is required.");
|
||||
this.db.prepare("update provider_auth_sessions set completed_at = ? where id = ? and org_id = ?")
|
||||
.run(new Date().toISOString(), session.id, input.orgId);
|
||||
return { sourceType: "provider_sign_in_session", sourceRef: session.id };
|
||||
// A sign-in credential can only come from the native OAuth login: parseStoredOAuth refuses anything else.
|
||||
return { sourceType: "provider_oauth_login", sourceRef: null };
|
||||
}
|
||||
|
||||
private createAuthSession(input: {
|
||||
orgId: string;
|
||||
providerId: string;
|
||||
providerKey: string;
|
||||
employeeId: string;
|
||||
signInUrl: string;
|
||||
}): ProviderAuthSessionDraft {
|
||||
const id = createProviderAuthSessionId();
|
||||
const state = randomToken();
|
||||
const nonce = randomToken();
|
||||
const codeVerifier = randomToken();
|
||||
const codeVerifierSha256 = createHash("sha256").update(codeVerifier).digest("hex");
|
||||
const redirectUri = `agentcompany://provider-auth/${input.providerId}`;
|
||||
const partition = `persist:agentcompany-provider-${input.providerId}`;
|
||||
const createdAt = new Date();
|
||||
const expiresAt = new Date(createdAt.getTime() + AUTH_SESSION_TTL_MS);
|
||||
this.db.prepare(`
|
||||
insert into provider_auth_sessions(
|
||||
id, org_id, provider_id, provider_key, employee_id, state, nonce,
|
||||
code_verifier_sha256, redirect_uri, sign_in_url, partition,
|
||||
created_at, expires_at, completed_at
|
||||
) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, null)
|
||||
`).run(
|
||||
id,
|
||||
input.orgId,
|
||||
input.providerId,
|
||||
input.providerKey,
|
||||
input.employeeId,
|
||||
state,
|
||||
nonce,
|
||||
codeVerifierSha256,
|
||||
redirectUri,
|
||||
input.signInUrl,
|
||||
partition,
|
||||
createdAt.toISOString(),
|
||||
expiresAt.toISOString(),
|
||||
);
|
||||
return { id, state, nonce, codeVerifierSha256, redirectUri, partition };
|
||||
}
|
||||
}
|
||||
|
||||
interface ProviderAuthSessionDraft {
|
||||
id: string;
|
||||
state: string;
|
||||
nonce: string;
|
||||
codeVerifierSha256: string;
|
||||
redirectUri: string;
|
||||
partition: string;
|
||||
}
|
||||
|
||||
function createProviderAuthSessionId(): string {
|
||||
return `auths_${randomToken()}`;
|
||||
}
|
||||
|
||||
function randomToken(): string {
|
||||
return randomBytes(24).toString("base64url");
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import { BrowserWindow, session } from "electron";
|
||||
|
||||
export interface LaunchProviderSignInWindowInput {
|
||||
providerId: string;
|
||||
label: string;
|
||||
signInUrl: string;
|
||||
partition: string;
|
||||
}
|
||||
|
||||
export function launchProviderSignInWindow(input: LaunchProviderSignInWindowInput): void {
|
||||
const providerSession = session.fromPartition(input.partition);
|
||||
providerSession.setPermissionRequestHandler((_webContents, _permission, callback) => callback(false));
|
||||
providerSession.setPermissionCheckHandler(() => false);
|
||||
|
||||
const win = new BrowserWindow({
|
||||
width: 1100,
|
||||
height: 760,
|
||||
title: input.label,
|
||||
webPreferences: {
|
||||
session: providerSession,
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
sandbox: true,
|
||||
webSecurity: true,
|
||||
},
|
||||
});
|
||||
win.webContents.setWindowOpenHandler(() => ({ action: "deny" }));
|
||||
attachNavigationPolicy(win, input.signInUrl);
|
||||
win.loadURL(input.signInUrl);
|
||||
}
|
||||
|
||||
function attachNavigationPolicy(win: BrowserWindow, signInUrl: string): void {
|
||||
const allowedHost = new URL(signInUrl).hostname.toLowerCase();
|
||||
const allow = (url: string) => {
|
||||
const host = new URL(url).hostname.toLowerCase();
|
||||
return host === allowedHost || host.endsWith(`.${allowedHost}`);
|
||||
};
|
||||
const blockOffHost = (event: Electron.Event, url: string) => {
|
||||
if (!allow(url)) event.preventDefault();
|
||||
};
|
||||
win.webContents.on("will-navigate", blockOffHost);
|
||||
win.webContents.on("will-redirect", blockOffHost);
|
||||
}
|
||||
@@ -30,29 +30,7 @@ import { refreshAntigravity, requireAntigravityClientSecret } from "../oauth/goo
|
||||
import type { IntegrationKeyStore } from "../integration-key-store";
|
||||
import { refreshGitHubCopilotToken } from "../oauth/github-copilot-flow";
|
||||
import { refreshKimiCode } from "../oauth/kimi-code-flow";
|
||||
|
||||
interface StoredOAuth {
|
||||
access: string;
|
||||
refresh: string;
|
||||
expires: number;
|
||||
accountId?: string;
|
||||
projectId?: string;
|
||||
enterpriseUrl?: string | null;
|
||||
}
|
||||
|
||||
function parseStoredOAuth(secret: string): StoredOAuth {
|
||||
let parsed: unknown;
|
||||
try {
|
||||
parsed = JSON.parse(secret);
|
||||
} catch {
|
||||
throw new Error("Stored OAuth credential is not valid JSON; reconnect the provider.");
|
||||
}
|
||||
const creds = parsed as StoredOAuth;
|
||||
if (typeof creds.access !== "string" || typeof creds.refresh !== "string" || typeof creds.expires !== "number") {
|
||||
throw new Error("Stored OAuth credential is incomplete; reconnect the provider.");
|
||||
}
|
||||
return creds;
|
||||
}
|
||||
import { parseStoredOAuth, type StoredOAuth } from "../oauth/stored-oauth";
|
||||
|
||||
function refreshOAuth(providerKey: string, creds: StoredOAuth, keys: IntegrationKeyStore): Promise<OAuthCredentials> {
|
||||
switch (providerKey) {
|
||||
@@ -61,8 +39,7 @@ function refreshOAuth(providerKey: string, creds: StoredOAuth, keys: Integration
|
||||
case "claude-pro":
|
||||
return refreshClaudePro(creds.refresh);
|
||||
case "google-antigravity":
|
||||
if (!creds.projectId) throw new Error("Antigravity credential is missing its project id; reconnect the provider.");
|
||||
return refreshAntigravity(creds.refresh, creds.projectId, requireAntigravityClientSecret(keys.read("google_antigravity_client_secret")));
|
||||
return refreshAntigravity(creds.refresh, requireProjectId(creds), requireAntigravityClientSecret(keys.read("google_antigravity_client_secret")));
|
||||
case "github-copilot":
|
||||
return refreshGitHubCopilotToken(creds.refresh, creds.enterpriseUrl ?? undefined);
|
||||
case "kimi-code":
|
||||
@@ -122,7 +99,7 @@ export async function resolveTurnCredential(deps: {
|
||||
if (deps.provider.auth_type === PROVIDER_AUTH_TYPE.CLOUD) {
|
||||
return resolveCloudCredential(deps.provider.provider_key, secret);
|
||||
}
|
||||
const creds = parseStoredOAuth(secret);
|
||||
const creds = parseStoredOAuth(deps.provider.provider_key, secret);
|
||||
if (Date.now() < creds.expires) return toTurnCredential(creds);
|
||||
if (!deps.provider.credential_id) throw new Error("Provider credential id is missing; reconnect the provider.");
|
||||
const refreshed = await refreshOAuth(deps.provider.provider_key, creds, deps.integrationKeys);
|
||||
@@ -134,3 +111,8 @@ export async function resolveTurnCredential(deps: {
|
||||
});
|
||||
return toTurnCredential(refreshed);
|
||||
}
|
||||
|
||||
function requireProjectId(creds: StoredOAuth): string {
|
||||
if (!creds.projectId) throw new Error("The Google Antigravity sign-in is missing its Google Cloud project id; sign in again.");
|
||||
return creds.projectId;
|
||||
}
|
||||
@@ -55,7 +55,6 @@ const api: DesktopApi = {
|
||||
markChatSpaceRead: (input) => invoke(IPC_CHANNELS.markChatSpaceRead, input),
|
||||
saveProviderCredential: (input) => invoke(IPC_CHANNELS.saveProviderCredential, input),
|
||||
revokeProviderCredential: (input) => invoke(IPC_CHANNELS.revokeProviderCredential, input),
|
||||
launchProviderSignIn: (input) => invoke(IPC_CHANNELS.launchProviderSignIn, input),
|
||||
startProviderOAuth: (input) => invoke(IPC_CHANNELS.startProviderOAuth, input),
|
||||
cancelProviderOAuth: (input) => invoke(IPC_CHANNELS.cancelProviderOAuth, input),
|
||||
checkProviderHealth: (input) => invoke(IPC_CHANNELS.checkProviderHealth, input),
|
||||
|
||||
@@ -52,7 +52,6 @@ export function ProvidersPanel({ orgId, employeeId, providers, authProfiles, cat
|
||||
providerId: active.id,
|
||||
secret,
|
||||
model: String(form.get("model") ?? "").trim() || null,
|
||||
employeeId,
|
||||
})).then((result) => { if (result) close(); });
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ export interface SaveProviderCredentialInput {
|
||||
providerId: string;
|
||||
secret: string;
|
||||
model: string | null;
|
||||
employeeId?: string | null;
|
||||
}
|
||||
|
||||
export interface RevokeProviderCredentialInput {
|
||||
@@ -13,12 +12,6 @@ export interface RevokeProviderCredentialInput {
|
||||
providerId: string;
|
||||
}
|
||||
|
||||
export interface LaunchProviderSignInInput {
|
||||
orgId: string;
|
||||
providerId: string;
|
||||
employeeId: string;
|
||||
}
|
||||
|
||||
export interface StartProviderOAuthInput {
|
||||
providerKey: string;
|
||||
enterpriseDomain?: string | null;
|
||||
|
||||
@@ -61,7 +61,6 @@ import type {
|
||||
ImportFileInput,
|
||||
OpenFileInput,
|
||||
GrantRoleInput,
|
||||
LaunchProviderSignInInput,
|
||||
LaunchSocialLoginInput,
|
||||
ResolveMigrationReconciliationInput,
|
||||
RunChaosDrillInput,
|
||||
@@ -162,7 +161,6 @@ export interface DesktopApi {
|
||||
markChatSpaceRead(input: MarkChatSpaceReadInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
saveProviderCredential(input: SaveProviderCredentialInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
revokeProviderCredential(input: RevokeProviderCredentialInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
launchProviderSignIn(input: LaunchProviderSignInInput): Promise<DesktopResult<null>>;
|
||||
startProviderOAuth(input: StartProviderOAuthInput): Promise<DesktopResult<StartProviderOAuthResult>>;
|
||||
cancelProviderOAuth(input: CancelProviderOAuthInput): Promise<DesktopResult<null>>;
|
||||
checkProviderHealth(input: CheckProviderHealthInput): Promise<DesktopResult<AppSnapshot>>;
|
||||
|
||||
@@ -28,7 +28,6 @@ export const IPC_CHANNELS = {
|
||||
markChatSpaceRead: "agentcompany:mark-chat-space-read",
|
||||
saveProviderCredential: "agentcompany:save-provider-credential",
|
||||
revokeProviderCredential: "agentcompany:revoke-provider-credential",
|
||||
launchProviderSignIn: "agentcompany:launch-provider-sign-in",
|
||||
startProviderOAuth: "agentcompany:start-provider-oauth",
|
||||
cancelProviderOAuth: "agentcompany:cancel-provider-oauth",
|
||||
checkProviderHealth: "agentcompany:check-provider-health",
|
||||
|
||||
Reference in new issue
Block a user