@jiangxiaosheng/digital-agent-platform 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +37 -0
  2. package/eslint.config.mjs +20 -0
  3. package/messages/en.json +184 -0
  4. package/messages/zh.json +184 -0
  5. package/middleware.ts +9 -0
  6. package/next-env.d.ts +5 -0
  7. package/next.config.ts +16 -0
  8. package/package.json +59 -0
  9. package/postcss.config.mjs +5 -0
  10. package/src/app/[locale]/agents/[id]/chat/page.tsx +833 -0
  11. package/src/app/[locale]/agents/[id]/edit/page.tsx +58 -0
  12. package/src/app/[locale]/agents/new/page.tsx +52 -0
  13. package/src/app/[locale]/agents/page.tsx +122 -0
  14. package/src/app/[locale]/knowledge/page.tsx +305 -0
  15. package/src/app/[locale]/layout.tsx +46 -0
  16. package/src/app/[locale]/page.tsx +6 -0
  17. package/src/app/[locale]/settings/page.tsx +1204 -0
  18. package/src/app/api/agents/[id]/route.ts +27 -0
  19. package/src/app/api/agents/route.ts +27 -0
  20. package/src/app/api/auth-config/test/route.ts +69 -0
  21. package/src/app/api/bash-output/route.ts +57 -0
  22. package/src/app/api/chat/[agentId]/route.ts +164 -0
  23. package/src/app/api/env-vars/[key]/route.ts +22 -0
  24. package/src/app/api/env-vars/route.ts +26 -0
  25. package/src/app/api/env-vars/scan/route.ts +66 -0
  26. package/src/app/api/file-serve/route.ts +58 -0
  27. package/src/app/api/fs/browse/route.ts +47 -0
  28. package/src/app/api/knowledge/[id]/documents/[docId]/route.ts +22 -0
  29. package/src/app/api/knowledge/[id]/route.ts +21 -0
  30. package/src/app/api/knowledge/[id]/upload/route.ts +90 -0
  31. package/src/app/api/knowledge/route.ts +15 -0
  32. package/src/app/api/knowledge/search/route.ts +26 -0
  33. package/src/app/api/models/route.ts +160 -0
  34. package/src/app/api/models-config/route.ts +41 -0
  35. package/src/app/api/models-config/test/route.ts +84 -0
  36. package/src/app/api/sessions/[agentId]/[sessionId]/route.ts +12 -0
  37. package/src/app/api/sessions/[agentId]/route.ts +19 -0
  38. package/src/app/api/sessions/history/route.ts +166 -0
  39. package/src/app/api/settings/route.ts +45 -0
  40. package/src/app/api/skills/[name]/route.ts +40 -0
  41. package/src/app/api/skills/install/route.ts +35 -0
  42. package/src/app/api/skills/route.ts +54 -0
  43. package/src/app/api/skills/search/route.ts +60 -0
  44. package/src/app/api/tts/route.ts +235 -0
  45. package/src/app/api/voice-provider/route.ts +98 -0
  46. package/src/app/api/workspace-files/route.ts +151 -0
  47. package/src/app/globals.css +176 -0
  48. package/src/app/layout.tsx +13 -0
  49. package/src/app/page.tsx +6 -0
  50. package/src/components/agents/agent-form.tsx +457 -0
  51. package/src/components/agents/model-selector.tsx +290 -0
  52. package/src/components/agents/skills-manager.tsx +347 -0
  53. package/src/components/chat/input-bar.tsx +561 -0
  54. package/src/components/chat/message-bubble.tsx +395 -0
  55. package/src/components/layout/sidebar.tsx +304 -0
  56. package/src/components/providers.tsx +19 -0
  57. package/src/components/ui/badge.tsx +26 -0
  58. package/src/components/ui/button.tsx +43 -0
  59. package/src/components/ui/card.tsx +46 -0
  60. package/src/components/ui/dialog.tsx +74 -0
  61. package/src/components/ui/directory-picker.tsx +185 -0
  62. package/src/components/ui/input.tsx +18 -0
  63. package/src/components/ui/label.tsx +16 -0
  64. package/src/components/ui/scroll-area.tsx +41 -0
  65. package/src/components/ui/select.tsx +93 -0
  66. package/src/components/ui/slider.tsx +35 -0
  67. package/src/components/ui/switch.tsx +35 -0
  68. package/src/components/ui/tabs.tsx +48 -0
  69. package/src/components/ui/textarea.tsx +17 -0
  70. package/src/components/ui/toaster.tsx +50 -0
  71. package/src/hooks/use-toast.ts +43 -0
  72. package/src/i18n/request.ts +13 -0
  73. package/src/i18n/routing.ts +7 -0
  74. package/src/lib/agent-factory.ts +200 -0
  75. package/src/lib/agents/store.ts +95 -0
  76. package/src/lib/db/client.ts +25 -0
  77. package/src/lib/db/schema.ts +66 -0
  78. package/src/lib/env-vars.ts +33 -0
  79. package/src/lib/knowledge/chunker.ts +34 -0
  80. package/src/lib/knowledge/embedder.ts +48 -0
  81. package/src/lib/knowledge/retriever.ts +94 -0
  82. package/src/lib/knowledge/store.ts +74 -0
  83. package/src/lib/models-config.ts +55 -0
  84. package/src/lib/settings.ts +33 -0
  85. package/src/lib/skills.ts +79 -0
  86. package/src/lib/system-prompt.ts +52 -0
  87. package/src/lib/tool-builder.ts +19 -0
  88. package/src/lib/utils.ts +6 -0
  89. package/src/lib/voice-providers-config.ts +71 -0
  90. package/src/middleware.ts +9 -0
  91. package/src/types/agent.ts +124 -0
  92. package/src/types/knowledge.ts +39 -0
  93. package/src/types/settings.ts +31 -0
  94. package/tsconfig.json +40 -0
