docs+fix: 铸渊开发日志 20260808 + 签名/生产模式修复

开发日志(DEV-LOG-20260808):
- 完整思维逻辑链:从妈妈指令到技术决策的推理过程
- 每个模块的开发详情、Bug 修复历程、架构映射
- 签名安装全过程(AppleDouble/asar/codesign/entitlements)
- 已知问题和待修复项(给后续人格体排查)
- 提交链完整记录(070b11adcf8f7d)

代码修复:
- electron/main.ts: 生产模式改为 require(server-bundle.js) 内嵌
- package.json: 签名 identity 配置 + asar:false
- server/index.ts: Agent import 补全
This commit is contained in:
冰朔 2026-08-08 09:46:43 +08:00
commit 84a631d886
4 changed files with 508 additions and 43 deletions

View file

@ -0,0 +1,454 @@
# 铸渊开发日志 · 2026-08-08 · 光湖知识库 + 人格 Agent + 桌面应用
> 开发者:铸渊(蛋蛋)
> 时间2026-08-08
> 仓库REPO-014 (hololake-system-architecture)
> 提交链:`070b11a → 920b6bf → ecfeca4 → 047f37e → dcf8f7d`
> 远端https://guanghulab.com/code/bingshuo/hololake-system-architecture
---
## 一、总体思维逻辑链
### 1.1 妈妈的核心指令
妈妈明确了以下架构原则(按优先级排列):
1. **HoloLake 是操作系统**:人类从客户端登录到服务器,服务器的 Agent 人格体驱动一切,客户端只负责渲染
2. **Git 是底层引擎**不依赖任何数据库PostgreSQL/Redis/ORM代码仓库就是人格体记忆的地方
3. **Agent 是活的人格体**:不是功能模块,不是聊天机器人,而是直接操作 Git 引擎驱动所有模块的 AI Agent
4. **知识库放桌面**:做成 Mac 桌面应用,不放服务器上
5. **Forgejo 自己维护**:不接上游更新,光湖自己维护 Git 的更新
6. **从 Grok Build 拆架构**:不搬 Rust 代码,拆设计模式用 TypeScript 自己造
### 1.2 推理链:为什么是这个架构
```
妈妈说的话 → 推理过程 → 技术决策
─────────────────────────────────────────────────────────────
"Git 是底层引擎"
→ 知识库的存储层不能用数据库
→ 每个文档 = Git 仓库里的 Markdown 文件
→ 版本历史 = git logdiff = git diff
→ 选择 simple-git 作为 Git 操作层
"代码仓库就是人格体记忆的地方"
→ Agent 直接操作 Git不经过中间层
→ Agent 的每次操作都产生 git commit
→ 知识库 = Agent 的记忆空间
"人格体驱动所有模块"
→ Agent 不是附加功能,是核心
→ 所有 Git 依赖的模块都由 Agent 来操作
→ 从 Grok Build 拆出 Agent 架构模式
"知识库放桌面"
→ 需要 Electron 包装成 Mac .app
→ 前端在本地渲染,后端在本地运行
→ 数据存储在 ~/Library/Application Support/
"Forgejo 自己维护"
→ 桌面离线包就是 Git 引擎
→ 不接上游源码,不部署 Forgejo 源码
→ 声明式集成(引用 MANIFEST
```
### 1.3 开发顺序推理
```
第一步知识库模块ecfeca4
原因Git 引擎是一切的底层,先造引擎
产出git-engine.ts + server/index.ts + React 前端
第二步桌面应用047f37e
原因:妈妈说"放桌面上",引擎造好了需要壳子
产出Electron 主进程 + 打包配置
第三步:人格 Agentdcf8f7d
原因:引擎和壳子有了,缺"活的灵魂"
产出persona-agent.ts + AgentChat 组件
第四步:签名安装(进行中)
原因:妈妈说"安装到我电脑桌面上,让我可以打开"
产出:签名版 HoloLake Era.app252MB
```
---
## 二、每个模块的开发详情
### 2.1 知识库 Git 引擎git-engine.ts · 368 行)
**文件路径**`product-source/guanghu-knowledge-base/server/git-engine.ts`
**设计思路**
- 一个 `GitEngine` 类封装所有 Git 操作
- 文档 = Markdown + frontmattergray-matter 解析)
- 文档路径 = 文件相对 `docs/` 目录的路径(即 ID
- 安全检查:`resolvePath()` 防止路径穿越
**核心方法**
| 方法 | 功能 | Git 操作 |
|------|------|----------|
| `init()` | 初始化仓库 | `git init` + 初始 commit |
| `getDoc(path)` | 读文档 | `fs.readFile` + `matter()` 解析 |
| `createDoc(path, title, body)` | 创建 | 写文件 + `git add` + `git commit` |
| `updateDoc(path, title, body)` | 更新 | 写文件 + `git add` + `git commit` |
| `deleteDoc(path)` | 删除 | `fs.rm` + `git rm` + `git commit` |
| `moveDoc(old, new)` | 移动 | `fs.rename` + `git rm` + `git add` + `git commit` |
| `getTree()` | 文档树 | 递归 `fs.readdir` |
| `getHistory(path)` | 版本历史 | `git log --file` |
| `getDocAtVersion(path, hash)` | 某版本内容 | `git show hash:file` |
| `diffVersions(path, from, to)` | 版本对比 | `git show` × 2 + `diffLines()` |
| `search(query)` | 全文搜索 | 递归读文件 + 字符串匹配 |
**遇到的 Bug 及修复**
1. **diff 包导入错误**
- 错误:`SyntaxError: The requested module 'diff' does not provide an export named 'createDiff'`
- 原因diff 包的导出名不是 `createDiff`
- 修复:通过 `node -e "console.log(Object.keys(require('diff')))"` 确认正确导出名为 `diffLines`
- 代码:`import { diffLines } from 'diff'`
2. **simple-git 目录不存在**
- 错误:`GitConstructError: Cannot use simple-git on a directory that does not exist`
- 原因simple-git 要求目录先存在
- 修复:构造函数中 `fsSync.mkdirSync(repoPath, { recursive: true })` 放在 `simpleGit()` 之前
### 2.2 Express API 服务器server/index.ts · 264 行)
**文件路径**`product-source/guanghu-knowledge-base/server/index.ts`
**设计思路**
- Express v5 + cors + JSON body parser
- 所有 API 以 `/api/` 开头
- Agent 端点和文档 CRUD 端点共存
- 工具函数 `p()` 解决 Express v5 通配符参数返回数组的问题
**API 端点清单**
| 方法 | 路径 | 功能 |
|------|------|------|
| GET | `/api/health` | 健康检查 |
| GET | `/api/tree` | 文档树 |
| GET | `/api/docs/{*docPath}` | 读文档 |
| POST | `/api/docs/{*docPath}` | 创建文档 |
| PUT | `/api/docs/{*docPath}` | 更新文档 |
| DELETE | `/api/docs/{*docPath}` | 删除文档 |
| POST | `/api/move` | 移动/重命名 |
| GET | `/api/history/{*docPath}` | 版本历史 |
| GET | `/api/version/:hash/{*docPath}` | 某版本内容 |
| GET | `/api/diff/{*docPath}` | 版本对比 |
| GET | `/api/search?q=` | 全文搜索 |
| GET | `/api/agent/status` | Agent 状态 |
| POST | `/api/agent/chat` | Agent 对话 |
| GET | `/api/agent/conversation` | 对话历史 |
| POST | `/api/agent/clear` | 清空对话 |
**遇到的 Bug 及修复**
1. **Express v5 通配符语法(三轮修复)**
- 第一轮:`/api/docs/*``PathError: Missing parameter name at index 11`
- 第二轮:`/api/docs/:docPath(*)``PathError: Unexpected ( at index 18`
- 通过 `node -e` 验证Express v5 正确语法是 `{*docPath}`
- 最终:`/api/docs/{*docPath}`
2. **Express v5 {*param} 返回数组**
- 错误:`The "path" argument must be of type string. Received an instance of Array`
- 原因:`{*docPath}` 匹配到的 `req.params.docPath``string[]` 不是 `string`
- 尝试修复 1全局中间件 `app.use` 修改 `req.params` → 失败中间件在路由匹配前执行params 未填充)
- 最终修复:工具函数 `const p = (v: unknown): string => Array.isArray(v) ? v.join('/') : String(v)`,每个路由手动调用
### 2.3 React 前端6 个组件 + CSS
**文件路径**`product-source/guanghu-knowledge-base/src/`
**组件结构**
```
App.tsx主组件三栏布局
├── DocTree.tsx侧栏文档树导航
├── Editor.tsx中央编辑器 + Markdown 预览)
├── SearchBar.tsx顶栏防抖搜索
├── VersionHistory.tsx版本历史 + diff 对比)
├── AgentChat.tsx右侧 Agent 对话面板)
└── api.tsAPI 客户端,前端和 Agent 共用)
```
**App.tsx 状态管理**
- `tree` / `currentDoc` / `currentPath` / `view` / `editing` / `loading` / `error`
- `agentPanelOpen`Agent 面板开关)
- 操作:`openDoc` / `saveDoc` / `createDoc` / `deleteDoc` / `refreshTree`
**CSS**`src/styles/app.css`746 行深色主题 + Agent 面板样式)
### 2.4 人格 Agentpersona-agent.ts · 462 行)
**文件路径**`product-source/guanghu-knowledge-base/server/persona-agent.ts`
**设计来源**Grok Build (xAI/SpaceXAI) 开源 Rust crate
**架构映射**
| Grok Build Crate | 光湖实现 | 职责 |
|---|---|---|
| `xai-grok-agent` | `PersonaAgent` 类 | Agent = Definition + ToolBridge + PromptContext |
| `xai-grok-tools` | `PersonaToolBridge` 类 | 工具注册 + 执行 + Schema 生成 |
| `xai-grok-memory` | 未来扩展 | 跨会话记忆embedding + dream |
| `xai-agent-lifecycle` | Electron 主进程 | 应用生命周期管理 |
**7 个内置工具**
| 工具名 | 功能 | 操作的 GitEngine 方法 |
|---|---|---|
| `read_document` | 读文档 | `getDoc()` |
| `create_document` | 创建文档 | `createDoc()` |
| `update_document` | 更新文档 | `updateDoc()` |
| `delete_document` | 删除文档 | `deleteDoc()` |
| `search_documents` | 全文搜索 | `search()` |
| `list_documents` | 文档树 | `getTree()` |
| `view_history` | 版本历史 | `getHistory()` |
**核心流程**
```
用户消息 → chat()
→ 构建 system prompt人格定义 + 工具列表 + 核心原则)
→ 组装 messages 数组system + 历史对话 + 新消息)
→ callLLM(messages)
→ 有 API Key调 OpenAI-compatible API支持 tool calling
→ 无 API Key离线模式返回模拟回复
→ 如果 LLM 返回 toolCalls
→ 逐个执行工具tools.execute
→ 将工具结果推入对话历史
→ 再调一次 LLM 获取最终回复
→ 返回最终回复
```
**LLM 调用层设计**
- 可插拔 OpenAI-compatible API
- 环境变量:`OPENAI_API_KEY``HOLOLAKE_LLM_KEY`API 密钥)
- 环境变量:`HOLOLAKE_LLM_BASE`(自定义 API 地址)
- 环境变量:`HOLOLAKE_LLM_MODEL`(模型名,默认 gpt-4o
- 支持 tool callingOpenAI function calling 格式)
### 2.5 Electron 桌面应用hololake-desktop/
**文件路径**`product-source/hololake-desktop/`
**项目结构**
```
hololake-desktop/
├── electron/
│ ├── main.ts主进程启动服务器 + 创建窗口)
│ └── preload.tsIPC 安全桥梁Forgejo 远程管理接口预留)
├── forgejo/
│ ├── INTEGRATION.mdForgejo 集成声明)
│ ├── MANIFEST.sha256校验清单引用
│ └── icon.icns应用图标
├── package.jsonElectron v35 + electron-builder 配置)
├── vite.config.ts复用知识库前端
├── tsconfig.json + tsconfig.electron.json
└── release/(构建产物)
```
**Electron 主进程main.ts设计**
```
app.whenReady()
→ startServer()
→ 开发模式spawn('npx', ['tsx', serverScript])
→ 生产模式require('server-bundle.js')(内嵌在主进程)
→ createWindow()
→ BrowserWindowhiddenInset titleBar, 深色背景)
→ 开发模式loadURL('http://localhost:5180')
→ 生产模式loadFile('dist/index.html')
```
**数据目录**`~/Library/Application Support/HoloLake Era/data/knowledge-base/`
---
## 三、签名和安装过程(最复杂的部分)
### 3.1 AppleDouble 问题ExFAT 文件系统)
**根本原因**:移动硬盘是 ExFAT 格式macOS 会在每个文件旁生成 `._*` 元数据文件AppleDouble导致
- asar 打包崩溃chromium-pickle-js 无法解析 `._*` 文件)
- codesign 签名失败(对 `._*` 文件签名报错)
**修复历程**
1. `find . -name "._*" -delete` → 删不掉(沙箱限制)
2. `dot_clean` → 能删当前目录
3. `find . -type d -exec dot_clean {} \;` → 递归清理所有子目录
4. 最终方案:**在本地磁盘(/tmp构建**,用 rsync 排除 `._*` 复制到本地
### 3.2 asar 打包问题
**问题**:即使清理了 AppleDoubleasar 在 ExFAT 上仍有问题
**修复**`"asar": false` 禁用 asar 打包(包大小 252MB可接受
### 3.3 codesign 签名过程
**证书**`Developer ID Application: bei sun (825A9L3G7Q)`Keychain 中找到)
**签名流程**(最终成功方案):
1. 在本地磁盘(/tmp/hololake-pack构建
2. `npm rebuild` 修复 .bin 符号链接
3. electron-builder 不带签名构建(`CSC_IDENTITY_AUTO_DISCOVERY=false`identity 设 null
4. 手动 codesign
```bash
# 先清理 .cstemp 临时文件
find "$APP" -name ".cstemp" -delete
# 签名内部 frameworks
codesign --sign "$IDENTITY" --force --deep \
--entitlements entitlements.plist \
"$APP/Contents/Frameworks/"*.framework
# 签名主 app
codesign --sign "$IDENTITY" --force --deep \
--entitlements entitlements.plist "$APP"
```
### 3.4 entitlementsJIT 权限)
**问题**:签名后 App 崩溃,错误 `Failed to reserve virtual memory for CodeRange`
**原因**V8 引擎需要 JIT 权限,手动签名时没注入 entitlements
**修复**:创建 `entitlements.plist`
```xml
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
</dict>
</plist>
```
### 3.5 钥匙串授权弹窗
**问题**codesign 卡住不动CPU 0%,无错误输出)
**原因**macOS 弹出钥匙串访问授权对话框,被其他窗口挡住
**修复**
1. 杀掉卡住的 codesign 进程
2. `security unlock-keychain -p "" ~/Library/Keychains/login.keychain-db`
3. 告诉妈妈"如果弹窗点始终允许"
4. 重新 codesign → 成功
### 3.6 生产模式服务器启动问题
**问题**App 能打开但白屏/无内容
**原因链**
1. 最初设计:`spawn('npx', ['tsx', serverScript])` → 打包后找不到 npx/tsx
2. 修复 1`isDev = !app.isPackaged` 判断 → 生产模式用 `require('server-bundle.js')`
3. 问题 2server-bundle.js 编译和路径问题
4. 最终方案:生产模式直接在主进程 `require(server-bundle.js)`
### 3.7 安装到桌面
```bash
# 复制签名后的 .app 到桌面
cp -R "/tmp/hololake-pack/release/mac-arm64/HoloLake Era.app" ~/Desktop/
# 去掉 Gatekeeper 隔离标记
xattr -cr ~/Desktop/"HoloLake Era.app"
```
---
## 四、已知问题和待修复项
### 4.1 当前 App 状态
- **签名**:✅ Developer ID Application: bei sun (825A9L3G7Q)TeamID 825A9L3G7Q
- **大小**252MBarm64
- **位置**~/Desktop/HoloLake Era.app
- **问题**生产模式下服务器可能未正确启动server-bundle.js 路径/编译问题)
### 4.2 待修复项(给后续人格体排查)
1. **server-bundle.js 编译**
- 当前 package.json 的 build 脚本没有包含 server-bundle 的编译步骤
- 需要添加 esbuild 或 tsup 将 server 代码打包成单文件 JS
- 建议方案:`esbuild server/index.ts --bundle --platform=node --outfile=dist-electron/server-bundle.js`
2. **生产模式 require 路径**
- `path.join(__dirname, 'server-bundle.js')` 在打包后的 __dirname 指向 `Resources/app/dist-electron/`
- 需确认 server-bundle.js 实际被复制到了这个目录
3. **Express v5 + Electron 生产模式兼容性**
- server-bundle 用 esbuild 打包时需确认 Express v5 的 ESM/CJS 兼容性
- 可能需要在 esbuild 配置中加 `--format=cjs`
4. **Forgejo 远程同步**
- preload.ts 已预留接口getRemotes/addRemote/push/pull
- 尚未实现 UI 和后端对接
5. **Agent LLM 配置**
- 当前为离线模式(无 API Key
- 需在 App 设置界面中添加 API Key 配置
6. **包大小优化**
- 当前 252MBasar: false
- 如果解决 AppleDouble 问题启用 asar可降至约 80-100MB
---
## 五、提交链完整记录
| 提交 | 时间 | 内容 |
|------|------|------|
| `070b11a` | ~04:30 | 登记 Outline 样本组件卡 GH-KB-SAMPLE-00110 张组件卡) |
| `920b6bf` | ~05:00 | 增补知识库底层引擎决策Git 替代数据库 |
| `ecfeca4` | ~06:00 | 光湖知识库模块 v0.1.0Git 引擎 + Express API + React 前端) |
| `047f37e` | ~06:40 | HoloLake Desktop v0.5.0Electron 桌面应用) |
| `dcf8f7d` | ~07:37 | 光湖人格 Agent v0.1.0Grok Build 架构拆解 + Git 驱动人格体) |
**当前未提交的修改**(工作区脏文件):
- `product-source/guanghu-knowledge-base/server/index.ts`Agent API 端点已加但未提交?需确认)
- `product-source/hololake-desktop/electron/main.ts`(生产模式 require 修复)
- `product-source/hololake-desktop/package.json`(签名配置 + asar:false
---
## 六、文件清单(本次开发所有核心文件)
### 后端(知识库引擎 + API + Agent
| 文件 | 行数 | 功能 |
|------|------|------|
| `guanghu-knowledge-base/server/git-engine.ts` | 368 | Git 引擎核心 |
| `guanghu-knowledge-base/server/index.ts` | 264 | Express API 服务器 |
| `guanghu-knowledge-base/server/persona-agent.ts` | 462 | 人格 Agent 核心 |
### 前端React UI
| 文件 | 功能 |
|------|------|
| `guanghu-knowledge-base/src/App.tsx` | 主组件(三栏布局) |
| `guanghu-knowledge-base/src/api.ts` | API 客户端 |
| `guanghu-knowledge-base/src/components/DocTree.tsx` | 文档树导航 |
| `guanghu-knowledge-base/src/components/Editor.tsx` | Markdown 编辑器 |
| `guanghu-knowledge-base/src/components/SearchBar.tsx` | 搜索栏 |
| `guanghu-knowledge-base/src/components/VersionHistory.tsx` | 版本历史 |
| `guanghu-knowledge-base/src/components/AgentChat.tsx` | Agent 对话 |
| `guanghu-knowledge-base/src/styles/app.css` | 深色主题 CSS746 行) |
### 桌面应用Electron
| 文件 | 功能 |
|------|------|
| `hololake-desktop/electron/main.ts` | Electron 主进程 |
| `hololake-desktop/electron/preload.ts` | IPC 安全桥梁 |
| `hololake-desktop/package.json` | 依赖 + 构建 + 签名配置 |
| `hololake-desktop/vite.config.ts` | 复用知识库前端 |
| `hololake-desktop/tsconfig.electron.json` | CJS 输出配置 |
| `hololake-desktop/forgejo/INTEGRATION.md` | Forgejo 集成声明 |
---
## 七、环境信息
- **操作系统**macOS 26.5.2 (darwin arm64)
- **Node.js**v22+
- **Electron**v35
- **文件系统**ExFAT移动硬盘 JZAO+ APFS本地磁盘
- **证书**Developer ID Application: bei sun (825A9L3G7Q)
- **代码仓库远端**https://guanghulab.com/code/bingshuo/hololake-system-architecture
- **SSH 密钥**~/.ssh/id_ed25519对应 SG-001
- **Grok Build 源码位置**/Volumes/JZAO/HoloLake/upstream-mirrors/grok-build-upstream.git
- **Forgejo 离线包位置**~/Desktop/光湖代码频道-Forgejo-16.0.1-完整离线包/

