@bolloon/bolloon-agent 0.3.35 → 0.3.37

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.
Files changed (48) hide show
  1. package/dist/agents/pi-sdk-tools.js +2 -1
  2. package/dist/cli/ink-app.js +44 -1
  3. package/dist/cli/mention-data.js +23 -0
  4. package/dist/cli-entry.js +113 -6
  5. package/dist/electron/config.js +9 -14
  6. package/dist/electron/dialogs.js +16 -53
  7. package/dist/electron/first-run.js +24 -65
  8. package/dist/electron/ipc.js +10 -14
  9. package/dist/electron/logger.js +7 -44
  10. package/dist/electron/main.js +42 -45
  11. package/dist/electron/menu.js +13 -18
  12. package/dist/electron/paths.js +12 -54
  13. package/dist/electron/server.js +18 -57
  14. package/dist/electron/tray.js +15 -53
  15. package/dist/electron/window.js +22 -61
  16. package/dist/electron-build/electron/config.js +21 -0
  17. package/dist/electron-build/electron/config.js.map +1 -0
  18. package/dist/electron-build/electron/dialogs.js +108 -0
  19. package/dist/electron-build/electron/dialogs.js.map +1 -0
  20. package/dist/electron-build/electron/first-run.js +170 -0
  21. package/dist/electron-build/electron/first-run.js.map +1 -0
  22. package/dist/electron-build/electron/ipc.js +20 -0
  23. package/dist/electron-build/electron/ipc.js.map +1 -0
  24. package/dist/electron-build/electron/logger.js +114 -0
  25. package/dist/electron-build/electron/logger.js.map +1 -0
  26. package/dist/electron-build/electron/main.js +79 -0
  27. package/dist/electron-build/electron/main.js.map +1 -0
  28. package/dist/electron-build/electron/menu.js +145 -0
  29. package/dist/electron-build/electron/menu.js.map +1 -0
  30. package/dist/electron-build/electron/paths.js +75 -0
  31. package/dist/electron-build/electron/paths.js.map +1 -0
  32. package/dist/electron-build/electron/server.js +119 -0
  33. package/dist/electron-build/electron/server.js.map +1 -0
  34. package/dist/electron-build/electron/tray.js +111 -0
  35. package/dist/electron-build/electron/tray.js.map +1 -0
  36. package/dist/electron-build/electron/window.js +111 -0
  37. package/dist/electron-build/electron/window.js.map +1 -0
  38. package/dist/electron-build/electron-preload.js +32 -0
  39. package/dist/electron-build/electron-preload.js.map +1 -0
  40. package/dist/electron-build/electron.js +8 -0
  41. package/dist/electron-build/electron.js.map +1 -0
  42. package/dist/electron-build/utils/auto-update.js +598 -0
  43. package/dist/electron-build/utils/auto-update.js.map +1 -0
  44. package/dist/electron-preload.js +16 -19
  45. package/dist/electron.js +1 -4
  46. package/dist/index.js +426 -0
  47. package/dist/utils/auto-update.js +12 -51
  48. package/package.json +5 -4
@@ -2131,7 +2131,8 @@ async function ensureKuboReady() {
2131
2131
  }
2132
2132
  }
2133
2133
  }
