@bolloon/bolloon-agent 0.3.16 → 0.3.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/error-classifier.js +118 -0
- package/dist/agents/parse-tool-call.js +196 -1
- package/dist/agents/pi-sdk-tools.js +13 -0
- package/dist/agents/pi-sdk.js +195 -262
- package/dist/cli/loading-tui.js +43 -36
- package/dist/cli-entry.js +15 -8
- package/dist/electron/config.js +9 -14
- package/dist/electron/dialogs.js +16 -53
- package/dist/electron/first-run.js +24 -65
- package/dist/electron/ipc.js +10 -14
- package/dist/electron/logger.js +7 -44
- package/dist/electron/main.js +42 -45
- package/dist/electron/menu.js +13 -18
- package/dist/electron/paths.js +12 -54
- package/dist/electron/server.js +18 -57
- package/dist/electron/tray.js +15 -53
- package/dist/electron/window.js +22 -61
- package/dist/electron-preload.js +16 -19
- package/dist/electron.js +1 -4
- package/dist/external-engines/discovery.js +11 -0
- package/dist/index.js +151 -12
- package/dist/lsp/lsp-manager.js +281 -0
- package/dist/lsp/lsp-tools.js +222 -0
- package/dist/utils/auto-update.js +12 -51
- package/dist/web/client.js +16 -0
- package/dist/web/index.html +7 -0
- package/dist/web/server.js +48 -7
- package/dist/web/style.css +58 -3
- package/package.json +3 -3
package/dist/cli/loading-tui.js
CHANGED
|
@@ -16,12 +16,19 @@ import * as path from 'path';
|
|
|
16
16
|
import { fileURLToPath } from 'url';
|
|
17
17
|
const RESET = '\x1b[0m';
|
|
18
18
|
const BOLD = '\x1b[1m';
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
const
|
|
19
|
+
const DIM = '\x1b[2m';
|
|
20
|
+
// Bolloon Web UI 配色 truecolor ANSI
|
|
21
|
+
function fg(r, g, b) { return `\x1b[38;2;${r};${g};${b}m`; }
|
|
22
|
+
function bg(r, g, b) { return `\x1b[48;2;${r};${g};${b}m`; }
|
|
23
|
+
const C_ACCENT = fg(0xc4, 0xd6, 0x40); // #c4d640
|
|
24
|
+
const C_ACCENT_BG = bg(0xc4, 0xd6, 0x40);
|
|
25
|
+
const C_TEXT = fg(0xd8, 0xd8, 0xc8); // #d8d8c8
|
|
26
|
+
const C_DIM = fg(0x90, 0x90, 0x88); // #909088
|
|
27
|
+
const C_MUTED = fg(0x60, 0x60, 0x58); // #606058
|
|
28
|
+
const C_OK = fg(0x22, 0xc5, 0x5e); // #22c55e
|
|
29
|
+
const C_ERROR = fg(0xef, 0x44, 0x44); // #ef4444
|
|
30
|
+
const C_WARN = fg(0xf5, 0x9e, 0x0b); // #f59e0b
|
|
31
|
+
const C_BORDER = fg(0x3a, 0x3a, 0x36); // #3a3a36
|
|
25
32
|
const HIDE = '\x1b[?25l';
|
|
26
33
|
const SHOW = '\x1b[?25h';
|
|
27
34
|
// 版本信息 — 从 package.json 读取, 不再硬编码
|
|
@@ -38,22 +45,22 @@ function getPackageVersion() {
|
|
|
38
45
|
const BOLLOON_VERSION = getPackageVersion();
|
|
39
46
|
// ── 品牌图标: 顶部带圆标注的 0 (气球) ──────────────
|
|
40
47
|
export const BOLLOON_ICON = [
|
|
41
|
-
`${
|
|
42
|
-
`${
|
|
43
|
-
`${
|
|
44
|
-
`${
|
|
45
|
-
`${
|
|
46
|
-
`${
|
|
48
|
+
`${C_ACCENT} ✦${RESET}`,
|
|
49
|
+
`${C_ACCENT} ╱ ╲${RESET}`,
|
|
50
|
+
`${C_ACCENT} ════◆════${RESET}`,
|
|
51
|
+
`${C_ACCENT} ╲ ╱${RESET}`,
|
|
52
|
+
`${C_ACCENT} ╲${RESET}`,
|
|
53
|
+
`${C_ACCENT} ✦ ╲${RESET}`,
|
|
47
54
|
].join('\n');
|
|
48
55
|
// ── 艺术字: BOLLOON (box 字体) + Bolloon Agent 副标题 ──
|
|
49
56
|
export const BOLLOON_BANNER = [
|
|
50
|
-
`${
|
|
51
|
-
`${
|
|
52
|
-
`${
|
|
53
|
-
`${
|
|
54
|
-
`${
|
|
55
|
-
`${
|
|
56
|
-
`${
|
|
57
|
+
`${C_TEXT}${BOLD}██████╗ ██████╗ ██╗ ██╗ ██████╗ ██████╗ ███╗ ██╗${RESET}`,
|
|
58
|
+
`${C_TEXT}${BOLD}██╔══██╗██╔═══██╗██║ ██║ ██╔═══██╗██╔═══██╗████╗ ██║${RESET}`,
|
|
59
|
+
`${C_TEXT}${BOLD}██████╔╝██║ ██║██║ ██║ ██║ ██║██║ ██║██╔██╗ ██║${RESET}`,
|
|
60
|
+
`${C_TEXT}${BOLD}██╔══██╗██║ ██║██║ ██║ ██║ ██║██║ ██║██║╚██╗██║${RESET}`,
|
|
61
|
+
`${C_TEXT}${BOLD}██████╔╝╚██████╔╝███████╗███████╗╚██████╔╝╚██████╔╝██║ ╚████║${RESET}`,
|
|
62
|
+
`${C_TEXT}${BOLD}╚═════╝ ╚═════╝ ╚══════╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝${RESET}`,
|
|
63
|
+
`${C_DIM}Bolloon Agent v${BOLLOON_VERSION}${RESET}`,
|
|
57
64
|
].join('\n');
|
|
58
65
|
/** 艺术字全部行 (图标在左, BOLLOON 艺术字在右), 供框内渲染 */
|
|
59
66
|
function brandArtLines() {
|
|
@@ -75,18 +82,18 @@ export function printBanner(version) {
|
|
|
75
82
|
console.log(BOLLOON_ICON);
|
|
76
83
|
console.log(BOLLOON_BANNER);
|
|
77
84
|
if (version)
|
|
78
|
-
console.log(`${
|
|
79
|
-
console.log(`${
|
|
85
|
+
console.log(`${C_DIM} Bolloon Agent v${version}${RESET}`);
|
|
86
|
+
console.log(`${C_DIM} P2P AI Agent · 文档智能体${RESET}`);
|
|
80
87
|
console.log('');
|
|
81
88
|
}
|
|
82
89
|
// ── 状态图标 ───────────────────────────────────────
|
|
83
90
|
export const STATUS_SYMBOL = {
|
|
84
|
-
pending: `${
|
|
85
|
-
active: `${
|
|
86
|
-
ok: `${
|
|
87
|
-
warn: `${
|
|
88
|
-
error: `${
|
|
89
|
-
info: `${
|
|
91
|
+
pending: `${C_MUTED}○${RESET}`,
|
|
92
|
+
active: `${C_WARN}⠹${RESET}`,
|
|
93
|
+
ok: `${C_OK}✓${RESET}`,
|
|
94
|
+
warn: `${C_WARN}⚠${RESET}`,
|
|
95
|
+
error: `${C_ERROR}✗${RESET}`,
|
|
96
|
+
info: `${C_ACCENT}●${RESET}`,
|
|
90
97
|
};
|
|
91
98
|
// 方角 (启动仪表盘 / 对话框 / 引用框)
|
|
92
99
|
const SQ = { tl: '┌', tr: '┐', bl: '└', br: '┘', v: '│', h: '─' };
|
|
@@ -191,7 +198,7 @@ export function renderDashboard(opts) {
|
|
|
191
198
|
}
|
|
192
199
|
for (const r of opts.rows) {
|
|
193
200
|
const sym = STATUS_SYMBOL[r.status ?? 'info'];
|
|
194
|
-
const detail = r.detail ? ` ${
|
|
201
|
+
const detail = r.detail ? ` ${C_DIM}${r.detail}${RESET}` : '';
|
|
195
202
|
lines.push(boxRow(`${sym} ${r.label}${detail}`, width));
|
|
196
203
|
}
|
|
197
204
|
lines.push(boxBottom(width));
|
|
@@ -265,7 +272,7 @@ function wrapText(text, width) {
|
|
|
265
272
|
/** 对话框默认显示上限 (按高度截断) */
|
|
266
273
|
const DEFAULT_MAX_LINES = 14;
|
|
267
274
|
export function renderMessageBox(opts) {
|
|
268
|
-
const color = opts.color ??
|
|
275
|
+
const color = opts.color ?? C_ACCENT;
|
|
269
276
|
const title = opts.title ?? 'Bolloon Agent';
|
|
270
277
|
const maxLines = opts.maxLines && opts.maxLines > 0 ? opts.maxLines : 0;
|
|
271
278
|
const bodyLines = wrapText(opts.body, 1000);
|
|
@@ -298,20 +305,20 @@ function renderReference(opts) {
|
|
|
298
305
|
const inner = Math.max(20, dispWidth(opts.title) + 8, dispWidth(`已压缩 ${opts.hidden} 行 · 完整内容已发送给智能体`), dispWidth(preview) + 2);
|
|
299
306
|
const width = Math.min(termWidth() - 2, opts.width ?? inner + 4);
|
|
300
307
|
const lines = [];
|
|
301
|
-
lines.push(boxTop(`${
|
|
302
|
-
lines.push(boxRow(`${
|
|
308
|
+
lines.push(boxTop(`${C_DIM}引用${RESET} ${opts.color}${opts.title}${RESET}`, width, RD));
|
|
309
|
+
lines.push(boxRow(`${C_DIM}已压缩 ${opts.hidden} 行 · 完整内容已发送给智能体${RESET}`, width, 'left', RD));
|
|
303
310
|
if (preview)
|
|
304
|
-
lines.push(boxRow(`${
|
|
311
|
+
lines.push(boxRow(`${C_DIM}▏ ${preview}${RESET}`, width, 'left', RD));
|
|
305
312
|
lines.push(boxBottom(width, RD));
|
|
306
313
|
return lines.join('\n');
|
|
307
314
|
}
|
|
308
315
|
/** 已发送消息框 (用户输入) */
|
|
309
316
|
export function renderUserMessage(body) {
|
|
310
|
-
return renderMessageBox({ title: '✓ 已发送', body, color:
|
|
317
|
+
return renderMessageBox({ title: '✓ 已发送', body, color: C_OK, maxLines: DEFAULT_MAX_LINES });
|
|
311
318
|
}
|
|
312
319
|
/** 智能体回复框 */
|
|
313
320
|
export function renderAgentMessage(body) {
|
|
314
|
-
return renderMessageBox({ title: '◉ Bolloon Agent', body, color:
|
|
321
|
+
return renderMessageBox({ title: '◉ Bolloon Agent', body, color: C_ACCENT, maxLines: DEFAULT_MAX_LINES });
|
|
315
322
|
}
|
|
316
323
|
/** 循环工作流连接线: 用 ╼ ╾ 串联相邻工具框 */
|
|
317
324
|
export function flowConnector(width) {
|
|
@@ -323,7 +330,7 @@ export function flowConnector(width) {
|
|
|
323
330
|
}
|
|
324
331
|
/** 渲染单个工具调用为圆角框 (参数 / 状态 / 输出预览) */
|
|
325
332
|
export function renderToolCall(v) {
|
|
326
|
-
const color = v.status === 'ok' ?
|
|
333
|
+
const color = v.status === 'ok' ? C_OK : C_ERROR;
|
|
327
334
|
const sym = v.status === 'ok' ? '✅' : '❌';
|
|
328
335
|
const w = Math.min(termWidth() - 2, v.width ?? 72);
|
|
329
336
|
const rows = [];
|
|
@@ -382,7 +389,7 @@ export class LoadingTUI {
|
|
|
382
389
|
out.push(boxRow(`${STATUS_SYMBOL[step.status]} ${step.label}`, w));
|
|
383
390
|
}
|
|
384
391
|
if (showSpinner) {
|
|
385
|
-
const sp =
|
|
392
|
+
const sp = C_WARN + FRAMES[this.frameIdx % FRAMES.length] + RESET;
|
|
386
393
|
out.push(boxRow(`${sp} ${this.currentLabel}`, w));
|
|
387
394
|
}
|
|
388
395
|
else {
|
package/dist/cli-entry.js
CHANGED
|
@@ -20,14 +20,17 @@ import { fileURLToPath } from 'url';
|
|
|
20
20
|
import { printBanner } from './cli/loading-tui.js';
|
|
21
21
|
import { discoverEngines, delegateToEngine } from './external-engines/index.js';
|
|
22
22
|
import { x402CheckBalance, x402Fetch } from './agents/x402/x402Pay.js';
|
|
23
|
+
import { createRequire } from 'module';
|
|
24
|
+
const _require = createRequire(import.meta.url);
|
|
23
25
|
const isWindows = process.platform === 'win32';
|
|
24
|
-
// ANSI 颜色
|
|
26
|
+
// ANSI 颜色 — Bolloon Web UI 配色 truecolor
|
|
27
|
+
function _fg(r, g, b) { return `\x1b[38;2;${r};${g};${b}m`; }
|
|
25
28
|
const RESET = '\x1b[0m';
|
|
26
29
|
const BOLD = '\x1b[1m';
|
|
27
|
-
const CYAN =
|
|
28
|
-
const YELLOW =
|
|
29
|
-
const GREEN =
|
|
30
|
-
const MAGENTA =
|
|
30
|
+
const CYAN = _fg(0xc4, 0xd6, 0x40); // #c4d640
|
|
31
|
+
const YELLOW = _fg(0xf5, 0x9e, 0x0b); // #f59e0b
|
|
32
|
+
const GREEN = _fg(0x22, 0xc5, 0x5e); // #22c55e
|
|
33
|
+
const MAGENTA = _fg(0xef, 0x44, 0x44); // #ef4444
|
|
31
34
|
// 版本信息 — 2026-07-20 Bug 3: 从 package.json 读取, 不再硬编码
|
|
32
35
|
const VERSION = (() => {
|
|
33
36
|
try {
|
|
@@ -100,9 +103,13 @@ function getMainScript() {
|
|
|
100
103
|
throw new Error('找不到入口脚本,请确保已执行 npm run build');
|
|
101
104
|
}
|
|
102
105
|
function getElectronPath() {
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
// ESM 兼容: use _require (createRequire) instead of raw require
|
|
107
|
+
try {
|
|
108
|
+
return _require('electron');
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return 'electron';
|
|
112
|
+
}
|
|
106
113
|
}
|
|
107
114
|
// 解析命令行参数
|
|
108
115
|
function parseArgs() {
|
package/dist/electron/config.js
CHANGED
|
@@ -1,21 +1,16 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isDev = exports.MAIN_WINDOW_MIN = exports.MAIN_WINDOW_DEFAULT = exports.WEB_SERVER_STARTUP_TIMEOUT_MS = exports.DEFAULT_HOST = exports.DEFAULT_PORT = void 0;
|
|
4
|
-
exports.preferredPort = preferredPort;
|
|
5
1
|
/**
|
|
6
2
|
* 常量配置 (env 解析在这里集中, 不散在 main 流程)
|
|
7
3
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
import { app } from 'electron';
|
|
5
|
+
export const DEFAULT_PORT = 54188;
|
|
10
6
|
/** Hard-pin to loopback; LAN exposure must be explicit. */
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
function preferredPort() {
|
|
7
|
+
export const DEFAULT_HOST = '127.0.0.1';
|
|
8
|
+
export const WEB_SERVER_STARTUP_TIMEOUT_MS = 15_000;
|
|
9
|
+
export const MAIN_WINDOW_DEFAULT = { width: 1200, height: 800 };
|
|
10
|
+
export const MAIN_WINDOW_MIN = { width: 800, height: 600 };
|
|
11
|
+
export function preferredPort() {
|
|
16
12
|
const raw = process.env.ELECTRON_PORT || process.env.PORT;
|
|
17
13
|
const n = parseInt(raw || '', 10);
|
|
18
|
-
return Number.isFinite(n) && n > 0 && n < 65536 ? n :
|
|
14
|
+
return Number.isFinite(n) && n > 0 && n < 65536 ? n : DEFAULT_PORT;
|
|
19
15
|
}
|
|
20
|
-
|
|
21
|
-
//# sourceMappingURL=config.js.map
|
|
16
|
+
export const isDev = process.env.NODE_ENV === 'development' || !app.isPackaged;
|
package/dist/electron/dialogs.js
CHANGED
|
@@ -1,61 +1,25 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.registerDialogIpc = registerDialogIpc;
|
|
37
1
|
/**
|
|
38
2
|
* 文件 dialog 桥 (open / save / dir) + 安全的 fs 桥 (read / write / exists)
|
|
39
3
|
*
|
|
40
4
|
* 5MB read 上限保护 — 渲染进程直接 fs.readFile 没法做限制, 走主进程就有界
|
|
41
5
|
* 所有 handler 解析 event.sender 拿到 window, 让 dialog 模态在该窗口上
|
|
42
6
|
*/
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
7
|
+
import { BrowserWindow, dialog, ipcMain } from 'electron';
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import { log } from './logger';
|
|
47
11
|
const MAX_READ_BYTES = 5 * 1024 * 1024; // 5MB
|
|
48
12
|
function windowFor(event) {
|
|
49
|
-
return
|
|
13
|
+
return BrowserWindow.fromWebContents(event.sender);
|
|
50
14
|
}
|
|
51
15
|
function resolveSafe(target) {
|
|
52
16
|
// 不去硬限制路径 — user 给 renderer 暴露 fs 已经信任了, 这里只 normalize
|
|
53
17
|
return path.resolve(target);
|
|
54
18
|
}
|
|
55
|
-
function registerDialogIpc() {
|
|
56
|
-
|
|
19
|
+
export function registerDialogIpc() {
|
|
20
|
+
ipcMain.handle('dialog:open-file', async (event, opts = {}) => {
|
|
57
21
|
const win = windowFor(event);
|
|
58
|
-
const result = await
|
|
22
|
+
const result = await dialog.showOpenDialog(win, {
|
|
59
23
|
title: opts.title,
|
|
60
24
|
defaultPath: opts.defaultPath,
|
|
61
25
|
filters: opts.filters,
|
|
@@ -63,25 +27,25 @@ function registerDialogIpc() {
|
|
|
63
27
|
});
|
|
64
28
|
return { canceled: result.canceled, filePaths: result.filePaths };
|
|
65
29
|
});
|
|
66
|
-
|
|
30
|
+
ipcMain.handle('dialog:save-file', async (event, opts = {}) => {
|
|
67
31
|
const win = windowFor(event);
|
|
68
|
-
const result = await
|
|
32
|
+
const result = await dialog.showSaveDialog(win, {
|
|
69
33
|
title: opts.title,
|
|
70
34
|
defaultPath: opts.defaultPath,
|
|
71
35
|
filters: opts.filters,
|
|
72
36
|
});
|
|
73
37
|
return { canceled: result.canceled, filePath: result.filePath };
|
|
74
38
|
});
|
|
75
|
-
|
|
39
|
+
ipcMain.handle('dialog:open-directory', async (event, opts = {}) => {
|
|
76
40
|
const win = windowFor(event);
|
|
77
|
-
const result = await
|
|
41
|
+
const result = await dialog.showOpenDialog(win, {
|
|
78
42
|
title: opts.title,
|
|
79
43
|
defaultPath: opts.defaultPath,
|
|
80
44
|
properties: ['openDirectory', 'createDirectory'],
|
|
81
45
|
});
|
|
82
46
|
return { canceled: result.canceled, filePaths: result.filePaths };
|
|
83
47
|
});
|
|
84
|
-
|
|
48
|
+
ipcMain.handle('fs:read-text-file', async (_event, opts) => {
|
|
85
49
|
const target = resolveSafe(opts.path);
|
|
86
50
|
const stat = fs.statSync(target);
|
|
87
51
|
if (stat.size > MAX_READ_BYTES) {
|
|
@@ -89,12 +53,12 @@ function registerDialogIpc() {
|
|
|
89
53
|
}
|
|
90
54
|
return fs.readFileSync(target, { encoding: opts.encoding ?? 'utf8' });
|
|
91
55
|
});
|
|
92
|
-
|
|
56
|
+
ipcMain.handle('fs:write-text-file', async (_event, opts) => {
|
|
93
57
|
const target = resolveSafe(opts.path);
|
|
94
58
|
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
95
59
|
fs.writeFileSync(target, opts.content, { encoding: opts.encoding ?? 'utf8' });
|
|
96
60
|
});
|
|
97
|
-
|
|
61
|
+
ipcMain.handle('fs:path-exists', async (_event, opts) => {
|
|
98
62
|
try {
|
|
99
63
|
fs.accessSync(resolveSafe(opts.path));
|
|
100
64
|
return true;
|
|
@@ -103,6 +67,5 @@ function registerDialogIpc() {
|
|
|
103
67
|
return false;
|
|
104
68
|
}
|
|
105
69
|
});
|
|
106
|
-
|
|
70
|
+
log('dialog + fs IPC handlers registered');
|
|
107
71
|
}
|
|
108
|
-
//# sourceMappingURL=dialogs.js.map
|
|
@@ -1,54 +1,14 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.hasSeenFirstRun = hasSeenFirstRun;
|
|
37
|
-
exports.markFirstRunSeen = markFirstRunSeen;
|
|
38
|
-
exports.showFirstRunOverlay = showFirstRunOverlay;
|
|
39
|
-
exports.registerFirstRunIpc = registerFirstRunIpc;
|
|
40
|
-
exports.maybeShowFirstRun = maybeShowFirstRun;
|
|
41
1
|
/**
|
|
42
2
|
* 首启检测 + 引导浮层
|
|
43
3
|
*
|
|
44
4
|
* 标记文件写在 userData (不是 ~/.bolloon/), 卸载 app 自然清掉
|
|
45
5
|
* 引导窗是父主窗的 modal, frame=false, 透明背景; 关闭时标记写入
|
|
46
6
|
*/
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
7
|
+
import { BrowserWindow, app, ipcMain } from 'electron';
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import { firstRunFlagPath, dataDir, logsDir } from './paths';
|
|
11
|
+
import { log } from './logger';
|
|
52
12
|
const OVERLAY_HTML = `
|
|
53
13
|
<!DOCTYPE html>
|
|
54
14
|
<html>
|
|
@@ -104,26 +64,26 @@ const OVERLAY_HTML = `
|
|
|
104
64
|
</body>
|
|
105
65
|
</html>
|
|
106
66
|
`;
|
|
107
|
-
function hasSeenFirstRun() {
|
|
67
|
+
export function hasSeenFirstRun() {
|
|
108
68
|
try {
|
|
109
|
-
return fs.existsSync(
|
|
69
|
+
return fs.existsSync(firstRunFlagPath());
|
|
110
70
|
}
|
|
111
71
|
catch {
|
|
112
72
|
return false;
|
|
113
73
|
}
|
|
114
74
|
}
|
|
115
|
-
function markFirstRunSeen() {
|
|
75
|
+
export function markFirstRunSeen() {
|
|
116
76
|
try {
|
|
117
|
-
fs.mkdirSync(path.dirname(
|
|
118
|
-
fs.writeFileSync(
|
|
77
|
+
fs.mkdirSync(path.dirname(firstRunFlagPath()), { recursive: true });
|
|
78
|
+
fs.writeFileSync(firstRunFlagPath(), new Date().toISOString());
|
|
119
79
|
}
|
|
120
80
|
catch (err) {
|
|
121
|
-
|
|
81
|
+
log(`写入首启标记失败: ${err.message}`, 'warn');
|
|
122
82
|
}
|
|
123
83
|
}
|
|
124
|
-
function showFirstRunOverlay(parent) {
|
|
84
|
+
export function showFirstRunOverlay(parent) {
|
|
125
85
|
return new Promise((resolve) => {
|
|
126
|
-
const overlay = new
|
|
86
|
+
const overlay = new BrowserWindow({
|
|
127
87
|
parent,
|
|
128
88
|
modal: true,
|
|
129
89
|
frame: false,
|
|
@@ -143,28 +103,27 @@ function showFirstRunOverlay(parent) {
|
|
|
143
103
|
markFirstRunSeen();
|
|
144
104
|
overlay.close();
|
|
145
105
|
};
|
|
146
|
-
|
|
106
|
+
ipcMain.once('first-run:ack', handler);
|
|
147
107
|
overlay.on('closed', () => {
|
|
148
|
-
|
|
108
|
+
ipcMain.removeListener('first-run:ack', handler);
|
|
149
109
|
resolve();
|
|
150
110
|
});
|
|
151
111
|
});
|
|
152
112
|
}
|
|
153
113
|
/** 注册 IPC handlers (给 preload 桥用) */
|
|
154
|
-
function registerFirstRunIpc() {
|
|
155
|
-
|
|
156
|
-
|
|
114
|
+
export function registerFirstRunIpc() {
|
|
115
|
+
ipcMain.handle('first-run:seen', () => hasSeenFirstRun());
|
|
116
|
+
ipcMain.handle('first-run:mark-seen', () => { markFirstRunSeen(); });
|
|
157
117
|
// 同步值 (不用 ipcRenderer.invoke 的 await) — 用 exposeInMainWorld 的 sync getter 更顺
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
118
|
+
ipcMain.handle('first-run:data-dir', () => dataDir());
|
|
119
|
+
ipcMain.handle('first-run:logs-dir', () => logsDir());
|
|
120
|
+
log('first-run IPC handlers registered');
|
|
161
121
|
}
|
|
162
122
|
/** 包装 — 决定要不要弹 overlay */
|
|
163
|
-
async function maybeShowFirstRun(parent) {
|
|
123
|
+
export async function maybeShowFirstRun(parent) {
|
|
164
124
|
if (hasSeenFirstRun())
|
|
165
125
|
return;
|
|
166
|
-
|
|
126
|
+
log('首启 — 弹出引导');
|
|
167
127
|
await showFirstRunOverlay(parent);
|
|
168
|
-
|
|
128
|
+
app.addRecentDocument(firstRunFlagPath()); // 跟踪最近文档, 让 user 知道有这文件
|
|
169
129
|
}
|
|
170
|
-
//# sourceMappingURL=first-run.js.map
|
package/dist/electron/ipc.js
CHANGED
|
@@ -1,20 +1,16 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.registerCoreIpc = registerCoreIpc;
|
|
4
1
|
/**
|
|
5
2
|
* IPC handler 集中注册 — version / userData path / open-external (legacy 3 个)
|
|
6
3
|
* dialog/fs 的注册在 dialogs.ts; updater 的注册在 updater.ts
|
|
7
4
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
function registerCoreIpc() {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
await
|
|
5
|
+
import { app, ipcMain, shell } from 'electron';
|
|
6
|
+
import { userDataDir, dataDir } from './paths';
|
|
7
|
+
import { log } from './logger';
|
|
8
|
+
export function registerCoreIpc() {
|
|
9
|
+
ipcMain.handle('get-version', () => app.getVersion());
|
|
10
|
+
ipcMain.handle('get-user-data-path', () => userDataDir());
|
|
11
|
+
ipcMain.handle('get-data-path', () => dataDir()); // 跟 userData 分开, 渲染层要用
|
|
12
|
+
ipcMain.handle('open-external', async (_event, url) => {
|
|
13
|
+
await shell.openExternal(url);
|
|
17
14
|
});
|
|
18
|
-
|
|
15
|
+
log('core IPC handlers registered');
|
|
19
16
|
}
|
|
20
|
-
//# sourceMappingURL=ipc.js.map
|
package/dist/electron/logger.js
CHANGED
|
@@ -1,39 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.log = log;
|
|
37
1
|
/**
|
|
38
2
|
* 简单 logger: 写日志到 <userData>/logs/bolloon.log, size-based 轮转
|
|
39
3
|
* (5MB 上限, 保留 .1 .2 .3 共 3 个备份 → ~20MB 上限)
|
|
@@ -41,8 +5,8 @@ exports.log = log;
|
|
|
41
5
|
* 同步写是因为 Electron 主进程日志量小, 同步 append 简单可靠, 不丢日志.
|
|
42
6
|
* 量大的会话级 jsonl 走自己路径 (lifecycle-hooks 等), 跟本 logger 无关.
|
|
43
7
|
*/
|
|
44
|
-
|
|
45
|
-
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import { mainLogPath, logsDir } from './paths';
|
|
46
10
|
const MAX_BYTES = 5 * 1024 * 1024;
|
|
47
11
|
const MAX_BACKUPS = 3; // 保留 bolloon.log.1 .2 .3
|
|
48
12
|
let currentSize = 0;
|
|
@@ -52,9 +16,9 @@ function ensureLogFile() {
|
|
|
52
16
|
return;
|
|
53
17
|
initialized = true;
|
|
54
18
|
try {
|
|
55
|
-
fs.mkdirSync(
|
|
19
|
+
fs.mkdirSync(logsDir(), { recursive: true });
|
|
56
20
|
try {
|
|
57
|
-
currentSize = fs.statSync(
|
|
21
|
+
currentSize = fs.statSync(mainLogPath()).size;
|
|
58
22
|
}
|
|
59
23
|
catch {
|
|
60
24
|
currentSize = 0;
|
|
@@ -67,7 +31,7 @@ function ensureLogFile() {
|
|
|
67
31
|
}
|
|
68
32
|
}
|
|
69
33
|
function rotate() {
|
|
70
|
-
const logPath =
|
|
34
|
+
const logPath = mainLogPath();
|
|
71
35
|
for (let i = MAX_BACKUPS; i >= 1; i--) {
|
|
72
36
|
const src = `${logPath}.${i}`;
|
|
73
37
|
const dst = `${logPath}.${i + 1}`;
|
|
@@ -90,14 +54,14 @@ function rotate() {
|
|
|
90
54
|
catch { /* missing ok */ }
|
|
91
55
|
currentSize = 0;
|
|
92
56
|
}
|
|
93
|
-
function log(message, level = 'info') {
|
|
57
|
+
export function log(message, level = 'info') {
|
|
94
58
|
ensureLogFile();
|
|
95
59
|
const ts = new Date().toISOString();
|
|
96
60
|
const line = `[${ts}] [${level}] ${message}\n`;
|
|
97
61
|
if (currentSize + line.length > MAX_BYTES)
|
|
98
62
|
rotate();
|
|
99
63
|
try {
|
|
100
|
-
fs.appendFileSync(
|
|
64
|
+
fs.appendFileSync(mainLogPath(), line);
|
|
101
65
|
currentSize += line.length;
|
|
102
66
|
}
|
|
103
67
|
catch (err) {
|
|
@@ -111,4 +75,3 @@ function log(message, level = 'info') {
|
|
|
111
75
|
else
|
|
112
76
|
console.log(line.trimEnd());
|
|
113
77
|
}
|
|
114
|
-
//# sourceMappingURL=logger.js.map
|