fix: complete HoloLake Agent and Forgejo desktop runtime
This commit is contained in:
parent
28f68fa9ac
commit
46bdd0ca73
15 changed files with 1217 additions and 638 deletions
|
|
@ -13,9 +13,10 @@
|
|||
* └── 数据存储:~/Library/Application Support/HoloLake Era/data/
|
||||
*/
|
||||
|
||||
import { app, BrowserWindow, shell, dialog } from 'electron';
|
||||
import { app, BrowserWindow, shell, dialog, ipcMain, safeStorage } from 'electron';
|
||||
import path from 'path';
|
||||
import { spawn, ChildProcess } from 'child_process';
|
||||
import fs from 'fs';
|
||||
|
||||
// ─── 配置 ───
|
||||
|
||||
|
|
@ -26,6 +27,31 @@ const CLIENT_PORT = 5180;
|
|||
// 数据目录:macOS 标准位置
|
||||
const DATA_DIR = path.join(app.getPath('userData'), 'data');
|
||||
const KB_REPO_PATH = path.join(DATA_DIR, 'knowledge-base');
|
||||
const MODEL_CONFIG_PATH = path.join(app.getPath('userData'), 'model-config.json');
|
||||
|
||||
interface ModelConfigFile {
|
||||
baseUrl: string;
|
||||
model: string;
|
||||
encryptedKey?: string;
|
||||
}
|
||||
|
||||
function readModelConfig(): ModelConfigFile {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(MODEL_CONFIG_PATH, 'utf8')) as ModelConfigFile;
|
||||
} catch {
|
||||
return { baseUrl: 'https://api.openai.com/v1', model: 'gpt-4o' };
|
||||
}
|
||||
}
|
||||
|
||||
function applyModelConfig(): ModelConfigFile {
|
||||
const config = readModelConfig();
|
||||
process.env.HOLOLAKE_LLM_BASE = config.baseUrl;
|
||||
process.env.HOLOLAKE_LLM_MODEL = config.model;
|
||||
if (config.encryptedKey && safeStorage.isEncryptionAvailable()) {
|
||||
process.env.HOLOLAKE_LLM_KEY = safeStorage.decryptString(Buffer.from(config.encryptedKey, 'base64'));
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
// ─── 后端服务器 ───
|
||||
|
||||
|
|
@ -73,7 +99,7 @@ function startServer(): Promise<void> {
|
|||
try {
|
||||
process.env.KB_PORT = String(SERVER_PORT);
|
||||
process.env.KB_REPO_PATH = KB_REPO_PATH;
|
||||
const serverBundle = path.join(__dirname, 'server-bundle.js');
|
||||
const serverBundle = path.join(__dirname, 'server-bundle.cjs');
|
||||
require(serverBundle);
|
||||
console.log(`[KB Server] 内嵌启动,端口 ${SERVER_PORT}`);
|
||||
resolve();
|
||||
|
|
@ -106,7 +132,7 @@ function createWindow(): void {
|
|||
trafficLightPosition: { x: 16, y: 16 },
|
||||
backgroundColor: '#0d1117',
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
preload: path.join(__dirname, 'preload.cjs'),
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false,
|
||||
},
|
||||
|
|
@ -130,10 +156,37 @@ function createWindow(): void {
|
|||
});
|
||||
}
|
||||
|
||||
ipcMain.handle('get-data-path', () => DATA_DIR);
|
||||
ipcMain.handle('agent:get-config', () => {
|
||||
const config = readModelConfig();
|
||||
return {
|
||||
baseUrl: config.baseUrl,
|
||||
model: config.model,
|
||||
configured: Boolean(config.encryptedKey && safeStorage.isEncryptionAvailable()),
|
||||
};
|
||||
});
|
||||
ipcMain.handle('agent:save-config', (_event, input: { baseUrl: string; model: string; apiKey?: string }) => {
|
||||
const baseUrl = String(input.baseUrl || '').trim().replace(/\/$/, '');
|
||||
const model = String(input.model || '').trim();
|
||||
if (!/^https:\/\//i.test(baseUrl)) throw new Error('模型服务地址必须使用 HTTPS');
|
||||
if (!model) throw new Error('模型名称不能为空');
|
||||
if (!safeStorage.isEncryptionAvailable()) throw new Error('macOS 加密存储当前不可用');
|
||||
const previous = readModelConfig();
|
||||
const encryptedKey = input.apiKey
|
||||
? safeStorage.encryptString(String(input.apiKey)).toString('base64')
|
||||
: previous.encryptedKey;
|
||||
if (!encryptedKey) throw new Error('请填写模型密钥');
|
||||
fs.mkdirSync(path.dirname(MODEL_CONFIG_PATH), { recursive: true });
|
||||
fs.writeFileSync(MODEL_CONFIG_PATH, JSON.stringify({ baseUrl, model, encryptedKey }), { mode: 0o600 });
|
||||
applyModelConfig();
|
||||
return { baseUrl, model, configured: true };
|
||||
});
|
||||
|
||||
// ─── 应用生命周期 ───
|
||||
|
||||
app.whenReady().then(async () => {
|
||||
try {
|
||||
applyModelConfig();
|
||||
await startServer();
|
||||
createWindow();
|
||||
} catch (err) {
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@ contextBridge.exposeInMainWorld('hololake', {
|
|||
// 数据目录
|
||||
getDataPath: () => ipcRenderer.invoke('get-data-path'),
|
||||
|
||||
// Forgejo 远程仓库管理(预留接口)
|
||||
forgejo: {
|
||||
getRemotes: () => ipcRenderer.invoke('forgejo:get-remotes'),
|
||||
addRemote: (url: string) => ipcRenderer.invoke('forgejo:add-remote', url),
|
||||
push: () => ipcRenderer.invoke('forgejo:push'),
|
||||
pull: () => ipcRenderer.invoke('forgejo:pull'),
|
||||
agent: {
|
||||
getConfig: () => ipcRenderer.invoke('agent:get-config'),
|
||||
saveConfig: (config: { baseUrl: string; model: string; apiKey?: string }) =>
|
||||
ipcRenderer.invoke('agent:save-config', config),
|
||||
},
|
||||
|
||||
// Forgejo 通过本地知识库 API 管理;此桥只保留非敏感应用信息。
|
||||
});
|
||||
|
|
|
|||
|
|
@ -23,3 +23,9 @@
|
|||
# - 不接 Forgejo 上游自动更新
|
||||
# - 光湖团队评估 Forgejo 新版功能后,拆出有用部分自行集成
|
||||
# - Forgejo 的 Git 引擎逐渐适配光湖协议(HLDP/GLS)
|
||||
#
|
||||
# 2026-08-08 运行实现:
|
||||
# - GitEngine 已实现状态、配置 remote、fetch、仅快进 pull、显式确认 push
|
||||
# - 桌面 UI 已提供连接与同步状态;认证继续使用本机钥匙串或 SSH
|
||||
# - Agent 只获得只读 inspect_repository 工具,不拥有自动 push 权限
|
||||
# - 本地 API 仅监听 127.0.0.1,不向局域网暴露
|
||||
|
|
|
|||
1191
product-source/hololake-desktop/package-lock.json
generated
1191
product-source/hololake-desktop/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -2,30 +2,32 @@
|
|||
"name": "hololake-desktop",
|
||||
"version": "0.5.0",
|
||||
"description": "HoloLake Era 桌面版 — Git 驱动的知识库管理",
|
||||
"main": "dist-electron/main.js",
|
||||
"main": "dist-electron/main.cjs",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "concurrently \"npm run dev:server\" \"npm run dev:client\"",
|
||||
"dev:server": "tsx watch ../guanghu-knowledge-base/server/index.ts",
|
||||
"dev:client": "vite",
|
||||
"dev:electron": "electron .",
|
||||
"build": "vite build && tsc -p tsconfig.electron.json",
|
||||
"build:electron": "esbuild electron/main.ts --bundle --platform=node --format=cjs --external:electron --outfile=dist-electron/main.cjs && esbuild electron/preload.ts --bundle --platform=node --format=cjs --external:electron --outfile=dist-electron/preload.cjs",
|
||||
"build:server": "NODE_PATH=./node_modules esbuild ../guanghu-knowledge-base/server/index.ts --bundle --platform=node --format=cjs --outfile=dist-electron/server-bundle.cjs",
|
||||
"build": "vite build && npm run build:electron && npm run build:server",
|
||||
"pack": "npm run build && electron-builder --mac --dir",
|
||||
"dist": "npm run build && electron-builder --mac",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^5.1.0",
|
||||
"cors": "^2.8.5",
|
||||
"simple-git": "^3.27.0",
|
||||
"diff": "^9.0.0",
|
||||
"express": "^5.1.0",
|
||||
"gray-matter": "^4.0.3",
|
||||
"marked": "^15.0.0",
|
||||
"diff": "^7.0.0"
|
||||
"simple-git": "^3.27.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^5.0.0",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/diff": "^6.0.0",
|
||||
"@types/express": "^5.0.0",
|
||||
"@types/node": "^22.0.0",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
|
|
@ -33,6 +35,7 @@
|
|||
"concurrently": "^9.1.0",
|
||||
"electron": "^35.0.0",
|
||||
"electron-builder": "^26.0.0",
|
||||
"esbuild": "^0.25.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"tsx": "^4.19.0",
|
||||
|
|
@ -47,10 +50,15 @@
|
|||
"icon": "forgejo/icon.icns",
|
||||
"identity": "bei sun (825A9L3G7Q)",
|
||||
"target": [
|
||||
{ "target": "dmg", "arch": ["arm64"] }
|
||||
{
|
||||
"target": "dmg",
|
||||
"arch": [
|
||||
"arm64"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"asar": false,
|
||||
"asar": true,
|
||||
"files": [
|
||||
"dist/**/*",
|
||||
"dist-electron/**/*",
|
||||
|
|
|
|||
|
|
@ -3,11 +3,17 @@ import react from '@vitejs/plugin-react';
|
|||
import path from 'path';
|
||||
|
||||
export default defineConfig({
|
||||
base: './',
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
// 复用知识库前端组件
|
||||
'@': path.resolve(__dirname, '../guanghu-knowledge-base/src'),
|
||||
'react/jsx-runtime': path.resolve(__dirname, 'node_modules/react/jsx-runtime.js'),
|
||||
'react-dom/client': path.resolve(__dirname, 'node_modules/react-dom/client.js'),
|
||||
'react-dom': path.resolve(__dirname, 'node_modules/react-dom/index.js'),
|
||||
'react': path.resolve(__dirname, 'node_modules/react/index.js'),
|
||||
'marked': path.resolve(__dirname, 'node_modules/marked/lib/marked.esm.js'),
|
||||
},
|
||||
},
|
||||
root: path.resolve(__dirname, '../guanghu-knowledge-base'),
|
||||
|
|
|
|||
Loading…
Reference in a new issue