2134
- async function kuboApi(pathAndQuery, init, timeoutMs = 30000) {
2134
+ /** Kubo HTTP API helper (POST only, 2026-08-03 实测 Kubo 只接受 POST). 2026-08-06 export 供 CLI /ipfs /ipns 命令用. */
2135
+ export async function kuboApi(pathAndQuery, init, timeoutMs = 30000) {
2135
2136
  const controller = new AbortController();
2136
2137
  const timer = setTimeout(() => controller.abort(), timeoutMs);
2137
2138
  try {
@@ -62,6 +62,21 @@ const InkApp = ({ onPrompt, initialStatus, getStatusUpdate, terminalW, terminalH
62
62
  const skillCache = useRef(null);
63
63
  const pluginCache = useRef(null);
64
64
  const fileCache = useRef(null);
65
+ // ── 程序化选择器 (2026-08-06: /login /model 等命令触发, 复用 MentionPopup 渲染) ──
66
+ const [picker, setPicker] = useState(null);
67
+ const pickerCb = useRef(null);
68
+ const pickerSelRef = useRef(0);
69
+ // 全局钩子: index.ts 命令打开/关闭选择器
70
+ useEffect(() => {
71
+ globalThis.__inkOpenPicker = (itemsArg, title, onPick) => {
72
+ pickerSelRef.current = 0;
73
+ pickerCb.current = onPick;
74
+ setPicker({ title: title || '选择', items: itemsArg, sel: 0 });
75
+ setInput('');
76
+ };
77
+ globalThis.__inkClosePicker = () => { pickerCb.current = null; setPicker(null); };
78
+ return () => { delete globalThis.__inkOpenPicker; delete globalThis.__inkClosePicker; };
79
+ }, []);
65
80
  // ── 输入历史 (↑/↓ 切换) ───────────────────────────────────────────────────
66
81
  const historyRef = useRef([]);
67
82
  const historyIdxRef = useRef(-1); // -1 = 正在编辑新草稿
@@ -296,6 +311,34 @@ const InkApp = ({ onPrompt, initialStatus, getStatusUpdate, terminalW, terminalH
296
311
  requestExit();
297
312
  return;
298
313
  }
314
+ // ── 程序化选择器: 全键接管 (↑↓ 选择, Enter 确认, Esc 关闭) ──
315
+ if (picker) {
316
+ const itemsArg = picker.items;
317
+ if (key.upArrow) {
318
+ pickerSelRef.current = Math.max(0, pickerSelRef.current - 1);
319
+ setPicker({ ...picker, sel: pickerSelRef.current });
320
+ return;
321
+ }
322
+ if (key.downArrow) {
323
+ pickerSelRef.current = Math.min(itemsArg.length - 1, pickerSelRef.current + 1);
324
+ setPicker({ ...picker, sel: pickerSelRef.current });
325
+ return;
326
+ }
327
+ if ((key.return || key.tab) && itemsArg.length > 0) {
328
+ const it = itemsArg[Math.min(pickerSelRef.current, itemsArg.length - 1)];
329
+ const cb = pickerCb.current;
330
+ pickerCb.current = null;
331
+ setPicker(null);
332
+ cb?.(it);
333
+ return;
334
+ }
335
+ if (key.escape) {
336
+ pickerCb.current = null;
337
+ setPicker(null);
338
+ return;
339
+ }
340
+ return; // 其余键忽略
341
+ }
299
342
  // ── 弹出窗打开: 全键接管 (TextInput focus=false 不处理) ──
300
343
  if (popupOpen) {
301
344
  if (key.upArrow) {
@@ -456,7 +499,7 @@ const InkApp = ({ onPrompt, initialStatus, getStatusUpdate, terminalW, terminalH
456
499
  }, 600);
457
500
  return () => clearInterval(timer);
458
501
  }, [thinking]);
459
- return (_jsxs(Box, { flexDirection: "column", height: "100%", children: [_jsxs(Box, { flexGrow: 1, flexDirection: "column", justifyContent: "flex-start", children: [_jsx(LogoBox, { width: terminalW }), _jsx(Messages, { msgs: msgs }), thinking && (_jsx(Box, { children: _jsxs(Text, { color: "yellow", children: [KAOMOJI[thinkingIdx.current], " \u601D\u8003\u4E2D..."] }) }))] }), _jsx(Box, { children: _jsx(Text, { bold: true, color: "#c4d640", children: '─'.repeat(terminalW) }) }), _jsx(Box, { children: _jsx(Text, { children: status }) }), _jsx(Box, { children: _jsx(Text, { bold: true, color: "#c4d640", children: '─'.repeat(terminalW) }) }), popupOpen && (tabState || mention) && (_jsx(MentionPopup, { title: popupTitle, items: filtered, sel: safeSel, width: terminalW, loading: loadingFiles })), _jsxs(Box, { children: [_jsx(Text, { bold: true, color: "#c4d640", children: "\u276F " }), _jsx(TextInput, { value: input, onChange: setInput, onSubmit: onSubmit, focus: !popupOpen, placeholder: "\u8F93\u5165\u6D88\u606F... @\u667A\u80FD\u4F53 /\u547D\u4EE4 #\u6587\u4EF6 \u00B7 Esc \u53CC\u51FB\u9000\u51FA \u00B7 /queue \u6392\u961F \u00B7 !\u7EC8\u7AEF\u547D\u4EE4" }, tiKey)] }), _jsx(Box, { children: _jsx(Text, { bold: true, color: "#c4d640", children: '─'.repeat(terminalW) }) })] }));
502
+ return (_jsxs(Box, { flexDirection: "column", height: "100%", children: [_jsxs(Box, { flexGrow: 1, flexDirection: "column", justifyContent: "flex-start", children: [_jsx(LogoBox, { width: terminalW }), _jsx(Messages, { msgs: msgs }), thinking && (_jsx(Box, { children: _jsxs(Text, { color: "yellow", children: [KAOMOJI[thinkingIdx.current], " \u601D\u8003\u4E2D..."] }) }))] }), _jsx(Box, { children: _jsx(Text, { bold: true, color: "#c4d640", children: '─'.repeat(terminalW) }) }), _jsx(Box, { children: _jsx(Text, { children: status }) }), _jsx(Box, { children: _jsx(Text, { bold: true, color: "#c4d640", children: '─'.repeat(terminalW) }) }), popupOpen && (tabState || mention) && (_jsx(MentionPopup, { title: popupTitle, items: filtered, sel: safeSel, width: terminalW, loading: loadingFiles })), picker && (_jsx(MentionPopup, { title: picker.title, items: picker.items, sel: Math.min(picker.sel, picker.items.length - 1), width: terminalW })), _jsxs(Box, { children: [_jsx(Text, { bold: true, color: "#c4d640", children: "\u276F " }), _jsx(TextInput, { value: input, onChange: setInput, onSubmit: onSubmit, focus: !popupOpen && !picker, placeholder: "\u8F93\u5165\u6D88\u606F... @\u667A\u80FD\u4F53 /\u547D\u4EE4 #\u6587\u4EF6 \u00B7 Esc \u53CC\u51FB\u9000\u51FA \u00B7 /queue \u6392\u961F \u00B7 !\u7EC8\u7AEF\u547D\u4EE4" }, tiKey)] }), _jsx(Box, { children: _jsx(Text, { bold: true, color: "#c4d640", children: '─'.repeat(terminalW) }) })] }));
460
503
  };
461
504
  export { InkApp };
462
505
  // ─── 启动 ────────────────────────────────────────────────────────────────────
@@ -21,6 +21,29 @@ const CLI_COMMANDS = [
21
21
  { kind: 'command', label: 'peers', hint: '查看 P2P 节点', insert: 'peers' },
22
22
  { kind: 'command', label: 'iroh', hint: '查看 iroh 状态', insert: 'iroh' },
23
23
  { kind: 'command', label: 'add_friend', hint: '添加好友 <64位hex公钥>', insert: 'add_friend' },
24
+ // 2026-08-06: 系统命令组
25
+ { kind: 'command', label: 'model', hint: '模型供应商选择器 (↑↓ 选择)', insert: 'model' },
26
+ { kind: 'command', label: 'login', hint: '登录模型供应商 (选择器)', insert: 'login' },
27
+ { kind: 'command', label: 'logout', hint: '查看当前供应商', insert: 'logout' },
28
+ { kind: 'command', label: 'now', hint: '当前状态总览', insert: 'now' },
29
+ { kind: 'command', label: 'session', hint: '当前会话信息', insert: 'session' },
30
+ { kind: 'command', label: 'loop', hint: '循环状态 (消息/token)', insert: 'loop' },
31
+ { kind: 'command', label: 'memory', hint: '记忆摘要', insert: 'memory' },
32
+ { kind: 'command', label: 'resume', hint: '恢复上下文', insert: 'resume' },
33
+ { kind: 'command', label: 'goal', hint: '进行中目标', insert: 'goal' },
34
+ { kind: 'command', label: 'tools', hint: '可用工具列表', insert: 'tools' },
35
+ { kind: 'command', label: 'skill', hint: '技能候选', insert: 'skill' },
36
+ { kind: 'command', label: 'mcp', hint: 'MCP 服务器列表', insert: 'mcp' },
37
+ { kind: 'command', label: 'agent', hint: '当前智能体', insert: 'agent' },
38
+ { kind: 'command', label: 'did', hint: 'DID 身份', insert: 'did' },
39
+ { kind: 'command', label: 'ipfs', hint: 'Kubo 状态', insert: 'ipfs' },
40
+ { kind: 'command', label: 'ipns', hint: 'IPNS keys', insert: 'ipns' },
41
+ { kind: 'command', label: 'wallet', hint: '钱包状态', insert: 'wallet' },
42
+ { kind: 'command', label: 'email', hint: '邮件配置', insert: 'email' },
43
+ { kind: 'command', label: 'judgement', hint: '判断力列表', insert: 'judgement' },
44
+ { kind: 'command', label: 'insight', hint: '洞察 (08-Insights)', insert: 'insight' },
45
+ { kind: 'command', label: 'wiki', hint: 'wiki 状态', insert: 'wiki' },
46
+ { kind: 'command', label: 'dream', hint: '随机灵感', insert: 'dream' },
24
47
  ];
25
48
  /** Web 端斜杠命令 (server /message 路由 → LLM 工具) */
26
49
  const WEB_COMMANDS = [
package/dist/cli-entry.js CHANGED
@@ -62,17 +62,24 @@ ${BOLD}选项:${RESET}
62
62
  --help, -h 显示帮助信息
63
63
 
64
64
  ${BOLD}命令:${RESET}
65
- bolloon --read <file> 读取文档
66
- bolloon --summarize <file> 总结文档
67
- bolloon --improve <file> <req> 改进文档
68
- bolloon x402 fetch <url> x402 自动支付 HTTP 请求
69
- bolloon x402 balance <address> 查询 x402 钱包余额
65
+ bolloon update [--now] 检查更新 / 立即更新 (bolloon update --now)
66
+ bolloon model [name] [model] 列出 / 切换模型供应商 (如: bolloon model deepseek deepseek-v4-flash)
67
+ bolloon read <file> 读取文档
68
+ bolloon summarize <file> 总结文档
69
+ bolloon improve <file> <req> 改进文档
70
+ bolloon engine list 列出外部编码智能体
71
+ bolloon engine run <prompt> 委派任务给智能体
72
+ bolloon x402 fetch <url> x402 自动支付 HTTP 请求
73
+ bolloon x402 balance <address> 查询 x402 钱包余额
70
74
 
71
75
  ${BOLD}示例:${RESET}
72
76
  bolloon # 启动图形界面
73
77
  bolloon --web # 启动 Web UI
74
- bolloon --read 想法.md # 读取文档
75
78
  bolloon --cli # 命令行模式
79
+ bolloon model # 查看当前模型供应商
80
+ bolloon model minimax # 切换到 MiniMax
81
+ bolloon update # 检查更新
82
+ bolloon update --now # 立即更新
76
83
 
77
84
  ${BOLD}环境变量:${RESET}
78
85
  MINIMAX_API_KEY MiniMax API 密钥
@@ -139,6 +146,16 @@ function parseArgs() {
139
146
  return { mode: 'engine', args: args.slice(1) };
140
147
  case 'x402':
141
148
  return { mode: 'x402', args: args.slice(1) };
149
+ // 2026-08-06: 子命令形式 (去掉 -- 前缀)
150
+ case 'update':
151
+ return { mode: 'update', args: args.slice(1) };
152
+ case 'model':
153
+ return { mode: 'model', args: args.slice(1) };
154
+ case 'read':
155
+ case 'summarize':
156
+ case 'improve':
157
+ // 映射回旧 --flag 格式传给主程序 (保留 index.ts 现有实现)
158
+ return { mode: 'passthrough', args: [`--${mode}`, ...args.slice(1)] };
142
159
  default:
143
160
  // 传递所有参数给主程序
144
161
  return { mode: 'passthrough', args };
@@ -259,6 +276,89 @@ async function handleX402Command(x402Args) {
259
276
  console.error(`未知 x402 子命令: ${sub}`);
260
277
  process.exit(1);
261
278
  }
279
+ /** update 子命令: 检查 / 执行更新 (bolloon update [--now|now] [packages]) */
280
+ async function handleUpdateCommand(updateArgs) {
281
+ const { checkForUpdates, performUpdate } = await import('./utils/auto-update.js');
282
+ // bolloon update --now / bolloon update now [packages] — 立即更新
283
+ if (updateArgs[0] === '--now' || updateArgs[0] === 'now') {
284
+ const packages = updateArgs.slice(1).filter(a => !a.startsWith('-'));
285
+ console.log('🔄 正在检查并更新...');
286
+ const result = await performUpdate(packages.length > 0 ? packages : undefined);
287
+ if (result.success) {
288
+ console.log(`${GREEN}✅ 更新成功${result.updatedPackages ? `: ${result.updatedPackages.join(', ')}` : ''}${RESET}`);
289
+ if (result.updated)
290
+ console.log(`${YELLOW} 请重新启动应用以使用新版本${RESET}`);
291
+ }
292
+ else {
293
+ console.error(`${MAGENTA}❌ 更新失败: ${result.error}${RESET}`);
294
+ process.exit(1);
295
+ }
296
+ return;
297
+ }
298
+ // 默认: 检查更新
299
+ console.log('🔄 正在检查更新...');
300
+ const info = await checkForUpdates();
301
+ if (info && info.outdated) {
302
+ console.log(`${CYAN}📦 发现更新可用:${RESET}\n`);
303
+ console.log(` 当前版本: ${info.version}`);
304
+ console.log(` 最新版本: ${info.latest}\n`);
305
+ console.log(` 待更新包:`);
306
+ for (const p of info.packages)
307
+ console.log(` - ${p.name}: ${p.current} → ${p.latest}`);
308
+ console.log(`\n 运行 ${GREEN}bolloon update --now${RESET} 执行更新`);
309
+ }
310
+ else {
311
+ console.log(`${GREEN}✅ 已是最新版本${RESET} (${info?.version || 'unknown'})`);
312
+ }
313
+ }
314
+ /** model 子命令: 列出 / 切换模型供应商 (bolloon model [name] [model]) */
315
+ async function handleModelCommand(modelArgs) {
316
+ const { llmConfigStore, PROVIDER_INFO } = await import('./llm/config-store.js');
317
+ await llmConfigStore.initialize();
318
+ // 无参: 列出所有供应商 + 当前 active
319
+ if (modelArgs.length === 0) {
320
+ const config = await llmConfigStore.getConfig();
321
+ console.log(`\n${BOLD}模型供应商${RESET} (当前: ${config.activeProvider})\n`);
322
+ console.log('─'.repeat(58));
323
+ for (const [name, p] of Object.entries(config.providers)) {
324
+ const info = PROVIDER_INFO[name] || {};
325
+ const active = name === config.activeProvider ? '●' : '○';
326
+ const keyState = p.apiKey ? '🔑' : p.requiresApiKey ? '⚠ 无 key' : '';
327
+ const model = p.model || (info.models && info.models[0]) || '';
328
+ console.log(` ${active} ${name.padEnd(10)} ${String(info.name || '').padEnd(16)} ${keyState.padEnd(8)} model: ${model}`);
329
+ }
330
+ console.log(`\n${BOLD}用法:${RESET}`);
331
+ console.log(` bolloon model <name> # 切换到该供应商`);
332
+ console.log(` bolloon model <name> <model> # 切换并指定模型`);
333
+ console.log(` 示例: bolloon model deepseek deepseek-v4-flash`);
334
+ return;
335
+ }
336
+ // 切换供应商
337
+ const name = modelArgs[0].toLowerCase();
338
+ const config = await llmConfigStore.getConfig();
339
+ const providers = config.providers;
340
+ if (!providers[name]) {
341
+ console.error(`${MAGENTA}❌ 未知供应商: ${name}${RESET}`);
342
+ console.error(` 可用: ${Object.keys(providers).join(', ')}`);
343
+ process.exit(1);
344
+ }
345
+ const provider = providers[name];
346
+ if (provider.requiresApiKey && !provider.apiKey) {
347
+ console.error(`${MAGENTA}❌ ${name} 需要 API key (当前未配置)${RESET}`);
348
+ console.error(` 配置方式: ① Web UI API 配置页 ② 环境变量 (如 DEEPSEEK_API_KEY)`);
349
+ process.exit(1);
350
+ }
351
+ await llmConfigStore.setActiveProvider(name);
352
+ let modelNote = '';
353
+ if (modelArgs[1]) {
354
+ await llmConfigStore.updateProvider(name, { model: modelArgs[1] });
355
+ modelNote = `, model=${modelArgs[1]}`;
356
+ }
357
+ const info = PROVIDER_INFO[name] || {};
358
+ console.log(`${GREEN}✅ 已切换到 ${name}${RESET} (${info.name || ''})${modelNote}`);
359
+ console.log(` 当前模型: ${modelArgs[1] || provider.model || (info.models && info.models[0]) || '默认'}`);
360
+ console.log(` 配置已持久化: ~/.bolloon/llm-config.json`);
361
+ }
262
362
  /** 引擎子命令: list / run */
263
363
  async function handleEngineCommand(engineArgs) {
264
364
  if (engineArgs.length === 0) {
@@ -462,6 +562,13 @@ async function main() {
462
562
  case 'x402':
463
563
  await handleX402Command(args);
464
564
  break;
565
+ // 2026-08-06: 子命令 (bolloon update / bolloon model)
566
+ case 'update':
567
+ await handleUpdateCommand(args);
568
+ break;
569
+ case 'model':
570
+ await handleModelCommand(args);
571
+ break;
465
572
  case 'passthrough':
466
573
  // 传递所有参数给主程序
467
574
  try {
@@ -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
- const electron_1 = require("electron");
9
- exports.DEFAULT_PORT = 54188;
4
+ import { app } from 'electron';
5
+ export const DEFAULT_PORT = 54188;
10
6
  /** Hard-pin to loopback; LAN exposure must be explicit. */
11
- exports.DEFAULT_HOST = '127.0.0.1';
12
- exports.WEB_SERVER_STARTUP_TIMEOUT_MS = 15_000;
13
- exports.MAIN_WINDOW_DEFAULT = { width: 1200, height: 800 };
14
- exports.MAIN_WINDOW_MIN = { width: 800, height: 600 };
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 : exports.DEFAULT_PORT;
14
+ return Number.isFinite(n) && n > 0 && n < 65536 ? n : DEFAULT_PORT;
19
15
  }
20
- exports.isDev = process.env.NODE_ENV === 'development' || !electron_1.app.isPackaged;
21
- //# sourceMappingURL=config.js.map
16
+ export const isDev = process.env.NODE_ENV === 'development' || !app.isPackaged;
@@ -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
- const electron_1 = require("electron");
44
- const fs = __importStar(require("fs"));
45
- const path = __importStar(require("path"));
46
- const logger_1 = require("./logger");
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 electron_1.BrowserWindow.fromWebContents(event.sender);
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
- electron_1.ipcMain.handle('dialog:open-file', async (event, opts = {}) => {
19
+ export function registerDialogIpc() {
20
+ ipcMain.handle('dialog:open-file', async (event, opts = {}) => {
57
21
  const win = windowFor(event);
58
- const result = await electron_1.dialog.showOpenDialog(win, {
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
- electron_1.ipcMain.handle('dialog:save-file', async (event, opts = {}) => {
30
+ ipcMain.handle('dialog:save-file', async (event, opts = {}) => {
67
31
  const win = windowFor(event);
68
- const result = await electron_1.dialog.showSaveDialog(win, {
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
- electron_1.ipcMain.handle('dialog:open-directory', async (event, opts = {}) => {
39
+ ipcMain.handle('dialog:open-directory', async (event, opts = {}) => {
76
40
  const win = windowFor(event);
77
- const result = await electron_1.dialog.showOpenDialog(win, {
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
- electron_1.ipcMain.handle('fs:read-text-file', async (_event, opts) => {
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
- electron_1.ipcMain.handle('fs:write-text-file', async (_event, opts) => {
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
- electron_1.ipcMain.handle('fs:path-exists', async (_event, opts) => {
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
- (0, logger_1.log)('dialog + fs IPC handlers registered');
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
- const electron_1 = require("electron");
48
- const fs = __importStar(require("fs"));
49
- const path = __importStar(require("path"));
50
- const paths_1 = require("./paths");
51
- const logger_1 = require("./logger");
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((0, paths_1.firstRunFlagPath)());
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((0, paths_1.firstRunFlagPath)()), { recursive: true });
118
- fs.writeFileSync((0, paths_1.firstRunFlagPath)(), new Date().toISOString());
77
+ fs.mkdirSync(path.dirname(firstRunFlagPath()), { recursive: true });
78
+ fs.writeFileSync(firstRunFlagPath(), new Date().toISOString());
119
79
  }
120
80
  catch (err) {
121
- (0, logger_1.log)(`写入首启标记失败: ${err.message}`, 'warn');
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 electron_1.BrowserWindow({
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
- electron_1.ipcMain.once('first-run:ack', handler);
106
+ ipcMain.once('first-run:ack', handler);
147
107
  overlay.on('closed', () => {
148
- electron_1.ipcMain.removeListener('first-run:ack', handler);
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
- electron_1.ipcMain.handle('first-run:seen', () => hasSeenFirstRun());
156
- electron_1.ipcMain.handle('first-run:mark-seen', () => { markFirstRunSeen(); });
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
- electron_1.ipcMain.handle('first-run:data-dir', () => (0, paths_1.dataDir)());
159
- electron_1.ipcMain.handle('first-run:logs-dir', () => (0, paths_1.logsDir)());
160
- (0, logger_1.log)('first-run IPC handlers registered');
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
- (0, logger_1.log)('首启 — 弹出引导');
126
+ log('首启 — 弹出引导');
167
127
  await showFirstRunOverlay(parent);
168
- electron_1.app.addRecentDocument((0, paths_1.firstRunFlagPath)()); // 跟踪最近文档, 让 user 知道有这文件
128
+ app.addRecentDocument(firstRunFlagPath()); // 跟踪最近文档, 让 user 知道有这文件
169
129
  }
170
- //# sourceMappingURL=first-run.js.map
@@ -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
- const electron_1 = require("electron");
9
- const paths_1 = require("./paths");
10
- const logger_1 = require("./logger");
11
- function registerCoreIpc() {
12
- electron_1.ipcMain.handle('get-version', () => electron_1.app.getVersion());
13
- electron_1.ipcMain.handle('get-user-data-path', () => (0, paths_1.userDataDir)());
14
- electron_1.ipcMain.handle('get-data-path', () => (0, paths_1.dataDir)()); // 跟 userData 分开, 渲染层要用
15
- electron_1.ipcMain.handle('open-external', async (_event, url) => {
16
- await electron_1.shell.openExternal(url);
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
- (0, logger_1.log)('core IPC handlers registered');
15
+ log('core IPC handlers registered');
19
16
  }
20
- //# sourceMappingURL=ipc.js.map