Refactor UUID generation, remove unused logger and encryption utilities, and clean up request handling
- Updated `generateUUID` function for improved readability and performance. - Deleted `logger.ts`, `other.ts`, `request.ts`, `storage.ts`, `tansParams.ts`, and `validate.ts` as they were no longer needed. - Simplified TypeScript configuration by removing unnecessary paths and aliases. - Enhanced Vite configuration for better project structure and maintainability.
This commit is contained in:
121
runtime-shared/lib/constants.ts
Normal file
121
runtime-shared/lib/constants.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
export enum IPC_EVENTS {
|
||||
EXTERNAL_OPEN = 'external-open',
|
||||
APP_SET_FRAMELESS = 'app:set-frameless',
|
||||
APP_LOAD_PAGE = 'app:load-page',
|
||||
TAB_CREATE = 'tab:create',
|
||||
TAB_LIST = 'tab:list',
|
||||
TAB_NAVIGATE = 'tab:navigate',
|
||||
TAB_RELOAD = 'tab:reload',
|
||||
TAB_BACK = 'tab:back',
|
||||
TAB_FORWARD = 'tab:forward',
|
||||
TAB_SWITCH = 'tab:switch',
|
||||
TAB_CLOSE = 'tab:close',
|
||||
LOG_TO_MAIN = 'log-to-main',
|
||||
READ_FILE = 'read-file',
|
||||
INVOKE = 'ipc:invoke',
|
||||
INVOKE_ASYNC = 'ipc:invokeAsync',
|
||||
APP_MINIMIZE = 'app:minimize',
|
||||
APP_MAXIMIZE = 'app:maximize',
|
||||
APP_QUIT = 'app:quit',
|
||||
FILE_READ = 'file:read',
|
||||
FILE_WRITE = 'file:write',
|
||||
GET_WINDOW_ID = 'get-window-id',
|
||||
CUSTOM_EVENT = 'custom:event',
|
||||
TIME_UPDATE = 'time:update',
|
||||
RENDERER_IS_READY = 'renderer-ready',
|
||||
SHOW_CONTEXT_MENU = 'show-context-menu',
|
||||
START_A_DIALOGUE = 'start-a-dialogue',
|
||||
OPEN_WINDOW = 'open-window',
|
||||
LOG_DEBUG = 'log-debug',
|
||||
LOG_INFO = 'log-info',
|
||||
LOG_WARN = 'log-warn',
|
||||
LOG_ERROR = 'log-error',
|
||||
CONFIG_UPDATED = 'config-updated',
|
||||
SET_CONFIG = 'set-config',
|
||||
GET_CONFIG = 'get-config',
|
||||
UPDATE_CONFIG = 'update-config',
|
||||
SET_THEME_MODE = 'set-theme-mode',
|
||||
GET_THEME_MODE = 'get-theme-mode',
|
||||
IS_DARK_THEME = 'is-dark-theme',
|
||||
THEME_MODE_UPDATED = 'theme-mode-updated',
|
||||
EXECUTE_SCRIPT = 'execute-script',
|
||||
TASK_PROGRESS = 'task:progress',
|
||||
TASK_STARTED = 'task:started',
|
||||
TASK_COMPLETED = 'task:completed',
|
||||
OPEN_CHANNEL = 'open-channel',
|
||||
SCRIPT_LIST = 'script:list',
|
||||
SCRIPT_GET = 'script:get',
|
||||
SCRIPT_SAVE = 'script:save',
|
||||
SCRIPT_DELETE = 'script:delete',
|
||||
SCRIPT_TOGGLE = 'script:toggle',
|
||||
SCRIPT_RUN = 'script:run',
|
||||
SCRIPT_RECORD_START = 'script:record-start',
|
||||
SCRIPT_RECORD_STOP = 'script:record-stop',
|
||||
SCRIPT_CODEGEN = 'script:codegen',
|
||||
GATEWAY_RPC = 'gateway:rpc',
|
||||
GATEWAY_EVENT = 'gateway:event',
|
||||
UPDATE_CHECK = 'update:check',
|
||||
UPDATE_DOWNLOAD = 'update:download',
|
||||
UPDATE_INSTALL = 'update:install',
|
||||
UPDATE_VERSION = 'update:version',
|
||||
UPDATE_STATUS_CHANGED = 'update:status-changed',
|
||||
}
|
||||
|
||||
export const MAIN_WIN_SIZE = {
|
||||
width: 1440,
|
||||
height: 900,
|
||||
minWidth: 1440,
|
||||
minHeight: 900,
|
||||
} as const;
|
||||
|
||||
export enum WINDOW_NAMES {
|
||||
MAIN = 'main',
|
||||
SETTING = 'setting',
|
||||
DIALOG = 'dialog',
|
||||
LOADING = 'loading',
|
||||
}
|
||||
|
||||
export enum CONFIG_KEYS {
|
||||
THEME_MODE = 'themeMode',
|
||||
PRIMARY_COLOR = 'primaryColor',
|
||||
LANGUAGE = 'language',
|
||||
FONT_SIZE = 'fontSize',
|
||||
MINIMIZE_TO_TRAY = 'minimizeToTray',
|
||||
PROVIDER = 'provider',
|
||||
DEFAULT_MODEL = 'defaultModel',
|
||||
AUTO_CHECK_UPDATE = 'autoCheckUpdate',
|
||||
AUTO_DOWNLOAD_UPDATE = 'autoDownloadUpdate',
|
||||
SELECTED_CHANNELS = 'selectedChannels',
|
||||
IMAGE_CACHE = 'imageCache',
|
||||
TASK_LIST = 'taskList',
|
||||
}
|
||||
|
||||
export enum MENU_IDS {
|
||||
CONVERSATION_ITEM = 'conversation-item',
|
||||
CONVERSATION_LIST = 'conversation-list',
|
||||
MESSAGE_ITEM = 'message-item',
|
||||
}
|
||||
|
||||
export enum CONVERSATION_ITEM_MENU_IDS {
|
||||
PIN = 'pin',
|
||||
RENAME = 'rename',
|
||||
DEL = 'del',
|
||||
}
|
||||
|
||||
export enum CONVERSATION_LIST_MENU_IDS {
|
||||
NEW_CONVERSATION = 'newConversation',
|
||||
SORT_BY = 'sortBy',
|
||||
SORT_BY_CREATE_TIME = 'sortByCreateTime',
|
||||
SORT_BY_UPDATE_TIME = 'sortByUpdateTime',
|
||||
SORT_BY_NAME = 'sortByName',
|
||||
SORT_BY_MODEL = 'sortByModel',
|
||||
SORT_ASCENDING = 'sortAscending',
|
||||
SORT_DESCENDING = 'sortDescending',
|
||||
BATCH_OPERATIONS = 'batchOperations',
|
||||
}
|
||||
|
||||
export enum MESSAGE_ITEM_MENU_IDS {
|
||||
COPY = 'copy',
|
||||
DELETE = 'delete',
|
||||
SELECT = 'select',
|
||||
}
|
||||
355
runtime-shared/lib/providers.ts
Normal file
355
runtime-shared/lib/providers.ts
Normal file
@@ -0,0 +1,355 @@
|
||||
export const PROVIDER_TYPES = [
|
||||
'anthropic',
|
||||
'openai',
|
||||
'google',
|
||||
'openrouter',
|
||||
'ark',
|
||||
'moonshot',
|
||||
'siliconflow',
|
||||
'deepseek',
|
||||
'minimax-portal',
|
||||
'minimax-portal-cn',
|
||||
'modelstudio',
|
||||
'ollama',
|
||||
'custom',
|
||||
] as const;
|
||||
|
||||
export type ProviderType = (typeof PROVIDER_TYPES)[number];
|
||||
|
||||
export const BUILTIN_PROVIDER_TYPES = [
|
||||
'anthropic',
|
||||
'openai',
|
||||
'google',
|
||||
'openrouter',
|
||||
'ark',
|
||||
'moonshot',
|
||||
'siliconflow',
|
||||
'deepseek',
|
||||
'minimax-portal',
|
||||
'minimax-portal-cn',
|
||||
'modelstudio',
|
||||
'ollama',
|
||||
] as const;
|
||||
|
||||
export const OLLAMA_PLACEHOLDER_API_KEY = 'ollama-local';
|
||||
|
||||
export interface ProviderConfig {
|
||||
id: string;
|
||||
name: string;
|
||||
type: ProviderType;
|
||||
baseUrl?: string;
|
||||
apiProtocol?: 'openai-completions' | 'openai-responses' | 'anthropic-messages';
|
||||
headers?: Record<string, string>;
|
||||
model?: string;
|
||||
fallbackModels?: string[];
|
||||
fallbackProviderIds?: string[];
|
||||
enabled: boolean;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface ProviderWithKeyInfo extends ProviderConfig {
|
||||
hasKey: boolean;
|
||||
keyMasked: string | null;
|
||||
}
|
||||
|
||||
export interface ProviderTypeInfo {
|
||||
id: ProviderType;
|
||||
name: string;
|
||||
icon: string;
|
||||
placeholder: string;
|
||||
model?: string;
|
||||
requiresApiKey: boolean;
|
||||
defaultBaseUrl?: string;
|
||||
showBaseUrl?: boolean;
|
||||
showModelId?: boolean;
|
||||
showModelIdInDevModeOnly?: boolean;
|
||||
modelIdPlaceholder?: string;
|
||||
defaultModelId?: string;
|
||||
isOAuth?: boolean;
|
||||
supportsApiKey?: boolean;
|
||||
apiKeyUrl?: string;
|
||||
docsUrl?: string;
|
||||
docsUrlZh?: string;
|
||||
codePlanPresetBaseUrl?: string;
|
||||
codePlanPresetModelId?: string;
|
||||
codePlanDocsUrl?: string;
|
||||
hidden?: boolean;
|
||||
}
|
||||
|
||||
export type ProviderAuthMode =
|
||||
| 'api_key'
|
||||
| 'oauth_device'
|
||||
| 'oauth_browser'
|
||||
| 'local';
|
||||
|
||||
export type ProviderVendorCategory =
|
||||
| 'official'
|
||||
| 'compatible'
|
||||
| 'local'
|
||||
| 'custom';
|
||||
|
||||
export interface ProviderVendorInfo extends ProviderTypeInfo {
|
||||
category: ProviderVendorCategory;
|
||||
envVar?: string;
|
||||
supportedAuthModes: ProviderAuthMode[];
|
||||
defaultAuthMode: ProviderAuthMode;
|
||||
supportsMultipleAccounts: boolean;
|
||||
}
|
||||
|
||||
export interface ProviderAccount {
|
||||
id: string;
|
||||
vendorId: ProviderType;
|
||||
label: string;
|
||||
authMode: ProviderAuthMode;
|
||||
baseUrl?: string;
|
||||
apiProtocol?: 'openai-completions' | 'openai-responses' | 'anthropic-messages';
|
||||
headers?: Record<string, string>;
|
||||
model?: string;
|
||||
fallbackModels?: string[];
|
||||
fallbackAccountIds?: string[];
|
||||
enabled: boolean;
|
||||
isDefault: boolean;
|
||||
metadata?: {
|
||||
region?: string;
|
||||
email?: string;
|
||||
resourceUrl?: string;
|
||||
customModels?: string[];
|
||||
};
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export const PROVIDER_TYPE_INFO: ProviderTypeInfo[] = [
|
||||
{
|
||||
id: 'anthropic',
|
||||
name: 'Anthropic',
|
||||
icon: 'A',
|
||||
placeholder: 'sk-ant-api03-...',
|
||||
model: 'Claude',
|
||||
requiresApiKey: true,
|
||||
docsUrl: 'https://platform.claude.com/docs/en/api/overview',
|
||||
},
|
||||
{
|
||||
id: 'openai',
|
||||
name: 'OpenAI',
|
||||
icon: 'O',
|
||||
placeholder: 'sk-proj-...',
|
||||
model: 'GPT',
|
||||
requiresApiKey: true,
|
||||
isOAuth: true,
|
||||
supportsApiKey: true,
|
||||
defaultModelId: 'gpt-5.4',
|
||||
showModelId: true,
|
||||
showModelIdInDevModeOnly: true,
|
||||
modelIdPlaceholder: 'gpt-5.4',
|
||||
apiKeyUrl: 'https://platform.openai.com/api-keys',
|
||||
},
|
||||
{
|
||||
id: 'google',
|
||||
name: 'Google',
|
||||
icon: 'G',
|
||||
placeholder: 'AIza...',
|
||||
model: 'Gemini',
|
||||
requiresApiKey: true,
|
||||
isOAuth: true,
|
||||
supportsApiKey: true,
|
||||
defaultModelId: 'gemini-3-pro-preview',
|
||||
showModelId: true,
|
||||
showModelIdInDevModeOnly: true,
|
||||
modelIdPlaceholder: 'gemini-3-pro-preview',
|
||||
apiKeyUrl: 'https://aistudio.google.com/app/apikey',
|
||||
},
|
||||
{
|
||||
id: 'openrouter',
|
||||
name: 'OpenRouter',
|
||||
icon: 'R',
|
||||
placeholder: 'sk-or-v1-...',
|
||||
model: 'Multi-Model',
|
||||
requiresApiKey: true,
|
||||
showModelId: true,
|
||||
modelIdPlaceholder: 'openai/gpt-5.4',
|
||||
defaultModelId: 'openai/gpt-5.4',
|
||||
docsUrl: 'https://openrouter.ai/models',
|
||||
},
|
||||
{
|
||||
id: 'minimax-portal-cn',
|
||||
name: 'MiniMax (CN)',
|
||||
icon: 'M',
|
||||
placeholder: 'sk-...',
|
||||
model: 'MiniMax',
|
||||
requiresApiKey: false,
|
||||
isOAuth: true,
|
||||
supportsApiKey: true,
|
||||
defaultModelId: 'MiniMax-M2.7',
|
||||
showModelId: true,
|
||||
showModelIdInDevModeOnly: true,
|
||||
modelIdPlaceholder: 'MiniMax-M2.7',
|
||||
apiKeyUrl: 'https://platform.minimaxi.com/',
|
||||
},
|
||||
{
|
||||
id: 'moonshot',
|
||||
name: 'Moonshot (CN)',
|
||||
icon: 'K',
|
||||
placeholder: 'sk-...',
|
||||
model: 'Kimi',
|
||||
requiresApiKey: true,
|
||||
defaultBaseUrl: 'https://api.moonshot.cn/v1',
|
||||
defaultModelId: 'kimi-k2.5',
|
||||
docsUrl: 'https://platform.moonshot.cn/',
|
||||
},
|
||||
{
|
||||
id: 'siliconflow',
|
||||
name: 'SiliconFlow (CN)',
|
||||
icon: 'S',
|
||||
placeholder: 'sk-...',
|
||||
model: 'Multi-Model',
|
||||
requiresApiKey: true,
|
||||
defaultBaseUrl: 'https://api.siliconflow.cn/v1',
|
||||
showModelId: true,
|
||||
showModelIdInDevModeOnly: true,
|
||||
modelIdPlaceholder: 'deepseek-ai/DeepSeek-V3',
|
||||
defaultModelId: 'deepseek-ai/DeepSeek-V3',
|
||||
docsUrl: 'https://docs.siliconflow.cn/cn/userguide/introduction',
|
||||
},
|
||||
{
|
||||
id: 'deepseek',
|
||||
name: 'DeepSeek',
|
||||
icon: 'D',
|
||||
placeholder: 'sk-...',
|
||||
model: 'DeepSeek',
|
||||
requiresApiKey: true,
|
||||
defaultBaseUrl: 'https://api.deepseek.com/v1',
|
||||
showModelId: true,
|
||||
modelIdPlaceholder: 'deepseek-chat',
|
||||
defaultModelId: 'deepseek-chat',
|
||||
apiKeyUrl: 'https://platform.deepseek.com/api_keys',
|
||||
docsUrl: 'https://api-docs.deepseek.com/',
|
||||
},
|
||||
{
|
||||
id: 'minimax-portal',
|
||||
name: 'MiniMax (Global)',
|
||||
icon: 'M',
|
||||
placeholder: 'sk-...',
|
||||
model: 'MiniMax',
|
||||
requiresApiKey: false,
|
||||
isOAuth: true,
|
||||
supportsApiKey: true,
|
||||
defaultModelId: 'MiniMax-M2.7',
|
||||
showModelId: true,
|
||||
showModelIdInDevModeOnly: true,
|
||||
modelIdPlaceholder: 'MiniMax-M2.7',
|
||||
apiKeyUrl: 'https://platform.minimax.io',
|
||||
},
|
||||
{
|
||||
id: 'modelstudio',
|
||||
name: 'Model Studio',
|
||||
icon: 'Q',
|
||||
placeholder: 'sk-...',
|
||||
model: 'Qwen',
|
||||
requiresApiKey: true,
|
||||
defaultBaseUrl: 'https://coding.dashscope.aliyuncs.com/v1',
|
||||
showBaseUrl: true,
|
||||
defaultModelId: 'qwen3.5-plus',
|
||||
showModelId: true,
|
||||
showModelIdInDevModeOnly: true,
|
||||
modelIdPlaceholder: 'qwen3.5-plus',
|
||||
apiKeyUrl: 'https://bailian.console.aliyun.com/',
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
id: 'ark',
|
||||
name: 'ByteDance Ark',
|
||||
icon: 'B',
|
||||
placeholder: 'your-ark-api-key',
|
||||
model: 'Doubao',
|
||||
requiresApiKey: true,
|
||||
defaultBaseUrl: 'https://ark.cn-beijing.volces.com/api/v3',
|
||||
showBaseUrl: true,
|
||||
showModelId: true,
|
||||
modelIdPlaceholder: 'ep-20260228000000-xxxxx',
|
||||
docsUrl: 'https://www.volcengine.com/',
|
||||
codePlanPresetBaseUrl: 'https://ark.cn-beijing.volces.com/api/coding/v3',
|
||||
codePlanPresetModelId: 'ark-code-latest',
|
||||
codePlanDocsUrl: 'https://www.volcengine.com/docs/82379/1928261?lang=zh',
|
||||
},
|
||||
{
|
||||
id: 'ollama',
|
||||
name: 'Ollama',
|
||||
icon: 'L',
|
||||
placeholder: 'Not required',
|
||||
requiresApiKey: false,
|
||||
defaultBaseUrl: 'http://localhost:11434/v1',
|
||||
showBaseUrl: true,
|
||||
showModelId: true,
|
||||
modelIdPlaceholder: 'qwen3:latest',
|
||||
},
|
||||
{
|
||||
id: 'custom',
|
||||
name: 'Custom',
|
||||
icon: 'C',
|
||||
placeholder: 'API key...',
|
||||
requiresApiKey: true,
|
||||
showBaseUrl: true,
|
||||
showModelId: true,
|
||||
modelIdPlaceholder: 'your-provider/model-id',
|
||||
docsUrl: 'https://icnnp7d0dymg.feishu.cn/wiki/BmiLwGBcEiloZDkdYnGc8RWnn6d#Ee1ldfvKJoVGvfxc32mcILwenth',
|
||||
docsUrlZh: 'https://icnnp7d0dymg.feishu.cn/wiki/BmiLwGBcEiloZDkdYnGc8RWnn6d#IWQCdfe5fobGU3xf3UGcgbLynGh',
|
||||
},
|
||||
];
|
||||
|
||||
export function getProviderIconUrl(_type: ProviderType | string): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function shouldInvertInDark(_type: ProviderType | string): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
export const SETUP_PROVIDERS = PROVIDER_TYPE_INFO;
|
||||
|
||||
export function getProviderTypeInfo(type: ProviderType): ProviderTypeInfo | undefined {
|
||||
return PROVIDER_TYPE_INFO.find((providerType) => providerType.id === type);
|
||||
}
|
||||
|
||||
export function getProviderDocsUrl(
|
||||
provider: Pick<ProviderTypeInfo, 'docsUrl' | 'docsUrlZh'> | undefined,
|
||||
language: string,
|
||||
): string | undefined {
|
||||
if (!provider?.docsUrl) {
|
||||
return undefined;
|
||||
}
|
||||
if (language.startsWith('zh') && provider.docsUrlZh) {
|
||||
return provider.docsUrlZh;
|
||||
}
|
||||
return provider.docsUrl;
|
||||
}
|
||||
|
||||
export function shouldShowProviderModelId(
|
||||
provider: Pick<ProviderTypeInfo, 'showModelId' | 'showModelIdInDevModeOnly'> | undefined,
|
||||
devModeUnlocked: boolean,
|
||||
): boolean {
|
||||
if (!provider?.showModelId) return false;
|
||||
if (provider.showModelIdInDevModeOnly && !devModeUnlocked) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function resolveProviderModelForSave(
|
||||
provider: Pick<ProviderTypeInfo, 'defaultModelId' | 'showModelId' | 'showModelIdInDevModeOnly'> | undefined,
|
||||
modelId: string,
|
||||
devModeUnlocked: boolean,
|
||||
): string | undefined {
|
||||
if (!shouldShowProviderModelId(provider, devModeUnlocked)) {
|
||||
return undefined;
|
||||
}
|
||||
const trimmedModelId = modelId.trim();
|
||||
return trimmedModelId || provider?.defaultModelId || undefined;
|
||||
}
|
||||
|
||||
export function resolveProviderApiKeyForSave(type: ProviderType | string, apiKey: string): string | undefined {
|
||||
const trimmed = apiKey.trim();
|
||||
if (type === 'ollama') {
|
||||
return trimmed || OLLAMA_PLACEHOLDER_API_KEY;
|
||||
}
|
||||
return trimmed || undefined;
|
||||
}
|
||||
53
runtime-shared/lib/script-types.ts
Normal file
53
runtime-shared/lib/script-types.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
export interface ScriptLastRun {
|
||||
time: string;
|
||||
success: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface AutomationScript {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
filename: string;
|
||||
enabled: boolean;
|
||||
channel: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
code?: string;
|
||||
lastRun?: ScriptLastRun;
|
||||
}
|
||||
|
||||
export interface ScriptSaveInput {
|
||||
id?: string;
|
||||
name: string;
|
||||
description: string;
|
||||
code: string;
|
||||
channel: string;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export interface ScriptExecutionResult {
|
||||
success: boolean;
|
||||
exitCode: number | null;
|
||||
stdoutTail: string;
|
||||
stderrTail: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export type ScriptRecordingStatus = 'idle' | 'recording' | 'stopped';
|
||||
|
||||
export interface ScriptMetaItem {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
filename: string;
|
||||
enabled: boolean;
|
||||
channel: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
lastRun?: ScriptLastRun;
|
||||
}
|
||||
|
||||
export interface ScriptsMeta {
|
||||
scripts: ScriptMetaItem[];
|
||||
}
|
||||
40
runtime-shared/lib/task-types.ts
Normal file
40
runtime-shared/lib/task-types.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
export type SubTaskStatus = 'pending' | 'running' | 'success' | 'failed';
|
||||
|
||||
export interface SubTask {
|
||||
id: string;
|
||||
taskId: string;
|
||||
scriptId: string;
|
||||
name: string;
|
||||
status: SubTaskStatus;
|
||||
progress: number;
|
||||
message: string;
|
||||
stdoutTail: string;
|
||||
stderrTail: string;
|
||||
error?: string;
|
||||
startedAt: string;
|
||||
completedAt?: string;
|
||||
}
|
||||
|
||||
export type TaskStatus = 'pending' | 'running' | 'success' | 'partial_failed' | 'failed';
|
||||
|
||||
export interface Task {
|
||||
id: string;
|
||||
title: string;
|
||||
operation: 'open' | 'close';
|
||||
roomType: string;
|
||||
dateRange: [string, string];
|
||||
status: TaskStatus;
|
||||
subTasks: SubTask[];
|
||||
roomList: any[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface TaskProgressPayload {
|
||||
taskId: string;
|
||||
subTaskId: string;
|
||||
progress?: number;
|
||||
message?: string;
|
||||
stdoutTail?: string;
|
||||
stderrTail?: string;
|
||||
}
|
||||
38
runtime-shared/lib/types.ts
Normal file
38
runtime-shared/lib/types.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { Task } from './task-types';
|
||||
import { CONFIG_KEYS, WINDOW_NAMES } from './constants';
|
||||
|
||||
export type ThemeMode = 'dark' | 'light' | 'system';
|
||||
export type WindowNames = `${WINDOW_NAMES}`;
|
||||
export type ConfigKeys = `${CONFIG_KEYS}`;
|
||||
|
||||
export interface IConfig {
|
||||
[CONFIG_KEYS.THEME_MODE]: ThemeMode;
|
||||
[CONFIG_KEYS.PRIMARY_COLOR]: string;
|
||||
[CONFIG_KEYS.LANGUAGE]: 'zh' | 'en';
|
||||
[CONFIG_KEYS.FONT_SIZE]: number;
|
||||
[CONFIG_KEYS.MINIMIZE_TO_TRAY]: boolean;
|
||||
[CONFIG_KEYS.PROVIDER]?: string;
|
||||
[CONFIG_KEYS.DEFAULT_MODEL]?: string | null;
|
||||
[CONFIG_KEYS.AUTO_CHECK_UPDATE]?: boolean;
|
||||
[CONFIG_KEYS.AUTO_DOWNLOAD_UPDATE]?: boolean;
|
||||
[CONFIG_KEYS.SELECTED_CHANNELS]: Array<{ id: string; channelName: string; channelUrl: string }>;
|
||||
[CONFIG_KEYS.IMAGE_CACHE]: Array<[string, any]>;
|
||||
[CONFIG_KEYS.TASK_LIST]?: Task[];
|
||||
}
|
||||
|
||||
export interface Provider {
|
||||
id: number;
|
||||
name: string;
|
||||
visible?: boolean;
|
||||
title?: string;
|
||||
type?: 'OpenAI';
|
||||
openAISetting?: string;
|
||||
createdAt: number;
|
||||
updatedAt: number;
|
||||
models: string[];
|
||||
}
|
||||
|
||||
export interface OpenAISetting {
|
||||
baseURL?: string;
|
||||
apiKey?: string;
|
||||
}
|
||||
33
runtime-shared/lib/utils.ts
Normal file
33
runtime-shared/lib/utils.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
export function debounce<T extends (...args: any[]) => any>(
|
||||
fn: T,
|
||||
delay: number,
|
||||
): (...args: Parameters<T>) => void {
|
||||
let timer: NodeJS.Timeout | null = null;
|
||||
|
||||
return function debounced(this: unknown, ...args: Parameters<T>) {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
timer = setTimeout(() => {
|
||||
fn.apply(this, args);
|
||||
}, delay);
|
||||
};
|
||||
}
|
||||
|
||||
export function cloneDeep<T>(obj: T): T {
|
||||
if (obj === null || typeof obj !== 'object') {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map((item) => cloneDeep(item)) as T;
|
||||
}
|
||||
|
||||
const clone = Object.assign({}, obj);
|
||||
for (const key in clone) {
|
||||
if (Object.prototype.hasOwnProperty.call(clone, key)) {
|
||||
clone[key] = cloneDeep(clone[key]);
|
||||
}
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
99
runtime-shared/locales/messages.ts
Normal file
99
runtime-shared/locales/messages.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
export type RuntimeMessageTree = {
|
||||
[key: string]: string | number | RuntimeMessageTree;
|
||||
};
|
||||
|
||||
export const runtimeLocaleMessages: Record<'en' | 'zh' | 'ja', RuntimeMessageTree> = {
|
||||
en: {
|
||||
settings: {
|
||||
title: 'Settings',
|
||||
},
|
||||
menu: {
|
||||
conversation: {
|
||||
newConversation: 'New Conversation',
|
||||
sortBy: 'Sort By',
|
||||
sortByCreateTime: 'Sort by Creation Time',
|
||||
sortByUpdateTime: 'Sort by Update Time',
|
||||
sortByName: 'Sort by Name',
|
||||
sortByModel: 'Sort by Model',
|
||||
sortAscending: 'Ascending',
|
||||
sortDescending: 'Descending',
|
||||
pinConversation: 'Pin Conversation',
|
||||
renameConversation: 'Rename Conversation',
|
||||
delConversation: 'Delete Conversation',
|
||||
batchOperations: 'Batch Operations',
|
||||
},
|
||||
message: {
|
||||
copyMessage: 'Copy Message',
|
||||
deleteMessage: 'Delete Message',
|
||||
selectMessage: 'Select Message',
|
||||
},
|
||||
},
|
||||
tray: {
|
||||
tooltip: 'ZN-AI',
|
||||
showWindow: 'Show Window',
|
||||
exit: 'Exit',
|
||||
},
|
||||
},
|
||||
zh: {
|
||||
settings: {
|
||||
title: '\u8bbe\u7f6e',
|
||||
},
|
||||
menu: {
|
||||
conversation: {
|
||||
newConversation: '\u65b0\u5efa\u5bf9\u8bdd',
|
||||
sortBy: '\u6392\u5e8f\u65b9\u5f0f',
|
||||
sortByCreateTime: '\u6309\u521b\u5efa\u65f6\u95f4\u6392\u5e8f',
|
||||
sortByUpdateTime: '\u6309\u66f4\u65b0\u65f6\u95f4\u6392\u5e8f',
|
||||
sortByName: '\u6309\u540d\u79f0\u6392\u5e8f',
|
||||
sortByModel: '\u6309\u6a21\u578b\u6392\u5e8f',
|
||||
sortAscending: '\u9012\u589e',
|
||||
sortDescending: '\u9012\u51cf',
|
||||
pinConversation: '\u7f6e\u9876\u5bf9\u8bdd',
|
||||
renameConversation: '\u91cd\u547d\u540d\u5bf9\u8bdd',
|
||||
delConversation: '\u5220\u9664\u5bf9\u8bdd',
|
||||
batchOperations: '\u6279\u91cf\u64cd\u4f5c',
|
||||
},
|
||||
message: {
|
||||
copyMessage: '\u590d\u5236\u6d88\u606f',
|
||||
deleteMessage: '\u5220\u9664\u6d88\u606f',
|
||||
selectMessage: '\u9009\u62e9\u6d88\u606f',
|
||||
},
|
||||
},
|
||||
tray: {
|
||||
tooltip: 'ZN-AI',
|
||||
showWindow: '\u663e\u793a\u7a97\u53e3',
|
||||
exit: '\u9000\u51fa',
|
||||
},
|
||||
},
|
||||
ja: {
|
||||
settings: {
|
||||
title: '\u8a2d\u5b9a',
|
||||
},
|
||||
menu: {
|
||||
conversation: {
|
||||
newConversation: '\u65b0\u3057\u3044\u4f1a\u8a71',
|
||||
sortBy: '\u4e26\u3079\u66ff\u3048',
|
||||
sortByCreateTime: '\u4f5c\u6210\u65e5\u6642\u3067\u4e26\u3079\u66ff\u3048',
|
||||
sortByUpdateTime: '\u66f4\u65b0\u65e5\u6642\u3067\u4e26\u3079\u66ff\u3048',
|
||||
sortByName: '\u540d\u524d\u3067\u4e26\u3079\u66ff\u3048',
|
||||
sortByModel: '\u30e2\u30c7\u30eb\u3067\u4e26\u3079\u66ff\u3048',
|
||||
sortAscending: '\u6607\u9806',
|
||||
sortDescending: '\u964d\u9806',
|
||||
pinConversation: '\u4f1a\u8a71\u3092\u30d4\u30f3\u7559\u3081',
|
||||
renameConversation: '\u4f1a\u8a71\u540d\u3092\u5909\u66f4',
|
||||
delConversation: '\u4f1a\u8a71\u3092\u524a\u9664',
|
||||
batchOperations: '\u4e00\u62ec\u64cd\u4f5c',
|
||||
},
|
||||
message: {
|
||||
copyMessage: '\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u30b3\u30d4\u30fc',
|
||||
deleteMessage: '\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u524a\u9664',
|
||||
selectMessage: '\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u9078\u629e',
|
||||
},
|
||||
},
|
||||
tray: {
|
||||
tooltip: 'ZN-AI',
|
||||
showWindow: '\u30a6\u30a3\u30f3\u30c9\u30a6\u3092\u8868\u793a',
|
||||
exit: '\u7d42\u4e86',
|
||||
},
|
||||
},
|
||||
};
|
||||
173
runtime-shared/shared/chat-model.ts
Normal file
173
runtime-shared/shared/chat-model.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
export interface AttachedFileMeta {
|
||||
fileName: string;
|
||||
mimeType: string;
|
||||
fileSize: number;
|
||||
preview: string | null;
|
||||
filePath?: string;
|
||||
source?: 'user-upload' | 'tool-result' | 'message-ref';
|
||||
}
|
||||
|
||||
export interface ContentBlockSource {
|
||||
type: string;
|
||||
media_type?: string;
|
||||
data?: string;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export interface ContentBlock {
|
||||
type: 'text' | 'image' | 'thinking' | 'tool_use' | 'tool_result' | 'toolCall' | 'toolResult';
|
||||
text?: string;
|
||||
thinking?: string;
|
||||
source?: ContentBlockSource;
|
||||
data?: string;
|
||||
mimeType?: string;
|
||||
id?: string;
|
||||
name?: string;
|
||||
input?: unknown;
|
||||
arguments?: unknown;
|
||||
content?: string | ContentBlock[];
|
||||
}
|
||||
|
||||
export type RawMessageRole = 'user' | 'assistant' | 'system' | 'toolresult' | 'tool_result';
|
||||
|
||||
export interface RawMessage {
|
||||
role: RawMessageRole;
|
||||
content: string | ContentBlock[];
|
||||
timestamp?: number;
|
||||
id?: string;
|
||||
toolCallId?: string;
|
||||
toolName?: string;
|
||||
details?: unknown;
|
||||
isError?: boolean;
|
||||
question?: string[];
|
||||
toolCall?: Record<string, unknown> | null;
|
||||
_attachedFiles?: AttachedFileMeta[];
|
||||
}
|
||||
|
||||
export interface ToolStatus {
|
||||
id?: string;
|
||||
toolCallId?: string;
|
||||
name: string;
|
||||
status: 'running' | 'completed' | 'error';
|
||||
durationMs?: number;
|
||||
summary?: string;
|
||||
updatedAt: number;
|
||||
}
|
||||
|
||||
export interface ChatSession {
|
||||
key: string;
|
||||
label?: string;
|
||||
displayName?: string;
|
||||
thinkingLevel?: string;
|
||||
model?: string;
|
||||
updatedAt?: number;
|
||||
}
|
||||
|
||||
export function extractText(message?: RawMessage | null): string {
|
||||
if (!message) return '';
|
||||
|
||||
if (typeof message.content === 'string') {
|
||||
return message.content;
|
||||
}
|
||||
|
||||
return message.content
|
||||
.filter((block) => block.type === 'text' && typeof block.text === 'string')
|
||||
.map((block) => block.text ?? '')
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
export function extractThinking(message?: RawMessage | null): string | null {
|
||||
if (!message || !Array.isArray(message.content)) return null;
|
||||
|
||||
const thinkingBlock = message.content.find((block) => block.type === 'thinking');
|
||||
return thinkingBlock?.thinking ?? null;
|
||||
}
|
||||
|
||||
export function extractImages(message?: RawMessage | null): Array<{ url?: string; data?: string; mimeType: string }> {
|
||||
if (!message || !Array.isArray(message.content)) return [];
|
||||
|
||||
const images: Array<{ url?: string; data?: string; mimeType: string }> = [];
|
||||
|
||||
for (const block of message.content) {
|
||||
if (block.type === 'image') {
|
||||
if (block.source?.type === 'base64' && block.source.data) {
|
||||
images.push({
|
||||
data: block.source.data,
|
||||
mimeType: block.source.media_type || 'image/jpeg',
|
||||
});
|
||||
} else if (block.source?.type === 'url' && block.source.url) {
|
||||
images.push({
|
||||
url: block.source.url,
|
||||
mimeType: block.source.media_type || 'image/jpeg',
|
||||
});
|
||||
} else if (block.data) {
|
||||
images.push({
|
||||
data: block.data,
|
||||
mimeType: block.mimeType || 'image/jpeg',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if ((block.type === 'tool_result' || block.type === 'toolResult') && Array.isArray(block.content)) {
|
||||
images.push(...extractImages({ role: 'toolresult', content: block.content }));
|
||||
}
|
||||
}
|
||||
|
||||
return images;
|
||||
}
|
||||
|
||||
export function extractToolUse(message?: RawMessage | null): Array<{ id?: string; name: string; input?: unknown }> {
|
||||
if (!message || !Array.isArray(message.content)) return [];
|
||||
|
||||
return message.content
|
||||
.filter((block) => block.type === 'tool_use' || block.type === 'toolCall')
|
||||
.map((block) => ({
|
||||
id: block.id,
|
||||
name: block.name || block.id || 'tool',
|
||||
input: block.input ?? block.arguments,
|
||||
}));
|
||||
}
|
||||
|
||||
export function formatTimestamp(ts?: number): string {
|
||||
if (!ts) return '';
|
||||
|
||||
const ms = ts < 1e12 ? ts * 1000 : ts;
|
||||
return new Date(ms).toLocaleString();
|
||||
}
|
||||
|
||||
export function isToolResultRole(role?: string): boolean {
|
||||
if (!role) return false;
|
||||
|
||||
const normalized = role.toLowerCase();
|
||||
return normalized === 'toolresult' || normalized === 'tool_result';
|
||||
}
|
||||
|
||||
export function isToolOnlyMessage(message?: RawMessage): boolean {
|
||||
if (!message) return false;
|
||||
if (isToolResultRole(message.role)) return true;
|
||||
if (!Array.isArray(message.content)) return false;
|
||||
|
||||
const hasToolBlock = message.content.some((block) =>
|
||||
['tool_use', 'tool_result', 'toolCall', 'toolResult'].includes(block.type),
|
||||
);
|
||||
const hasTextBlock = message.content.some((block) => block.type === 'text' && block.text?.trim());
|
||||
const hasImageBlock = message.content.some((block) => block.type === 'image');
|
||||
|
||||
return hasToolBlock && !hasTextBlock && !hasImageBlock;
|
||||
}
|
||||
|
||||
export function isInternalMessage(message: { role?: string; content?: unknown }): boolean {
|
||||
if (message.role === 'system') return true;
|
||||
|
||||
if (message.role === 'assistant') {
|
||||
const text = typeof message.content === 'string'
|
||||
? message.content
|
||||
: extractText(message as RawMessage);
|
||||
|
||||
if (/^(HEARTBEAT_OK|NO_REPLY)\s*$/.test(text)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
Reference in New Issue
Block a user