work-agent 0.1.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.
- package/README.md +234 -0
- package/app/(admin)/approvals/page.tsx +16 -0
- package/app/(admin)/audit/page.tsx +18 -0
- package/app/(admin)/layout.tsx +47 -0
- package/app/(admin)/scheduled-tasks/page.tsx +17 -0
- package/app/(admin)/settings/page.tsx +46 -0
- package/app/(admin)/skills/[name]/page.tsx +378 -0
- package/app/(admin)/skills/page.tsx +406 -0
- package/app/(admin)/statistics/page.tsx +416 -0
- package/app/(admin)/tickets/[id]/page.tsx +348 -0
- package/app/(admin)/tickets/new/page.tsx +309 -0
- package/app/(admin)/tickets/page.tsx +27 -0
- package/app/api/audit/route.ts +30 -0
- package/app/api/auth/feishu/callback/route.ts +72 -0
- package/app/api/auth/feishu/login/route.ts +17 -0
- package/app/api/auth/feishu/sso/route.ts +78 -0
- package/app/api/auth/login/route.ts +85 -0
- package/app/api/auth/oauth/route.ts +168 -0
- package/app/api/config/providers/route.ts +105 -0
- package/app/api/config/route.ts +115 -0
- package/app/api/config/status/route.ts +56 -0
- package/app/api/config/test/route.ts +212 -0
- package/app/api/documents/[id]/route.ts +88 -0
- package/app/api/documents/route.ts +53 -0
- package/app/api/health/route.ts +32 -0
- package/app/api/knowledge/[id]/route.ts +152 -0
- package/app/api/knowledge/from-session/route.ts +27 -0
- package/app/api/knowledge/route.ts +100 -0
- package/app/api/market/knowledge/[id]/route.ts +92 -0
- package/app/api/market/knowledge/route.ts +130 -0
- package/app/api/marketplace/skills/[id]/approve/route.ts +68 -0
- package/app/api/marketplace/skills/[id]/certify/route.ts +54 -0
- package/app/api/marketplace/skills/[id]/install/route.ts +180 -0
- package/app/api/marketplace/skills/[id]/promote-to-system/route.ts +219 -0
- package/app/api/marketplace/skills/[id]/rate/route.ts +90 -0
- package/app/api/marketplace/skills/[id]/ratings/route.ts +55 -0
- package/app/api/marketplace/skills/[id]/reject/route.ts +68 -0
- package/app/api/marketplace/skills/[id]/route.ts +177 -0
- package/app/api/marketplace/skills/route.ts +235 -0
- package/app/api/memory/route.ts +40 -0
- package/app/api/my/files/[id]/route.ts +52 -0
- package/app/api/my/files/route.ts +230 -0
- package/app/api/my/knowledge/route.ts +36 -0
- package/app/api/pi-chat/route.ts +443 -0
- package/app/api/recommend/route.ts +38 -0
- package/app/api/scheduled-tasks/[id]/execute/route.ts +132 -0
- package/app/api/scheduled-tasks/[id]/route.ts +165 -0
- package/app/api/scheduled-tasks/[id]/toggle/route.ts +53 -0
- package/app/api/scheduled-tasks/route.ts +101 -0
- package/app/api/sessions/[id]/messages/route.ts +212 -0
- package/app/api/sessions/route.ts +101 -0
- package/app/api/share/file/[id]/route.ts +37 -0
- package/app/api/skills/[name]/execute/route.ts +121 -0
- package/app/api/skills/[name]/route.ts +167 -0
- package/app/api/skills/create/route.ts +65 -0
- package/app/api/skills/generate/route.ts +405 -0
- package/app/api/skills/installed/route.ts +151 -0
- package/app/api/skills/route.ts +174 -0
- package/app/api/skills/translate/route.ts +40 -0
- package/app/api/skills/user/[name]/route.ts +159 -0
- package/app/api/skills/user/route.ts +90 -0
- package/app/api/statistics/route.ts +94 -0
- package/app/api/task-executions/[id]/route.ts +34 -0
- package/app/api/task-executions/route.ts +29 -0
- package/app/api/tickets/[id]/approve/route.ts +129 -0
- package/app/api/tickets/[id]/execute/route.ts +201 -0
- package/app/api/tickets/[id]/route.ts +127 -0
- package/app/api/tickets/route.ts +103 -0
- package/app/api/user/skills/route.ts +175 -0
- package/app/api/users/route.ts +80 -0
- package/app/chat/page.tsx +5 -0
- package/app/globals.css +84 -0
- package/app/h5/layout.tsx +5 -0
- package/app/h5/mobile-approvals-page.tsx +167 -0
- package/app/h5/mobile-chat-page.tsx +951 -0
- package/app/h5/mobile-profile-page.tsx +147 -0
- package/app/h5/mobile-tickets-page.tsx +121 -0
- package/app/h5/page.tsx +23 -0
- package/app/h5/ticket-action-buttons.tsx +80 -0
- package/app/layout.tsx +26 -0
- package/app/login/page.tsx +318 -0
- package/app/market/knowledge/[id]/page.tsx +77 -0
- package/app/market/knowledge/page.tsx +358 -0
- package/app/market/layout.tsx +29 -0
- package/app/market/page.tsx +18 -0
- package/app/market/skills/page.tsx +397 -0
- package/app/my/files/page.tsx +511 -0
- package/app/my/knowledge/[id]/page.tsx +271 -0
- package/app/my/knowledge/new/page.tsx +234 -0
- package/app/my/knowledge/page.tsx +248 -0
- package/app/my/layout.tsx +32 -0
- package/app/my/memory/page.tsx +164 -0
- package/app/my/page.tsx +18 -0
- package/app/my/scheduled-tasks/[id]/edit/page.tsx +290 -0
- package/app/my/scheduled-tasks/[id]/executions/page.tsx +275 -0
- package/app/my/scheduled-tasks/[id]/page.tsx +284 -0
- package/app/my/scheduled-tasks/new/page.tsx +230 -0
- package/app/my/scheduled-tasks/page.tsx +27 -0
- package/app/my/skills/[name]/page.tsx +320 -0
- package/app/my/skills/new/page.tsx +394 -0
- package/app/my/skills/page.tsx +303 -0
- package/app/page.tsx +2288 -0
- package/app/share/[sessionId]/page.tsx +226 -0
- package/app/share/file/[id]/page.tsx +140 -0
- package/bin/README.md +63 -0
- package/bin/generate-api-system +300 -0
- package/bin/postinstall.js +95 -0
- package/bin/work-agent.js +173 -0
- package/components/ai-elements/agent.tsx +142 -0
- package/components/ai-elements/artifact.tsx +149 -0
- package/components/ai-elements/attachments.tsx +427 -0
- package/components/ai-elements/audio-player.tsx +232 -0
- package/components/ai-elements/canvas.tsx +26 -0
- package/components/ai-elements/chain-of-thought.tsx +223 -0
- package/components/ai-elements/checkpoint.tsx +72 -0
- package/components/ai-elements/code-block.tsx +555 -0
- package/components/ai-elements/commit.tsx +449 -0
- package/components/ai-elements/confirmation.tsx +173 -0
- package/components/ai-elements/connection.tsx +28 -0
- package/components/ai-elements/context.tsx +410 -0
- package/components/ai-elements/controls.tsx +19 -0
- package/components/ai-elements/conversation.tsx +167 -0
- package/components/ai-elements/edge.tsx +144 -0
- package/components/ai-elements/environment-variables.tsx +325 -0
- package/components/ai-elements/file-tree.tsx +298 -0
- package/components/ai-elements/image.tsx +25 -0
- package/components/ai-elements/inline-citation.tsx +294 -0
- package/components/ai-elements/jsx-preview.tsx +250 -0
- package/components/ai-elements/message.tsx +367 -0
- package/components/ai-elements/mic-selector.tsx +372 -0
- package/components/ai-elements/model-selector.tsx +214 -0
- package/components/ai-elements/node.tsx +72 -0
- package/components/ai-elements/open-in-chat.tsx +367 -0
- package/components/ai-elements/package-info.tsx +235 -0
- package/components/ai-elements/panel.tsx +16 -0
- package/components/ai-elements/persona.tsx +280 -0
- package/components/ai-elements/plan.tsx +144 -0
- package/components/ai-elements/prompt-input.tsx +1341 -0
- package/components/ai-elements/queue.tsx +275 -0
- package/components/ai-elements/reasoning.tsx +355 -0
- package/components/ai-elements/sandbox.tsx +133 -0
- package/components/ai-elements/schema-display.tsx +473 -0
- package/components/ai-elements/shimmer.tsx +78 -0
- package/components/ai-elements/snippet.tsx +141 -0
- package/components/ai-elements/sources.tsx +78 -0
- package/components/ai-elements/speech-input.tsx +324 -0
- package/components/ai-elements/stack-trace.tsx +531 -0
- package/components/ai-elements/suggestion.tsx +58 -0
- package/components/ai-elements/task.tsx +88 -0
- package/components/ai-elements/terminal.tsx +277 -0
- package/components/ai-elements/test-results.tsx +497 -0
- package/components/ai-elements/tool.tsx +174 -0
- package/components/ai-elements/toolbar.tsx +17 -0
- package/components/ai-elements/transcription.tsx +126 -0
- package/components/ai-elements/voice-selector.tsx +525 -0
- package/components/ai-elements/web-preview.tsx +282 -0
- package/components/audit-log-list.tsx +114 -0
- package/components/chat/EmptyPreviewState.tsx +12 -0
- package/components/chat/KnowledgePickerDialog.tsx +464 -0
- package/components/chat/KnowledgePreview.tsx +70 -0
- package/components/chat/KnowledgePreviewPanel.tsx +86 -0
- package/components/chat/MentionInput.tsx +309 -0
- package/components/chat/OrganizeDialog.tsx +258 -0
- package/components/chat/RecommendationBanner.tsx +94 -0
- package/components/chat/SaveToKnowledgeDialog.tsx +193 -0
- package/components/chat/SkillSelector.tsx +305 -0
- package/components/chat/SkillSwitcher.tsx +163 -0
- package/components/client-layout.tsx +15 -0
- package/components/knowledge/KnowledgeMetadataPanel.tsx +293 -0
- package/components/layout-wrapper.tsx +18 -0
- package/components/mobile-layout.tsx +62 -0
- package/components/scheduled-task-list.tsx +356 -0
- package/components/setup-guide.tsx +484 -0
- package/components/sub-nav.tsx +54 -0
- package/components/ticket-detail-content.tsx +383 -0
- package/components/ticket-list.tsx +366 -0
- package/components/top-nav.tsx +132 -0
- package/components/ui/accordion.tsx +58 -0
- package/components/ui/alert.tsx +59 -0
- package/components/ui/avatar.tsx +50 -0
- package/components/ui/badge.tsx +36 -0
- package/components/ui/button-group.tsx +83 -0
- package/components/ui/button.tsx +57 -0
- package/components/ui/card.tsx +91 -0
- package/components/ui/carousel.tsx +262 -0
- package/components/ui/collapsible.tsx +11 -0
- package/components/ui/command.tsx +153 -0
- package/components/ui/dialog.tsx +122 -0
- package/components/ui/dropdown-menu.tsx +200 -0
- package/components/ui/hover-card.tsx +29 -0
- package/components/ui/input-group.tsx +170 -0
- package/components/ui/input.tsx +22 -0
- package/components/ui/label.tsx +26 -0
- package/components/ui/popover.tsx +31 -0
- package/components/ui/progress.tsx +28 -0
- package/components/ui/scroll-area.tsx +48 -0
- package/components/ui/select.tsx +174 -0
- package/components/ui/separator.tsx +31 -0
- package/components/ui/spinner.tsx +16 -0
- package/components/ui/switch.tsx +29 -0
- package/components/ui/table.tsx +120 -0
- package/components/ui/tabs.tsx +55 -0
- package/components/ui/textarea.tsx +22 -0
- package/components/ui/tooltip.tsx +30 -0
- package/components/welcome-guide.tsx +182 -0
- package/components.json +24 -0
- package/lib/command-parser.ts +331 -0
- package/lib/dangerous-commands.ts +672 -0
- package/lib/db.ts +2250 -0
- package/lib/feishu-auth.ts +135 -0
- package/lib/file-storage.ts +306 -0
- package/lib/file-tool.ts +583 -0
- package/lib/knowledge-tool.ts +152 -0
- package/lib/knowledge-types.ts +66 -0
- package/lib/market-client.ts +313 -0
- package/lib/market-db.ts +736 -0
- package/lib/market-types.ts +51 -0
- package/lib/memory-tool.ts +211 -0
- package/lib/memory.ts +197 -0
- package/lib/pi-config.ts +436 -0
- package/lib/pi-session.ts +799 -0
- package/lib/pinyin.ts +13 -0
- package/lib/recommendation.ts +227 -0
- package/lib/risk-estimator.ts +350 -0
- package/lib/scheduled-task-tool.ts +184 -0
- package/lib/scheduler-init.ts +43 -0
- package/lib/scheduler.ts +416 -0
- package/lib/secure-bash-tool.ts +413 -0
- package/lib/skill-engine.ts +396 -0
- package/lib/skill-generator.ts +269 -0
- package/lib/skill-loader.ts +234 -0
- package/lib/skill-tool.ts +188 -0
- package/lib/skill-types.ts +82 -0
- package/lib/skills-init.ts +58 -0
- package/lib/ticket-tool.ts +246 -0
- package/lib/user-skill-types.ts +30 -0
- package/lib/user-skills.ts +362 -0
- package/lib/utils.ts +6 -0
- package/lib/workflow.ts +154 -0
- package/lib/zip-tool.ts +191 -0
- package/next.config.js +8 -0
- package/package.json +106 -0
- package/public/.gitkeep +1 -0
- package/public/icon.svg +1 -0
- package/tsconfig.json +42 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Status API
|
|
3
|
+
*
|
|
4
|
+
* Returns the current configuration status of the system.
|
|
5
|
+
* Used to determine if setup is required.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { NextResponse } from 'next/server';
|
|
9
|
+
import { loadModelsConfig, PI_CONFIG_DIR } from '@/lib/pi-config';
|
|
10
|
+
|
|
11
|
+
export const runtime = 'nodejs';
|
|
12
|
+
export const dynamic = 'force-dynamic';
|
|
13
|
+
|
|
14
|
+
export async function GET() {
|
|
15
|
+
try {
|
|
16
|
+
const config = loadModelsConfig();
|
|
17
|
+
const hasProviders = config?.providers && Object.keys(config.providers).length > 0;
|
|
18
|
+
|
|
19
|
+
// Check for environment variables (new AI_ prefix and legacy)
|
|
20
|
+
const hasAiEnvConfig = !!(
|
|
21
|
+
process.env.AI_PROVIDER && process.env.AI_API_KEY
|
|
22
|
+
);
|
|
23
|
+
const hasLegacyEnvKey = !!(
|
|
24
|
+
process.env.ZHIPU_API_KEY || process.env.ANTHROPIC_API_KEY
|
|
25
|
+
);
|
|
26
|
+
const hasEnvApiKey = hasAiEnvConfig || hasLegacyEnvKey;
|
|
27
|
+
|
|
28
|
+
// Check if any provider has valid configuration
|
|
29
|
+
let isConfigured = hasEnvApiKey;
|
|
30
|
+
if (!isConfigured && hasProviders) {
|
|
31
|
+
for (const provider of Object.values(config!.providers)) {
|
|
32
|
+
if (provider.apiKey && provider.baseUrl) {
|
|
33
|
+
isConfigured = true;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return NextResponse.json({
|
|
40
|
+
isConfigured,
|
|
41
|
+
configDir: PI_CONFIG_DIR,
|
|
42
|
+
hasProviders: hasProviders,
|
|
43
|
+
hasEnvApiKey,
|
|
44
|
+
envProvider: process.env.AI_PROVIDER || null,
|
|
45
|
+
providers: hasProviders
|
|
46
|
+
? Object.keys(config!.providers)
|
|
47
|
+
: [],
|
|
48
|
+
});
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error('[config/status] Error:', error);
|
|
51
|
+
return NextResponse.json(
|
|
52
|
+
{ error: 'Failed to check configuration', isConfigured: false },
|
|
53
|
+
{ status: 500 }
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Test API
|
|
3
|
+
*
|
|
4
|
+
* Tests the connection to an AI provider.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
8
|
+
|
|
9
|
+
export const runtime = 'nodejs';
|
|
10
|
+
export const dynamic = 'force-dynamic';
|
|
11
|
+
|
|
12
|
+
interface TestRequest {
|
|
13
|
+
providerId: string;
|
|
14
|
+
baseUrl: string;
|
|
15
|
+
api: string;
|
|
16
|
+
apiKey: string;
|
|
17
|
+
modelId?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* POST /api/config/test - Test provider connection
|
|
22
|
+
*/
|
|
23
|
+
export async function POST(request: NextRequest) {
|
|
24
|
+
try {
|
|
25
|
+
const body: TestRequest = await request.json();
|
|
26
|
+
const { providerId, baseUrl, api, apiKey, modelId } = body;
|
|
27
|
+
|
|
28
|
+
console.log('[config/test] Testing connection:', {
|
|
29
|
+
providerId,
|
|
30
|
+
baseUrl,
|
|
31
|
+
api,
|
|
32
|
+
modelId,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (!baseUrl || !api) {
|
|
36
|
+
return NextResponse.json(
|
|
37
|
+
{ success: false, error: '缺少必要参数' },
|
|
38
|
+
{ status: 400 }
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Test connection based on API type
|
|
43
|
+
let testResult: { success: boolean; message: string };
|
|
44
|
+
|
|
45
|
+
if (api === 'openai-completions') {
|
|
46
|
+
testResult = await testOpenAIConnection(baseUrl, apiKey, modelId);
|
|
47
|
+
} else if (api === 'anthropic-messages') {
|
|
48
|
+
testResult = await testAnthropicConnection(baseUrl, apiKey, modelId);
|
|
49
|
+
} else {
|
|
50
|
+
return NextResponse.json(
|
|
51
|
+
{ success: false, error: `不支持的 API 类型: ${api}` },
|
|
52
|
+
{ status: 400 }
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
console.log('[config/test] Test result:', testResult);
|
|
57
|
+
|
|
58
|
+
if (testResult.success) {
|
|
59
|
+
return NextResponse.json({
|
|
60
|
+
success: true,
|
|
61
|
+
message: testResult.message,
|
|
62
|
+
});
|
|
63
|
+
} else {
|
|
64
|
+
return NextResponse.json(
|
|
65
|
+
{ success: false, error: testResult.message },
|
|
66
|
+
{ status: 200 }
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error('[config/test] Error:', error);
|
|
71
|
+
return NextResponse.json(
|
|
72
|
+
{
|
|
73
|
+
success: false,
|
|
74
|
+
error: error instanceof Error ? error.message : '测试连接失败'
|
|
75
|
+
},
|
|
76
|
+
{ status: 500 }
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Test OpenAI-compatible API connection
|
|
83
|
+
*/
|
|
84
|
+
async function testOpenAIConnection(
|
|
85
|
+
baseUrl: string,
|
|
86
|
+
apiKey: string,
|
|
87
|
+
modelId?: string
|
|
88
|
+
): Promise<{ success: boolean; message: string }> {
|
|
89
|
+
try {
|
|
90
|
+
// For Ollama, try to get model list first
|
|
91
|
+
if (baseUrl.includes('11434')) {
|
|
92
|
+
try {
|
|
93
|
+
const tagsUrl = baseUrl.replace('/v1', '/api/tags');
|
|
94
|
+
console.log('[config/test] Fetching Ollama models from:', tagsUrl);
|
|
95
|
+
|
|
96
|
+
const tagsResponse = await fetch(tagsUrl, {
|
|
97
|
+
method: 'GET',
|
|
98
|
+
headers: {
|
|
99
|
+
'Content-Type': 'application/json',
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (tagsResponse.ok) {
|
|
104
|
+
const tagsData = await tagsResponse.json();
|
|
105
|
+
const models = tagsData.models || [];
|
|
106
|
+
const modelCount = models.length;
|
|
107
|
+
|
|
108
|
+
if (modelId) {
|
|
109
|
+
const hasModel = models.some((m: any) =>
|
|
110
|
+
m.name === modelId || m.name.startsWith(modelId + ':')
|
|
111
|
+
);
|
|
112
|
+
if (hasModel) {
|
|
113
|
+
return {
|
|
114
|
+
success: true,
|
|
115
|
+
message: `连接成功!找到模型 ${modelId}(共 ${modelCount} 个模型)`
|
|
116
|
+
};
|
|
117
|
+
} else {
|
|
118
|
+
return {
|
|
119
|
+
success: false,
|
|
120
|
+
message: `连接成功但未找到模型 ${modelId}(可用模型: ${models.slice(0, 5).map((m: any) => m.name).join(', ')}${modelCount > 5 ? '...' : ''})`
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
return {
|
|
125
|
+
success: true,
|
|
126
|
+
message: `连接成功!发现 ${modelCount} 个模型`
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.log('[config/test] Failed to fetch Ollama models, trying chat completion');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Try a simple chat completion request
|
|
136
|
+
const testUrl = `${baseUrl}/chat/completions`;
|
|
137
|
+
console.log('[config/test] Testing OpenAI endpoint:', testUrl);
|
|
138
|
+
|
|
139
|
+
const response = await fetch(testUrl, {
|
|
140
|
+
method: 'POST',
|
|
141
|
+
headers: {
|
|
142
|
+
'Content-Type': 'application/json',
|
|
143
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
144
|
+
},
|
|
145
|
+
body: JSON.stringify({
|
|
146
|
+
model: modelId || 'gpt-3.5-turbo',
|
|
147
|
+
messages: [{ role: 'user', content: 'test' }],
|
|
148
|
+
max_tokens: 5,
|
|
149
|
+
}),
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
if (response.ok) {
|
|
153
|
+
return { success: true, message: '连接成功!API 响应正常' };
|
|
154
|
+
} else {
|
|
155
|
+
const errorData = await response.json().catch(() => ({}));
|
|
156
|
+
const errorMessage = errorData.error?.message || response.statusText;
|
|
157
|
+
return {
|
|
158
|
+
success: false,
|
|
159
|
+
message: `API 返回错误 (${response.status}): ${errorMessage}`
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
} catch (error) {
|
|
163
|
+
return {
|
|
164
|
+
success: false,
|
|
165
|
+
message: `连接失败: ${error instanceof Error ? error.message : '网络错误'}`
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Test Anthropic API connection
|
|
172
|
+
*/
|
|
173
|
+
async function testAnthropicConnection(
|
|
174
|
+
baseUrl: string,
|
|
175
|
+
apiKey: string,
|
|
176
|
+
modelId?: string
|
|
177
|
+
): Promise<{ success: boolean; message: string }> {
|
|
178
|
+
try {
|
|
179
|
+
const testUrl = `${baseUrl}/v1/messages`;
|
|
180
|
+
console.log('[config/test] Testing Anthropic endpoint:', testUrl);
|
|
181
|
+
|
|
182
|
+
const response = await fetch(testUrl, {
|
|
183
|
+
method: 'POST',
|
|
184
|
+
headers: {
|
|
185
|
+
'Content-Type': 'application/json',
|
|
186
|
+
'x-api-key': apiKey,
|
|
187
|
+
'anthropic-version': '2023-06-01',
|
|
188
|
+
},
|
|
189
|
+
body: JSON.stringify({
|
|
190
|
+
model: modelId || 'claude-3-5-sonnet-20241022',
|
|
191
|
+
max_tokens: 10,
|
|
192
|
+
messages: [{ role: 'user', content: 'test' }],
|
|
193
|
+
}),
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
if (response.ok) {
|
|
197
|
+
return { success: true, message: '连接成功!API 响应正常' };
|
|
198
|
+
} else {
|
|
199
|
+
const errorData = await response.json().catch(() => ({}));
|
|
200
|
+
const errorMessage = errorData.error?.message || response.statusText;
|
|
201
|
+
return {
|
|
202
|
+
success: false,
|
|
203
|
+
message: `API 返回错误 (${response.status}): ${errorMessage}`
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
} catch (error) {
|
|
207
|
+
return {
|
|
208
|
+
success: false,
|
|
209
|
+
message: `连接失败: ${error instanceof Error ? error.message : '网络错误'}`
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { getDocument, updateDocument, deleteDocument } from '@/lib/db';
|
|
3
|
+
|
|
4
|
+
// GET /api/documents/[id] - 获取单个文档
|
|
5
|
+
export async function GET(
|
|
6
|
+
request: NextRequest,
|
|
7
|
+
{ params }: { params: Promise<{ id: string }> }
|
|
8
|
+
) {
|
|
9
|
+
try {
|
|
10
|
+
const { id } = await params;
|
|
11
|
+
const document = await getDocument(id);
|
|
12
|
+
|
|
13
|
+
if (!document) {
|
|
14
|
+
return NextResponse.json(
|
|
15
|
+
{ error: 'Document not found' },
|
|
16
|
+
{ status: 404 }
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return NextResponse.json({ document });
|
|
21
|
+
} catch (error) {
|
|
22
|
+
console.error('Failed to get document:', error);
|
|
23
|
+
return NextResponse.json(
|
|
24
|
+
{ error: 'Failed to get document' },
|
|
25
|
+
{ status: 500 }
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// PUT /api/documents/[id] - 更新文档
|
|
31
|
+
export async function PUT(
|
|
32
|
+
request: NextRequest,
|
|
33
|
+
{ params }: { params: Promise<{ id: string }> }
|
|
34
|
+
) {
|
|
35
|
+
try {
|
|
36
|
+
const { id } = await params;
|
|
37
|
+
const body = await request.json();
|
|
38
|
+
const { title, content, category, tags } = body;
|
|
39
|
+
|
|
40
|
+
const document = await updateDocument(id, {
|
|
41
|
+
title,
|
|
42
|
+
content,
|
|
43
|
+
category,
|
|
44
|
+
tags,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (!document) {
|
|
48
|
+
return NextResponse.json(
|
|
49
|
+
{ error: 'Document not found' },
|
|
50
|
+
{ status: 404 }
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return NextResponse.json({ document });
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error('Failed to update document:', error);
|
|
57
|
+
return NextResponse.json(
|
|
58
|
+
{ error: 'Failed to update document' },
|
|
59
|
+
{ status: 500 }
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// DELETE /api/documents/[id] - 删除文档
|
|
65
|
+
export async function DELETE(
|
|
66
|
+
request: NextRequest,
|
|
67
|
+
{ params }: { params: Promise<{ id: string }> }
|
|
68
|
+
) {
|
|
69
|
+
try {
|
|
70
|
+
const { id } = await params;
|
|
71
|
+
const success = await deleteDocument(id);
|
|
72
|
+
|
|
73
|
+
if (!success) {
|
|
74
|
+
return NextResponse.json(
|
|
75
|
+
{ error: 'Document not found' },
|
|
76
|
+
{ status: 404 }
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return NextResponse.json({ success: true });
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error('Failed to delete document:', error);
|
|
83
|
+
return NextResponse.json(
|
|
84
|
+
{ error: 'Failed to delete document' },
|
|
85
|
+
{ status: 500 }
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { getDocuments, createDocument } from '@/lib/db';
|
|
3
|
+
|
|
4
|
+
// GET /api/documents - 获取文档列表
|
|
5
|
+
export async function GET(request: NextRequest) {
|
|
6
|
+
try {
|
|
7
|
+
const searchParams = request.nextUrl.searchParams;
|
|
8
|
+
const category = searchParams.get('category') || undefined;
|
|
9
|
+
const author = searchParams.get('author') || undefined;
|
|
10
|
+
const limit = searchParams.get('limit') ? parseInt(searchParams.get('limit')!) : undefined;
|
|
11
|
+
|
|
12
|
+
const documents = await getDocuments({ category, author, limit });
|
|
13
|
+
|
|
14
|
+
return NextResponse.json({ documents });
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error('Failed to get documents:', error);
|
|
17
|
+
return NextResponse.json(
|
|
18
|
+
{ error: 'Failed to get documents' },
|
|
19
|
+
{ status: 500 }
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// POST /api/documents - 创建新文档
|
|
25
|
+
export async function POST(request: NextRequest) {
|
|
26
|
+
try {
|
|
27
|
+
const body = await request.json();
|
|
28
|
+
const { title, content, category, tags, author } = body;
|
|
29
|
+
|
|
30
|
+
if (!title || !author) {
|
|
31
|
+
return NextResponse.json(
|
|
32
|
+
{ error: 'Title and author are required' },
|
|
33
|
+
{ status: 400 }
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const document = await createDocument({
|
|
38
|
+
title,
|
|
39
|
+
content: content || '',
|
|
40
|
+
category,
|
|
41
|
+
tags,
|
|
42
|
+
author,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return NextResponse.json({ document }, { status: 201 });
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error('Failed to create document:', error);
|
|
48
|
+
return NextResponse.json(
|
|
49
|
+
{ error: 'Failed to create document' },
|
|
50
|
+
{ status: 500 }
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 健康检查和调度器初始化 API
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
6
|
+
import { initializeScheduler, isSchedulerInitialized } from '@/lib/scheduler-init';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* GET /api/health - 健康检查,同时确保调度器已启动
|
|
10
|
+
*/
|
|
11
|
+
export async function GET(request: NextRequest) {
|
|
12
|
+
try {
|
|
13
|
+
// 确保调度器已初始化
|
|
14
|
+
if (!isSchedulerInitialized()) {
|
|
15
|
+
await initializeScheduler();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return NextResponse.json({
|
|
19
|
+
status: 'ok',
|
|
20
|
+
timestamp: new Date().toISOString(),
|
|
21
|
+
scheduler: 'running',
|
|
22
|
+
});
|
|
23
|
+
} catch (error) {
|
|
24
|
+
return NextResponse.json(
|
|
25
|
+
{
|
|
26
|
+
status: 'error',
|
|
27
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
28
|
+
},
|
|
29
|
+
{ status: 500 },
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { getDocument, updateDocument, deleteDocument, updateKnowledgeActive, getUserById } from '@/lib/db';
|
|
3
|
+
import type { KnowledgeType, KnowledgeSourceType } from '@/lib/db';
|
|
4
|
+
|
|
5
|
+
// GET /api/knowledge/[id] - 获取单个知识
|
|
6
|
+
export async function GET(
|
|
7
|
+
request: NextRequest,
|
|
8
|
+
{ params }: { params: Promise<{ id: string }> }
|
|
9
|
+
) {
|
|
10
|
+
try {
|
|
11
|
+
const { id } = await params;
|
|
12
|
+
const document = await getDocument(id);
|
|
13
|
+
|
|
14
|
+
if (!document) {
|
|
15
|
+
return NextResponse.json(
|
|
16
|
+
{ error: 'Knowledge not found' },
|
|
17
|
+
{ status: 404 }
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return NextResponse.json({ document });
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.error('Failed to get knowledge:', error);
|
|
24
|
+
return NextResponse.json(
|
|
25
|
+
{ error: 'Failed to get knowledge' },
|
|
26
|
+
{ status: 500 }
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// PUT /api/knowledge/[id] - 更新知识
|
|
32
|
+
export async function PUT(
|
|
33
|
+
request: NextRequest,
|
|
34
|
+
{ params }: { params: Promise<{ id: string }> }
|
|
35
|
+
) {
|
|
36
|
+
try {
|
|
37
|
+
const { id } = await params;
|
|
38
|
+
const body = await request.json();
|
|
39
|
+
const { title, content, category, tags, type, sourceType, isActive } = body;
|
|
40
|
+
|
|
41
|
+
const document = await updateDocument(id, {
|
|
42
|
+
title,
|
|
43
|
+
content,
|
|
44
|
+
category,
|
|
45
|
+
tags,
|
|
46
|
+
type: type as KnowledgeType,
|
|
47
|
+
sourceType: sourceType as KnowledgeSourceType,
|
|
48
|
+
isActive,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (!document) {
|
|
52
|
+
return NextResponse.json(
|
|
53
|
+
{ error: 'Knowledge not found' },
|
|
54
|
+
{ status: 404 }
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return NextResponse.json({ document });
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error('Failed to update knowledge:', error);
|
|
61
|
+
return NextResponse.json(
|
|
62
|
+
{ error: 'Failed to update knowledge' },
|
|
63
|
+
{ status: 500 }
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// DELETE /api/knowledge/[id] - 删除知识
|
|
69
|
+
// 查询参数: userId, role (admin 可删除任何知识)
|
|
70
|
+
export async function DELETE(
|
|
71
|
+
request: NextRequest,
|
|
72
|
+
{ params }: { params: Promise<{ id: string }> }
|
|
73
|
+
) {
|
|
74
|
+
try {
|
|
75
|
+
const { id } = await params;
|
|
76
|
+
const { searchParams } = new URL(request.url);
|
|
77
|
+
const userId = searchParams.get('userId');
|
|
78
|
+
const role = searchParams.get('role');
|
|
79
|
+
|
|
80
|
+
// 获取知识详情
|
|
81
|
+
const document = await getDocument(id);
|
|
82
|
+
if (!document) {
|
|
83
|
+
return NextResponse.json(
|
|
84
|
+
{ error: 'Knowledge not found' },
|
|
85
|
+
{ status: 404 }
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 权限检查:管理员可删除任何知识,普通用户只能删除自己的
|
|
90
|
+
if (userId && role !== 'admin') {
|
|
91
|
+
if (document.author !== userId) {
|
|
92
|
+
return NextResponse.json(
|
|
93
|
+
{ error: '无权删除此知识' },
|
|
94
|
+
{ status: 403 }
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const success = await deleteDocument(id);
|
|
100
|
+
|
|
101
|
+
if (!success) {
|
|
102
|
+
return NextResponse.json(
|
|
103
|
+
{ error: 'Knowledge not found' },
|
|
104
|
+
{ status: 404 }
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return NextResponse.json({ success: true });
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error('Failed to delete knowledge:', error);
|
|
111
|
+
return NextResponse.json(
|
|
112
|
+
{ error: 'Failed to delete knowledge' },
|
|
113
|
+
{ status: 500 }
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// PATCH /api/knowledge/[id] - 更新激活状态
|
|
119
|
+
export async function PATCH(
|
|
120
|
+
request: NextRequest,
|
|
121
|
+
{ params }: { params: Promise<{ id: string }> }
|
|
122
|
+
) {
|
|
123
|
+
try {
|
|
124
|
+
const { id } = await params;
|
|
125
|
+
const body = await request.json();
|
|
126
|
+
const { isActive } = body;
|
|
127
|
+
|
|
128
|
+
if (isActive === undefined) {
|
|
129
|
+
return NextResponse.json(
|
|
130
|
+
{ error: 'isActive is required' },
|
|
131
|
+
{ status: 400 }
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const document = await updateKnowledgeActive(id, isActive);
|
|
136
|
+
|
|
137
|
+
if (!document) {
|
|
138
|
+
return NextResponse.json(
|
|
139
|
+
{ error: 'Knowledge not found' },
|
|
140
|
+
{ status: 404 }
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return NextResponse.json({ document });
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.error('Failed to update knowledge active status:', error);
|
|
147
|
+
return NextResponse.json(
|
|
148
|
+
{ error: 'Failed to update knowledge active status' },
|
|
149
|
+
{ status: 500 }
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { createKnowledgeFromSession } from '@/lib/db';
|
|
3
|
+
|
|
4
|
+
// POST /api/knowledge/from-session - 从会话创建知识
|
|
5
|
+
export async function POST(request: NextRequest) {
|
|
6
|
+
try {
|
|
7
|
+
const body = await request.json();
|
|
8
|
+
const { sessionId, title, summary } = body;
|
|
9
|
+
|
|
10
|
+
if (!sessionId || !summary) {
|
|
11
|
+
return NextResponse.json(
|
|
12
|
+
{ error: 'sessionId and summary are required' },
|
|
13
|
+
{ status: 400 }
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const document = await createKnowledgeFromSession(sessionId, summary, title);
|
|
18
|
+
|
|
19
|
+
return NextResponse.json({ success: true, document }, { status: 201 });
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.error('Failed to create knowledge from session:', error);
|
|
22
|
+
return NextResponse.json(
|
|
23
|
+
{ error: 'Failed to create knowledge from session' },
|
|
24
|
+
{ status: 500 }
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
}
|