feat(core): 实现插件加载系统并优化日志管理

- 新增插件加载系统,支持从指定目录加载插件
- 引入log4j2日志框架,优化日志记录和管理
- 重构主程序启动逻辑,增加加载动画和系统信息输出
- 优化窗口样式和布局,提高用户体验
This commit is contained in:
tzdwindows 7
2025-02-10 13:30:38 +08:00
parent d70a1f63f6
commit a8f9611db4
14 changed files with 601 additions and 215 deletions

View File

@@ -30,6 +30,16 @@ dependencies {
implementation 'org.commonjava.googlecode.markdown4j:markdown4j:2.2-cj-1.1'
// https://mvnrepository.com/artifact/com.google.code.gson/gson
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 {

View File

@@ -6,8 +6,12 @@ import com.axis.innovators.box.events.SubscribeEvent;
import com.axis.innovators.box.gui.FridaWindow;
import com.axis.innovators.box.gui.LocalWindow;
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 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 java.awt.*;
@@ -19,9 +23,23 @@ import java.io.IOException;
* @author tzdwindows 7
*/
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 {
LibraryLoad.loadLibrary("FridaNative");
try {
LibraryLoad.loadLibrary("FridaNative");
} catch (Exception e) {
logger.error("Failed to load the 'FridaNative' library", e);
throw new RuntimeException(e);
}
}
@SubscribeEvent
@@ -31,60 +49,131 @@ public class Main {
placeholder.setForeground(new Color(127, 140, 153));
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());
// 设置系统外观
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ignored) {}
} catch (Exception e) {
logger.warn("Failed to load the system facade class", e);
}
// 创建窗口
SwingUtilities.invokeLater(() -> {
MainWindow ex = new MainWindow();
int id = 0;
MainWindow.ToolCategory debugCategory = new MainWindow.ToolCategory("调试工具",
"debug/debug.png",
"用于调试指定Windows工具的一个分类");
JFrame loadingFrame = new JFrame("加载中...");
loadingFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
loadingFrame.setSize(400, 200);
loadingFrame.setLocationRelativeTo(null);
loadingFrame.setIconImage(LoadIcon.loadIcon("logo.png", 64).getImage());
debugCategory.addTool(new MainWindow.ToolItem("Frida注入工具", "debug/frida/frida_main.png",
"使用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);
JPanel loadingPanel = new JPanel(new BorderLayout());
JProgressBar progressBar = new JProgressBar(0, 100);
progressBar.setStringPainted(true);
JLabel statusLabel = new JLabel("Initializing...", SwingConstants.CENTER);
JLabel timeLabel = new JLabel("Time elapsed: 0s", SwingConstants.CENTER);
JLabel logoLabel = new JLabel(LoadIcon.loadIcon("logo.png", 64));
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");
});
}
}));
// 在后面注册你自己的项或是添加你自己的分类
// ....
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);
});
while (completedTasks < totalTasks) {
try {
if (classifyTask == 0){
PluginLoader.loadPlugins();
logger.info("Loaded plugins");
classifyTask++;
}
// 非显示任务请添加在这添加
// ....
} catch (IOException e) {
logger.error("Failed to load plugins", e);
throw new RuntimeException(e);
}
}));
}
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();
ex.setVisible(true);
});
debugCategory.addTool(new MainWindow.ToolItem("Frida注入工具", "debug/frida/frida_main.png",
"使用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();
}
}

View File

