feat(core): 实现插件加载系统并优化日志管理
- 新增插件加载系统,支持从指定目录加载插件 - 引入log4j2日志框架,优化日志记录和管理 - 重构主程序启动逻辑,增加加载动画和系统信息输出 - 优化窗口样式和布局,提高用户体验
This commit is contained in:
10
build.gradle
10
build.gradle
@@ -30,6 +30,16 @@ dependencies {
|
|||||||
implementation 'org.commonjava.googlecode.markdown4j:markdown4j:2.2-cj-1.1'
|
implementation 'org.commonjava.googlecode.markdown4j:markdown4j:2.2-cj-1.1'
|
||||||
// https://mvnrepository.com/artifact/com.google.code.gson/gson
|
// https://mvnrepository.com/artifact/com.google.code.gson/gson
|
||||||
implementation 'com.google.code.gson:gson:2.8.9'
|
implementation 'com.google.code.gson:gson:2.8.9'
|
||||||
|
|
||||||
|
implementation 'org.apache.logging.log4j:log4j-api:2.20.0'
|
||||||
|
implementation 'org.apache.logging.log4j:log4j-core:2.20.0'
|
||||||
|
implementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.20.0'
|
||||||
|
|
||||||
|
implementation 'org.ow2.asm:asm:7.1'
|
||||||
|
implementation 'org.ow2.asm:asm-commons:7.1'
|
||||||
|
implementation 'org.ow2.asm:asm-analysis:7.1'
|
||||||
|
implementation 'org.ow2.asm:asm-util:7.0'
|
||||||
|
implementation 'org.ow2.asm:asm-tree:7.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
|
|||||||
@@ -6,8 +6,12 @@ import com.axis.innovators.box.events.SubscribeEvent;
|
|||||||
import com.axis.innovators.box.gui.FridaWindow;
|
import com.axis.innovators.box.gui.FridaWindow;
|
||||||
import com.axis.innovators.box.gui.LocalWindow;
|
import com.axis.innovators.box.gui.LocalWindow;
|
||||||
import com.axis.innovators.box.gui.MainWindow;
|
import com.axis.innovators.box.gui.MainWindow;
|
||||||
|
import com.axis.innovators.box.gui.LoadIcon;
|
||||||
|
import com.axis.innovators.box.plugins.PluginLoader;
|
||||||
import com.axis.innovators.box.tools.LibraryLoad;
|
import com.axis.innovators.box.tools.LibraryLoad;
|
||||||
import org.markdown4j.Markdown4jProcessor;
|
import com.axis.innovators.box.tools.SystemInfoUtil;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
@@ -19,9 +23,23 @@ import java.io.IOException;
|
|||||||
* @author tzdwindows 7
|
* @author tzdwindows 7
|
||||||
*/
|
*/
|
||||||
public class Main {
|
public class Main {
|
||||||
|
private static final Logger logger = LogManager.getLogger(Main.class);
|
||||||
|
private static final String VERSIONS = "0.0.1";
|
||||||
|
private static final String[] AUTHOR = new String[]{
|
||||||
|
"tzdwindows 7"
|
||||||
|
};
|
||||||
|
/** 我是总任务数 **/
|
||||||
|
public static int totalTasks = 1;
|
||||||
|
/** 我是当前任务数 **/
|
||||||
|
public static int completedTasks = 0;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
LibraryLoad.loadLibrary("FridaNative");
|
try {
|
||||||
|
LibraryLoad.loadLibrary("FridaNative");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed to load the 'FridaNative' library", e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
@@ -31,60 +49,131 @@ public class Main {
|
|||||||
placeholder.setForeground(new Color(127, 140, 153));
|
placeholder.setForeground(new Color(127, 140, 153));
|
||||||
event.content().add(placeholder, BorderLayout.CENTER);
|
event.content().add(placeholder, BorderLayout.CENTER);
|
||||||
|
|
||||||
// 我不想写这个了你们自己实现
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) {
|
||||||
|
// 输出版本和作者信息
|
||||||
|
logger.info("Application Version: {}", VERSIONS);
|
||||||
|
logger.info("Authors: {}", String.join(", ", AUTHOR));
|
||||||
|
|
||||||
|
// 输出系统信息
|
||||||
|
logger.info("Operating System: {}", SystemInfoUtil.getOSName());
|
||||||
|
logger.info("OS Architecture: {}", SystemInfoUtil.getOSArch());
|
||||||
|
logger.info("CPU: {}", SystemInfoUtil.getCPUInfo());
|
||||||
|
logger.info("GPU: {}", SystemInfoUtil.getGPUInfo());
|
||||||
|
|
||||||
// 注册事件
|
// 注册事件
|
||||||
GlobalEventBus.EVENT_BUS.register(new Main());
|
GlobalEventBus.EVENT_BUS.register(new Main());
|
||||||
|
|
||||||
// 设置系统外观
|
// 设置系统外观
|
||||||
try {
|
try {
|
||||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||||
} catch (Exception ignored) {}
|
} catch (Exception e) {
|
||||||
|
logger.warn("Failed to load the system facade class", e);
|
||||||
|
}
|
||||||
|
|
||||||
// 创建窗口
|
JFrame loadingFrame = new JFrame("加载中...");
|
||||||
SwingUtilities.invokeLater(() -> {
|
loadingFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
||||||
MainWindow ex = new MainWindow();
|
loadingFrame.setSize(400, 200);
|
||||||
int id = 0;
|
loadingFrame.setLocationRelativeTo(null);
|
||||||
MainWindow.ToolCategory debugCategory = new MainWindow.ToolCategory("调试工具",
|
loadingFrame.setIconImage(LoadIcon.loadIcon("logo.png", 64).getImage());
|
||||||
"debug/debug.png",
|
|
||||||
"用于调试指定Windows工具的一个分类");
|
|
||||||
|
|
||||||
debugCategory.addTool(new MainWindow.ToolItem("Frida注入工具", "debug/frida/frida_main.png",
|
JPanel loadingPanel = new JPanel(new BorderLayout());
|
||||||
"使用frida注入目标进程的脚本程序 " +
|
JProgressBar progressBar = new JProgressBar(0, 100);
|
||||||
"\n作者:tzdwindows 7", ++id, new AbstractAction() {
|
progressBar.setStringPainted(true);
|
||||||
@Override
|
JLabel statusLabel = new JLabel("Initializing...", SwingConstants.CENTER);
|
||||||
public void actionPerformed(ActionEvent e) {
|
JLabel timeLabel = new JLabel("Time elapsed: 0s", SwingConstants.CENTER);
|
||||||
Window owner = SwingUtilities.windowForComponent((Component) e.getSource());
|
JLabel logoLabel = new JLabel(LoadIcon.loadIcon("logo.png", 64));
|
||||||
FridaWindow fridaWindow = new FridaWindow(owner);
|
|
||||||
fridaWindow.setVisible(true);
|
loadingPanel.add(logoLabel, BorderLayout.NORTH);
|
||||||
|
loadingPanel.add(progressBar, BorderLayout.CENTER);
|
||||||
|
loadingPanel.add(statusLabel, BorderLayout.SOUTH);
|
||||||
|
loadingPanel.add(timeLabel, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
loadingFrame.add(loadingPanel);
|
||||||
|
loadingFrame.setVisible(true);
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
int classifyTask = 0;
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
while (completedTasks < totalTasks) {
|
||||||
|
completedTasks++;
|
||||||
|
final int progress = (int) ((completedTasks / (double) totalTasks) * 100);
|
||||||
|
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
progressBar.setValue(progress);
|
||||||
|
statusLabel.setText("Loading: " + progress + "%");
|
||||||
|
long elapsedTime = (System.currentTimeMillis() - startTime) / 1000;
|
||||||
|
timeLabel.setText("Time elapsed: " + elapsedTime + "s");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}));
|
});
|
||||||
|
while (completedTasks < totalTasks) {
|
||||||
// 在后面注册你自己的项或是添加你自己的分类
|
try {
|
||||||
// ....
|
if (classifyTask == 0){
|
||||||
|
PluginLoader.loadPlugins();
|
||||||
ex.addToolCategory(debugCategory);
|
logger.info("Loaded plugins");
|
||||||
|
classifyTask++;
|
||||||
MainWindow.ToolCategory aICategory = new MainWindow.ToolCategory("AI工具",
|
}
|
||||||
"ai/ai.png",
|
// 非显示任务请添加在这添加
|
||||||
"人工智能/大语言模型");
|
// ....
|
||||||
|
} catch (IOException e) {
|
||||||
aICategory.addTool(new MainWindow.ToolItem("本地AI执行工具", "ai/local/local_main.png",
|
logger.error("Failed to load plugins", e);
|
||||||
"在本机对开源大语言模型进行推理" +
|
throw new RuntimeException(e);
|
||||||
"\n作者:tzdwindows 7", ++id, new AbstractAction() {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
Window owner = SwingUtilities.windowForComponent((Component) e.getSource());
|
|
||||||
LocalWindow dialog = new LocalWindow(owner);
|
|
||||||
dialog.setVisible(true);
|
|
||||||
}
|
}
|
||||||
}));
|
}
|
||||||
|
|
||||||
ex.addToolCategory(aICategory);
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
loadingFrame.dispose();
|
||||||
|
try {
|
||||||
|
MainWindow ex = new MainWindow();
|
||||||
|
int id = 0;
|
||||||
|
MainWindow.ToolCategory debugCategory = new MainWindow.ToolCategory("调试工具",
|
||||||
|
"debug/debug.png",
|
||||||
|
"用于调试指定Windows工具的一个分类");
|
||||||
|
|
||||||
ex.initUI();
|
debugCategory.addTool(new MainWindow.ToolItem("Frida注入工具", "debug/frida/frida_main.png",
|
||||||
ex.setVisible(true);
|
"使用frida注入目标进程的脚本程序 " +
|
||||||
});
|
"\n作者:tzdwindows 7", ++id, new AbstractAction() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
Window owner = SwingUtilities.windowForComponent((Component) e.getSource());
|
||||||
|
FridaWindow fridaWindow = new FridaWindow(owner);
|
||||||
|
fridaWindow.setVisible(true);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 在后面注册你自己的项或是添加你自己的分类
|
||||||
|
// ....
|
||||||
|
|
||||||
|
ex.addToolCategory(debugCategory);
|
||||||
|
|
||||||
|
MainWindow.ToolCategory aICategory = new MainWindow.ToolCategory("AI工具",
|
||||||
|
"ai/ai.png",
|
||||||
|
"人工智能/大语言模型");
|
||||||
|
|
||||||
|
aICategory.addTool(new MainWindow.ToolItem("本地AI执行工具", "ai/local/local_main.png",
|
||||||
|
"在本机对开源大语言模型进行推理" +
|
||||||
|
"\n作者:tzdwindows 7", ++id, new AbstractAction() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
Window owner = SwingUtilities.windowForComponent((Component) e.getSource());
|
||||||
|
LocalWindow dialog = new LocalWindow(owner);
|
||||||
|
dialog.setVisible(true);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
ex.addToolCategory(aICategory);
|
||||||
|
|
||||||
|
ex.initUI();
|
||||||
|
ex.setVisible(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("There was a problem starting the main thread", e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package com.axis.innovators.box.gui;
|
package com.axis.innovators.box.gui;
|
||||||
|
|
||||||
import org.tzd.frida.windows.CallbackMessage;
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.tzd.frida.windows.Frida;
|
import org.tzd.frida.windows.Frida;
|
||||||
import org.tzd.frida.windows.FridaRunnable;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
@@ -22,6 +22,7 @@ import java.util.List;
|
|||||||
* @author tzdwindows 7
|
* @author tzdwindows 7
|
||||||
*/
|
*/
|
||||||
public class FridaWindow extends JDialog {
|
public class FridaWindow extends JDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger(FridaWindow.class);
|
||||||
private JTextArea scriptArea;
|
private JTextArea scriptArea;
|
||||||
private JTextArea logArea;
|
private JTextArea logArea;
|
||||||
private JTextField pidField;
|
private JTextField pidField;
|
||||||
@@ -149,32 +150,40 @@ public class FridaWindow extends JDialog {
|
|||||||
|
|
||||||
Frida frida = new Frida(script, pid);
|
Frida frida = new Frida(script, pid);
|
||||||
if (!isRepetition) {
|
if (!isRepetition) {
|
||||||
frida.run(() -> {
|
try {
|
||||||
// 执行注入操作
|
frida.run(() -> {
|
||||||
}).execute(frida1 -> frida1.addCallbackMessage(message ->
|
}).execute(frida1 -> frida1.addCallbackMessage(message ->
|
||||||
SwingUtilities.invokeLater(() ->
|
SwingUtilities.invokeLater(() -> {
|
||||||
logArea.append("[LOG] " + message + "\n")
|
logger.info("[LOG] {}", message);
|
||||||
)
|
logArea.append("[LOG] " + message + "\n");
|
||||||
)).start();
|
})
|
||||||
|
)).start();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
logger.error("Injection failure", ex);
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
isRepetition = true;
|
isRepetition = true;
|
||||||
} else {
|
} else {
|
||||||
frida.run(() -> {}).execute(frida12 -> {}).start();
|
try {
|
||||||
|
frida.run(() -> {}).execute(frida12 -> {}).start();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
logger.error("Injection failure", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (NumberFormatException ex) {
|
} catch (NumberFormatException ex) {
|
||||||
|
logger.error("无效的进程ID", ex);
|
||||||
JOptionPane.showMessageDialog(this, "无效的进程ID", "错误", JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(this, "无效的进程ID", "错误", JOptionPane.ERROR_MESSAGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openProcessSelectionWindow(ActionEvent e) {
|
private void openProcessSelectionWindow(ActionEvent e) {
|
||||||
// 打开一个窗口,显示所有进程的ID、名称和图标
|
|
||||||
ProcessSelectionWindow selectionWindow = new ProcessSelectionWindow(this);
|
ProcessSelectionWindow selectionWindow = new ProcessSelectionWindow(this);
|
||||||
selectionWindow.setVisible(true);
|
selectionWindow.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 选择进程的子窗口
|
|
||||||
private static class ProcessSelectionWindow extends JDialog {
|
private static class ProcessSelectionWindow extends JDialog {
|
||||||
private List<ProcessInfo> processList; // 存储所有进程信息
|
private List<ProcessInfo> processList;
|
||||||
private JTable table;
|
private JTable table;
|
||||||
|
|
||||||
public ProcessSelectionWindow(Window owner) {
|
public ProcessSelectionWindow(Window owner) {
|
||||||
@@ -215,7 +224,6 @@ public class FridaWindow extends JDialog {
|
|||||||
searchPanel.add(searchField, BorderLayout.CENTER);
|
searchPanel.add(searchField, BorderLayout.CENTER);
|
||||||
panel.add(searchPanel, BorderLayout.NORTH);
|
panel.add(searchPanel, BorderLayout.NORTH);
|
||||||
|
|
||||||
// 显示进程信息的表格
|
|
||||||
String[] columns = {"进程名称", "进程ID"};
|
String[] columns = {"进程名称", "进程ID"};
|
||||||
Object[][] data = getTableData(processList);
|
Object[][] data = getTableData(processList);
|
||||||
table = new JTable(data, columns);
|
table = new JTable(data, columns);
|
||||||
@@ -239,11 +247,11 @@ public class FridaWindow extends JDialog {
|
|||||||
private void filterProcesses(String query) {
|
private void filterProcesses(String query) {
|
||||||
List<ProcessInfo> filteredList = new ArrayList<>();
|
List<ProcessInfo> filteredList = new ArrayList<>();
|
||||||
for (ProcessInfo process : processList) {
|
for (ProcessInfo process : processList) {
|
||||||
if (process.getName().toLowerCase().contains(query.toLowerCase())) {
|
if (process.name().toLowerCase().contains(query.toLowerCase())) {
|
||||||
filteredList.add(process);
|
filteredList.add(process);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 更新表格数据
|
|
||||||
Object[][] filteredData = getTableData(filteredList);
|
Object[][] filteredData = getTableData(filteredList);
|
||||||
table.setModel(new DefaultTableModel(filteredData, new String[]{"进程名称", "进程ID"}));
|
table.setModel(new DefaultTableModel(filteredData, new String[]{"进程名称", "进程ID"}));
|
||||||
}
|
}
|
||||||
@@ -252,15 +260,18 @@ public class FridaWindow extends JDialog {
|
|||||||
Object[][] data = new Object[processes.size()][2];
|
Object[][] data = new Object[processes.size()][2];
|
||||||
for (int i = 0; i < processes.size(); i++) {
|
for (int i = 0; i < processes.size(); i++) {
|
||||||
ProcessInfo process = processes.get(i);
|
ProcessInfo process = processes.get(i);
|
||||||
data[i] = new Object[]{process.getName(), process.getPid()};
|
data[i] = new Object[]{process.name(), process.pid()};
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行cmd命令获取所有进程信息
|
||||||
|
* @return 进程信息列表
|
||||||
|
*/
|
||||||
private List<ProcessInfo> getProcesses() {
|
private List<ProcessInfo> getProcesses() {
|
||||||
List<ProcessInfo> processList = new ArrayList<>();
|
List<ProcessInfo> processList = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
// 执行cmd命令获取所有进程信息
|
|
||||||
Process process = Runtime.getRuntime().exec("tasklist /fo csv /nh");
|
Process process = Runtime.getRuntime().exec("tasklist /fo csv /nh");
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||||
String line;
|
String line;
|
||||||
@@ -275,28 +286,11 @@ public class FridaWindow extends JDialog {
|
|||||||
}
|
}
|
||||||
reader.close();
|
reader.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
logger.error("Failed to get process information ", e);
|
||||||
}
|
}
|
||||||
return processList;
|
return processList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 存储进程信息的类
|
private record ProcessInfo(long pid, String name) {}
|
||||||
private static class ProcessInfo {
|
|
||||||
private long pid;
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
public ProcessInfo(long pid, String name) {
|
|
||||||
this.pid = pid;
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getPid() {
|
|
||||||
return pid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package com.axis.innovators.box.gui;
|
package com.axis.innovators.box.gui;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
@@ -10,6 +13,7 @@ import java.net.URL;
|
|||||||
* @author tzdwindows 7
|
* @author tzdwindows 7
|
||||||
*/
|
*/
|
||||||
public class LoadIcon {
|
public class LoadIcon {
|
||||||
|
private static final Logger logger = LogManager.getLogger(LoadIcon.class);
|
||||||
private static final String ICON_PATH = "/icons/";
|
private static final String ICON_PATH = "/icons/";
|
||||||
public static ImageIcon loadIcon(String filename, int size) {
|
public static ImageIcon loadIcon(String filename, int size) {
|
||||||
try {
|
try {
|
||||||
@@ -21,6 +25,7 @@ public class LoadIcon {
|
|||||||
Image image = new ImageIcon(imgUrl).getImage();
|
Image image = new ImageIcon(imgUrl).getImage();
|
||||||
return new ImageIcon(image.getScaledInstance(size, size, Image.SCALE_SMOOTH));
|
return new ImageIcon(image.getScaledInstance(size, size, Image.SCALE_SMOOTH));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed to load icon image path '{}'", filename, e);
|
||||||
return createPlaceholderIcon(size);
|
return createPlaceholderIcon(size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.axis.innovators.box.gui;
|
package com.axis.innovators.box.gui;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.tzd.lm.LM;
|
import org.tzd.lm.LM;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
@@ -8,24 +10,21 @@ import java.awt.event.ActionEvent;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 本地AI执行工具
|
* 本地AI执行工具
|
||||||
* @author tzdwindows 7
|
* @author tzdwindows 7
|
||||||
*/
|
*/
|
||||||
public class LocalWindow extends JDialog {
|
public class LocalWindow extends JDialog {
|
||||||
|
private static final Logger logger = LogManager.getLogger(LocalWindow.class);
|
||||||
private final Color bgColor = new Color(45, 45, 48);
|
private final Color bgColor = new Color(45, 45, 48);
|
||||||
private final Color textColor = new Color(240, 240, 240);
|
private final Color textColor = new Color(240, 240, 240);
|
||||||
private final Font mainFont = new Font("微软雅黑", Font.PLAIN, 14);
|
private final Font mainFont = new Font("微软雅黑", Font.PLAIN, 14);
|
||||||
private final Color bgColor1 = new Color(235, 241, 250);
|
private final Color bgColor1 = new Color(235, 241, 250);
|
||||||
private final Color bgColor2 = new Color(255, 255, 255);
|
private final Color bgColor2 = new Color(255, 255, 255);
|
||||||
private final Color userTextColor = new Color(0, 100, 200);
|
|
||||||
private final Color aiTextColor = new Color(0, 150, 0); // AI消息绿色
|
|
||||||
|
|
||||||
private JTextArea chatArea;
|
private JTextArea chatArea;
|
||||||
private JTextField inputField;
|
private JTextField inputField;
|
||||||
private JButton sendButton;
|
|
||||||
private JProgressBar progressBar;
|
private JProgressBar progressBar;
|
||||||
private JComboBox<String> contextBox;
|
private JComboBox<String> contextBox;
|
||||||
private final LinkedList<Long> contextHandles = new LinkedList<>();
|
private final LinkedList<Long> contextHandles = new LinkedList<>();
|
||||||
@@ -40,19 +39,7 @@ public class LocalWindow extends JDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initializeUI() {
|
private void initializeUI() {
|
||||||
// 主面板设置
|
JPanel mainPanel = getjPanel();
|
||||||
JPanel mainPanel = new JPanel(new BorderLayout()) {
|
|
||||||
@Override
|
|
||||||
protected void paintComponent(Graphics g) {
|
|
||||||
super.paintComponent(g);
|
|
||||||
Graphics2D g2d = (Graphics2D) g;
|
|
||||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
||||||
GradientPaint gp = new GradientPaint(0, 0, bgColor1, getWidth(), getHeight(), bgColor2);
|
|
||||||
g2d.setPaint(gp);
|
|
||||||
g2d.fillRect(0, 0, getWidth(), getHeight());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
mainPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
|
||||||
|
|
||||||
// 聊天区域
|
// 聊天区域
|
||||||
chatArea = new JTextArea();
|
chatArea = new JTextArea();
|
||||||
@@ -88,6 +75,22 @@ public class LocalWindow extends JDialog {
|
|||||||
this.setLocationRelativeTo(getOwner());
|
this.setLocationRelativeTo(getOwner());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JPanel getjPanel() {
|
||||||
|
JPanel mainPanel = new JPanel(new BorderLayout()) {
|
||||||
|
@Override
|
||||||
|
protected void paintComponent(Graphics g) {
|
||||||
|
super.paintComponent(g);
|
||||||
|
Graphics2D g2d = (Graphics2D) g;
|
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
GradientPaint gp = new GradientPaint(0, 0, bgColor1, getWidth(), getHeight(), bgColor2);
|
||||||
|
g2d.setPaint(gp);
|
||||||
|
g2d.fillRect(0, 0, getWidth(), getHeight());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mainPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||||
|
return mainPanel;
|
||||||
|
}
|
||||||
|
|
||||||
private JToolBar createToolBar() {
|
private JToolBar createToolBar() {
|
||||||
JToolBar toolBar = new JToolBar();
|
JToolBar toolBar = new JToolBar();
|
||||||
toolBar.setFloatable(false);
|
toolBar.setFloatable(false);
|
||||||
@@ -135,11 +138,10 @@ public class LocalWindow extends JDialog {
|
|||||||
inputField.setForeground(Color.black);
|
inputField.setForeground(Color.black);
|
||||||
inputField.setBackground(Color.white);
|
inputField.setBackground(Color.white);
|
||||||
|
|
||||||
sendButton = new JButton("发送");
|
JButton sendButton = new JButton("发送");
|
||||||
styleButton(sendButton);
|
styleButton(sendButton);
|
||||||
sendButton.addActionListener(this::handleSendMessage);
|
sendButton.addActionListener(this::handleSendMessage);
|
||||||
|
|
||||||
// 温度调节
|
|
||||||
tempSpinner = new JSpinner(new SpinnerNumberModel(0.8, 0.0, 1.0, 0.1));
|
tempSpinner = new JSpinner(new SpinnerNumberModel(0.8, 0.0, 1.0, 0.1));
|
||||||
tempSpinner.setFont(mainFont);
|
tempSpinner.setFont(mainFont);
|
||||||
tempSpinner.setBackground(bgColor);
|
tempSpinner.setBackground(bgColor);
|
||||||
@@ -177,6 +179,7 @@ public class LocalWindow extends JDialog {
|
|||||||
currentModelHandle = get();
|
currentModelHandle = get();
|
||||||
createNewContext();
|
createNewContext();
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
|
logger.error("Model loading failed , loading location {}", LM.DEEP_SEEK ,ex);
|
||||||
JOptionPane.showMessageDialog(LocalWindow.this, "模型加载失败");
|
JOptionPane.showMessageDialog(LocalWindow.this, "模型加载失败");
|
||||||
}
|
}
|
||||||
progressBar.setVisible(false);
|
progressBar.setVisible(false);
|
||||||
@@ -192,7 +195,7 @@ public class LocalWindow extends JDialog {
|
|||||||
contextBox.setSelectedIndex(contextHandles.size()-1);
|
contextBox.setSelectedIndex(contextHandles.size()-1);
|
||||||
contextBox.setBackground(Color.lightGray);
|
contextBox.setBackground(Color.lightGray);
|
||||||
contextBox.setForeground(Color.black);
|
contextBox.setForeground(Color.black);
|
||||||
chatArea.setText(""); // 清除聊天区域内容
|
chatArea.setText("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,16 +222,26 @@ public class LocalWindow extends JDialog {
|
|||||||
protected void process(java.util.List<String> chunks) {
|
protected void process(java.util.List<String> chunks) {
|
||||||
chunks.forEach(msg -> appendAIMessage(msg));
|
chunks.forEach(msg -> appendAIMessage(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void done() {
|
||||||
|
try {
|
||||||
|
String result = get();
|
||||||
|
logger.info(result);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
logger.error("SwingWorker execution error: ", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}.execute();
|
}.execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isTemporary = true;
|
static boolean isTemporary = true;
|
||||||
private final List<String> messages = new ArrayList<>();
|
|
||||||
private void appendAIMessage(String message) {
|
private void appendAIMessage(String message) {
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
if (isTemporary) {
|
if (isTemporary) {
|
||||||
chatArea.append("【AI】\n");
|
chatArea.append("【AI】\n");
|
||||||
|
logger.info("【AI】");
|
||||||
isTemporary = false;
|
isTemporary = false;
|
||||||
}
|
}
|
||||||
// 处理Markdown语法
|
// 处理Markdown语法
|
||||||
@@ -237,27 +250,33 @@ public class LocalWindow extends JDialog {
|
|||||||
.replaceAll("</think>", "</think></fold>");
|
.replaceAll("</think>", "</think></fold>");
|
||||||
chatArea.append(message2);
|
chatArea.append(message2);
|
||||||
chatArea.setCaretPosition(chatArea.getDocument().getLength());
|
chatArea.setCaretPosition(chatArea.getDocument().getLength());
|
||||||
messages.add(message2);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendMessage(String message) {
|
private void appendMessage(String message) {
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
logger.info("\n【用户】\n{}", message);
|
||||||
chatArea.append("\n【用户】\n" + message + "\n\n");
|
chatArea.append("\n【用户】\n" + message + "\n\n");
|
||||||
chatArea.setCaretPosition(chatArea.getDocument().getLength());
|
chatArea.setCaretPosition(chatArea.getDocument().getLength());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveConversation() {
|
private void saveConversation() {
|
||||||
String content = chatArea.getText();
|
//String content = chatArea.getText();
|
||||||
// TODO: 调用保存实现
|
// TODO: 调用保存实现
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
contextHandles.forEach(LM::llamaFreeContext);
|
if (contextHandles.isEmpty()){
|
||||||
|
logger.warn("No context to free");
|
||||||
|
} else {
|
||||||
|
contextHandles.forEach(LM::llamaFreeContext);
|
||||||
|
}
|
||||||
if (currentModelHandle != -1) {
|
if (currentModelHandle != -1) {
|
||||||
LM.llamaFreeModel(currentModelHandle);
|
LM.llamaFreeModel(currentModelHandle);
|
||||||
|
} else {
|
||||||
|
logger.warn("Wrong model handle");
|
||||||
}
|
}
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.axis.innovators.box.gui;
|
package com.axis.innovators.box.gui;
|
||||||
|
|
||||||
import com.axis.innovators.box.events.*;
|
import com.axis.innovators.box.events.*;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.Timer;
|
import javax.swing.Timer;
|
||||||
@@ -9,7 +11,6 @@ import javax.swing.plaf.basic.BasicScrollBarUI;
|
|||||||
import javax.swing.plaf.basic.BasicTabbedPaneUI;
|
import javax.swing.plaf.basic.BasicTabbedPaneUI;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
@@ -23,6 +24,7 @@ import java.util.List;
|
|||||||
* @author tzdwindows 7
|
* @author tzdwindows 7
|
||||||
*/
|
*/
|
||||||
public class MainWindow extends JFrame {
|
public class MainWindow extends JFrame {
|
||||||
|
private static final Logger logger = LogManager.getLogger(MainWindow.class);
|
||||||
private final Map<JComponent, Float> cardScales = new HashMap<>();
|
private final Map<JComponent, Float> cardScales = new HashMap<>();
|
||||||
private final Map<JComponent, Integer> cardElevations = new HashMap<>();
|
private final Map<JComponent, Integer> cardElevations = new HashMap<>();
|
||||||
// 选项卡颜色
|
// 选项卡颜色
|
||||||
@@ -81,7 +83,7 @@ public class MainWindow extends JFrame {
|
|||||||
|
|
||||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
|
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
logger.error("Failed to load background image", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -271,11 +273,6 @@ public class MainWindow extends JFrame {
|
|||||||
JPanel content = new JPanel(new BorderLayout());
|
JPanel content = new JPanel(new BorderLayout());
|
||||||
content.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
content.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||||
|
|
||||||
//JLabel placeholder = new JLabel("设置功能开发中...", SwingConstants.CENTER);
|
|
||||||
//placeholder.setFont(new Font("微软雅黑", Font.PLAIN, 24));
|
|
||||||
//placeholder.setForeground(new Color(127, 140, 153));
|
|
||||||
//content.add(placeholder, BorderLayout.CENTER);
|
|
||||||
|
|
||||||
GlobalEventBus.EVENT_BUS.post(new SettingsLoadEvents(dialog, content));
|
GlobalEventBus.EVENT_BUS.post(new SettingsLoadEvents(dialog, content));
|
||||||
|
|
||||||
dialog.add(content);
|
dialog.add(content);
|
||||||
@@ -373,40 +370,6 @@ public class MainWindow extends JFrame {
|
|||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BufferedImage captureWindowImage(Window window) {
|
|
||||||
try {
|
|
||||||
Rectangle bounds = window.getBounds();
|
|
||||||
BufferedImage capture = new Robot().createScreenCapture(bounds);
|
|
||||||
return capture.getSubimage(0, 0, window.getWidth(), window.getHeight());
|
|
||||||
} catch (AWTException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static JPanel getjPanel(BufferedImage[] blurredImage, Window window) {
|
|
||||||
JPanel blurPanel = new JPanel() {
|
|
||||||
private float currentOpacity = 1.0f;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void paintComponent(Graphics g) {
|
|
||||||
super.paintComponent(g);
|
|
||||||
|
|
||||||
if (blurredImage[0] != null) {
|
|
||||||
Graphics2D g2d = (Graphics2D) g.create();
|
|
||||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, currentOpacity));
|
|
||||||
g2d.drawImage(blurredImage[0], 0, 0, null);
|
|
||||||
g2d.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 面板属性设置
|
|
||||||
blurPanel.setBounds(0, 0, window.getWidth(), window.getHeight());
|
|
||||||
blurPanel.setOpaque(false);
|
|
||||||
return blurPanel;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private String createToolTipHTML(ToolItem tool) {
|
private String createToolTipHTML(ToolItem tool) {
|
||||||
return "<html><body style='width: 300px; padding: 10px;'>" +
|
return "<html><body style='width: 300px; padding: 10px;'>" +
|
||||||
@@ -415,40 +378,6 @@ public class MainWindow extends JFrame {
|
|||||||
"</body></html>";
|
"</body></html>";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void animateHover(JComponent component, float targetScale, int targetElevation) {
|
|
||||||
final int ANIMATION_DURATION = 200;
|
|
||||||
final float startScale = cardScales.getOrDefault(component, 1.0f);
|
|
||||||
final int startElevation = cardElevations.getOrDefault(component, 2);
|
|
||||||
|
|
||||||
new Timer(10, new AbstractAction() {
|
|
||||||
long startTime = -1;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
if (startTime < 0) {
|
|
||||||
startTime = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
long elapsed = System.currentTimeMillis() - startTime;
|
|
||||||
float progress = Math.min(1.0f, elapsed / (float) ANIMATION_DURATION);
|
|
||||||
|
|
||||||
// 使用缓动函数实现平滑动画
|
|
||||||
float easedProgress = (float) (1 - Math.pow(1 - progress, 3));
|
|
||||||
|
|
||||||
float currentScale = startScale + (targetScale - startScale) * easedProgress;
|
|
||||||
int currentElevation = (int) (startElevation + (targetElevation - startElevation) * easedProgress);
|
|
||||||
|
|
||||||
cardScales.put(component, currentScale);
|
|
||||||
cardElevations.put(component, currentElevation);
|
|
||||||
|
|
||||||
component.repaint();
|
|
||||||
|
|
||||||
if (progress >= 1.0f) {
|
|
||||||
((Timer) e.getSource()).stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 工具类别内部类
|
// 工具类别内部类
|
||||||
public static class ToolCategory {
|
public static class ToolCategory {
|
||||||
private final String name;
|
private final String name;
|
||||||
@@ -504,30 +433,6 @@ public class MainWindow extends JFrame {
|
|||||||
* @param action 工具的点击事件
|
* @param action 工具的点击事件
|
||||||
*/
|
*/
|
||||||
public record ToolItem(String title, String icon, String description, int id, Action action) {
|
public record ToolItem(String title, String icon, String description, int id, Action action) {
|
||||||
private static JPanel getjPanel() {
|
|
||||||
JPanel content = new JPanel() {
|
|
||||||
@Override
|
|
||||||
protected void paintComponent(Graphics g) {
|
|
||||||
Graphics2D g2d = (Graphics2D) g;
|
|
||||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
||||||
|
|
||||||
// 渐变背景
|
|
||||||
GradientPaint gp = new GradientPaint(
|
|
||||||
0, 0, new Color(245, 247, 250),
|
|
||||||
getWidth(), getHeight(), new Color(255, 255, 255)
|
|
||||||
);
|
|
||||||
g2d.setPaint(gp);
|
|
||||||
g2d.fillRoundRect(0, 0, getWidth(), getHeight(), 20, 20);
|
|
||||||
|
|
||||||
// 边框阴影
|
|
||||||
g2d.setColor(new Color(0, 0, 0, 20));
|
|
||||||
g2d.drawRoundRect(0, 0, getWidth() - 1, getHeight() - 1, 20, 20);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
content.setLayout(new BorderLayout(20, 20));
|
|
||||||
content.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return title;
|
return title;
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.axis.innovators.box.plugins;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tzdwindows 7
|
||||||
|
*/
|
||||||
|
public class PluginDescriptor {
|
||||||
|
private String id;
|
||||||
|
private String name;
|
||||||
|
private List<String> supportedVersions;
|
||||||
|
private String icon;
|
||||||
|
private String description;
|
||||||
|
private Object instance;
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public String getId() { return id; }
|
||||||
|
public void setId(String id) { this.id = id; }
|
||||||
|
public String getName() { return name; }
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
public List<String> getSupportedVersions() { return supportedVersions; }
|
||||||
|
public void setSupportedVersions(List<String> supportedVersions) { this.supportedVersions = supportedVersions; }
|
||||||
|
public String getIcon() { return icon; }
|
||||||
|
public void setIcon(String icon) { this.icon = icon; }
|
||||||
|
public String getDescription() { return description; }
|
||||||
|
public void setDescription(String description) { this.description = description; }
|
||||||
|
public Object getInstance() { return instance; }
|
||||||
|
public void setInstance(Object instance) { this.instance = instance; }
|
||||||
|
}
|
||||||
148
src/main/java/com/axis/innovators/box/plugins/PluginLoader.java
Normal file
148
src/main/java/com/axis/innovators/box/plugins/PluginLoader.java
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
package com.axis.innovators.box.plugins;
|
||||||
|
|
||||||
|
import com.axis.innovators.box.Main;
|
||||||
|
import com.axis.innovators.box.tools.FolderCreator;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.jar.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载插件的系统
|
||||||
|
* @author tzdwindows 7
|
||||||
|
*/
|
||||||
|
public class PluginLoader {
|
||||||
|
private static final Logger logger = LogManager.getLogger(PluginLoader.class);
|
||||||
|
public static final String PLUGIN_PATH = FolderCreator.getPluginFolder();
|
||||||
|
private static final List<PluginDescriptor> loadedPlugins = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载插件
|
||||||
|
* @throws IOException 插件加载失败
|
||||||
|
*/
|
||||||
|
public static void loadPlugins() throws IOException {
|
||||||
|
File pluginDir = new File(PLUGIN_PATH);
|
||||||
|
File[] jars = pluginDir.listFiles((dir, name) -> name.toLowerCase().endsWith(".jar"));
|
||||||
|
|
||||||
|
if (jars == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Main.totalTasks = jars.length;
|
||||||
|
for (int i = 0; i < jars.length; i++) {
|
||||||
|
processJarFile(jars[i]);
|
||||||
|
Main.completedTasks = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void processJarFile(File jarFile) throws IOException {
|
||||||
|
try (JarFile jar = new JarFile(jarFile)) {
|
||||||
|
JarEntry pluginFile = jar.getJarEntry("plug-in.box");
|
||||||
|
if (pluginFile != null) {
|
||||||
|
processWithManifest(jar, pluginFile, jarFile);
|
||||||
|
} else {
|
||||||
|
processWithAnnotations(jar, jarFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void processWithManifest(JarFile jar, JarEntry entry, File jarFile) {
|
||||||
|
Properties props = new Properties();
|
||||||
|
try (InputStream is = jar.getInputStream(entry)) {
|
||||||
|
props.load(is);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("Failed to load plugin from jar: {}", jarFile.getName(), e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PluginDescriptor descriptor = new PluginDescriptor();
|
||||||
|
descriptor.setId(props.getProperty("id"));
|
||||||
|
descriptor.setName(props.getProperty("name"));
|
||||||
|
descriptor.setDescription(props.getProperty("description"));
|
||||||
|
descriptor.setIcon(props.getProperty("icon"));
|
||||||
|
descriptor.setSupportedVersions(
|
||||||
|
Arrays.asList(props.getProperty("supportedVersions").split(","))
|
||||||
|
);
|
||||||
|
|
||||||
|
loadMainClass(props.getProperty("mainClass"), jarFile, descriptor);
|
||||||
|
if (descriptor.getInstance() != null) {
|
||||||
|
loadedPlugins.add(descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void processWithAnnotations(JarFile jar, File jarFile) {
|
||||||
|
URLClassLoader classLoader = createClassLoader(jarFile);
|
||||||
|
Enumeration<JarEntry> entries = jar.entries();
|
||||||
|
|
||||||
|
while (entries.hasMoreElements()) {
|
||||||
|
JarEntry entry = entries.nextElement();
|
||||||
|
if (entry.getName().endsWith(".class")) {
|
||||||
|
processClassEntry(entry, classLoader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static URLClassLoader createClassLoader(File jarFile) {
|
||||||
|
try {
|
||||||
|
return new URLClassLoader(
|
||||||
|
new URL[]{jarFile.toURI().toURL()},
|
||||||
|
PluginLoader.class.getClassLoader()
|
||||||
|
);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
logger.error("Error creating URLClassLoader", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void processClassEntry(JarEntry entry, URLClassLoader classLoader) {
|
||||||
|
String className = entry.getName()
|
||||||
|
.replace("/", ".")
|
||||||
|
.replace(".class", "");
|
||||||
|
|
||||||
|
try {
|
||||||
|
Class<?> clazz = classLoader.loadClass(className);
|
||||||
|
PluginMeta meta = clazz.getAnnotation(PluginMeta.class);
|
||||||
|
if (meta != null) {
|
||||||
|
PluginDescriptor descriptor = new PluginDescriptor();
|
||||||
|
descriptor.setId(meta.id());
|
||||||
|
descriptor.setName(meta.name());
|
||||||
|
descriptor.setDescription(meta.description());
|
||||||
|
descriptor.setIcon(meta.icon());
|
||||||
|
descriptor.setSupportedVersions(Arrays.asList(meta.supportedVersions()));
|
||||||
|
|
||||||
|
try {
|
||||||
|
Object instance = clazz.getDeclaredConstructor().newInstance();
|
||||||
|
descriptor.setInstance(instance);
|
||||||
|
loadedPlugins.add(descriptor);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed to instantiate plugin class: {}", className, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ClassNotFoundException | NoClassDefFoundError e) {
|
||||||
|
logger.error("Error loading class: {}", className, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadMainClass(String mainClassName, File jarFile, PluginDescriptor descriptor) {
|
||||||
|
if (mainClassName == null || mainClassName.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (URLClassLoader classLoader = new URLClassLoader(
|
||||||
|
new URL[]{jarFile.toURI().toURL()},
|
||||||
|
PluginLoader.class.getClassLoader())
|
||||||
|
) {
|
||||||
|
Class<?> mainClass = classLoader.loadClass(mainClassName);
|
||||||
|
Object instance = mainClass.getDeclaredConstructor().newInstance();
|
||||||
|
descriptor.setInstance(instance);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed to load main class: {}", mainClassName, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<PluginDescriptor> getLoadedPlugins() {
|
||||||
|
return Collections.unmodifiableList(loadedPlugins);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.axis.innovators.box.plugins;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author tzdwindows 7
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
public @interface PluginMeta {
|
||||||
|
String id();
|
||||||
|
String name();
|
||||||
|
String[] supportedVersions();
|
||||||
|
String description();
|
||||||
|
String icon() default "";
|
||||||
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
package com.axis.innovators.box.tools;
|
package com.axis.innovators.box.tools;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -7,14 +10,24 @@ import java.io.File;
|
|||||||
* @author tzdwindows 7
|
* @author tzdwindows 7
|
||||||
*/
|
*/
|
||||||
public class FolderCreator {
|
public class FolderCreator {
|
||||||
|
private static final Logger logger = LogManager.getLogger(FolderCreator.class);
|
||||||
public static final String LIBRARY_NAME = "library";
|
public static final String LIBRARY_NAME = "library";
|
||||||
public static final String MODEL_PATH = "model";
|
public static final String MODEL_PATH = "model";
|
||||||
|
public static final String PLUGIN_PATH = "plug-in";
|
||||||
|
|
||||||
|
public static String getPluginFolder() {
|
||||||
|
String folder = createFolder(PLUGIN_PATH);
|
||||||
|
if (folder == null) {
|
||||||
|
logger.error("Plugin folder creation failed, please use administrator privileges to execute this procedure");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return folder;
|
||||||
|
}
|
||||||
|
|
||||||
public static String getModelFolder() {
|
public static String getModelFolder() {
|
||||||
String folder = createFolder(MODEL_PATH);
|
String folder = createFolder(MODEL_PATH);
|
||||||
if (folder == null) {
|
if (folder == null) {
|
||||||
System.out.println("Model folder creation failure");
|
logger.error("Model folder creation failure, please use administrator privileges to execute this procedure");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return folder;
|
return folder;
|
||||||
@@ -23,7 +36,7 @@ public class FolderCreator {
|
|||||||
public static String getLibraryFolder() {
|
public static String getLibraryFolder() {
|
||||||
String folder = createFolder(LIBRARY_NAME);
|
String folder = createFolder(LIBRARY_NAME);
|
||||||
if (folder == null) {
|
if (folder == null) {
|
||||||
System.out.println("Library folder creation failure");
|
logger.error("Library folder creation failed, please use administrator privileges to execute this procedure");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return folder;
|
return folder;
|
||||||
@@ -39,7 +52,7 @@ public class FolderCreator {
|
|||||||
File folder = new File(jarDir, folderName);
|
File folder = new File(jarDir, folderName);
|
||||||
if (!folder.exists()) {
|
if (!folder.exists()) {
|
||||||
if (!folder.mkdir()) {
|
if (!folder.mkdir()) {
|
||||||
System.out.println("Folder creation failure");
|
logger.error("Folder creation failure");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package com.axis.innovators.box.tools;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户计算机信息
|
||||||
|
* @author tzdwindows 7
|
||||||
|
*/
|
||||||
|
public class SystemInfoUtil {
|
||||||
|
private static final Logger logger = LogManager.getLogger(SystemInfoUtil.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取操作系统架构
|
||||||
|
*/
|
||||||
|
public static String getOSArch() {
|
||||||
|
return System.getProperty("os.arch");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取操作系统名称
|
||||||
|
*/
|
||||||
|
public static String getOSName() {
|
||||||
|
return System.getProperty("os.name");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 CPU 信息
|
||||||
|
*/
|
||||||
|
public static String getCPUInfo() {
|
||||||
|
try {
|
||||||
|
if (System.getProperty("os.name").toLowerCase().contains("win")) {
|
||||||
|
// Windows 系统
|
||||||
|
Process process = Runtime.getRuntime().exec("wmic cpu get name");
|
||||||
|
return getString(process);
|
||||||
|
} else if (System.getProperty("os.name").toLowerCase().contains("linux")) {
|
||||||
|
// Linux 系统
|
||||||
|
Process process = Runtime.getRuntime().exec("lscpu");
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
if (line.contains("Model name")) {
|
||||||
|
return line.split(":")[1].trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed to get CPU info", e);
|
||||||
|
}
|
||||||
|
return "Unknown CPU";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 GPU 信息
|
||||||
|
*/
|
||||||
|
public static String getGPUInfo() {
|
||||||
|
try {
|
||||||
|
if (System.getProperty("os.name").toLowerCase().contains("win")) {
|
||||||
|
// Windows 系统
|
||||||
|
Process process = Runtime.getRuntime().exec("wmic path win32_videocontroller get name");
|
||||||
|
return getString(process);
|
||||||
|
} else if (System.getProperty("os.name").toLowerCase().contains("linux")) {
|
||||||
|
// Linux 系统
|
||||||
|
Process process = Runtime.getRuntime().exec("lspci | grep VGA");
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||||
|
String line = reader.readLine();
|
||||||
|
if (line != null) {
|
||||||
|
return line.split(":")[2].trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed to get GPU info", e);
|
||||||
|
}
|
||||||
|
return "Unknown GPU";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getString(Process process) throws IOException {
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||||
|
String line;
|
||||||
|
List<String> gpuInfo = new ArrayList<>();
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
if (!line.trim().isEmpty() && !line.contains("Name")) {
|
||||||
|
gpuInfo.add(line.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return String.join(", ", gpuInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@ package org.tzd.lm;
|
|||||||
|
|
||||||
import com.axis.innovators.box.tools.FolderCreator;
|
import com.axis.innovators.box.tools.FolderCreator;
|
||||||
import com.axis.innovators.box.tools.LibraryLoad;
|
import com.axis.innovators.box.tools.LibraryLoad;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LM推理类
|
* LM推理类
|
||||||
@@ -10,21 +12,32 @@ import com.axis.innovators.box.tools.LibraryLoad;
|
|||||||
public class LM {
|
public class LM {
|
||||||
public static boolean CUDA = true;
|
public static boolean CUDA = true;
|
||||||
public final static String DEEP_SEEK = FolderCreator.getModelFolder() + "\\DeepSeek-R1-Distill-Qwen-1.5B-Q8_0.gguf";
|
public final static String DEEP_SEEK = FolderCreator.getModelFolder() + "\\DeepSeek-R1-Distill-Qwen-1.5B-Q8_0.gguf";
|
||||||
|
private static final Logger logger = LogManager.getLogger(LM.class);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
if (!CUDA) {
|
if (!CUDA) {
|
||||||
|
logger.warn("The cpu will be used for inference");
|
||||||
LibraryLoad.loadLibrary("cpu/ggml-base");
|
LibraryLoad.loadLibrary("cpu/ggml-base");
|
||||||
LibraryLoad.loadLibrary("cpu/ggml-cpu");
|
LibraryLoad.loadLibrary("cpu/ggml-cpu");
|
||||||
LibraryLoad.loadLibrary("cpu/ggml");
|
LibraryLoad.loadLibrary("cpu/ggml");
|
||||||
LibraryLoad.loadLibrary("cpu/llama");
|
LibraryLoad.loadLibrary("cpu/llama");
|
||||||
} else {
|
} else {
|
||||||
LibraryLoad.loadLibrary("cuda/ggml-base");
|
try {
|
||||||
LibraryLoad.loadLibrary("cuda/ggml-cpu");
|
LibraryLoad.loadLibrary("cuda/ggml-base");
|
||||||
LibraryLoad.loadLibrary("cuda/ggml-rpc");
|
LibraryLoad.loadLibrary("cuda/ggml-cpu");
|
||||||
// cuda版本 cuda-cu12.4-x64(确保你有)
|
LibraryLoad.loadLibrary("cuda/ggml-rpc");
|
||||||
LibraryLoad.loadLibrary("cuda/ggml-cuda");
|
// cuda版本 cuda-cu12.4-x64(确保你有)
|
||||||
LibraryLoad.loadLibrary("cuda/ggml");
|
LibraryLoad.loadLibrary("cuda/ggml-cuda");
|
||||||
LibraryLoad.loadLibrary("cuda/llama");
|
LibraryLoad.loadLibrary("cuda/ggml");
|
||||||
|
LibraryLoad.loadLibrary("cuda/llama");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Wrong cuda Settings", e);
|
||||||
|
logger.warn("The cuda library could not be loaded, the cpu will be used for inference");
|
||||||
|
LibraryLoad.loadLibrary("cpu/ggml-base");
|
||||||
|
LibraryLoad.loadLibrary("cpu/ggml-cpu");
|
||||||
|
LibraryLoad.loadLibrary("cpu/ggml");
|
||||||
|
LibraryLoad.loadLibrary("cpu/llama");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
LibraryLoad.loadLibrary("LM");
|
LibraryLoad.loadLibrary("LM");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import com.google.gson.Gson;
|
|||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 使用AI接口获取回复
|
||||||
* @author tzdwindows 7
|
* @author tzdwindows 7
|
||||||
*/
|
*/
|
||||||
public class LMApi {
|
public class LMApi {
|
||||||
|
|||||||
45
src/main/resources/log4j2.xml
Normal file
45
src/main/resources/log4j2.xml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Configuration status="WARN">
|
||||||
|
<Appenders>
|
||||||
|
<!-- 固定日志文件(按时间和大小滚动) -->
|
||||||
|
<RollingFile name="MainFileAppender"
|
||||||
|
fileName="logs/app.log"
|
||||||
|
filePattern="logs/app-%d{yyyy-MM-dd}-%i.log">
|
||||||
|
<PatternLayout>
|
||||||
|
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Pattern>
|
||||||
|
</PatternLayout>
|
||||||
|
<Policies>
|
||||||
|
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
|
||||||
|
<SizeBasedTriggeringPolicy size="10 MB"/>
|
||||||
|
</Policies>
|
||||||
|
<DefaultRolloverStrategy max="7"/>
|
||||||
|
</RollingFile>
|
||||||
|
|
||||||
|
<!-- 每次运行独立的日志文件(带时间和PID) -->
|
||||||
|
<RollingFile name="SessionFileAppender"
|
||||||
|
fileName="logs/app-${date:yyyy-MM-dd_HH-mm-ss}.log"
|
||||||
|
filePattern="logs/app-%d{yyyy-MM-dd}-%i.log"
|
||||||
|
immediateFlush="true"
|
||||||
|
createOnDemand="false">
|
||||||
|
<PatternLayout>
|
||||||
|
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Pattern>
|
||||||
|
</PatternLayout>
|
||||||
|
<Policies>
|
||||||
|
<OnStartupTriggeringPolicy minSize="0"/>
|
||||||
|
</Policies>
|
||||||
|
</RollingFile>
|
||||||
|
|
||||||
|
<Console name="Console" target="SYSTEM_OUT">
|
||||||
|
<PatternLayout pattern="[%t] [%d{yyyy-MM-dd HH:mm:ss}] [%-5level] %msg%n%throwable"/>
|
||||||
|
</Console>
|
||||||
|
</Appenders>
|
||||||
|
|
||||||
|
<Loggers>
|
||||||
|
<Root level="debug">
|
||||||
|
<!-- 同时输出到控制台和两个文件 -->
|
||||||
|
<AppenderRef ref="Console"/>
|
||||||
|
<AppenderRef ref="MainFileAppender"/>
|
||||||
|
<AppenderRef ref="SessionFileAppender"/>
|
||||||
|
</Root>
|
||||||
|
</Loggers>
|
||||||
|
</Configuration>
|
||||||
Reference in New Issue
Block a user