@@ -0,0 +1,55 @@
1
+ import path from "path";
2
+ import os from "os";
3
+ import fs from "fs";
4
+
5
+ export interface ModelEntry {
6
+ id: string;
7
+ name?: string;
8
+ contextWindow?: number;
9
+ maxTokens?: number;
10
+ input?: string[];
11
+ cost?: { input: number; output: number; cacheRead?: number; cacheWrite?: number };
12
+ reasoning?: boolean;
13
+ thinkingLevelMap?: Record<string, string | null>;
14
+ }
15
+
16
+ export interface ProviderEntry {
17
+ baseUrl?: string;
18
+ api?: string;
19
+ apiKey?: string;
20
+ models?: ModelEntry[];
21
+ }
22
+
23
+ export interface ModelsConfig {
24
+ providers: Record<string, ProviderEntry>;
25
+ }
26
+
27
+ export function getAgentDir(): string {
28
+ return process.env.PI_AGENT_DIR ?? path.join(os.homedir(), ".pi", "agent");
29
+ }
30
+ export function getModelsPath(): string { return path.join(getAgentDir(), "models.json"); }
31
+ export function getAuthPath(): string { return path.join(getAgentDir(), "auth.json"); }
32
+
33
+ export function readModelsConfig(): ModelsConfig {
34
+ const p = getModelsPath();
35
+ if (!fs.existsSync(p)) return { providers: {} };
36
+ try { return JSON.parse(fs.readFileSync(p, "utf8")); } catch { return { providers: {} }; }
37
+ }
38
+
39
+ export function writeModelsConfig(cfg: ModelsConfig): void {
40
+ const p = getModelsPath();
41
+ fs.mkdirSync(path.dirname(p), { recursive: true });
42
+ fs.writeFileSync(p, JSON.stringify(cfg, null, 2), "utf8");
43
+ }
44
+
45
+ export function readAuthConfig(): Record<string, { type: string; key: string }> {
46
+ const p = getAuthPath();
47
+ if (!fs.existsSync(p)) return {};
48
+ try { return JSON.parse(fs.readFileSync(p, "utf8")); } catch { return {}; }
49
+ }
50
+
51
+ export function writeAuthConfig(auth: Record<string, { type: string; key: string }>): void {
52
+ const p = getAuthPath();
53
+ fs.mkdirSync(path.dirname(p), { recursive: true });
54
+ fs.writeFileSync(p, JSON.stringify(auth, null, 2), "utf8");
55
+ }
@@ -0,0 +1,33 @@
1
+ import { getDb } from "./db/client";
2
+ import type { AppSettings } from "@/types/settings";
3
+ import { DEFAULT_SETTINGS } from "@/types/settings";
4
+ import path from "path";
5
+ import os from "os";
6
+
7
+ const SETTINGS_KEY = "app_settings";
8
+
9
+ export function getSettings(): AppSettings {
10
+ const db = getDb();
11
+ const row = db.prepare("SELECT value FROM settings WHERE key = ?").get(SETTINGS_KEY) as
12
+ | { value: string }
13
+ | undefined;
14
+ if (!row) return { ...DEFAULT_SETTINGS, storagePath: path.join(os.homedir(), ".digital-agents") };
15
+ try {
16
+ return JSON.parse(row.value) as AppSettings;
17
+ } catch {
18
+ return { ...DEFAULT_SETTINGS, storagePath: path.join(os.homedir(), ".digital-agents") };
19
+ }
20
+ }
21
+
22
+ export function saveSettings(settings: AppSettings): void {
23
+ const db = getDb();
24
+ db.prepare("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)").run(
25
+ SETTINGS_KEY,
26
+ JSON.stringify(settings)
27
+ );
28
+ }
29
+
30
+ export function getApiKey(provider: string): string | undefined {
31
+ const settings = getSettings();
32
+ return (settings.apiKeys as Record<string, string>)[provider];
33
+ }
@@ -0,0 +1,79 @@
1
+ import path from "path";
2
+ import os from "os";
3
+ import fs from "fs";
4
+
5
+ export interface SkillInfo {
6
+ name: string;
7
+ description: string;
8
+ filePath: string;
9
+ source: "global";
10
+ hasSetup: boolean;
11
+ /** SDK will only load skills that have a non-empty description in frontmatter */
12
+ sdkCompatible: boolean;
13
+ }
14
+
15
+ export function getSkillsDir(): string {
16
+ return path.join(os.homedir(), ".pi", "agent", "skills");
17
+ }
18
+
19
+ export function parseFrontmatter(content: string): Record<string, unknown> {
20
+ const m = content.match(/^---\s*\n([\s\S]*?)\n---/);
21
+ if (!m) return {};
22
+ const fm: Record<string, unknown> = {};
23
+ for (const line of m[1].split("\n")) {
24
+ const idx = line.indexOf(":");
25
+ if (idx === -1) continue;
26
+ const key = line.slice(0, idx).trim();
27
+ // Handle multi-line values (|, >)
28
+ const rawVal = line.slice(idx + 1).trim();
29
+ const val = rawVal.replace(/^[|>]\s*$/, "");
30
+ fm[key] = val;
31
+ }
32
+ return fm;
33
+ }
34
+
35
+ export function loadSkillFromPath(skillPath: string, entryName: string): SkillInfo | null {
36
+ try {
37
+ const stat = fs.statSync(skillPath);
38
+ let mdPath: string;
39
+ let isDir = false;
40
+ if (stat.isDirectory()) {
41
+ mdPath = path.join(skillPath, "SKILL.md");
42
+ isDir = true;
43
+ } else {
44
+ mdPath = skillPath;
45
+ }
46
+ if (!fs.existsSync(mdPath)) return null;
47
+ const content = fs.readFileSync(mdPath, "utf8");
48
+ const fm = parseFrontmatter(content);
49
+
50
+ // Skill name: prefer frontmatter name, fall back to entry/directory name
51
+ const name = ((fm.name as string) || "").trim() || entryName.replace(/\.md$/, "");
52
+
53
+ // Description: required by SDK — if missing, SDK won't discover this skill
54
+ const description = ((fm.description as string) || "").trim();
55
+
56
+ // SDK compatibility: requires non-empty description in frontmatter
57
+ const hasFrontmatter = content.trimStart().startsWith("---");
58
+ const sdkCompatible = hasFrontmatter && description.length > 0;
59
+
60
+ return {
61
+ name,
62
+ description,
63
+ filePath: isDir ? skillPath : mdPath,
64
+ source: "global",
65
+ hasSetup: content.toLowerCase().includes("## setup"),
66
+ sdkCompatible,
67
+ };
68
+ } catch { return null; }
69
+ }
70
+
71
+ export function listInstalledSkills(): SkillInfo[] {
72
+ const dir = getSkillsDir();
73
+ if (!fs.existsSync(dir)) return [];
74
+ const entries = fs.readdirSync(dir);
75
+ return entries
76
+ .map((e) => loadSkillFromPath(path.join(dir, e), e))
77
+ .filter((s): s is SkillInfo => s !== null)
78
+ .sort((a, b) => a.name.localeCompare(b.name));
79
+ }
@@ -0,0 +1,52 @@
1
+ import type { DigitalAgent } from "@/types/agent";
2
+ import { readEnvVars } from "./env-vars";
3
+ import os from "os";
4
+ import path from "path";
5
+
6
+ export function buildSystemPrompt(agent: DigitalAgent, ragContext?: string): string {
7
+ const { persona, name, description, systemPrompt } = agent;
8
+
9
+ const toneMap: Record<string, string> = {
10
+ formal: "正式、专业",
11
+ casual: "随意、轻松",
12
+ friendly: "友善、亲切",
13
+ professional: "专业、严谨",
14
+ };
15
+
16
+ const lines: string[] = [];
17
+
18
+ // ── 身份标识(始终注入,优先级最高)──────────────────
19
+ lines.push(`你是「${name}」。${description ? description : ""}`);
20
+ lines.push(`语气风格:${toneMap[persona.tone] ?? persona.tone}。交流语言:${persona.language}。`);
21
+ lines.push(`当前时间:${new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" })}。`);
22
+
23
+ // ── 系统环境变量配置信息 ──────────────────────────
24
+ const envVars = readEnvVars();
25
+ const envKeys = Object.keys(envVars);
26
+ const envConfigPath = path.join(os.homedir(), ".digital-agents", "env.json");
27
+ if (envKeys.length > 0) {
28
+ lines.push(`
29
+ <system_env>
30
+ 以下环境变量已在系统中配置,可直接在 bash 命令中使用:
31
+ ${envKeys.map(k => `- ${k}`).join("\n")}
32
+ 配置文件路径:${envConfigPath}
33
+ </system_env>`);
34
+ }
35
+
36
+ // ── 用户自定义人设 Prompt ──────────────────────────
37
+ if (systemPrompt.trim()) {
38
+ lines.push("\n" + systemPrompt.trim());
39
+ }
40
+
41
+ // ── RAG 知识库检索结果 ────────────────────────────
42
+ if (ragContext?.trim()) {
43
+ lines.push(`
44
+ <knowledge_context>
45
+ 以下是与用户问题相关的参考资料,请优先基于此回答:
46
+
47
+ ${ragContext}
48
+ </knowledge_context>`);
49
+ }
50
+
51
+ return lines.join("\n");
52
+ }
@@ -0,0 +1,19 @@
1
+ import type { DigitalAgent } from "@/types/agent";
2
+
3
+ // Returns { builtinNames, customTools } for createAgentSession
4
+ // Note: webSearch (Tavily) removed — handled by tavily-search skill + TAVILY_API_KEY env var
5
+ export function buildToolConfig(agent: DigitalAgent) {
6
+ const { tools } = agent;
7
+
8
+ const builtinNames: string[] = [];
9
+ if (tools.fileRead) builtinNames.push("read");
10
+ if (tools.fileWrite) builtinNames.push("write", "edit");
11
+ if (tools.bash) builtinNames.push("bash");
12
+ if (tools.grep) builtinNames.push("grep");
13
+ if (tools.find) builtinNames.push("find", "ls");
14
+
15
+ const customTools: { name: string }[] = [];
16
+ if (tools.calculator) customTools.push({ name: "calculator" });
17
+
18
+ return { builtinNames, customTools };
19
+ }
@@ -0,0 +1,6 @@
1
+ import { type ClassValue, clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,71 @@
1
+ import path from "path";
2
+ import os from "os";
3
+ import fs from "fs";
4
+
5
+ export type VoiceProviderType = "aliyun" | "tencent" | "baidu" | "openai";
6
+
7
+ export interface AliyunTTSConfig {
8
+ appKey: string;
9
+ accessKeyId: string;
10
+ accessKeySecret: string;
11
+ }
12
+
13
+ export interface TencentTTSConfig {
14
+ secretId: string;
15
+ secretKey: string;
16
+ region?: string;
17
+ }
18
+
19
+ export interface BaiduTTSConfig {
20
+ apiKey: string;
21
+ secretKey: string;
22
+ }
23
+
24
+ export interface OpenAITTSConfig {
25
+ apiKey: string;
26
+ model?: "tts-1" | "tts-1-hd";
27
+ voice?: "alloy" | "echo" | "fable" | "onyx" | "nova" | "shimmer";
28
+ }
29
+
30
+ export interface VoiceProviderConfig {
31
+ currentProvider: VoiceProviderType;
32
+ providers: {
33
+ aliyun?: AliyunTTSConfig;
34
+ tencent?: TencentTTSConfig;
35
+ baidu?: BaiduTTSConfig;
36
+ openai?: OpenAITTSConfig;
37
+ };
38
+ }
39
+
40
+ export function getAgentDir(): string {
41
+ return process.env.PI_AGENT_DIR ?? path.join(os.homedir(), ".pi", "agent");
42
+ }
43
+
44
+ export function getVoiceProviderPath(): string {
45
+ return path.join(getAgentDir(), "voice-provider.json");
46
+ }
47
+
48
+ export function readVoiceProviderConfig(): VoiceProviderConfig {
49
+ const p = getVoiceProviderPath();
50
+ if (!fs.existsSync(p)) {
51
+ return {
52
+ currentProvider: "openai",
53
+ providers: {},
54
+ };
55
+ }
56
+ try {
57
+ return JSON.parse(fs.readFileSync(p, "utf8"));
58
+ } catch {
59
+ return {
60
+ currentProvider: "openai",
61
+ providers: {},
62
+ };
63
+ }
64
+ }
65
+
66
+ export function writeVoiceProviderConfig(cfg: VoiceProviderConfig): void {
67
+ const p = getVoiceProviderPath();
68
+ const dir = path.dirname(p);
69
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
70
+ fs.writeFileSync(p, JSON.stringify(cfg, null, 2), "utf8");
71
+ }
@@ -0,0 +1,9 @@
1
+ import createMiddleware from "next-intl/middleware";
2
+ import { routing } from "./i18n/routing";
3
+
4
+ export default createMiddleware(routing);
5
+
6
+ export const config = {
7
+ // Only match page routes — exclude API routes, Next.js internals, and static files
8
+ matcher: ["/((?!api|_next|_vercel|.*\\..*).*)" ],
9
+ };
@@ -0,0 +1,124 @@
1
+ export type AttachedImage = {
2
+ id: string;
3
+ data: string; // raw base64 (no data: prefix)
4
+ mimeType: string; // "image/jpeg" | "image/png" | etc.
5
+ name: string;
6
+ previewUrl: string; // data URL for <img> display
7
+ };
8
+
9
+ export type DigitalAgent = {
10
+ id: string;
11
+ name: string;
12
+ avatar: string;
13
+ description: string;
14
+ systemPrompt: string;
15
+ persona: {
16
+ tone: "formal" | "casual" | "friendly" | "professional";
17
+ language: string;
18
+ };
19
+ model: {
20
+ provider: string;
21
+ modelId: string;
22
+ temperature: number;
23
+ maxTokens: number;
24
+ thinkingLevel: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
25
+ };
26
+ voice: {
27
+ enabled: boolean;
28
+ };
29
+ tools: {
30
+ bash: boolean;
31
+ fileRead: boolean;
32
+ fileWrite: boolean;
33
+ grep: boolean;
34
+ find: boolean;
35
+ calculator: boolean;
36
+ };
37
+ knowledgeBases: string[];
38
+ enabledSkills: string[];
39
+ workspacePath: string; // 数字人专属工作区目录,空字符串表示使用 ~/.pi/agent
40
+ createdAt: string;
41
+ updatedAt: string;
42
+ };
43
+
44
+ export type CreateAgentInput = Omit<DigitalAgent, "id" | "createdAt" | "updatedAt">;
45
+ export type UpdateAgentInput = Partial<CreateAgentInput>;
46
+
47
+ export const DEFAULT_AGENT: CreateAgentInput = {
48
+ name: "",
49
+ avatar: "🤖",
50
+ description: "",
51
+ systemPrompt: "",
52
+ persona: { tone: "friendly", language: "zh-CN" },
53
+ model: {
54
+ provider: "anthropic",
55
+ modelId: "claude-opus-4-5",
56
+ temperature: 0.7,
57
+ maxTokens: 8192,
58
+ thinkingLevel: "medium",
59
+ },
60
+ voice: {
61
+ enabled: false,
62
+ },
63
+ tools: {
64
+ bash: false,
65
+ fileRead: false,
66
+ fileWrite: false,
67
+ grep: false,
68
+ find: false,
69
+ calculator: false,
70
+ },
71
+ knowledgeBases: [],
72
+ enabledSkills: [],
73
+ workspacePath: "",
74
+ };
75
+
76
+ export const AVATAR_OPTIONS = [
77
+ "🤖", "🧠", "💡", "🎯", "🌟", "🦾", "👾", "🎭",
78
+ "👨‍💼", "👩‍💼", "👨‍🏫", "👩‍🏫", "👨‍💻", "👩‍💻", "🧑‍🎨", "🧑‍🔬",
79
+ "🐉", "🦊", "🐼", "🦁", "🐬", "🦋", "🌈", "⚡",
80
+ ];
81
+
82
+ export type SessionStats = {
83
+ tokens: { input: number; output: number; cacheRead: number; cacheWrite: number; total: number };
84
+ cost: number;
85
+ contextUsage?: { tokens: number; percent: number; contextWindow: number };
86
+ userMessages: number;
87
+ assistantMessages: number;
88
+ };
89
+
90
+ export type AgentChatEvent =
91
+ | { type: "message_update"; delta: string }
92
+ | { type: "tool_start"; toolName: string; toolCallId: string }
93
+ | { type: "tool_update"; toolCallId: string; content: string }
94
+ | { type: "tool_end"; toolCallId: string; toolName?: string; isError: boolean; output?: string; fullOutputPath?: string; truncated?: boolean }
95
+ | { type: "thinking_delta"; delta: string }
96
+ | { type: "compaction_start" }
97
+ | { type: "compaction_end" }
98
+ | { type: "session_file"; path: string }
99
+ | { type: "session_stats"; stats: SessionStats }
100
+ | { type: "aborted" }
101
+ | { type: "error"; message: string }
102
+ | { type: "done" };
103
+
104
+ export type ChatMessage = {
105
+ id: string;
106
+ role: "user" | "assistant";
107
+ content: string;
108
+ thinking?: string;
109
+ toolCalls?: ToolCall[];
110
+ images?: AttachedImage[];
111
+ entryId?: string;
112
+ createdAt: Date;
113
+ };
114
+
115
+ export type ToolCall = {
116
+ id: string;
117
+ name: string;
118
+ input?: string;
119
+ output?: string;
120
+ fullOutputPath?: string;
121
+ truncated?: boolean;
122
+ isError?: boolean;
123
+ isLoading?: boolean;
124
+ };
@@ -0,0 +1,39 @@
1
+ export type KnowledgeBase = {
2
+ id: string;
3
+ name: string;
4
+ description: string;
5
+ embeddingModel: string;
6
+ documentCount: number;
7
+ chunkCount: number;
8
+ createdAt: string;
9
+ updatedAt: string;
10
+ };
11
+
12
+ export type KbDocument = {
13
+ id: string;
14
+ kbId: string;
15
+ filename: string;
16
+ filePath: string;
17
+ mimeType: string;
18
+ chunkCount: number;
19
+ status: "processing" | "ready" | "error";
20
+ error?: string;
21
+ createdAt: string;
22
+ };
23
+
24
+ export type KbChunk = {
25
+ id: string;
26
+ kbId: string;
27
+ docId: string;
28
+ content: string;
29
+ metadata: {
30
+ filename: string;
31
+ pageNum?: number;
32
+ chunkIndex: number;
33
+ };
34
+ };
35
+
36
+ export type SearchResult = {
37
+ chunk: KbChunk;
38
+ score: number;
39
+ };
@@ -0,0 +1,31 @@
1
+ export type AppSettings = {
2
+ apiKeys: {
3
+ anthropic?: string;
4
+ openai?: string;
5
+ google?: string;
6
+ };
7
+ ollama: {
8
+ host: string;
9
+ };
10
+ defaultModel: {
11
+ provider: string;
12
+ modelId: string;
13
+ };
14
+ theme: "light" | "dark" | "system";
15
+ language: "zh" | "en";
16
+ storagePath: string;
17
+ };
18
+
19
+ export const DEFAULT_SETTINGS: AppSettings = {
20
+ apiKeys: {},
21
+ ollama: {
22
+ host: "http://localhost:11434",
23
+ },
24
+ defaultModel: {
25
+ provider: "anthropic",
26
+ modelId: "claude-opus-4-5",
27
+ },
28
+ theme: "system",
29
+ language: "zh",
30
+ storagePath: "",
31
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": [
4
+ "dom",
5
+ "dom.iterable",
6
+ "esnext"
7
+ ],
8
+ "allowJs": true,
9
+ "skipLibCheck": true,
10
+ "strict": true,
11
+ "noEmit": true,
12
+ "esModuleInterop": true,
13
+ "module": "esnext",
14
+ "moduleResolution": "bundler",
15
+ "resolveJsonModule": true,
16
+ "isolatedModules": true,
17
+ "jsx": "preserve",
18
+ "incremental": true,
19
+ "plugins": [
20
+ {
21
+ "name": "next"
22
+ }
23
+ ],
24
+ "paths": {
25
+ "@/*": [
26
+ "./src/*"
27
+ ]
28
+ },
29
+ "target": "ES2017"
30
+ },
31
+ "include": [
32
+ "next-env.d.ts",
33
+ "**/*.ts",
34
+ "**/*.tsx",
35
+ ".next/types/**/*.ts"
36
+ ],
37
+ "exclude": [
38
+ "node_modules"
39
+ ]
40
+ }