@@ -1,8 +1,8 @@
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.FridaRunnable;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
@@ -22,6 +22,7 @@ import java.util.List;
* @author tzdwindows 7
*/
public class FridaWindow extends JDialog {
private static final Logger logger = LogManager.getLogger(FridaWindow.class);
private JTextArea scriptArea;
private JTextArea logArea;
private JTextField pidField;
@@ -149,32 +150,40 @@ public class FridaWindow extends JDialog {
Frida frida = new Frida(script, pid);
if (!isRepetition) {
frida.run(() -> {
// 执行注入操作
}).execute(frida1 -> frida1.addCallbackMessage(message ->
SwingUtilities.invokeLater(() ->
logArea.append("[LOG] " + message + "\n")
)
)).start();
try {
frida.run(() -> {
}).execute(frida1 -> frida1.addCallbackMessage(message ->
SwingUtilities.invokeLater(() -> {
logger.info("[LOG] {}", message);
logArea.append("[LOG] " + message + "\n");
})
)).start();
} catch (Exception ex) {
logger.error("Injection failure", ex);
throw new RuntimeException(ex);
}
isRepetition = true;
} else {
frida.run(() -> {}).execute(frida12 -> {}).start();
try {
frida.run(() -> {}).execute(frida12 -> {}).start();
} catch (Exception ex) {
logger.error("Injection failure", ex);
}
}
} catch (NumberFormatException ex) {
logger.error("无效的进程ID", ex);
JOptionPane.showMessageDialog(this, "无效的进程ID", "错误", JOptionPane.ERROR_MESSAGE);
}
}
private void openProcessSelectionWindow(ActionEvent e) {
// 打开一个窗口显示所有进程的ID、名称和图标
ProcessSelectionWindow selectionWindow = new ProcessSelectionWindow(this);
selectionWindow.setVisible(true);
}
// 选择进程的子窗口
private static class ProcessSelectionWindow extends JDialog {
private List<ProcessInfo> processList; // 存储所有进程信息
private List<ProcessInfo> processList;
private JTable table;
public ProcessSelectionWindow(Window owner) {
@@ -215,7 +224,6 @@ public class FridaWindow extends JDialog {
searchPanel.add(searchField, BorderLayout.CENTER);
panel.add(searchPanel, BorderLayout.NORTH);
// 显示进程信息的表格
String[] columns = {"进程名称", "进程ID"};
Object[][] data = getTableData(processList);
table = new JTable(data, columns);
@@ -239,11 +247,11 @@ public class FridaWindow extends JDialog {
private void filterProcesses(String query) {
List<ProcessInfo> filteredList = new ArrayList<>();
for (ProcessInfo process : processList) {
if (process.getName().toLowerCase().contains(query.toLowerCase())) {
if (process.name().toLowerCase().contains(query.toLowerCase())) {
filteredList.add(process);
}
}
// 更新表格数据
Object[][] filteredData = getTableData(filteredList);
table.setModel(new DefaultTableModel(filteredData, new String[]{"进程名称", "进程ID"}));
}
@@ -252,15 +260,18 @@ public class FridaWindow extends JDialog {
Object[][] data = new Object[processes.size()][2];
for (int i = 0; i < processes.size(); i++) {
ProcessInfo process = processes.get(i);
data[i] = new Object[]{process.getName(), process.getPid()};
data[i] = new Object[]{process.name(), process.pid()};
}
return data;
}
/**
* 执行cmd命令获取所有进程信息
* @return 进程信息列表
*/
private List<ProcessInfo> getProcesses() {
List<ProcessInfo> processList = new ArrayList<>();
try {
// 执行cmd命令获取所有进程信息
Process process = Runtime.getRuntime().exec("tasklist /fo csv /nh");
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
@@ -275,28 +286,11 @@ public class FridaWindow extends JDialog {
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
logger.error("Failed to get process information ", e);
}
return processList;
}
}
// 存储进程信息的类
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;
}
}
private record ProcessInfo(long pid, String name) {}
}

View File

@@ -1,5 +1,8 @@
package com.axis.innovators.box.gui;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
@@ -10,6 +13,7 @@ import java.net.URL;
* @author tzdwindows 7
*/
public class LoadIcon {
private static final Logger logger = LogManager.getLogger(LoadIcon.class);
private static final String ICON_PATH = "/icons/";
public static ImageIcon loadIcon(String filename, int size) {
try {
@@ -21,6 +25,7 @@ public class LoadIcon {
Image image = new ImageIcon(imgUrl).getImage();
return new ImageIcon(image.getScaledInstance(size, size, Image.SCALE_SMOOTH));
} catch (Exception e) {
logger.error("Failed to load icon image path '{}'", filename, e);
return createPlaceholderIcon(size);
}
}

View File

@@ -1,5 +1,7 @@
package com.axis.innovators.box.gui;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.tzd.lm.LM;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
@@ -8,24 +10,21 @@ import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
/**
* 本地AI执行工具
* @author tzdwindows 7
*/
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 textColor = new Color(240, 240, 240);
private final Font mainFont = new Font("微软雅黑", Font.PLAIN, 14);
private final Color bgColor1 = new Color(235, 241, 250);
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 JTextField inputField;
private JButton sendButton;
private JProgressBar progressBar;
private JComboBox<String> contextBox;
private final LinkedList<Long> contextHandles = new LinkedList<>();
@@ -40,19 +39,7 @@ public class LocalWindow extends JDialog {
}
private void initializeUI() {
// 主面板设置
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));
JPanel mainPanel = getjPanel();
// 聊天区域
chatArea = new JTextArea();
@@ -88,6 +75,22 @@ public class LocalWindow extends JDialog {
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() {
JToolBar toolBar = new JToolBar();
toolBar.setFloatable(false);
@@ -135,11 +138,10 @@ public class LocalWindow extends JDialog {
inputField.setForeground(Color.black);
inputField.setBackground(Color.white);
sendButton = new JButton("发送");
JButton sendButton = new JButton("发送");
styleButton(sendButton);
sendButton.addActionListener(this::handleSendMessage);
// 温度调节
tempSpinner = new JSpinner(new SpinnerNumberModel(0.8, 0.0, 1.0, 0.1));
tempSpinner.setFont(mainFont);
tempSpinner.setBackground(bgColor);
@@ -177,6 +179,7 @@ public class LocalWindow extends JDialog {
currentModelHandle = get();
createNewContext();
} catch (Exception ex) {
logger.error("Model loading failed , loading location {}", LM.DEEP_SEEK ,ex);
JOptionPane.showMessageDialog(LocalWindow.this, "模型加载失败");
}
progressBar.setVisible(false);
@@ -192,7 +195,7 @@ public class LocalWindow extends JDialog {
contextBox.setSelectedIndex(contextHandles.size()-1);
contextBox.setBackground(Color.lightGray);
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) {
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();
}
}
static boolean isTemporary = true;
private final List<String> messages = new ArrayList<>();
private void appendAIMessage(String message) {
SwingUtilities.invokeLater(() -> {
if (isTemporary) {
chatArea.append("【AI】\n");
logger.info("【AI】");
isTemporary = false;
}
// 处理Markdown语法
@@ -237,27 +250,33 @@ public class LocalWindow extends JDialog {
.replaceAll("</think>", "</think></fold>");
chatArea.append(message2);
chatArea.setCaretPosition(chatArea.getDocument().getLength());
messages.add(message2);
});
}
private void appendMessage(String message) {
SwingUtilities.invokeLater(() -> {
logger.info("\n【用户】\n{}", message);
chatArea.append("\n【用户】\n" + message + "\n\n");
chatArea.setCaretPosition(chatArea.getDocument().getLength());
});
}
private void saveConversation() {
String content = chatArea.getText();
//String content = chatArea.getText();
// TODO: 调用保存实现
}
@Override
public void dispose() {
contextHandles.forEach(LM::llamaFreeContext);
if (contextHandles.isEmpty()){
logger.warn("No context to free");
} else {
contextHandles.forEach(LM::llamaFreeContext);
}
if (currentModelHandle != -1) {
LM.llamaFreeModel(currentModelHandle);
} else {
logger.warn("Wrong model handle");
}
super.dispose();
}

View File

@@ -1,6 +1,8 @@
package com.axis.innovators.box.gui;
import com.axis.innovators.box.events.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.swing.*;
import javax.swing.Timer;
@@ -9,7 +11,6 @@ import javax.swing.plaf.basic.BasicScrollBarUI;
import javax.swing.plaf.basic.BasicTabbedPaneUI;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
@@ -23,6 +24,7 @@ import java.util.List;
* @author tzdwindows 7
*/
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, Integer> cardElevations = new HashMap<>();
// 选项卡颜色
@@ -81,7 +83,7 @@ public class MainWindow extends JFrame {
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
} 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());
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));
dialog.add(content);
@@ -373,40 +370,6 @@ public class MainWindow extends JFrame {
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) {
return "<html><body style='width: 300px; padding: 10px;'>" +
@@ -415,40 +378,6 @@ public class MainWindow extends JFrame {
"</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 {
private final String name;
@@ -504,30 +433,6 @@ public class MainWindow extends JFrame {
* @param 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() {
return title;

View File

@@ -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; }
}

View 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);
}
}

View File

@@ -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 "";
}

View File

@@ -1,5 +1,8 @@
package com.axis.innovators.box.tools;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
/**
@@ -7,14 +10,24 @@ import java.io.File;
* @author tzdwindows 7
*/
public class FolderCreator {
private static final Logger logger = LogManager.getLogger(FolderCreator.class);
public static final String LIBRARY_NAME = "library";
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() {
String folder = createFolder(MODEL_PATH);
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 folder;
@@ -23,7 +36,7 @@ public class FolderCreator {
public static String getLibraryFolder() {
String folder = createFolder(LIBRARY_NAME);
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 folder;
@@ -39,7 +52,7 @@ public class FolderCreator {
File folder = new File(jarDir, folderName);
if (!folder.exists()) {
if (!folder.mkdir()) {
System.out.println("Folder creation failure");
logger.error("Folder creation failure");
return null;
}
}

View File

@@ -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);
}
}

View File

@@ -2,6 +2,8 @@ package org.tzd.lm;
import com.axis.innovators.box.tools.FolderCreator;
import com.axis.innovators.box.tools.LibraryLoad;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* LM推理类
@@ -10,21 +12,32 @@ import com.axis.innovators.box.tools.LibraryLoad;
public class LM {
public static boolean CUDA = true;
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 {
if (!CUDA) {
logger.warn("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");
} else {
LibraryLoad.loadLibrary("cuda/ggml-base");
LibraryLoad.loadLibrary("cuda/ggml-cpu");
LibraryLoad.loadLibrary("cuda/ggml-rpc");
// cuda版本 cuda-cu12.4-x64确保你有
LibraryLoad.loadLibrary("cuda/ggml-cuda");
LibraryLoad.loadLibrary("cuda/ggml");
LibraryLoad.loadLibrary("cuda/llama");
try {
LibraryLoad.loadLibrary("cuda/ggml-base");
LibraryLoad.loadLibrary("cuda/ggml-cpu");
LibraryLoad.loadLibrary("cuda/ggml-rpc");
// cuda版本 cuda-cu12.4-x64确保你有
LibraryLoad.loadLibrary("cuda/ggml-cuda");
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");
}

View File

@@ -13,6 +13,7 @@ import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
/**
* 使用AI接口获取回复
* @author tzdwindows 7
*/
public class LMApi {

View 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>