Commit 657746b3 authored by AI-甘富林's avatar AI-甘富林

feat(client): add xhs feishu runtime settings

parent 48d00eba
......@@ -536,7 +536,11 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
digitalHumanQiniuSecretKey,
videoAnalyzerApiKey,
replicationBriefApiKey,
vectCutApiKey
vectCutApiKey,
xhsFeishuAppId,
xhsFeishuAppSecret,
xhsFeishuAppToken,
xhsFeishuTableId
] = await Promise.all([
secretManager.getApiKey(),
getEffectiveGatewayToken(config),
......@@ -550,7 +554,11 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
secretManager.getDigitalHumanQiniuSecretKey(),
secretManager.getVideoAnalyzerApiKey(),
secretManager.getReplicationBriefApiKey(),
secretManager.getVectCutApiKey()
secretManager.getVectCutApiKey(),
secretManager.getXhsFeishuAppId(),
secretManager.getXhsFeishuAppSecret(),
secretManager.getXhsFeishuAppToken(),
secretManager.getXhsFeishuTableId()
]);
const nextConfig: AppConfig = {
......@@ -593,6 +601,12 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
...config.douyinRuntimeConfig.vectcut,
apiKeyConfigured: Boolean(vectCutApiKey)
}
},
xhsFeishuConfig: {
appIdConfigured: Boolean(xhsFeishuAppId),
appSecretConfigured: Boolean(xhsFeishuAppSecret),
appTokenConfigured: Boolean(xhsFeishuAppToken),
tableIdConfigured: Boolean(xhsFeishuTableId)
}
};
......@@ -608,7 +622,11 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
|| nextConfig.expertModelConfig.digitalHuman.qiniuSecretKeyConfigured !== config.expertModelConfig.digitalHuman.qiniuSecretKeyConfigured
|| nextConfig.douyinRuntimeConfig.videoAnalyzer.apiKeyConfigured !== config.douyinRuntimeConfig.videoAnalyzer.apiKeyConfigured
|| nextConfig.douyinRuntimeConfig.replicationBrief.apiKeyConfigured !== config.douyinRuntimeConfig.replicationBrief.apiKeyConfigured
|| nextConfig.douyinRuntimeConfig.vectcut.apiKeyConfigured !== config.douyinRuntimeConfig.vectcut.apiKeyConfigured;
|| nextConfig.douyinRuntimeConfig.vectcut.apiKeyConfigured !== config.douyinRuntimeConfig.vectcut.apiKeyConfigured
|| nextConfig.xhsFeishuConfig.appIdConfigured !== config.xhsFeishuConfig.appIdConfigured
|| nextConfig.xhsFeishuConfig.appSecretConfigured !== config.xhsFeishuConfig.appSecretConfigured
|| nextConfig.xhsFeishuConfig.appTokenConfigured !== config.xhsFeishuConfig.appTokenConfigured
|| nextConfig.xhsFeishuConfig.tableIdConfigured !== config.xhsFeishuConfig.tableIdConfigured;
if (secretStateChanged) {
await configService.persist({
......@@ -636,7 +654,11 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
digitalHumanQiniuSecretKey,
videoAnalyzerApiKey,
replicationBriefApiKey,
vectCutApiKey
vectCutApiKey,
xhsFeishuAppId,
xhsFeishuAppSecret,
xhsFeishuAppToken,
xhsFeishuTableId
] = await Promise.all([
secretManager.getCopywritingModelApiKey(),
secretManager.getImageModelApiKey(),
......@@ -647,7 +669,11 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
secretManager.getDigitalHumanQiniuSecretKey(),
secretManager.getVideoAnalyzerApiKey(),
secretManager.getReplicationBriefApiKey(),
secretManager.getVectCutApiKey()
secretManager.getVectCutApiKey(),
secretManager.getXhsFeishuAppId(),
secretManager.getXhsFeishuAppSecret(),
secretManager.getXhsFeishuAppToken(),
secretManager.getXhsFeishuTableId()
]);
const runtime = buildProjectModelRuntime(projectId, config, {
copywritingApiKey,
......@@ -659,7 +685,11 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
digitalHumanQiniuSecretKey,
videoAnalyzerApiKey,
replicationBriefApiKey,
vectCutApiKey
vectCutApiKey,
xhsFeishuAppId,
xhsFeishuAppSecret,
xhsFeishuAppToken,
xhsFeishuTableId
});
const envFilePath = await materializeProjectModelRuntime(projectRoot, runtime);
......@@ -1033,6 +1063,18 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
if (typeof input.douyinRuntimeConfig?.vectcut?.apiKey === "string") {
await secretManager.setVectCutApiKey(input.douyinRuntimeConfig.vectcut.apiKey || undefined);
}
if (typeof input.xhsFeishuConfig?.appId === "string") {
await secretManager.setXhsFeishuAppId(input.xhsFeishuConfig.appId || undefined);
}
if (typeof input.xhsFeishuConfig?.appSecret === "string") {
await secretManager.setXhsFeishuAppSecret(input.xhsFeishuConfig.appSecret || undefined);
}
if (typeof input.xhsFeishuConfig?.appToken === "string") {
await secretManager.setXhsFeishuAppToken(input.xhsFeishuConfig.appToken || undefined);
}
if (typeof input.xhsFeishuConfig?.tableId === "string") {
await secretManager.setXhsFeishuTableId(input.xhsFeishuConfig.tableId || undefined);
}
if (
config.setupMode === "direct-provider"
|| previousConfig.setupMode !== config.setupMode
......@@ -2360,5 +2402,3 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
......@@ -12,7 +12,8 @@ import {
type ModelEndpointConfig,
type RuntimeModePreference,
type SaveConfigInput,
type SetupMode
type SetupMode,
type XhsFeishuConfig
} from "@qjclaw/shared-types";
export type RuntimeCloudApiBaseUrlSource = "config" | "env" | "default";
......@@ -74,6 +75,7 @@ interface LegacyConfig {
apiKeyConfigured?: boolean;
};
};
xhsFeishuConfig?: Partial<XhsFeishuConfig>;
}
function normalizeGatewayUrl(raw: string): string {
......@@ -206,6 +208,15 @@ function createDefaultDouyinRuntimeConfig(): DouyinRuntimeConfig {
};
}
function createDefaultXhsFeishuConfig(): XhsFeishuConfig {
return {
appIdConfigured: false,
appSecretConfigured: false,
appTokenConfigured: false,
tableIdConfigured: false
};
}
function mergeExpertModelConfig(
current: ExpertModelConfig,
input?: SaveConfigInput["expertModelConfig"]
......@@ -277,6 +288,18 @@ function mergeDouyinRuntimeConfig(
};
}
function mergeXhsFeishuConfig(
current: XhsFeishuConfig,
input?: SaveConfigInput["xhsFeishuConfig"]
): XhsFeishuConfig {
return {
appIdConfigured: typeof input?.appId === "string" ? Boolean(input.appId.trim()) : current.appIdConfigured,
appSecretConfigured: typeof input?.appSecret === "string" ? Boolean(input.appSecret.trim()) : current.appSecretConfigured,
appTokenConfigured: typeof input?.appToken === "string" ? Boolean(input.appToken.trim()) : current.appTokenConfigured,
tableIdConfigured: typeof input?.tableId === "string" ? Boolean(input.tableId.trim()) : current.tableIdConfigured
};
}
export function getRuntimeCloudApiTarget(config: Pick<AppConfig, "runtimeCloudApiBaseUrl">): RuntimeCloudApiTarget {
return resolveRuntimeCloudApiTarget(config.runtimeCloudApiBaseUrl);
}
......@@ -310,7 +333,8 @@ export class AppConfigService {
runtimeCloudApiBaseUrl: migrateDeprecatedRuntimeCloudApiBaseUrl(input.runtimeCloudApiBaseUrl),
runtimeMode: normalizeRuntimeMode(input.runtimeMode),
expertModelConfig: mergeExpertModelConfig(current.expertModelConfig, input.expertModelConfig),
douyinRuntimeConfig: mergeDouyinRuntimeConfig(current.douyinRuntimeConfig, input.douyinRuntimeConfig)
douyinRuntimeConfig: mergeDouyinRuntimeConfig(current.douyinRuntimeConfig, input.douyinRuntimeConfig),
xhsFeishuConfig: mergeXhsFeishuConfig(current.xhsFeishuConfig, input.xhsFeishuConfig)
};
await this.writeConfig(config);
......@@ -347,13 +371,15 @@ export class AppConfigService {
runtimeCloudApiBaseUrl: "",
runtimeMode: normalizeRuntimeMode(process.env.QJCLAW_RUNTIME_MODE),
expertModelConfig: createDefaultExpertModelConfig(),
douyinRuntimeConfig: createDefaultDouyinRuntimeConfig()
douyinRuntimeConfig: createDefaultDouyinRuntimeConfig(),
xhsFeishuConfig: createDefaultXhsFeishuConfig()
};
}
private normalizeConfig(config: LegacyConfig): AppConfig {
const defaultExpertModelConfig = createDefaultExpertModelConfig();
const defaultDouyinRuntimeConfig = createDefaultDouyinRuntimeConfig();
const defaultXhsFeishuConfig = createDefaultXhsFeishuConfig();
return {
setupMode: normalizeSetupMode(config.setupMode),
provider: config.provider ?? "openai",
......@@ -415,6 +441,12 @@ export class AppConfigService {
: defaultDouyinRuntimeConfig.vectcut.fileBaseUrl,
apiKeyConfigured: Boolean(config.douyinRuntimeConfig?.vectcut?.apiKeyConfigured)
}
},
xhsFeishuConfig: {
appIdConfigured: Boolean(config.xhsFeishuConfig?.appIdConfigured ?? defaultXhsFeishuConfig.appIdConfigured),
appSecretConfigured: Boolean(config.xhsFeishuConfig?.appSecretConfigured ?? defaultXhsFeishuConfig.appSecretConfigured),
appTokenConfigured: Boolean(config.xhsFeishuConfig?.appTokenConfigured ?? defaultXhsFeishuConfig.appTokenConfigured),
tableIdConfigured: Boolean(config.xhsFeishuConfig?.tableIdConfigured ?? defaultXhsFeishuConfig.tableIdConfigured)
}
};
}
......
......@@ -13,6 +13,10 @@ export interface ProjectModelRuntimeSecrets {
videoAnalyzerApiKey?: string;
replicationBriefApiKey?: string;
vectCutApiKey?: string;
xhsFeishuAppId?: string;
xhsFeishuAppSecret?: string;
xhsFeishuAppToken?: string;
xhsFeishuTableId?: string;
}
export interface ProjectModelRuntimePreparation {
......@@ -107,7 +111,7 @@ export function validateProjectModelRuntime(
const copywritingBaseUrl = normalizeOpenAiCompatibleBaseUrl(config.expertModelConfig.copywriting.baseUrl);
const copywritingModelId = normalizeValue(config.expertModelConfig.copywriting.modelId);
const copywritingApiKey = normalizeValue(secrets.copywritingApiKey);
const imageBaseUrl = withoutTrailingSlash(config.expertModelConfig.image.baseUrl);
const imageBaseUrl = normalizeArkBaseUrl(config.expertModelConfig.image.baseUrl);
const imageModelId = normalizeValue(config.expertModelConfig.image.modelId);
const imageApiKey = normalizeValue(secrets.imageApiKey);
......@@ -219,7 +223,7 @@ export function buildProjectModelRuntime(
const copywritingBaseUrl = normalizeOpenAiCompatibleBaseUrl(config.expertModelConfig.copywriting.baseUrl);
const copywritingModelId = normalizeValue(config.expertModelConfig.copywriting.modelId);
const copywritingApiKey = normalizeValue(secrets.copywritingApiKey);
const imageBaseUrl = withoutTrailingSlash(config.expertModelConfig.image.baseUrl);
const imageBaseUrl = normalizeArkBaseUrl(config.expertModelConfig.image.baseUrl);
const imageModelId = normalizeValue(config.expertModelConfig.image.modelId);
const imageApiKey = normalizeValue(secrets.imageApiKey);
const videoBaseUrl = withoutTrailingSlash(config.expertModelConfig.video.baseUrl);
......@@ -238,6 +242,10 @@ export function buildProjectModelRuntime(
const vectCutBaseUrl = withoutTrailingSlash(config.douyinRuntimeConfig.vectcut.baseUrl);
const vectCutFileBaseUrl = withoutTrailingSlash(config.douyinRuntimeConfig.vectcut.fileBaseUrl);
const vectCutApiKey = normalizeValue(secrets.vectCutApiKey);
const xhsFeishuAppId = normalizeValue(secrets.xhsFeishuAppId);
const xhsFeishuAppSecret = normalizeValue(secrets.xhsFeishuAppSecret);
const xhsFeishuAppToken = normalizeValue(secrets.xhsFeishuAppToken);
const xhsFeishuTableId = normalizeValue(secrets.xhsFeishuTableId);
const env: Record<string, string> = {};
if (XHS_PROJECT_IDS.has(normalizedProjectId)) {
......@@ -263,6 +271,18 @@ export function buildProjectModelRuntime(
if (imageModelId) {
env.XHS_IMAGE_MODEL = imageModelId;
}
if (xhsFeishuAppId) {
env.FEISHU_APP_ID = xhsFeishuAppId;
}
if (xhsFeishuAppSecret) {
env.FEISHU_APP_SECRET = xhsFeishuAppSecret;
}
if (xhsFeishuAppToken) {
env.FEISHU_APP_TOKEN = xhsFeishuAppToken;
}
if (xhsFeishuTableId) {
env.FEISHU_TABLE_ID = xhsFeishuTableId;
}
}
if (DOUYIN_PROJECT_IDS.has(normalizedProjectId)) {
......
......@@ -17,6 +17,10 @@ interface SecretRecord {
videoAnalyzerApiKey?: string;
replicationBriefApiKey?: string;
vectCutApiKey?: string;
xhsFeishuAppId?: string;
xhsFeishuAppSecret?: string;
xhsFeishuAppToken?: string;
xhsFeishuTableId?: string;
}
interface SecretAccessor {
......@@ -38,7 +42,11 @@ type SecretName =
| "digitalHumanQiniuSecretKey"
| "videoAnalyzerApiKey"
| "replicationBriefApiKey"
| "vectCutApiKey";
| "vectCutApiKey"
| "xhsFeishuAppId"
| "xhsFeishuAppSecret"
| "xhsFeishuAppToken"
| "xhsFeishuTableId";
type KeytarModule = typeof import("keytar");
const KEYTAR_SERVICE = "QianjiangClaw";
......@@ -57,7 +65,11 @@ const KEYTAR_ACCOUNT_MAP: Record<SecretName, string> = {
digitalHumanQiniuSecretKey: "digital-human-qiniu-secret-key",
videoAnalyzerApiKey: "douyin-video-analyzer-api-key",
replicationBriefApiKey: "douyin-replication-brief-api-key",
vectCutApiKey: "douyin-vectcut-api-key"
vectCutApiKey: "douyin-vectcut-api-key",
xhsFeishuAppId: "xhs-feishu-app-id",
xhsFeishuAppSecret: "xhs-feishu-app-secret",
xhsFeishuAppToken: "xhs-feishu-app-token",
xhsFeishuTableId: "xhs-feishu-table-id"
};
class FileSecretStore implements SecretAccessor {
......@@ -277,6 +289,38 @@ export class SecretManager {
return this.store.get("vectCutApiKey");
}
async setXhsFeishuAppId(value?: string): Promise<void> {
await this.store.set("xhsFeishuAppId", value);
}
async getXhsFeishuAppId(): Promise<string | undefined> {
return this.store.get("xhsFeishuAppId");
}
async setXhsFeishuAppSecret(value?: string): Promise<void> {
await this.store.set("xhsFeishuAppSecret", value);
}
async getXhsFeishuAppSecret(): Promise<string | undefined> {
return this.store.get("xhsFeishuAppSecret");
}
async setXhsFeishuAppToken(value?: string): Promise<void> {
await this.store.set("xhsFeishuAppToken", value);
}
async getXhsFeishuAppToken(): Promise<string | undefined> {
return this.store.get("xhsFeishuAppToken");
}
async setXhsFeishuTableId(value?: string): Promise<void> {
await this.store.set("xhsFeishuTableId", value);
}
async getXhsFeishuTableId(): Promise<string | undefined> {
return this.store.get("xhsFeishuTableId");
}
private async tryLoadKeytar(): Promise<KeytarModule | null> {
try {
const imported = await import("keytar");
......@@ -301,7 +345,11 @@ export class SecretManager {
"digitalHumanQiniuSecretKey",
"videoAnalyzerApiKey",
"replicationBriefApiKey",
"vectCutApiKey"
"vectCutApiKey",
"xhsFeishuAppId",
"xhsFeishuAppSecret",
"xhsFeishuAppToken",
"xhsFeishuTableId"
] as const) {
const existing = await this.store.get(secretName);
if (existing) {
......
This diff is collapsed.
......@@ -3251,6 +3251,11 @@ button.secondary {
gap: 14px;
}
.main-shell.settings-main-shell {
padding-top: 8px;
gap: 0;
}
.page-topbar {
align-items: center;
min-height: 72px;
......@@ -4108,11 +4113,11 @@ button.secondary {
min-height: 0;
display: grid;
gap: 10px;
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-template-rows: minmax(164px, 0.43fr) minmax(0, 1.57fr);
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-template-rows: minmax(236px, 0.58fr) minmax(0, 1.42fr);
grid-template-areas:
"connection diagnostics"
"models models";
"connection diagnostics xhs-feishu"
"models models models";
overflow: hidden;
}
......@@ -4124,6 +4129,26 @@ button.secondary {
grid-area: diagnostics;
}
.settings-panel-xhs-feishu {
grid-area: xhs-feishu;
}
.settings-panel-xhs-feishu .settings-section-card-compact {
grid-template-rows: auto auto auto;
align-content: start;
gap: 7px;
padding: 8px;
}
.settings-panel-xhs-feishu .settings-section-headline {
align-items: center;
}
.settings-panel-xhs-feishu .settings-section-kicker {
min-height: 22px;
padding-inline: 9px;
}
.settings-panel-models {
grid-area: models;
}
......@@ -4403,6 +4428,32 @@ button.secondary {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.settings-field-grid-xhs-feishu {
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 7px;
align-content: start;
}
.settings-field-grid-xhs-feishu .settings-input-label {
gap: 3px;
}
.settings-field-grid-xhs-feishu .settings-input-label input {
min-height: 34px;
padding: 7px 10px;
}
.settings-panel-xhs-feishu .settings-actions {
margin-top: 0;
align-items: center;
}
.settings-panel-xhs-feishu .settings-primary-button {
min-height: 30px;
padding: 6px 14px;
font-size: 12px;
}
.settings-panel .status-chip {
min-height: 24px;
padding: 0 9px;
......@@ -4500,6 +4551,15 @@ button.secondary {
}
@media (max-width: 1180px) {
.settings-console-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-template-rows: minmax(164px, auto) minmax(210px, auto) minmax(0, 1fr);
grid-template-areas:
"connection diagnostics"
"xhs-feishu xhs-feishu"
"models models";
}
.model-config-grid-four {
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-auto-rows: minmax(188px, auto);
......@@ -4536,6 +4596,7 @@ button.secondary {
grid-template-areas:
"connection"
"diagnostics"
"xhs-feishu"
"models";
overflow: visible;
}
......
......@@ -589,6 +589,13 @@ export interface DouyinRuntimeConfig {
vectcut: VectCutModelConfig;
}
export interface XhsFeishuConfig {
appIdConfigured: boolean;
appSecretConfigured: boolean;
appTokenConfigured: boolean;
tableIdConfigured: boolean;
}
export const FIXED_EXPERT_MODEL_ENDPOINTS = {
copywriting: {
baseUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1",
......@@ -645,6 +652,7 @@ export interface AppConfig {
runtimeMode: RuntimeModePreference;
expertModelConfig: ExpertModelConfig;
douyinRuntimeConfig: DouyinRuntimeConfig;
xhsFeishuConfig: XhsFeishuConfig;
}
export interface DiagnosticsExportResult {
......@@ -678,6 +686,13 @@ export interface VectCutModelInput {
apiKey?: string;
}
export interface XhsFeishuConfigInput {
appId?: string;
appSecret?: string;
appToken?: string;
tableId?: string;
}
export interface SaveConfigInput {
setupMode: SetupMode;
provider: string;
......@@ -702,6 +717,7 @@ export interface SaveConfigInput {
replicationBrief?: DouyinTextModelInput;
vectcut?: VectCutModelInput;
};
xhsFeishuConfig?: XhsFeishuConfigInput;
}
export interface AuthSessionSummary {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment