refactor: reorganize imports and update type references across services
- Updated import paths for types and utilities in config-service, menu-service, script-execution-service, script-store-service, and window-service to reflect new structure. - Moved utility functions like debounce and cloneDeep to shared utilities. - Created new runtime and script types files to centralize type definitions. - Removed deprecated task-types and locale messages files. - Updated constants to use shared definitions and added new placeholders for providers. - Enhanced provider type information with additional placeholders for different languages.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
require("electron");
|
||||
require("./main-BynUR3CU.js");
|
||||
require("./main-TNsjwKgR.js");
|
||||
require("electron-squirrel-startup");
|
||||
require("electron-log");
|
||||
require("bytenode");
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use strict";
|
||||
const electron = require("electron");
|
||||
var IPC_EVENTS = /* @__PURE__ */ ((IPC_EVENTS2) => {
|
||||
IPC_EVENTS2["HOST_API_FETCH"] = "hostapi:fetch";
|
||||
IPC_EVENTS2["EXTERNAL_OPEN"] = "external-open";
|
||||
IPC_EVENTS2["APP_SET_FRAMELESS"] = "app:set-frameless";
|
||||
IPC_EVENTS2["APP_LOAD_PAGE"] = "app:load-page";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ConfigKeys, IConfig } from '@runtime/lib/types';
|
||||
import type { ConfigKeys, IConfig } from '@electron/types/runtime';
|
||||
import configManager from '@electron/service/config-service';
|
||||
import type { HostApiResult } from '@src/types/runtime';
|
||||
import type { HostApiContext } from '../context';
|
||||
|
||||
99
electron/locales/messages.ts
Normal file
99
electron/locales/messages.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
export type RuntimeMessageTree = {
|
||||
[key: string]: string | number | RuntimeMessageTree;
|
||||
};
|
||||
|
||||
export const runtimeLocaleMessages: Record<'en' | 'zh' | 'th', 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: '设置',
|
||||
},
|
||||
menu: {
|
||||
conversation: {
|
||||
newConversation: '新建对话',
|
||||
sortBy: '排序方式',
|
||||
sortByCreateTime: '按创建时间排序',
|
||||
sortByUpdateTime: '按更新时间排序',
|
||||
sortByName: '按名称排序',
|
||||
sortByModel: '按模型排序',
|
||||
sortAscending: '递增',
|
||||
sortDescending: '递减',
|
||||
pinConversation: '置顶对话',
|
||||
renameConversation: '重命名对话',
|
||||
delConversation: '删除对话',
|
||||
batchOperations: '批量操作',
|
||||
},
|
||||
message: {
|
||||
copyMessage: '复制消息',
|
||||
deleteMessage: '删除消息',
|
||||
selectMessage: '选择消息',
|
||||
},
|
||||
},
|
||||
tray: {
|
||||
tooltip: 'ZN-AI',
|
||||
showWindow: '显示窗口',
|
||||
exit: '退出',
|
||||
},
|
||||
},
|
||||
th: {
|
||||
settings: {
|
||||
title: 'ตั้งค่า',
|
||||
},
|
||||
menu: {
|
||||
conversation: {
|
||||
newConversation: 'การสนทนาใหม่',
|
||||
sortBy: 'จัดเรียงตาม',
|
||||
sortByCreateTime: 'จัดเรียงตามเวลาสร้าง',
|
||||
sortByUpdateTime: 'จัดเรียงตามเวลาอัปเดต',
|
||||
sortByName: 'จัดเรียงตามชื่อ',
|
||||
sortByModel: 'จัดเรียงตามโมเดล',
|
||||
sortAscending: 'เรียงจากน้อยไปมาก',
|
||||
sortDescending: 'เรียงจากมากไปน้อย',
|
||||
pinConversation: 'ปักหมุดการสนทนา',
|
||||
renameConversation: 'เปลี่ยนชื่อการสนทนา',
|
||||
delConversation: 'ลบการสนทนา',
|
||||
batchOperations: 'ดำเนินการแบบกลุ่ม',
|
||||
},
|
||||
message: {
|
||||
copyMessage: 'คัดลอกข้อความ',
|
||||
deleteMessage: 'ลบข้อความ',
|
||||
selectMessage: 'เลือกข้อความ',
|
||||
},
|
||||
},
|
||||
tray: {
|
||||
tooltip: 'ZN-AI',
|
||||
showWindow: 'แสดงหน้าต่าง',
|
||||
exit: 'ออก',
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import { app, BrowserWindow, ipcMain } from 'electron'
|
||||
import { CONFIG_KEYS } from '@runtime/lib/constants'
|
||||
import { CONFIG_KEYS, IPC_EVENTS } from '@runtime/lib/constants'
|
||||
import { setupMainWindow } from './wins';
|
||||
import started from 'electron-squirrel-startup'
|
||||
import configManager from '@electron/service/config-service'
|
||||
@@ -75,7 +75,7 @@ async function requestUpstreamHostApi(path: string, method: string, headers: Rec
|
||||
}
|
||||
}
|
||||
|
||||
ipcMain.handle('hostapi:fetch', async (_event, { path, method, headers, body }) => {
|
||||
ipcMain.handle(IPC_EVENTS.HOST_API_FETCH, async (_event, { path, method, headers, body }) => {
|
||||
const normalizedMethod = method || 'GET';
|
||||
|
||||
// 1. 优先本地处理 Host API 路由(逐步对齐 ClawX)
|
||||
@@ -92,7 +92,7 @@ ipcMain.handle('hostapi:fetch', async (_event, { path, method, headers, body })
|
||||
});
|
||||
|
||||
// Gateway RPC IPC handler
|
||||
ipcMain.handle('gateway:rpc', async (_event, method: string, params: any) => {
|
||||
ipcMain.handle(IPC_EVENTS.GATEWAY_RPC, async (_event, method: string, params: any) => {
|
||||
return gatewayManager.rpc(method, params);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { BrowserWindow, ipcMain } from 'electron'
|
||||
import type { ConfigKeys, IConfig } from '@runtime/lib/types'
|
||||
import { CONFIG_KEYS, IPC_EVENTS } from '@runtime/lib/constants'
|
||||
import { debounce } from '@runtime/lib/utils'
|
||||
|
||||
import logManager from '@electron/service/logger'
|
||||
import type { ConfigKeys, IConfig } from '@electron/types/runtime'
|
||||
import { getUserDataDir } from '@electron/utils/paths'
|
||||
import { debounce } from '@electron/utils/shared'
|
||||
|
||||
type AppConfig = IConfig & {
|
||||
[CONFIG_KEYS.GATEWAY_AUTO_START]: boolean;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ipcMain, Menu, type MenuItemConstructorOptions } from 'electron';
|
||||
import { CONFIG_KEYS, IPC_EVENTS } from '@runtime/lib/constants';
|
||||
import { cloneDeep } from '@runtime/lib/utils';
|
||||
import { createTranslator } from '@electron/utils'
|
||||
import { cloneDeep } from '@electron/utils/shared';
|
||||
import logManager from '@electron/service/logger'
|
||||
import configManager from '@electron/service/config-service'
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
getScriptPathById,
|
||||
updateLastRun,
|
||||
} from '@electron/service/script-store-service';
|
||||
import type { ScriptExecutionResult } from '@runtime/lib/script-types';
|
||||
import type { ScriptExecutionResult } from '../../types/script-types';
|
||||
|
||||
const executor = new executeScriptService();
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import type {
|
||||
ScriptMetaItem,
|
||||
ScriptsMeta,
|
||||
ScriptSaveInput,
|
||||
} from '@runtime/lib/script-types';
|
||||
} from '../../types/script-types';
|
||||
|
||||
const META_FILENAME = 'scripts.meta.json';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { WindowNames } from '@runtime/lib/types'
|
||||
import type { WindowNames } from '@electron/types/runtime'
|
||||
|
||||
import { CONFIG_KEYS, IPC_EVENTS, WINDOW_NAMES } from '@runtime/lib/constants'
|
||||
import { app, BrowserWindow, BrowserWindowConstructorOptions, ipcMain, IpcMainInvokeEvent, type IpcMainEvent } from 'electron'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Task } from './task-types';
|
||||
import { CONFIG_KEYS, WINDOW_NAMES } from './constants';
|
||||
import type { Task } from '@src/lib/task-types';
|
||||
import { CONFIG_KEYS, WINDOW_NAMES } from '@runtime/lib/constants';
|
||||
|
||||
export type ThemeMode = 'dark' | 'light' | 'system';
|
||||
export type WindowNames = `${WINDOW_NAMES}`;
|
||||
@@ -8,7 +8,7 @@ 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.LANGUAGE]: 'zh' | 'en' | 'th';
|
||||
[CONFIG_KEYS.FONT_SIZE]: number;
|
||||
[CONFIG_KEYS.MINIMIZE_TO_TRAY]: boolean;
|
||||
[CONFIG_KEYS.LAUNCH_AT_STARTUP]: boolean;
|
||||
@@ -3,7 +3,7 @@ import logManager from '@electron/service/logger'
|
||||
import configManager from '@electron/service/config-service'
|
||||
import path from 'node:path'
|
||||
import { app } from 'electron'
|
||||
import { runtimeLocaleMessages, type RuntimeMessageTree } from '@runtime/locales/messages'
|
||||
import { runtimeLocaleMessages, type RuntimeMessageTree } from '@electron/locales/messages'
|
||||
|
||||
const messages: Record<string, RuntimeMessageTree> = runtimeLocaleMessages
|
||||
|
||||
|
||||
11
global.d.ts
vendored
11
global.d.ts
vendored
@@ -1,8 +1,9 @@
|
||||
import { IPC_EVENTS } from '@runtime/lib/constants'
|
||||
import type { ConfigKeys } from '@runtime/lib/types'
|
||||
import type { AutomationScript, ScriptSaveInput, ScriptExecutionResult } from '@runtime/lib/script-types'
|
||||
import { CONFIG_KEYS, IPC_EVENTS } from '@runtime/lib/constants'
|
||||
import type { AutomationScript, ScriptSaveInput, ScriptExecutionResult } from '@src/lib/script-types'
|
||||
|
||||
declare global {
|
||||
type ConfigKeys = `${CONFIG_KEYS}`
|
||||
|
||||
// 定义每个通道的参数和返回值类型
|
||||
interface IPCTypings {
|
||||
// 同步通信
|
||||
@@ -42,6 +43,10 @@ declare global {
|
||||
params: [event: any]
|
||||
return: void
|
||||
}
|
||||
[IPC_EVENTS.HOST_API_FETCH]: {
|
||||
params: [request: { path: string; method?: string; headers?: Record<string, string>; body?: unknown }]
|
||||
return: Promise<unknown>
|
||||
}
|
||||
|
||||
// 任务事件
|
||||
[IPC_EVENTS.TASK_PROGRESS]: { params: [payload: any]; return: void; }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export enum IPC_EVENTS {
|
||||
HOST_API_FETCH = 'hostapi:fetch',
|
||||
EXTERNAL_OPEN = 'external-open',
|
||||
APP_SET_FRAMELESS = 'app:set-frameless',
|
||||
APP_LOAD_PAGE = 'app:load-page',
|
||||
@@ -120,3 +121,7 @@ export enum MESSAGE_ITEM_MENU_IDS {
|
||||
DELETE = 'delete',
|
||||
SELECT = 'select',
|
||||
}
|
||||
|
||||
export const DEFAULT_THEME_MODE = 'system' as const;
|
||||
|
||||
export const DEFAULT_LANGUAGE = 'zh' as const;
|
||||
|
||||
@@ -2,9 +2,13 @@ import {
|
||||
DEFAULT_AGENT_ID,
|
||||
DEFAULT_CHANNEL_ACCOUNT_ID,
|
||||
DEFAULT_MAIN_SESSION_SUFFIX,
|
||||
buildAgentSessionKey,
|
||||
buildMainSessionKey,
|
||||
buildChannelAccountOwnerKey,
|
||||
normalizeAgentId,
|
||||
normalizeChannelAccountId,
|
||||
normalizeChannelType,
|
||||
normalizeSessionSuffix,
|
||||
parseChannelAccountOwnerKey,
|
||||
resolveChannelAccountOwner,
|
||||
type AgentSummary,
|
||||
@@ -29,27 +33,6 @@ export interface ParsedSessionKey {
|
||||
isAgentSession: boolean;
|
||||
}
|
||||
|
||||
export function normalizeAgentId(value: string | null | undefined): string {
|
||||
const normalized = String(value ?? '').trim().toLowerCase();
|
||||
return normalized || DEFAULT_AGENT_ID;
|
||||
}
|
||||
|
||||
export function normalizeSessionSuffix(value: string | null | undefined): string {
|
||||
const normalized = String(value ?? '').trim().toLowerCase();
|
||||
return normalized || DEFAULT_MAIN_SESSION_SUFFIX;
|
||||
}
|
||||
|
||||
export function buildAgentSessionKey(agentId: string, sessionId: string): string {
|
||||
return `agent:${normalizeAgentId(agentId)}:${normalizeSessionSuffix(sessionId)}`;
|
||||
}
|
||||
|
||||
export function buildMainSessionKey(
|
||||
agentId: string,
|
||||
sessionId = DEFAULT_MAIN_SESSION_SUFFIX,
|
||||
): string {
|
||||
return buildAgentSessionKey(agentId, sessionId);
|
||||
}
|
||||
|
||||
export function parseSessionKey(sessionKey: string): ParsedSessionKey {
|
||||
const trimmed = String(sessionKey ?? '').trim();
|
||||
|
||||
@@ -97,9 +80,13 @@ export {
|
||||
DEFAULT_AGENT_ID,
|
||||
DEFAULT_CHANNEL_ACCOUNT_ID,
|
||||
DEFAULT_MAIN_SESSION_SUFFIX,
|
||||
buildAgentSessionKey,
|
||||
buildMainSessionKey,
|
||||
buildChannelAccountOwnerKey,
|
||||
normalizeAgentId,
|
||||
normalizeChannelAccountId,
|
||||
normalizeChannelType,
|
||||
normalizeSessionSuffix,
|
||||
parseChannelAccountOwnerKey,
|
||||
resolveChannelAccountOwner,
|
||||
};
|
||||
|
||||
@@ -58,6 +58,8 @@ export interface ProviderTypeInfo {
|
||||
name: string;
|
||||
icon: string;
|
||||
placeholder: string;
|
||||
placeholderZh?: string;
|
||||
placeholderTh?: string;
|
||||
model?: string;
|
||||
requiresApiKey: boolean;
|
||||
defaultBaseUrl?: string;
|
||||
@@ -124,7 +126,7 @@ export const PROVIDER_TYPE_INFO: ProviderTypeInfo[] = [
|
||||
{
|
||||
id: 'anthropic',
|
||||
name: 'Anthropic',
|
||||
icon: 'A',
|
||||
icon: '🤖',
|
||||
placeholder: 'sk-ant-api03-...',
|
||||
model: 'Claude',
|
||||
requiresApiKey: true,
|
||||
@@ -133,7 +135,7 @@ export const PROVIDER_TYPE_INFO: ProviderTypeInfo[] = [
|
||||
{
|
||||
id: 'openai',
|
||||
name: 'OpenAI',
|
||||
icon: 'O',
|
||||
icon: '💚',
|
||||
placeholder: 'sk-proj-...',
|
||||
model: 'GPT',
|
||||
requiresApiKey: true,
|
||||
@@ -148,7 +150,7 @@ export const PROVIDER_TYPE_INFO: ProviderTypeInfo[] = [
|
||||
{
|
||||
id: 'google',
|
||||
name: 'Google',
|
||||
icon: 'G',
|
||||
icon: '🔷',
|
||||
placeholder: 'AIza...',
|
||||
model: 'Gemini',
|
||||
requiresApiKey: true,
|
||||
@@ -163,7 +165,7 @@ export const PROVIDER_TYPE_INFO: ProviderTypeInfo[] = [
|
||||
{
|
||||
id: 'openrouter',
|
||||
name: 'OpenRouter',
|
||||
icon: 'R',
|
||||
icon: '🌐',
|
||||
placeholder: 'sk-or-v1-...',
|
||||
model: 'Multi-Model',
|
||||
requiresApiKey: true,
|
||||
@@ -175,7 +177,7 @@ export const PROVIDER_TYPE_INFO: ProviderTypeInfo[] = [
|
||||
{
|
||||
id: 'minimax-portal-cn',
|
||||
name: 'MiniMax (CN)',
|
||||
icon: 'M',
|
||||
icon: '☁️',
|
||||
placeholder: 'sk-...',
|
||||
model: 'MiniMax',
|
||||
requiresApiKey: false,
|
||||
@@ -192,7 +194,7 @@ export const PROVIDER_TYPE_INFO: ProviderTypeInfo[] = [
|
||||
{
|
||||
id: 'moonshot',
|
||||
name: 'Moonshot (CN)',
|
||||
icon: 'K',
|
||||
icon: '🌙',
|
||||
placeholder: 'sk-...',
|
||||
model: 'Kimi',
|
||||
requiresApiKey: true,
|
||||
@@ -203,7 +205,7 @@ export const PROVIDER_TYPE_INFO: ProviderTypeInfo[] = [
|
||||
{
|
||||
id: 'siliconflow',
|
||||
name: 'SiliconFlow (CN)',
|
||||
icon: 'S',
|
||||
icon: '🌊',
|
||||
placeholder: 'sk-...',
|
||||
model: 'Multi-Model',
|
||||
requiresApiKey: true,
|
||||
@@ -217,7 +219,7 @@ export const PROVIDER_TYPE_INFO: ProviderTypeInfo[] = [
|
||||
{
|
||||
id: 'deepseek',
|
||||
name: 'DeepSeek',
|
||||
icon: 'D',
|
||||
icon: '🐋',
|
||||
placeholder: 'sk-...',
|
||||
model: 'DeepSeek',
|
||||
requiresApiKey: true,
|
||||
@@ -231,7 +233,7 @@ export const PROVIDER_TYPE_INFO: ProviderTypeInfo[] = [
|
||||
{
|
||||
id: 'minimax-portal',
|
||||
name: 'MiniMax (Global)',
|
||||
icon: 'M',
|
||||
icon: '☁️',
|
||||
placeholder: 'sk-...',
|
||||
model: 'MiniMax',
|
||||
requiresApiKey: false,
|
||||
@@ -248,7 +250,7 @@ export const PROVIDER_TYPE_INFO: ProviderTypeInfo[] = [
|
||||
{
|
||||
id: 'modelstudio',
|
||||
name: 'Model Studio',
|
||||
icon: 'Q',
|
||||
icon: '☁️',
|
||||
placeholder: 'sk-...',
|
||||
model: 'Qwen',
|
||||
requiresApiKey: true,
|
||||
@@ -264,7 +266,7 @@ export const PROVIDER_TYPE_INFO: ProviderTypeInfo[] = [
|
||||
{
|
||||
id: 'ark',
|
||||
name: 'ByteDance Ark',
|
||||
icon: 'B',
|
||||
icon: 'A',
|
||||
placeholder: 'your-ark-api-key',
|
||||
model: 'Doubao',
|
||||
requiresApiKey: true,
|
||||
@@ -280,8 +282,10 @@ export const PROVIDER_TYPE_INFO: ProviderTypeInfo[] = [
|
||||
{
|
||||
id: 'ollama',
|
||||
name: 'Ollama',
|
||||
icon: 'L',
|
||||
icon: '🦙',
|
||||
placeholder: 'Not required',
|
||||
placeholderZh: '无需填写',
|
||||
placeholderTh: 'ไม่ต้องกรอก',
|
||||
requiresApiKey: false,
|
||||
defaultBaseUrl: 'http://localhost:11434/v1',
|
||||
showBaseUrl: true,
|
||||
@@ -291,8 +295,10 @@ export const PROVIDER_TYPE_INFO: ProviderTypeInfo[] = [
|
||||
{
|
||||
id: 'custom',
|
||||
name: 'Custom',
|
||||
icon: 'C',
|
||||
icon: '⚙️',
|
||||
placeholder: 'API key...',
|
||||
placeholderZh: 'API Key...',
|
||||
placeholderTh: 'API Key...',
|
||||
requiresApiKey: true,
|
||||
showBaseUrl: true,
|
||||
showModelId: true,
|
||||
@@ -329,6 +335,22 @@ export function getProviderDocsUrl(
|
||||
return provider.docsUrl;
|
||||
}
|
||||
|
||||
export function getProviderPlaceholder(
|
||||
provider: Pick<ProviderTypeInfo, 'placeholder' | 'placeholderZh' | 'placeholderTh'> | undefined,
|
||||
language: string,
|
||||
): string | undefined {
|
||||
if (!provider?.placeholder) {
|
||||
return undefined;
|
||||
}
|
||||
if (language.startsWith('zh') && provider.placeholderZh) {
|
||||
return provider.placeholderZh;
|
||||
}
|
||||
if (language.startsWith('th') && provider.placeholderTh) {
|
||||
return provider.placeholderTh;
|
||||
}
|
||||
return provider.placeholder;
|
||||
}
|
||||
|
||||
export function shouldShowProviderModelId(
|
||||
provider: Pick<ProviderTypeInfo, 'showModelId' | 'showModelIdInDevModeOnly'> | undefined,
|
||||
devModeUnlocked: boolean,
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
export type RuntimeMessageTree = {
|
||||
[key: string]: string | number | RuntimeMessageTree;
|
||||
};
|
||||
|
||||
export const runtimeLocaleMessages: Record<'en' | 'zh' | 'th', 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',
|
||||
},
|
||||
},
|
||||
th: {
|
||||
settings: {
|
||||
title: '\u0e15\u0e31\u0e49\u0e07\u0e04\u0e48\u0e32',
|
||||
},
|
||||
menu: {
|
||||
conversation: {
|
||||
newConversation: '\u0e01\u0e32\u0e23\u0e2a\u0e19\u0e17\u0e19\u0e32\u0e43\u0e2b\u0e21\u0e48',
|
||||
sortBy: '\u0e08\u0e31\u0e14\u0e40\u0e23\u0e35\u0e22\u0e07\u0e15\u0e32\u0e21',
|
||||
sortByCreateTime: '\u0e08\u0e31\u0e14\u0e40\u0e23\u0e35\u0e22\u0e07\u0e15\u0e32\u0e21\u0e40\u0e27\u0e25\u0e32\u0e2a\u0e23\u0e49\u0e32\u0e07',
|
||||
sortByUpdateTime: '\u0e08\u0e31\u0e14\u0e40\u0e23\u0e35\u0e22\u0e07\u0e15\u0e32\u0e21\u0e40\u0e27\u0e25\u0e32\u0e2d\u0e31\u0e1b\u0e40\u0e14\u0e15',
|
||||
sortByName: '\u0e08\u0e31\u0e14\u0e40\u0e23\u0e35\u0e22\u0e07\u0e15\u0e32\u0e21\u0e0a\u0e37\u0e48\u0e2d',
|
||||
sortByModel: '\u0e08\u0e31\u0e14\u0e40\u0e23\u0e35\u0e22\u0e07\u0e15\u0e32\u0e21\u0e42\u0e21\u0e40\u0e14\u0e25',
|
||||
sortAscending: '\u0e40\u0e23\u0e35\u0e22\u0e07\u0e08\u0e32\u0e01\u0e19\u0e49\u0e2d\u0e22\u0e44\u0e1b\u0e21\u0e32\u0e01',
|
||||
sortDescending: '\u0e40\u0e23\u0e35\u0e22\u0e07\u0e08\u0e32\u0e01\u0e21\u0e32\u0e01\u0e44\u0e1b\u0e19\u0e49\u0e2d\u0e22',
|
||||
pinConversation: '\u0e1b\u0e31\u0e01\u0e2b\u0e21\u0e38\u0e14\u0e01\u0e32\u0e23\u0e2a\u0e19\u0e17\u0e19\u0e32',
|
||||
renameConversation: '\u0e40\u0e1b\u0e25\u0e35\u0e48\u0e22\u0e19\u0e0a\u0e37\u0e48\u0e2d\u0e01\u0e32\u0e23\u0e2a\u0e19\u0e17\u0e19\u0e32',
|
||||
delConversation: '\u0e25\u0e1a\u0e01\u0e32\u0e23\u0e2a\u0e19\u0e17\u0e19\u0e32',
|
||||
batchOperations: '\u0e14\u0e33\u0e40\u0e19\u0e34\u0e19\u0e01\u0e32\u0e23\u0e41\u0e1a\u0e1a\u0e01\u0e25\u0e38\u0e48\u0e21',
|
||||
},
|
||||
message: {
|
||||
copyMessage: '\u0e04\u0e31\u0e14\u0e25\u0e2d\u0e01\u0e02\u0e49\u0e2d\u0e04\u0e27\u0e32\u0e21',
|
||||
deleteMessage: '\u0e25\u0e1a\u0e02\u0e49\u0e2d\u0e04\u0e27\u0e32\u0e21',
|
||||
selectMessage: '\u0e40\u0e25\u0e37\u0e2d\u0e01\u0e02\u0e49\u0e2d\u0e04\u0e27\u0e32\u0e21',
|
||||
},
|
||||
},
|
||||
tray: {
|
||||
tooltip: 'ZN-AI',
|
||||
showWindow: '\u0e41\u0e2a\u0e14\u0e07\u0e2b\u0e19\u0e49\u0e32\u0e15\u0e48\u0e32\u0e07',
|
||||
exit: '\u0e2d\u0e2d\u0e01',
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -1,30 +1,7 @@
|
||||
import { CONFIG_KEYS as RUNTIME_CONFIG_KEYS } from '../types/runtime';
|
||||
|
||||
export const IPC_EVENTS = {
|
||||
HOST_API_FETCH: 'hostapi:fetch',
|
||||
GATEWAY_RPC: 'gateway:rpc',
|
||||
GATEWAY_EVENT: 'gateway:event',
|
||||
GET_CONFIG: 'get-config',
|
||||
SET_CONFIG: 'set-config',
|
||||
GET_THEME_MODE: 'get-theme-mode',
|
||||
SET_THEME_MODE: 'set-theme-mode',
|
||||
THEME_MODE_UPDATED: 'theme-mode-updated',
|
||||
TASK_PROGRESS: 'task:progress',
|
||||
TASK_STARTED: 'task:started',
|
||||
TASK_COMPLETED: 'task:completed',
|
||||
OPEN_CHANNEL: 'open-channel',
|
||||
EXECUTE_SCRIPT: 'execute-script',
|
||||
} as const;
|
||||
|
||||
export const CONFIG_KEYS = RUNTIME_CONFIG_KEYS;
|
||||
|
||||
export const WINDOW_NAMES = {
|
||||
MAIN: 'main',
|
||||
SETTING: 'setting',
|
||||
DIALOG: 'dialog',
|
||||
LOADING: 'loading',
|
||||
} as const;
|
||||
|
||||
export const DEFAULT_THEME_MODE = 'system' as const;
|
||||
|
||||
export const DEFAULT_LANGUAGE = 'zh' as const;
|
||||
export {
|
||||
CONFIG_KEYS,
|
||||
DEFAULT_LANGUAGE,
|
||||
DEFAULT_THEME_MODE,
|
||||
IPC_EVENTS,
|
||||
WINDOW_NAMES,
|
||||
} from '../../runtime-shared/lib/constants';
|
||||
|
||||
@@ -1,261 +1 @@
|
||||
import type { LanguageCode } from '../types/runtime';
|
||||
|
||||
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;
|
||||
placeholderZh?: string;
|
||||
placeholderTh?: 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: '🤖',
|
||||
placeholder: 'sk-ant-api03-...',
|
||||
model: 'Claude',
|
||||
requiresApiKey: true,
|
||||
docsUrl: 'https://platform.claude.com/docs/en/api/overview',
|
||||
},
|
||||
{
|
||||
id: 'openai',
|
||||
name: 'OpenAI',
|
||||
icon: '💚',
|
||||
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: '🔷',
|
||||
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: '🌐', 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: '☁️', placeholder: 'sk-...', model: 'MiniMax', requiresApiKey: false, isOAuth: true, supportsApiKey: true, defaultBaseUrl: 'https://api.minimaxi.com/v1', apiProtocol: 'openai-completions', defaultModelId: 'MiniMax-M2.7', showModelId: true, showModelIdInDevModeOnly: true, modelIdPlaceholder: 'MiniMax-M2.7', apiKeyUrl: 'https://platform.minimaxi.com/' },
|
||||
{ id: 'moonshot', name: 'Moonshot (CN)', icon: '🌙', 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: '🌊', 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: '🐋', 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: '☁️', placeholder: 'sk-...', model: 'MiniMax', requiresApiKey: false, isOAuth: true, supportsApiKey: true, defaultBaseUrl: 'https://api.minimax.io/v1', apiProtocol: 'openai-completions', defaultModelId: 'MiniMax-M2.7', showModelId: true, showModelIdInDevModeOnly: true, modelIdPlaceholder: 'MiniMax-M2.7', apiKeyUrl: 'https://platform.minimax.io' },
|
||||
{ id: 'modelstudio', name: 'Model Studio', icon: '☁️', 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: 'A', 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: '🦙', placeholder: 'Not required', placeholderZh: '无需填写', placeholderTh: 'ไม่ต้องกรอก', requiresApiKey: false, defaultBaseUrl: 'http://localhost:11434/v1', showBaseUrl: true, showModelId: true, modelIdPlaceholder: 'qwen3:latest' },
|
||||
{
|
||||
id: 'custom',
|
||||
name: 'Custom',
|
||||
icon: '⚙️',
|
||||
placeholder: 'API key...',
|
||||
placeholderZh: 'API Key...',
|
||||
placeholderTh: '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 | LanguageCode,
|
||||
): string | undefined {
|
||||
if (!provider?.docsUrl) {
|
||||
return undefined;
|
||||
}
|
||||
if (language.startsWith('zh') && provider.docsUrlZh) {
|
||||
return provider.docsUrlZh;
|
||||
}
|
||||
return provider.docsUrl;
|
||||
}
|
||||
|
||||
export function getProviderPlaceholder(
|
||||
provider: Pick<ProviderTypeInfo, 'placeholder' | 'placeholderZh' | 'placeholderTh'> | undefined,
|
||||
language: string | LanguageCode,
|
||||
): string | undefined {
|
||||
if (!provider?.placeholder) {
|
||||
return undefined;
|
||||
}
|
||||
if (language.startsWith('zh') && provider.placeholderZh) {
|
||||
return provider.placeholderZh;
|
||||
}
|
||||
if (language.startsWith('th') && provider.placeholderTh) {
|
||||
return provider.placeholderTh;
|
||||
}
|
||||
return provider.placeholder;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
export * from '../../runtime-shared/lib/providers';
|
||||
|
||||
@@ -1,173 +1 @@
|
||||
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;
|
||||
}
|
||||
export * from '../../runtime-shared/shared/chat-model';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Task } from '../lib/task-types';
|
||||
import type { RawMessage } from '../shared/chat-model';
|
||||
import { CONFIG_KEYS } from '../../runtime-shared/lib/constants';
|
||||
|
||||
export type RuntimeRefreshTopic =
|
||||
| 'providers'
|
||||
@@ -20,21 +21,6 @@ export type IpcArgs = readonly unknown[];
|
||||
|
||||
export type IpcListener = (...args: unknown[]) => void;
|
||||
|
||||
export const CONFIG_KEYS = {
|
||||
THEME_MODE: 'themeMode',
|
||||
PRIMARY_COLOR: 'primaryColor',
|
||||
LANGUAGE: 'language',
|
||||
FONT_SIZE: 'fontSize',
|
||||
MINIMIZE_TO_TRAY: 'minimizeToTray',
|
||||
LAUNCH_AT_STARTUP: 'launchAtStartup',
|
||||
PROVIDER: 'provider',
|
||||
DEFAULT_MODEL: 'defaultModel',
|
||||
GATEWAY_AUTO_START: 'gatewayAutoStart',
|
||||
SELECTED_CHANNELS: 'selectedChannels',
|
||||
IMAGE_CACHE: 'imageCache',
|
||||
TASK_LIST: 'taskList',
|
||||
} as const;
|
||||
|
||||
export type ConfigKey = (typeof CONFIG_KEYS)[keyof typeof CONFIG_KEYS];
|
||||
|
||||
export type ConfigValueKey = keyof ConfigValueMap;
|
||||
|
||||
Reference in New Issue
Block a user