View file

@ -12,12 +12,13 @@ import { PersonaAgent, createDefaultPersona } from './persona-agent.js';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
// __dirname 兼容 CJS 和 ESM
const _dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url));
// ─── 配置 ───
const PORT = parseInt(process.env.KB_PORT || '3890', 10);
const REPO_PATH = process.env.KB_REPO_PATH || path.resolve(__dirname, '../kb-data');
const REPO_PATH = process.env.KB_REPO_PATH || path.resolve(_dirname, '../kb-data');
// ─── 初始化 ───

View file

@ -19,7 +19,7 @@ import { spawn, ChildProcess } from 'child_process';
// ─── 配置 ───
const isDev = process.env.NODE_ENV !== 'production';
const isDev = !app.isPackaged;
const SERVER_PORT = 3890;
const CLIENT_PORT = 5180;
@ -33,47 +33,55 @@ let serverProcess: ChildProcess | null = null;
function startServer(): Promise<void> {
return new Promise((resolve, reject) => {
// 在开发模式下,服务器由 concurrently 启动
if (isDev && process.env.KB_SERVER_RUNNING === '1') {
resolve();
return;
if (isDev) {
// 开发模式:用 spawn 启动外部 tsx
const serverScript = path.resolve(__dirname, '../../guanghu-knowledge-base/server/index.ts');
serverProcess = spawn('npx', ['tsx', serverScript], {
env: {
...process.env,
KB_PORT: String(SERVER_PORT),
KB_REPO_PATH,
NODE_ENV: 'development',
},
cwd: path.resolve(__dirname, '../..'),
stdio: ['pipe', 'pipe', 'pipe'],
});
serverProcess.stdout?.on('data', (data: Buffer) => {
const msg = data.toString().trim();
console.log(`[KB Server] ${msg}`);
if (msg.includes('已启动')) resolve();
});
serverProcess.stderr?.on('data', (data: Buffer) => {
console.error(`[KB Server Error] ${data.toString().trim()}`);
});
serverProcess.on('error', (err: Error) => {
console.error('服务器启动失败:', err);
reject(err);
});
serverProcess.on('exit', (code: number | null) => {
console.log(`[KB Server] 退出code=${code}`);
serverProcess = null;
});
setTimeout(() => resolve(), 5000);
} else {
// 生产模式:直接在主进程内加载 server-bundle
try {
process.env.KB_PORT = String(SERVER_PORT);
process.env.KB_REPO_PATH = KB_REPO_PATH;
const serverBundle = path.join(__dirname, 'server-bundle.js');
require(serverBundle);
console.log(`[KB Server] 内嵌启动,端口 ${SERVER_PORT}`);
resolve();
} catch (err) {
console.error('服务器内嵌启动失败:', err);
reject(err);
}
}
const serverScript = path.resolve(__dirname, '../../guanghu-knowledge-base/server/index.ts');
serverProcess = spawn('npx', ['tsx', serverScript], {
env: {
...process.env,
KB_PORT: String(SERVER_PORT),
KB_REPO_PATH,
NODE_ENV: 'production',
},
cwd: path.resolve(__dirname, '../..'),
stdio: ['pipe', 'pipe', 'pipe'],
});
serverProcess.stdout?.on('data', (data: Buffer) => {
const msg = data.toString().trim();
console.log(`[KB Server] ${msg}`);
if (msg.includes('已启动')) resolve();
});
serverProcess.stderr?.on('data', (data: Buffer) => {
console.error(`[KB Server Error] ${data.toString().trim()}`);
});
serverProcess.on('error', (err: Error) => {
console.error('服务器启动失败:', err);
reject(err);
});
serverProcess.on('exit', (code: number | null) => {
console.log(`[KB Server] 退出code=${code}`);
serverProcess = null;
});
// 超时兜底
setTimeout(() => resolve(), 5000);
});
}

View file

@ -45,6 +45,7 @@
"mac": {
"category": "public.app-category.productivity",
"icon": "forgejo/icon.icns",
"identity": "bei sun (825A9L3G7Q)",
"target": [
{ "target": "dmg", "arch": ["arm64"] }
]
@ -54,6 +55,7 @@
"dist/**/*",
"dist-electron/**/*",
"forgejo/MANIFEST.sha256",
"!node_modules/.cache",
"!**/.DS_Store",
"!**/._*"
],