aiexecode 1.0.157
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/LICENSE +68 -0
- package/README.md +347 -0
- package/config_template/mcp_config.json +3 -0
- package/config_template/package_name_store.json +5 -0
- package/config_template/settings.json +5 -0
- package/index.js +879 -0
- package/mcp-agent-lib/example/01-basic-usage.js +82 -0
- package/mcp-agent-lib/example/02-quick-start.js +52 -0
- package/mcp-agent-lib/example/03-http-server.js +76 -0
- package/mcp-agent-lib/example/04-multiple-servers.js +117 -0
- package/mcp-agent-lib/example/05-error-handling.js +116 -0
- package/mcp-agent-lib/example/06-resources-and-prompts.js +174 -0
- package/mcp-agent-lib/example/07-advanced-configuration.js +191 -0
- package/mcp-agent-lib/example/08-real-world-chatbot.js +331 -0
- package/mcp-agent-lib/example/README.md +346 -0
- package/mcp-agent-lib/index.js +19 -0
- package/mcp-agent-lib/init.sh +3 -0
- package/mcp-agent-lib/package-lock.json +1216 -0
- package/mcp-agent-lib/package.json +53 -0
- package/mcp-agent-lib/sampleFastMCPClient/client.py +25 -0
- package/mcp-agent-lib/sampleFastMCPClient/run.sh +3 -0
- package/mcp-agent-lib/sampleFastMCPServer/run.sh +3 -0
- package/mcp-agent-lib/sampleFastMCPServer/server.py +12 -0
- package/mcp-agent-lib/sampleFastMCPServerElicitationRequest/run.sh +3 -0
- package/mcp-agent-lib/sampleFastMCPServerElicitationRequest/server.py +43 -0
- package/mcp-agent-lib/sampleFastMCPServerRootsRequest/server.py +63 -0
- package/mcp-agent-lib/sampleMCPHost/index.js +386 -0
- package/mcp-agent-lib/sampleMCPHost/mcp_config.json +24 -0
- package/mcp-agent-lib/sampleMCPHostFeatures/elicitation.js +151 -0
- package/mcp-agent-lib/sampleMCPHostFeatures/index.js +166 -0
- package/mcp-agent-lib/sampleMCPHostFeatures/roots.js +197 -0
- package/mcp-agent-lib/src/mcp_client.js +1860 -0
- package/mcp-agent-lib/src/mcp_message_logger.js +517 -0
- package/package.json +72 -0
- package/payload_viewer/out/404/index.html +1 -0
- package/payload_viewer/out/404.html +1 -0
- package/payload_viewer/out/_next/static/chunks/060f9a97930f3d04.js +1 -0
- package/payload_viewer/out/_next/static/chunks/103c802c8f4a5ea1.js +1 -0
- package/payload_viewer/out/_next/static/chunks/16474fd6c6910c45.js +1 -0
- package/payload_viewer/out/_next/static/chunks/17722e3ac4e00587.js +1 -0
- package/payload_viewer/out/_next/static/chunks/305b077a9873cf54.js +1 -0
- package/payload_viewer/out/_next/static/chunks/4c1d05c6741c2bdd.js +5 -0
- package/payload_viewer/out/_next/static/chunks/538cc02e54714b23.js +1 -0
- package/payload_viewer/out/_next/static/chunks/6251fa5907d2b226.js +5 -0
- package/payload_viewer/out/_next/static/chunks/a6dad97d9634a72d.js +1 -0
- package/payload_viewer/out/_next/static/chunks/b6c0459f3789d25c.js +1 -0
- package/payload_viewer/out/_next/static/chunks/b75131b58f8ca46a.css +3 -0
- package/payload_viewer/out/_next/static/chunks/bd2dcf98c9b362f6.js +1 -0
- package/payload_viewer/out/_next/static/chunks/c8a542ae21335479.js +1 -0
- package/payload_viewer/out/_next/static/chunks/cdd12d5c1a5a6064.js +1 -0
- package/payload_viewer/out/_next/static/chunks/e411019f55d87c42.js +1 -0
- package/payload_viewer/out/_next/static/chunks/e60ef129113f6e24.js +1 -0
- package/payload_viewer/out/_next/static/chunks/f1ac9047ac4a3fde.js +1 -0
- package/payload_viewer/out/_next/static/chunks/turbopack-0ac29803ce3c3c7a.js +3 -0
- package/payload_viewer/out/_next/static/chunks/turbopack-89db4c64206a73e4.js +3 -0
- package/payload_viewer/out/_next/static/chunks/turbopack-a5b8235fa59d7119.js +3 -0
- package/payload_viewer/out/_next/static/media/4fa387ec64143e14-s.c1fdd6c2.woff2 +0 -0
- package/payload_viewer/out/_next/static/media/7178b3e590c64307-s.b97b3418.woff2 +0 -0
- package/payload_viewer/out/_next/static/media/797e433ab948586e-s.p.dbea232f.woff2 +0 -0
- package/payload_viewer/out/_next/static/media/8a480f0b521d4e75-s.8e0177b5.woff2 +0 -0
- package/payload_viewer/out/_next/static/media/bbc41e54d2fcbd21-s.799d8ef8.woff2 +0 -0
- package/payload_viewer/out/_next/static/media/caa3a2e1cccd8315-s.p.853070df.woff2 +0 -0
- package/payload_viewer/out/_next/static/media/favicon.0b3bf435.ico +0 -0
- package/payload_viewer/out/_next/static/uqQYtKpKV7kCSkUbLgdfJ/_buildManifest.js +14 -0
- package/payload_viewer/out/_next/static/uqQYtKpKV7kCSkUbLgdfJ/_clientMiddlewareManifest.json +1 -0
- package/payload_viewer/out/_next/static/uqQYtKpKV7kCSkUbLgdfJ/_ssgManifest.js +1 -0
- package/payload_viewer/out/favicon.ico +0 -0
- package/payload_viewer/out/file.svg +1 -0
- package/payload_viewer/out/globe.svg +1 -0
- package/payload_viewer/out/index.html +1 -0
- package/payload_viewer/out/index.txt +23 -0
- package/payload_viewer/out/next.svg +1 -0
- package/payload_viewer/out/vercel.svg +1 -0
- package/payload_viewer/out/window.svg +1 -0
- package/payload_viewer/web_server.js +861 -0
- package/prompts/completion_judge.txt +128 -0
- package/prompts/orchestrator.txt +1213 -0
- package/src/LLMClient/client.js +1375 -0
- package/src/LLMClient/converters/input-normalizer.js +238 -0
- package/src/LLMClient/converters/responses-to-claude.js +503 -0
- package/src/LLMClient/converters/responses-to-gemini.js +648 -0
- package/src/LLMClient/converters/responses-to-ollama.js +348 -0
- package/src/LLMClient/converters/responses-to-zai.js +667 -0
- package/src/LLMClient/errors.js +398 -0
- package/src/LLMClient/index.js +36 -0
- package/src/ai_based/completion_judge.js +421 -0
- package/src/ai_based/orchestrator.js +527 -0
- package/src/ai_based/pip_package_installer.js +173 -0
- package/src/ai_based/pip_package_lookup.js +197 -0
- package/src/cli/mcp_cli.js +70 -0
- package/src/cli/mcp_commands.js +255 -0
- package/src/commands/agents.js +18 -0
- package/src/commands/apikey.js +55 -0
- package/src/commands/bg.js +140 -0
- package/src/commands/commands.js +56 -0
- package/src/commands/debug.js +54 -0
- package/src/commands/exit.js +19 -0
- package/src/commands/help.js +35 -0
- package/src/commands/mcp.js +128 -0
- package/src/commands/model.js +176 -0
- package/src/commands/setup.js +13 -0
- package/src/commands/skills.js +51 -0
- package/src/commands/tools.js +165 -0
- package/src/commands/viewer.js +147 -0
- package/src/config/ai_models.js +312 -0
- package/src/config/config.js +10 -0
- package/src/config/constants.js +71 -0
- package/src/config/feature_flags.js +15 -0
- package/src/frontend/App.js +1263 -0
- package/src/frontend/README.md +81 -0
- package/src/frontend/components/AutocompleteMenu.js +47 -0
- package/src/frontend/components/BackgroundProcessList.js +175 -0
- package/src/frontend/components/BlankLine.js +62 -0
- package/src/frontend/components/ConversationItem.js +893 -0
- package/src/frontend/components/CurrentModelView.js +43 -0
- package/src/frontend/components/FileDiffViewer.js +616 -0
- package/src/frontend/components/Footer.js +25 -0
- package/src/frontend/components/Header.js +42 -0
- package/src/frontend/components/HelpView.js +154 -0
- package/src/frontend/components/Input.js +344 -0
- package/src/frontend/components/LoadingIndicator.js +31 -0
- package/src/frontend/components/ModelListView.js +49 -0
- package/src/frontend/components/ModelUpdatedView.js +22 -0
- package/src/frontend/components/SessionSpinner.js +66 -0
- package/src/frontend/components/SetupWizard.js +242 -0
- package/src/frontend/components/StreamOutput.js +34 -0
- package/src/frontend/components/TodoList.js +56 -0
- package/src/frontend/components/ToolApprovalPrompt.js +452 -0
- package/src/frontend/design/themeColors.js +42 -0
- package/src/frontend/hooks/useCompletion.js +84 -0
- package/src/frontend/hooks/useFileCompletion.js +467 -0
- package/src/frontend/hooks/useKeypress.js +145 -0
- package/src/frontend/index.js +65 -0
- package/src/frontend/utils/GridRenderer.js +140 -0
- package/src/frontend/utils/InlineFormatter.js +156 -0
- package/src/frontend/utils/diffUtils.js +235 -0
- package/src/frontend/utils/inputBuffer.js +441 -0
- package/src/frontend/utils/markdownParser.js +377 -0
- package/src/frontend/utils/outputRedirector.js +47 -0
- package/src/frontend/utils/renderInkComponent.js +42 -0
- package/src/frontend/utils/syntaxHighlighter.js +149 -0
- package/src/frontend/utils/toolUIFormatter.js +261 -0
- package/src/system/agents_loader.js +170 -0
- package/src/system/ai_request.js +737 -0
- package/src/system/background_process.js +317 -0
- package/src/system/code_executer.js +1233 -0
- package/src/system/command_loader.js +40 -0
- package/src/system/command_parser.js +133 -0
- package/src/system/conversation_state.js +265 -0
- package/src/system/conversation_trimmer.js +265 -0
- package/src/system/custom_command_loader.js +395 -0
- package/src/system/file_integrity.js +466 -0
- package/src/system/import_analyzer.py +174 -0
- package/src/system/log.js +82 -0
- package/src/system/mcp_integration.js +304 -0
- package/src/system/output_helper.js +89 -0
- package/src/system/session.js +1393 -0
- package/src/system/session_memory.js +481 -0
- package/src/system/skill_loader.js +324 -0
- package/src/system/system_info.js +483 -0
- package/src/system/tool_approval.js +160 -0
- package/src/system/tool_registry.js +184 -0
- package/src/system/ui_events.js +279 -0
- package/src/tools/code_editor.js +792 -0
- package/src/tools/file_reader.js +385 -0
- package/src/tools/glob.js +263 -0
- package/src/tools/response_message.js +30 -0
- package/src/tools/ripgrep.js +554 -0
- package/src/tools/skill_tool.js +122 -0
- package/src/tools/todo_write.js +182 -0
- package/src/tools/web_download.py +74 -0
- package/src/tools/web_downloader.js +83 -0
- package/src/util/clone.js +174 -0
- package/src/util/config.js +203 -0
- package/src/util/config_migration.js +174 -0
- package/src/util/debug_log.js +49 -0
- package/src/util/exit_handler.js +53 -0
- package/src/util/file_reference_parser.js +132 -0
- package/src/util/mcp_config_manager.js +159 -0
- package/src/util/output_formatter.js +50 -0
- package/src/util/path_helper.js +27 -0
- package/src/util/path_validator.js +178 -0
- package/src/util/prompt_loader.js +184 -0
- package/src/util/rag_helper.js +101 -0
- package/src/util/safe_fs.js +645 -0
- package/src/util/setup_wizard.js +62 -0
- package/src/util/text_formatter.js +33 -0
- package/src/util/version_check.js +116 -0
|
@@ -0,0 +1,861 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import OpenAI from 'openai';
|
|
6
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
7
|
+
function consolelog() { }
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(__filename);
|
|
11
|
+
|
|
12
|
+
// Get home directory
|
|
13
|
+
function getHomeDirectory() {
|
|
14
|
+
return process.env.HOME || process.env.USERPROFILE || '/home/ubuntu';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Get AI Agent config directory
|
|
18
|
+
const CONFIG_DIR = path.join(getHomeDirectory(), '.aiexe');
|
|
19
|
+
const PAYLOAD_LOG_DIR = path.join(CONFIG_DIR, 'payload_log');
|
|
20
|
+
|
|
21
|
+
// Load AI Agent settings
|
|
22
|
+
function loadAIAgentSettings() {
|
|
23
|
+
try {
|
|
24
|
+
const settingsPath = path.join(CONFIG_DIR, 'settings.json');
|
|
25
|
+
if (fs.existsSync(settingsPath)) {
|
|
26
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
|
27
|
+
consolelog('✅ AI Agent settings loaded from ~/.aiexe/settings.json');
|
|
28
|
+
return settings;
|
|
29
|
+
}
|
|
30
|
+
} catch (error) {
|
|
31
|
+
consolelog('⚠️ Could not load AI Agent settings:', error.message);
|
|
32
|
+
}
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const aiAgentSettings = loadAIAgentSettings();
|
|
37
|
+
|
|
38
|
+
// Initialize OpenAI client (check multiple sources for API key)
|
|
39
|
+
let openai = null;
|
|
40
|
+
const openaiApiKey = process.env.OPENAI_API_KEY || aiAgentSettings.OPENAI_API_KEY;
|
|
41
|
+
|
|
42
|
+
if (openaiApiKey) {
|
|
43
|
+
openai = new OpenAI({
|
|
44
|
+
apiKey: openaiApiKey,
|
|
45
|
+
});
|
|
46
|
+
consolelog('✅ OpenAI client initialized');
|
|
47
|
+
} else {
|
|
48
|
+
consolelog('⚠️ OpenAI API key not found - API testing will return mock responses');
|
|
49
|
+
consolelog(' Checked: process.env.OPENAI_API_KEY and ~/.aiexe/settings.json');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Initialize Z.AI client (Anthropic SDK with Z.AI base URL)
|
|
53
|
+
let zaiClient = null;
|
|
54
|
+
const zaiApiKey = process.env.ZAI_API_KEY || aiAgentSettings.API_KEY;
|
|
55
|
+
const ZAI_BASE_URL = 'https://api.z.ai/api/anthropic';
|
|
56
|
+
|
|
57
|
+
// Check if API key is Z.AI format (32-char hex + '.' + 16-char alphanumeric)
|
|
58
|
+
function isZaiApiKey(apiKey) {
|
|
59
|
+
if (!apiKey || typeof apiKey !== 'string') return false;
|
|
60
|
+
return /^[a-f0-9]{32}\.[A-Za-z0-9]{16}$/.test(apiKey);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (zaiApiKey && isZaiApiKey(zaiApiKey)) {
|
|
64
|
+
zaiClient = new Anthropic({
|
|
65
|
+
apiKey: zaiApiKey,
|
|
66
|
+
baseURL: ZAI_BASE_URL,
|
|
67
|
+
});
|
|
68
|
+
consolelog('✅ Z.AI client initialized (Anthropic SDK with Z.AI base URL)');
|
|
69
|
+
} else {
|
|
70
|
+
consolelog('⚠️ Z.AI API key not found or invalid format - Z.AI testing will return mock responses');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Initialize Anthropic client
|
|
74
|
+
let anthropicClient = null;
|
|
75
|
+
const anthropicApiKey = process.env.ANTHROPIC_API_KEY || aiAgentSettings.ANTHROPIC_API_KEY;
|
|
76
|
+
|
|
77
|
+
// Check if API key is Anthropic format (sk-ant-...)
|
|
78
|
+
function isAnthropicApiKey(apiKey) {
|
|
79
|
+
if (!apiKey || typeof apiKey !== 'string') return false;
|
|
80
|
+
return apiKey.startsWith('sk-ant-');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (anthropicApiKey && isAnthropicApiKey(anthropicApiKey)) {
|
|
84
|
+
anthropicClient = new Anthropic({
|
|
85
|
+
apiKey: anthropicApiKey,
|
|
86
|
+
});
|
|
87
|
+
consolelog('✅ Anthropic client initialized');
|
|
88
|
+
} else {
|
|
89
|
+
consolelog('⚠️ Anthropic API key not found or invalid format - Anthropic testing will return mock responses');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
// ID generation function
|
|
94
|
+
function generateId() {
|
|
95
|
+
return Math.random().toString(36).substring(2, 12) + Date.now().toString(36);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function startWebServer(port = 3000) {
|
|
99
|
+
const app = express();
|
|
100
|
+
|
|
101
|
+
// Parse JSON bodies (increase limit for large AI request/response payloads)
|
|
102
|
+
app.use(express.json({ limit: '50mb' }));
|
|
103
|
+
|
|
104
|
+
// Serve static files from Next.js build
|
|
105
|
+
const outPath = path.join(__dirname, 'out');
|
|
106
|
+
const isProduction = true; // Always production mode
|
|
107
|
+
|
|
108
|
+
// Serve all static files from 'out' directory (Next.js export output)
|
|
109
|
+
if (fs.existsSync(outPath)) {
|
|
110
|
+
app.use(express.static(outPath));
|
|
111
|
+
consolelog(`📁 Serving Next.js static files from: ${outPath}`);
|
|
112
|
+
} else {
|
|
113
|
+
consolelog(`⚠️ Warning: 'out' directory not found at ${outPath}`);
|
|
114
|
+
consolelog(` Please run: npm run build`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Helper function to parse log filename
|
|
118
|
+
function parseLogFileName(fileName) {
|
|
119
|
+
const match = fileName.match(/^(\d{4}-\d{2}-\d{2})_(\d{9})_(.+)_(REQ|RES)\.json$/);
|
|
120
|
+
|
|
121
|
+
if (match) {
|
|
122
|
+
const [, date, timestamp, component, type] = match;
|
|
123
|
+
const hour = timestamp.substring(0, 2);
|
|
124
|
+
const minute = timestamp.substring(2, 4);
|
|
125
|
+
const second = timestamp.substring(4, 6);
|
|
126
|
+
const millisecond = timestamp.substring(6, 9);
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
date,
|
|
130
|
+
timestamp,
|
|
131
|
+
component,
|
|
132
|
+
type,
|
|
133
|
+
datetime: new Date(`${date}T${hour}:${minute}:${second}.${millisecond}Z`)
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Helper function to create workflow groups (improved version from payload_viewer_old)
|
|
141
|
+
function createWorkflowGroups(files) {
|
|
142
|
+
const workflows = new Map();
|
|
143
|
+
|
|
144
|
+
files.forEach(file => {
|
|
145
|
+
if (!file.parsedInfo) return;
|
|
146
|
+
|
|
147
|
+
const { date, timestamp } = file.parsedInfo;
|
|
148
|
+
const workflowKey = `${date}_${timestamp.substring(0, 6)}`; // Group by date and time up to seconds
|
|
149
|
+
|
|
150
|
+
if (!workflows.has(workflowKey)) {
|
|
151
|
+
workflows.set(workflowKey, {
|
|
152
|
+
id: workflowKey,
|
|
153
|
+
date: date,
|
|
154
|
+
startTime: file.parsedInfo.datetime,
|
|
155
|
+
files: [],
|
|
156
|
+
components: new Set(),
|
|
157
|
+
status: 'unknown'
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const workflow = workflows.get(workflowKey);
|
|
162
|
+
workflow.files.push(file);
|
|
163
|
+
workflow.components.add(file.parsedInfo.component);
|
|
164
|
+
|
|
165
|
+
// Determine workflow status
|
|
166
|
+
if (workflow.files.some(f => f.parsedInfo.component === 'verifier' && f.parsedInfo.type === 'RES')) {
|
|
167
|
+
workflow.status = 'completed';
|
|
168
|
+
} else if (workflow.files.length > 0) {
|
|
169
|
+
workflow.status = 'in_progress';
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Convert Map to array and sort by time
|
|
174
|
+
return Array.from(workflows.values())
|
|
175
|
+
.sort((a, b) => b.startTime - a.startTime)
|
|
176
|
+
.map(workflow => ({
|
|
177
|
+
...workflow,
|
|
178
|
+
components: Array.from(workflow.components),
|
|
179
|
+
duration: calculateWorkflowDuration(workflow.files),
|
|
180
|
+
startTime: workflow.startTime,
|
|
181
|
+
endTime: workflow.files.length > 0 ?
|
|
182
|
+
new Date(Math.max(...workflow.files.map(f => f.parsedInfo ? new Date(f.parsedInfo.datetime).getTime() : 0))).toISOString() :
|
|
183
|
+
workflow.startTime
|
|
184
|
+
}));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Helper function to calculate workflow duration
|
|
188
|
+
function calculateWorkflowDuration(files) {
|
|
189
|
+
if (files.length < 2) return 0;
|
|
190
|
+
|
|
191
|
+
const times = files
|
|
192
|
+
.filter(f => f.parsedInfo)
|
|
193
|
+
.map(f => new Date(f.parsedInfo.datetime))
|
|
194
|
+
.sort((a, b) => a.getTime() - b.getTime());
|
|
195
|
+
|
|
196
|
+
if (times.length < 2) return 0;
|
|
197
|
+
|
|
198
|
+
return times[times.length - 1].getTime() - times[0].getTime();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// API routes for payload logs
|
|
202
|
+
app.get('/api/logs', (req, res) => {
|
|
203
|
+
try {
|
|
204
|
+
const logDir = PAYLOAD_LOG_DIR;
|
|
205
|
+
if (!fs.existsSync(logDir)) {
|
|
206
|
+
return res.json({ files: [], workflows: [] });
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const files = fs.readdirSync(logDir)
|
|
210
|
+
.filter(file => file.endsWith('.json'))
|
|
211
|
+
.map(file => {
|
|
212
|
+
const filePath = path.join(logDir, file);
|
|
213
|
+
const stats = fs.statSync(filePath);
|
|
214
|
+
const parsedInfo = parseLogFileName(file);
|
|
215
|
+
|
|
216
|
+
const fileInfo = {
|
|
217
|
+
name: file,
|
|
218
|
+
size: stats.size,
|
|
219
|
+
modified: stats.mtime.toISOString(),
|
|
220
|
+
created: stats.birthtime.toISOString(),
|
|
221
|
+
extension: path.extname(file),
|
|
222
|
+
parsedInfo: parsedInfo || undefined
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// Convert datetime to ISO string if it exists
|
|
226
|
+
if (fileInfo.parsedInfo && fileInfo.parsedInfo.datetime) {
|
|
227
|
+
fileInfo.parsedInfo.datetime = fileInfo.parsedInfo.datetime.toISOString();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return fileInfo;
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// Sort files by time
|
|
234
|
+
files.sort((a, b) => {
|
|
235
|
+
const timeA = a.parsedInfo?.datetime ? new Date(a.parsedInfo.datetime) : new Date(a.created);
|
|
236
|
+
const timeB = b.parsedInfo?.datetime ? new Date(b.parsedInfo.datetime) : new Date(b.created);
|
|
237
|
+
return timeB.getTime() - timeA.getTime();
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
const workflows = createWorkflowGroups(files);
|
|
241
|
+
|
|
242
|
+
res.json({ files, workflows });
|
|
243
|
+
} catch (error) {
|
|
244
|
+
consolelog('Error reading log files:', error);
|
|
245
|
+
res.status(500).json({ error: 'Failed to read log files' });
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
app.get('/api/logs/:filename', (req, res) => {
|
|
250
|
+
try {
|
|
251
|
+
const filename = decodeURIComponent(req.params.filename);
|
|
252
|
+
const filePath = path.join(PAYLOAD_LOG_DIR, filename);
|
|
253
|
+
|
|
254
|
+
// Security check
|
|
255
|
+
if (!filePath.startsWith(PAYLOAD_LOG_DIR)) {
|
|
256
|
+
return res.status(400).json({ error: 'Invalid file path' });
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (!fs.existsSync(filePath)) {
|
|
260
|
+
return res.status(404).json({ error: 'File not found' });
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
264
|
+
const data = JSON.parse(content);
|
|
265
|
+
res.json(data);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
consolelog('Error reading file:', error);
|
|
268
|
+
if (error instanceof SyntaxError) {
|
|
269
|
+
res.status(400).json({ error: 'Invalid JSON file' });
|
|
270
|
+
} else {
|
|
271
|
+
res.status(500).json({ error: 'Failed to read file' });
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
app.put('/api/logs/:filename', (req, res) => {
|
|
277
|
+
try {
|
|
278
|
+
const filename = decodeURIComponent(req.params.filename);
|
|
279
|
+
const filePath = path.join(PAYLOAD_LOG_DIR, filename);
|
|
280
|
+
|
|
281
|
+
// Security check
|
|
282
|
+
if (!filePath.startsWith(PAYLOAD_LOG_DIR)) {
|
|
283
|
+
return res.status(400).json({ error: 'Invalid file path' });
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const body = JSON.stringify(req.body, null, 2);
|
|
287
|
+
|
|
288
|
+
// Backup original file
|
|
289
|
+
const backupPath = `${filePath}.backup.${Date.now()}`;
|
|
290
|
+
if (fs.existsSync(filePath)) {
|
|
291
|
+
fs.copyFileSync(filePath, backupPath);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Save file
|
|
295
|
+
fs.writeFileSync(filePath, body, 'utf-8');
|
|
296
|
+
|
|
297
|
+
res.json({ success: true });
|
|
298
|
+
} catch (error) {
|
|
299
|
+
consolelog('Error saving file:', error);
|
|
300
|
+
res.status(500).json({ error: 'Failed to save file' });
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// OpenAI API test endpoint
|
|
305
|
+
app.post('/api/test-openai', async (req, res) => {
|
|
306
|
+
try {
|
|
307
|
+
const payload = req.body;
|
|
308
|
+
|
|
309
|
+
// Actual OpenAI API call (if API key is available)
|
|
310
|
+
if (openai && openaiApiKey) {
|
|
311
|
+
// Use OpenAI responses.create (pass payload as is)
|
|
312
|
+
let response = await openai.responses.create(payload);
|
|
313
|
+
let allToolOutputs = [];
|
|
314
|
+
let currentPayload = payload;
|
|
315
|
+
let allResponses = [response];
|
|
316
|
+
|
|
317
|
+
// Handle function_calls iteratively
|
|
318
|
+
let maxIterations = 10; // Prevent infinite loops
|
|
319
|
+
let iteration = 0;
|
|
320
|
+
let hasProcessedFunctionCalls = false;
|
|
321
|
+
|
|
322
|
+
// Process function_calls while they exist
|
|
323
|
+
while (iteration < maxIterations) {
|
|
324
|
+
// Check response structure (response or response.data)
|
|
325
|
+
const responseData = response.data || response;
|
|
326
|
+
|
|
327
|
+
if (!responseData || !responseData.output || !Array.isArray(responseData.output)) {
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const functionCalls = responseData.output.filter(item => item.type === 'function_call');
|
|
332
|
+
|
|
333
|
+
if (functionCalls.length === 0) {
|
|
334
|
+
if (hasProcessedFunctionCalls) {
|
|
335
|
+
break; // No more function_calls to execute
|
|
336
|
+
} else {
|
|
337
|
+
break; // No function_calls in first response
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
hasProcessedFunctionCalls = true;
|
|
342
|
+
const currentIterationOutputs = [];
|
|
343
|
+
|
|
344
|
+
// Execute each function_call sequentially
|
|
345
|
+
for (const functionCall of functionCalls) {
|
|
346
|
+
try {
|
|
347
|
+
// Mock function execution for now
|
|
348
|
+
const toolOutput = {
|
|
349
|
+
tool_call_id: functionCall.call_id || functionCall.id || generateId(),
|
|
350
|
+
output: JSON.stringify({
|
|
351
|
+
result: "Mock function execution result",
|
|
352
|
+
status: "success",
|
|
353
|
+
timestamp: new Date().toISOString()
|
|
354
|
+
})
|
|
355
|
+
};
|
|
356
|
+
currentIterationOutputs.push(toolOutput);
|
|
357
|
+
allToolOutputs.push(toolOutput);
|
|
358
|
+
} catch (error) {
|
|
359
|
+
consolelog(`Function call execution failed: ${error.message}`);
|
|
360
|
+
const errorOutput = {
|
|
361
|
+
tool_call_id: functionCall.call_id || functionCall.id || generateId(),
|
|
362
|
+
output: JSON.stringify({
|
|
363
|
+
error: `Execution failed: ${error.message}`,
|
|
364
|
+
status: "failed",
|
|
365
|
+
timestamp: new Date().toISOString()
|
|
366
|
+
})
|
|
367
|
+
};
|
|
368
|
+
currentIterationOutputs.push(errorOutput);
|
|
369
|
+
allToolOutputs.push(errorOutput);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Continuation request with tool_outputs
|
|
374
|
+
if (currentIterationOutputs.length > 0) {
|
|
375
|
+
currentPayload = {
|
|
376
|
+
...payload,
|
|
377
|
+
tool_outputs: [...allToolOutputs],
|
|
378
|
+
response_id: response?.id || responseData?.id,
|
|
379
|
+
messages: [
|
|
380
|
+
...(payload.messages || []),
|
|
381
|
+
]
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
try {
|
|
385
|
+
response = await openai.responses.create(currentPayload);
|
|
386
|
+
allResponses.push(response);
|
|
387
|
+
iteration++;
|
|
388
|
+
} catch (error) {
|
|
389
|
+
consolelog(`Continuation request failed: ${error.message}`);
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
} else {
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Final response composition
|
|
398
|
+
const finalResponse = allResponses[0];
|
|
399
|
+
const finalResponseData = finalResponse.data || finalResponse;
|
|
400
|
+
|
|
401
|
+
res.json({
|
|
402
|
+
success: true,
|
|
403
|
+
provider: 'openai',
|
|
404
|
+
status: 200,
|
|
405
|
+
data: {
|
|
406
|
+
...finalResponseData,
|
|
407
|
+
provider: 'openai',
|
|
408
|
+
tool_outputs: allToolOutputs,
|
|
409
|
+
all_responses: allResponses,
|
|
410
|
+
iterations_executed: iteration,
|
|
411
|
+
function_calls_processed: allToolOutputs.length
|
|
412
|
+
},
|
|
413
|
+
tool_outputs: allToolOutputs,
|
|
414
|
+
iterations: iteration,
|
|
415
|
+
originalPayload: payload,
|
|
416
|
+
convertedPayload: payload
|
|
417
|
+
});
|
|
418
|
+
} else {
|
|
419
|
+
// Mock response when API key is not available
|
|
420
|
+
res.json({
|
|
421
|
+
success: false,
|
|
422
|
+
provider: 'openai',
|
|
423
|
+
error: 'OPENAI_API_KEY environment variable not set',
|
|
424
|
+
mockResponse: {
|
|
425
|
+
taskName: "api_test_RES",
|
|
426
|
+
timestamp: new Date().toISOString(),
|
|
427
|
+
data: {
|
|
428
|
+
id: `resp_${generateId()}`,
|
|
429
|
+
object: "response",
|
|
430
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
431
|
+
status: "completed",
|
|
432
|
+
model: payload.model || 'gpt-5-mini',
|
|
433
|
+
provider: 'openai',
|
|
434
|
+
output: [{
|
|
435
|
+
id: `msg_${generateId()}`,
|
|
436
|
+
type: "message",
|
|
437
|
+
status: "completed",
|
|
438
|
+
content: [{
|
|
439
|
+
type: "output_text",
|
|
440
|
+
text: 'This is a mock response. Set OPENAI_API_KEY to get real responses.'
|
|
441
|
+
}],
|
|
442
|
+
role: "assistant"
|
|
443
|
+
}],
|
|
444
|
+
usage: {
|
|
445
|
+
input_tokens: 10,
|
|
446
|
+
output_tokens: 15,
|
|
447
|
+
total_tokens: 25
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
},
|
|
451
|
+
originalPayload: payload
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
} catch (error) {
|
|
455
|
+
res.status(500).json({
|
|
456
|
+
success: false,
|
|
457
|
+
provider: 'openai',
|
|
458
|
+
error: error.message,
|
|
459
|
+
originalPayload: req.body
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
// Anthropic API test endpoint
|
|
465
|
+
app.post('/api/test-anthropic', async (req, res) => {
|
|
466
|
+
try {
|
|
467
|
+
const payload = req.body;
|
|
468
|
+
|
|
469
|
+
if (anthropicClient && anthropicApiKey && isAnthropicApiKey(anthropicApiKey)) {
|
|
470
|
+
// Convert Responses API format to Anthropic Messages API format if needed
|
|
471
|
+
let anthropicPayload = { ...payload };
|
|
472
|
+
|
|
473
|
+
// If payload uses Responses API format (input array), convert to messages
|
|
474
|
+
if (payload.input && Array.isArray(payload.input)) {
|
|
475
|
+
const messages = [];
|
|
476
|
+
let systemMessage = null;
|
|
477
|
+
|
|
478
|
+
for (const item of payload.input) {
|
|
479
|
+
if (item.role === 'system') {
|
|
480
|
+
if (Array.isArray(item.content)) {
|
|
481
|
+
systemMessage = item.content.map(c => c.text || c).join('\n');
|
|
482
|
+
} else if (typeof item.content === 'string') {
|
|
483
|
+
systemMessage = item.content;
|
|
484
|
+
}
|
|
485
|
+
} else if (item.role === 'user' || item.role === 'assistant') {
|
|
486
|
+
let content;
|
|
487
|
+
if (Array.isArray(item.content)) {
|
|
488
|
+
content = item.content.map(c => ({
|
|
489
|
+
type: 'text',
|
|
490
|
+
text: c.text || c
|
|
491
|
+
}));
|
|
492
|
+
} else {
|
|
493
|
+
content = [{ type: 'text', text: item.content || '' }];
|
|
494
|
+
}
|
|
495
|
+
messages.push({ role: item.role, content });
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
anthropicPayload = {
|
|
500
|
+
model: payload.model || 'claude-3-haiku-20240307',
|
|
501
|
+
max_tokens: payload.max_output_tokens || payload.max_tokens || 4096,
|
|
502
|
+
messages: messages,
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
if (systemMessage) {
|
|
506
|
+
anthropicPayload.system = systemMessage;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Ensure required fields
|
|
511
|
+
if (!anthropicPayload.model) anthropicPayload.model = 'claude-3-haiku-20240307';
|
|
512
|
+
if (!anthropicPayload.max_tokens) anthropicPayload.max_tokens = 4096;
|
|
513
|
+
|
|
514
|
+
// Make the API call
|
|
515
|
+
const response = await anthropicClient.messages.create(anthropicPayload);
|
|
516
|
+
|
|
517
|
+
// Convert Anthropic response to Responses API format
|
|
518
|
+
const output = [];
|
|
519
|
+
let outputText = '';
|
|
520
|
+
|
|
521
|
+
if (response.content && Array.isArray(response.content)) {
|
|
522
|
+
for (const block of response.content) {
|
|
523
|
+
if (block.type === 'text') {
|
|
524
|
+
outputText += block.text;
|
|
525
|
+
} else if (block.type === 'tool_use') {
|
|
526
|
+
output.push({
|
|
527
|
+
type: 'function_call',
|
|
528
|
+
id: block.id,
|
|
529
|
+
call_id: block.id,
|
|
530
|
+
name: block.name,
|
|
531
|
+
arguments: JSON.stringify(block.input)
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if (outputText) {
|
|
538
|
+
output.unshift({
|
|
539
|
+
type: 'message',
|
|
540
|
+
id: `msg_${generateId()}`,
|
|
541
|
+
status: 'completed',
|
|
542
|
+
role: 'assistant',
|
|
543
|
+
content: [{
|
|
544
|
+
type: 'output_text',
|
|
545
|
+
text: outputText
|
|
546
|
+
}]
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const convertedResponse = {
|
|
551
|
+
id: response.id,
|
|
552
|
+
object: 'response',
|
|
553
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
554
|
+
status: response.stop_reason === 'end_turn' ? 'completed' : 'incomplete',
|
|
555
|
+
model: response.model,
|
|
556
|
+
provider: 'anthropic',
|
|
557
|
+
output: output,
|
|
558
|
+
usage: {
|
|
559
|
+
input_tokens: response.usage?.input_tokens || 0,
|
|
560
|
+
output_tokens: response.usage?.output_tokens || 0,
|
|
561
|
+
total_tokens: (response.usage?.input_tokens || 0) + (response.usage?.output_tokens || 0),
|
|
562
|
+
cache_creation_input_tokens: response.usage?.cache_creation_input_tokens,
|
|
563
|
+
cache_read_input_tokens: response.usage?.cache_read_input_tokens
|
|
564
|
+
},
|
|
565
|
+
stop_reason: response.stop_reason
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
res.json({
|
|
569
|
+
success: true,
|
|
570
|
+
provider: 'anthropic',
|
|
571
|
+
status: 200,
|
|
572
|
+
data: convertedResponse,
|
|
573
|
+
originalPayload: payload,
|
|
574
|
+
convertedPayload: anthropicPayload,
|
|
575
|
+
rawResponse: response
|
|
576
|
+
});
|
|
577
|
+
} else {
|
|
578
|
+
// Mock response when API key is not available
|
|
579
|
+
res.json({
|
|
580
|
+
success: false,
|
|
581
|
+
provider: 'anthropic',
|
|
582
|
+
error: 'Anthropic API key not found or invalid format',
|
|
583
|
+
hint: 'Anthropic API key format: sk-ant-...',
|
|
584
|
+
mockResponse: {
|
|
585
|
+
taskName: "api_test_RES",
|
|
586
|
+
timestamp: new Date().toISOString(),
|
|
587
|
+
data: {
|
|
588
|
+
id: `resp_${generateId()}`,
|
|
589
|
+
object: "response",
|
|
590
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
591
|
+
status: "completed",
|
|
592
|
+
model: payload.model || 'claude-3-haiku-20240307',
|
|
593
|
+
provider: 'anthropic',
|
|
594
|
+
output: [{
|
|
595
|
+
id: `msg_${generateId()}`,
|
|
596
|
+
type: "message",
|
|
597
|
+
status: "completed",
|
|
598
|
+
content: [{
|
|
599
|
+
type: "output_text",
|
|
600
|
+
text: 'This is a mock response. Set a valid Anthropic API key to get real responses.'
|
|
601
|
+
}],
|
|
602
|
+
role: "assistant"
|
|
603
|
+
}],
|
|
604
|
+
usage: {
|
|
605
|
+
input_tokens: 10,
|
|
606
|
+
output_tokens: 15,
|
|
607
|
+
total_tokens: 25
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
originalPayload: payload
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
} catch (error) {
|
|
615
|
+
res.status(500).json({
|
|
616
|
+
success: false,
|
|
617
|
+
provider: 'anthropic',
|
|
618
|
+
error: error.message,
|
|
619
|
+
originalPayload: req.body
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
// Z.AI (GLM) API test endpoint - Uses Anthropic SDK with Z.AI base URL
|
|
625
|
+
app.post('/api/test-zai', async (req, res) => {
|
|
626
|
+
try {
|
|
627
|
+
const payload = req.body;
|
|
628
|
+
|
|
629
|
+
if (zaiClient && zaiApiKey && isZaiApiKey(zaiApiKey)) {
|
|
630
|
+
// Convert Responses API format to Anthropic Messages API format if needed
|
|
631
|
+
let zaiPayload = { ...payload };
|
|
632
|
+
|
|
633
|
+
// If payload uses Responses API format (input array), convert to messages
|
|
634
|
+
if (payload.input && Array.isArray(payload.input)) {
|
|
635
|
+
const messages = [];
|
|
636
|
+
let systemMessage = null;
|
|
637
|
+
|
|
638
|
+
for (const item of payload.input) {
|
|
639
|
+
if (item.role === 'system') {
|
|
640
|
+
// Extract system message content
|
|
641
|
+
if (Array.isArray(item.content)) {
|
|
642
|
+
systemMessage = item.content.map(c => c.text || c).join('\n');
|
|
643
|
+
} else if (typeof item.content === 'string') {
|
|
644
|
+
systemMessage = item.content;
|
|
645
|
+
}
|
|
646
|
+
} else if (item.role === 'user' || item.role === 'assistant') {
|
|
647
|
+
let content;
|
|
648
|
+
if (Array.isArray(item.content)) {
|
|
649
|
+
content = item.content.map(c => ({
|
|
650
|
+
type: 'text',
|
|
651
|
+
text: c.text || c
|
|
652
|
+
}));
|
|
653
|
+
} else {
|
|
654
|
+
content = [{ type: 'text', text: item.content || '' }];
|
|
655
|
+
}
|
|
656
|
+
messages.push({ role: item.role, content });
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
zaiPayload = {
|
|
661
|
+
model: payload.model || 'glm-4.5',
|
|
662
|
+
max_tokens: payload.max_output_tokens || payload.max_tokens || 8192,
|
|
663
|
+
messages: messages,
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
if (systemMessage) {
|
|
667
|
+
zaiPayload.system = systemMessage;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// Ensure required fields
|
|
672
|
+
if (!zaiPayload.model) zaiPayload.model = 'glm-4.5';
|
|
673
|
+
if (!zaiPayload.max_tokens) zaiPayload.max_tokens = 8192;
|
|
674
|
+
|
|
675
|
+
// Make the API call
|
|
676
|
+
const response = await zaiClient.messages.create(zaiPayload);
|
|
677
|
+
|
|
678
|
+
// Convert Anthropic response to Responses API format
|
|
679
|
+
const output = [];
|
|
680
|
+
let outputText = '';
|
|
681
|
+
|
|
682
|
+
if (response.content && Array.isArray(response.content)) {
|
|
683
|
+
for (const block of response.content) {
|
|
684
|
+
if (block.type === 'text') {
|
|
685
|
+
outputText += block.text;
|
|
686
|
+
} else if (block.type === 'tool_use') {
|
|
687
|
+
output.push({
|
|
688
|
+
type: 'function_call',
|
|
689
|
+
id: block.id,
|
|
690
|
+
call_id: block.id,
|
|
691
|
+
name: block.name,
|
|
692
|
+
arguments: JSON.stringify(block.input)
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
if (outputText) {
|
|
699
|
+
output.unshift({
|
|
700
|
+
type: 'message',
|
|
701
|
+
id: `msg_${generateId()}`,
|
|
702
|
+
status: 'completed',
|
|
703
|
+
role: 'assistant',
|
|
704
|
+
content: [{
|
|
705
|
+
type: 'output_text',
|
|
706
|
+
text: outputText
|
|
707
|
+
}]
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
const convertedResponse = {
|
|
712
|
+
id: response.id,
|
|
713
|
+
object: 'response',
|
|
714
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
715
|
+
status: response.stop_reason === 'end_turn' ? 'completed' : 'incomplete',
|
|
716
|
+
model: response.model,
|
|
717
|
+
provider: 'zai',
|
|
718
|
+
output: output,
|
|
719
|
+
usage: {
|
|
720
|
+
input_tokens: response.usage?.input_tokens || 0,
|
|
721
|
+
output_tokens: response.usage?.output_tokens || 0,
|
|
722
|
+
total_tokens: (response.usage?.input_tokens || 0) + (response.usage?.output_tokens || 0),
|
|
723
|
+
cache_creation_input_tokens: response.usage?.cache_creation_input_tokens,
|
|
724
|
+
cache_read_input_tokens: response.usage?.cache_read_input_tokens
|
|
725
|
+
},
|
|
726
|
+
stop_reason: response.stop_reason
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
res.json({
|
|
730
|
+
success: true,
|
|
731
|
+
provider: 'zai',
|
|
732
|
+
status: 200,
|
|
733
|
+
data: convertedResponse,
|
|
734
|
+
originalPayload: payload,
|
|
735
|
+
convertedPayload: zaiPayload,
|
|
736
|
+
rawResponse: response
|
|
737
|
+
});
|
|
738
|
+
} else {
|
|
739
|
+
// Mock response when API key is not available
|
|
740
|
+
res.json({
|
|
741
|
+
success: false,
|
|
742
|
+
provider: 'zai',
|
|
743
|
+
error: 'Z.AI API key not found or invalid format',
|
|
744
|
+
hint: 'Z.AI API key format: 32-char hex + "." + 16-char alphanumeric',
|
|
745
|
+
mockResponse: {
|
|
746
|
+
taskName: "api_test_RES",
|
|
747
|
+
timestamp: new Date().toISOString(),
|
|
748
|
+
data: {
|
|
749
|
+
id: `resp_${generateId()}`,
|
|
750
|
+
object: "response",
|
|
751
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
752
|
+
status: "completed",
|
|
753
|
+
model: payload.model || 'glm-4.5',
|
|
754
|
+
provider: 'zai',
|
|
755
|
+
output: [{
|
|
756
|
+
id: `msg_${generateId()}`,
|
|
757
|
+
type: "message",
|
|
758
|
+
status: "completed",
|
|
759
|
+
content: [{
|
|
760
|
+
type: "output_text",
|
|
761
|
+
text: 'This is a mock response. Set a valid Z.AI API key to get real responses.'
|
|
762
|
+
}],
|
|
763
|
+
role: "assistant"
|
|
764
|
+
}],
|
|
765
|
+
usage: {
|
|
766
|
+
input_tokens: 10,
|
|
767
|
+
output_tokens: 15,
|
|
768
|
+
total_tokens: 25
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
},
|
|
772
|
+
originalPayload: payload
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
} catch (error) {
|
|
776
|
+
res.status(500).json({
|
|
777
|
+
success: false,
|
|
778
|
+
provider: 'zai',
|
|
779
|
+
error: error.message,
|
|
780
|
+
originalPayload: req.body
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
// Health check endpoint
|
|
786
|
+
app.get('/api/health', (req, res) => {
|
|
787
|
+
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
// Fallback to index.html for SPA routing (production mode)
|
|
791
|
+
app.use((req, res) => {
|
|
792
|
+
const indexPath = path.join(__dirname, 'out', 'index.html');
|
|
793
|
+
if (fs.existsSync(indexPath)) {
|
|
794
|
+
res.sendFile(indexPath);
|
|
795
|
+
} else {
|
|
796
|
+
res.status(404).send(`
|
|
797
|
+
<html>
|
|
798
|
+
<head>
|
|
799
|
+
<title>Frontend Not Built</title>
|
|
800
|
+
<style>
|
|
801
|
+
body { font-family: Arial, sans-serif; margin: 40px; background: #f5f5f5; }
|
|
802
|
+
.container { max-width: 600px; margin: 0 auto; background: white; padding: 40px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
|
|
803
|
+
h1 { color: #e74c3c; }
|
|
804
|
+
pre { background: #f8f8f8; padding: 15px; border-radius: 4px; overflow-x: auto; }
|
|
805
|
+
</style>
|
|
806
|
+
</head>
|
|
807
|
+
<body>
|
|
808
|
+
<div class="container">
|
|
809
|
+
<h1>⚠️ Frontend Not Built</h1>
|
|
810
|
+
<p>The Next.js frontend has not been built yet.</p>
|
|
811
|
+
|
|
812
|
+
<h2>To build the frontend:</h2>
|
|
813
|
+
<pre>cd payload_viewer && npm install && npm run build</pre>
|
|
814
|
+
|
|
815
|
+
<h2>Or use the start script:</h2>
|
|
816
|
+
<pre>cd payload_viewer && ./start.sh</pre>
|
|
817
|
+
|
|
818
|
+
<p>After building, refresh this page.</p>
|
|
819
|
+
</div>
|
|
820
|
+
</body>
|
|
821
|
+
</html>
|
|
822
|
+
`);
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
return new Promise((resolve, reject) => {
|
|
827
|
+
try {
|
|
828
|
+
const server = app.listen(port, () => {
|
|
829
|
+
console.log(`🌐 Payload viewer available at: http://localhost:${port}`);
|
|
830
|
+
resolve(server);
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
server.on('error', (err) => {
|
|
834
|
+
if (err.code === 'EADDRINUSE') {
|
|
835
|
+
console.log(`⚠️ Port ${port} is already in use. Trying port ${port + 1}...`);
|
|
836
|
+
server.close();
|
|
837
|
+
// Try next port
|
|
838
|
+
const nextServer = app.listen(port + 1, () => {
|
|
839
|
+
console.log(`🌐 Payload viewer available at: http://localhost:${port + 1}`);
|
|
840
|
+
resolve(nextServer);
|
|
841
|
+
});
|
|
842
|
+
} else {
|
|
843
|
+
reject(err);
|
|
844
|
+
}
|
|
845
|
+
});
|
|
846
|
+
} catch (error) {
|
|
847
|
+
reject(error);
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// Start the server if this file is run directly
|
|
853
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
854
|
+
const port = parseInt(process.argv[2], 10) || 3000;
|
|
855
|
+
startWebServer(port).catch(err => {
|
|
856
|
+
console.error('Failed to start server:', err);
|
|
857
|
+
process.exit(1);
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
export { startWebServer };
|