@pheem49/mint 1.4.2 → 1.5.1
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/GUIDE_TH.md +113 -0
- package/README.md +267 -78
- package/assets/CLI_Screen.png +0 -0
- package/main.js +76 -890
- package/mint-cli-logic.js +3 -107
- package/mint-cli.js +594 -29
- package/models/Shiroko_Model/Shiroko/Shiroko_Core/72d86db84cfa9730b894c241fd24c0db.png +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core/items_pinned_to_model.json +14 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//345/221/206/347/214/253.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//345/221/206/347/214/253/347/234/274/347/217/240/346/221/207/346/231/203.exp3.json +15 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//345/233/264/350/243/231.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/215/347/205/247.exp3.json +50 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/277/347/254/224.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/202/271/344/270/200/344/270/213.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/214/253/345/222/252/346/273/244/351/225/234.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/234/274/351/225/234.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_00.png +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_01.png +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_02.png +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_03.png +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.cdi3.json +1498 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.moc3 +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.model3.json +47 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.physics3.json +6658 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.vtube.json +1299 -0
- package/models/Shiroko_Model/Shiroko//342/232/241/351/253/230/344/272/256/342/232/241/344/275/277/347/224/250/346/225/231/347/250/213/344/270/216/346/263/250/346/204/217/344/272/213/351/241/271.txt +23 -0
- package/package.json +37 -4
- package/src/AI_Brain/Gemini_API.js +223 -65
- package/src/AI_Brain/autonomous_brain.js +11 -0
- package/src/AI_Brain/behavior_memory.js +26 -5
- package/src/AI_Brain/headless_agent.js +4 -0
- package/src/AI_Brain/knowledge_base.js +61 -8
- package/src/AI_Brain/memory_store.js +354 -10
- package/src/Automation_Layer/file_operations.js +1 -1
- package/src/CLI/chat_router.js +20 -7
- package/src/CLI/chat_ui.js +596 -825
- package/src/CLI/code_agent.js +347 -56
- package/src/CLI/gmail_auth.js +210 -0
- package/src/CLI/image_input.js +90 -0
- package/src/CLI/list_features.js +2 -0
- package/src/CLI/onboarding.js +364 -55
- package/src/CLI/updater.js +210 -0
- package/src/Channels/brave_search_bridge.js +35 -0
- package/src/Channels/discord_bridge.js +68 -0
- package/src/Channels/google_search_bridge.js +38 -0
- package/src/Channels/line_bridge.js +60 -0
- package/src/Channels/slack_bridge.js +53 -0
- package/src/Channels/telegram_bridge.js +49 -0
- package/src/Channels/whatsapp_bridge.js +55 -0
- package/src/Command_Parser/parser.js +12 -1
- package/src/Plugins/gmail.js +251 -0
- package/src/Plugins/google_calendar.js +245 -19
- package/src/Plugins/notion.js +256 -0
- package/src/System/action_executor.js +178 -0
- package/src/System/bridge_manager.js +76 -0
- package/src/System/chat_history_manager.js +23 -5
- package/src/System/config_manager.js +71 -7
- package/src/System/custom_workflows.js +31 -2
- package/src/System/google_tts_urls.js +51 -0
- package/src/System/granular_automation.js +122 -53
- package/src/System/ipc_handlers.js +238 -0
- package/src/System/proactive_loop.js +153 -0
- package/src/System/safety_manager.js +273 -0
- package/src/System/sandbox_runner.js +182 -0
- package/src/System/screen_capture.js +175 -0
- package/src/System/system_automation.js +127 -81
- package/src/System/system_info.js +70 -0
- package/src/System/task_manager.js +15 -5
- package/src/System/tool_registry.js +280 -0
- package/src/System/window_manager.js +212 -0
- package/src/UI/live2d_manager.js +368 -0
- package/src/UI/renderer.js +208 -24
- package/src/UI/settings.html +24 -0
- package/src/UI/settings.js +14 -4
- package/src/UI/styles.css +466 -32
- package/.codex +0 -0
- package/docs/assets/Agent_Mint.png +0 -0
- package/docs/assets/CLI_Screen.png +0 -0
- package/docs/assets/Settings.png +0 -0
- package/docs/assets/icon.png +0 -0
- package/docs/index.html +0 -132
- package/docs/style.css +0 -579
- package/index.html +0 -16
- package/src/UI/index.html +0 -126
- package/tech_news.txt +0 -3
- package/test_knowledge.txt +0 -3
- package/tests/agent_orchestrator.test.js +0 -41
- package/tests/chat_router.test.js +0 -42
- package/tests/code_agent.test.js +0 -69
- package/tests/config_manager.test.js +0 -141
- package/tests/docker.test.js +0 -46
- package/tests/file_operations.test.js +0 -57
- package/tests/memory_store.test.js +0 -185
- package/tests/provider_routing.test.js +0 -67
- package/tests/spotify.test.js +0 -201
- package/tests/system_monitor.test.js +0 -37
- package/tests/workspace_manager.test.js +0 -56
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
let electronClipboard = null;
|
|
2
|
+
try {
|
|
3
|
+
({ clipboard: electronClipboard } = require('electron'));
|
|
4
|
+
} catch (_) {
|
|
5
|
+
electronClipboard = {
|
|
6
|
+
writeText: () => {}
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
const { openApp } = require('../Automation_Layer/open_app');
|
|
10
|
+
const { openWebsite, openSearch } = require('../Automation_Layer/open_website');
|
|
11
|
+
const { performWebAutomation } = require('../Automation_Layer/browser_automation');
|
|
12
|
+
const { createFolder, openFile, deleteFile, findPath } = require('../Automation_Layer/file_operations');
|
|
13
|
+
const { indexFile, indexFolder } = require('../AI_Brain/knowledge_base');
|
|
14
|
+
const { getSystemInfo, getWeather } = require('./system_info');
|
|
15
|
+
const pluginManager = require('../Plugins/plugin_manager');
|
|
16
|
+
const mcpManager = require('../Plugins/mcp_manager');
|
|
17
|
+
const SystemAutomation = require('./system_automation');
|
|
18
|
+
const safetyManager = require('./safety_manager');
|
|
19
|
+
const toolRegistry = require('./tool_registry');
|
|
20
|
+
const os = require('os');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
|
|
23
|
+
async function executeAction(action, options = {}) {
|
|
24
|
+
if (process.env.MINT_DEBUG === '1') {
|
|
25
|
+
console.log("Executing action:", action);
|
|
26
|
+
}
|
|
27
|
+
toolRegistry.validateToolInput(action.type, action);
|
|
28
|
+
const clipboard = options.clipboard || electronClipboard;
|
|
29
|
+
const safety = safetyManager.assertActionAllowed(action, {
|
|
30
|
+
allowApproval: options.allowApproval === true,
|
|
31
|
+
allowDangerous: options.allowDangerous === true
|
|
32
|
+
});
|
|
33
|
+
safetyManager.appendActionLog({
|
|
34
|
+
source: options.source || 'action_executor',
|
|
35
|
+
action: action.type,
|
|
36
|
+
target: action.target || action.path || '',
|
|
37
|
+
tier: safety.tier,
|
|
38
|
+
approved: options.allowApproval === true || options.allowDangerous === true || safety.tier === safetyManager.TIERS.SAFE
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
switch (action.type) {
|
|
42
|
+
case 'open_url':
|
|
43
|
+
openWebsite(action.target);
|
|
44
|
+
break;
|
|
45
|
+
case 'search':
|
|
46
|
+
openSearch(action.target);
|
|
47
|
+
break;
|
|
48
|
+
case 'open_app':
|
|
49
|
+
openApp(action.target);
|
|
50
|
+
break;
|
|
51
|
+
case 'web_automation':
|
|
52
|
+
return await performWebAutomation(action.target);
|
|
53
|
+
case 'create_folder':
|
|
54
|
+
safetyManager.assertPathCapability(action.target, 'write', {
|
|
55
|
+
defaultBase: path.join(os.homedir(), 'Desktop')
|
|
56
|
+
});
|
|
57
|
+
createFolder(action.target);
|
|
58
|
+
break;
|
|
59
|
+
case 'open_file': {
|
|
60
|
+
safetyManager.assertPathCapability(action.target, 'read');
|
|
61
|
+
const fileRes = await openFile(action.target);
|
|
62
|
+
return fileRes || `Successfully opened file: ${action.target} ✅`;
|
|
63
|
+
}
|
|
64
|
+
case 'open_folder': {
|
|
65
|
+
safetyManager.assertPathCapability(action.target, 'read');
|
|
66
|
+
const folderRes = await openFile(action.target);
|
|
67
|
+
return folderRes || `Successfully opened folder: ${action.target} ✅`;
|
|
68
|
+
}
|
|
69
|
+
case 'delete_file':
|
|
70
|
+
safetyManager.assertPathCapability(action.target, 'write');
|
|
71
|
+
await deleteFile(action.target);
|
|
72
|
+
break;
|
|
73
|
+
case 'find_path':
|
|
74
|
+
return await executeFindPath(action);
|
|
75
|
+
case 'clipboard_write':
|
|
76
|
+
clipboard.writeText(action.target);
|
|
77
|
+
break;
|
|
78
|
+
case 'learn_file':
|
|
79
|
+
safetyManager.assertPathCapability(action.target, 'read');
|
|
80
|
+
return await indexFile(action.target);
|
|
81
|
+
case 'learn_folder':
|
|
82
|
+
safetyManager.assertPathCapability(action.target, 'read');
|
|
83
|
+
return await indexFolder(action.target);
|
|
84
|
+
case 'system_info':
|
|
85
|
+
return await handleSystemInfo(action.target);
|
|
86
|
+
case 'mcp_tool': {
|
|
87
|
+
const mcpResult = await mcpManager.callTool(action.server, action.target, action.args);
|
|
88
|
+
return JSON.stringify(mcpResult.content);
|
|
89
|
+
}
|
|
90
|
+
case 'mouse_move': {
|
|
91
|
+
const granularAutomation = require('./granular_automation');
|
|
92
|
+
return await granularAutomation.mouseMove(action.x, action.y);
|
|
93
|
+
}
|
|
94
|
+
case 'mouse_click': {
|
|
95
|
+
const granularAutomation = require('./granular_automation');
|
|
96
|
+
return await granularAutomation.mouseClick(action.x, action.y, action.button || 1);
|
|
97
|
+
}
|
|
98
|
+
case 'type_text': {
|
|
99
|
+
const granularAutomation = require('./granular_automation');
|
|
100
|
+
return await granularAutomation.typeText(action.target);
|
|
101
|
+
}
|
|
102
|
+
case 'key_tap': {
|
|
103
|
+
const granularAutomation = require('./granular_automation');
|
|
104
|
+
return await granularAutomation.keyTap(action.target);
|
|
105
|
+
}
|
|
106
|
+
case 'plugin':
|
|
107
|
+
return await pluginManager.executePlugin(action.pluginName, action.target);
|
|
108
|
+
case 'system_automation':
|
|
109
|
+
return await handleSystemAutomation(action.target);
|
|
110
|
+
default:
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function handleSystemInfo(target = '') {
|
|
116
|
+
const query = String(target || '').trim();
|
|
117
|
+
if (query) {
|
|
118
|
+
const weather = await getWeather(query);
|
|
119
|
+
return JSON.stringify({
|
|
120
|
+
type: 'weather',
|
|
121
|
+
target: query,
|
|
122
|
+
...weather
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
return JSON.stringify({
|
|
126
|
+
type: 'system_info',
|
|
127
|
+
data: getSystemInfo()
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function executeFindPath(action) {
|
|
132
|
+
const result = findPath(action.target, {
|
|
133
|
+
type: action.pathType,
|
|
134
|
+
maxResults: 10,
|
|
135
|
+
roots: safetyManager.getAllowedRoots('read')
|
|
136
|
+
});
|
|
137
|
+
if (!result.success) {
|
|
138
|
+
return result.message;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (action.openAfter === true) {
|
|
142
|
+
if (result.matches.length === 1) {
|
|
143
|
+
const match = result.matches[0];
|
|
144
|
+
const openResult = await openFile(match.path);
|
|
145
|
+
return openResult || `Successfully found and opened ${match.type === 'dir' ? 'folder' : 'file'}: ${match.path} ✅`;
|
|
146
|
+
}
|
|
147
|
+
return `Found multiple matches for "${action.target}". Please be more specific:\n${result.matches.map(m => `- [${m.type}] ${m.path}`).join('\n')}`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return `Found matches for "${action.target}":\n${result.matches.map(m => `- [${m.type}] ${m.path}`).join('\n')}`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function handleSystemAutomation(target) {
|
|
154
|
+
const [cmd, value] = target.split(':');
|
|
155
|
+
switch (cmd) {
|
|
156
|
+
case 'volume':
|
|
157
|
+
return await SystemAutomation.setVolume(parseInt(value));
|
|
158
|
+
case 'mute':
|
|
159
|
+
return await SystemAutomation.mute();
|
|
160
|
+
case 'brightness':
|
|
161
|
+
return await SystemAutomation.setBrightness(parseInt(value));
|
|
162
|
+
case 'sleep':
|
|
163
|
+
return await SystemAutomation.sleep();
|
|
164
|
+
case 'restart':
|
|
165
|
+
return await SystemAutomation.restart();
|
|
166
|
+
case 'shutdown':
|
|
167
|
+
return await SystemAutomation.shutdown();
|
|
168
|
+
case 'minimize_all':
|
|
169
|
+
return await SystemAutomation.minimizeAll();
|
|
170
|
+
default:
|
|
171
|
+
if (SystemAutomation[target]) {
|
|
172
|
+
return await SystemAutomation[target]();
|
|
173
|
+
}
|
|
174
|
+
throw new Error(`Unknown system automation command: ${target}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
module.exports = { executeAction, handleSystemAutomation, handleSystemInfo };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const { readConfig } = require('./config_manager');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
class BridgeManager {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.bridges = new Map();
|
|
8
|
+
this.channelsDir = path.join(__dirname, '..', 'Channels');
|
|
9
|
+
|
|
10
|
+
if (!fs.existsSync(this.channelsDir)) {
|
|
11
|
+
fs.mkdirSync(this.channelsDir, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async init() {
|
|
16
|
+
const config = readConfig();
|
|
17
|
+
console.log('[BridgeManager] Initializing messaging bridges...');
|
|
18
|
+
|
|
19
|
+
// Load Discord Bridge
|
|
20
|
+
if (config.enableDiscordBridge && config.discordBotToken) {
|
|
21
|
+
await this.startBridge('discord', config.discordBotToken);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Load Telegram Bridge
|
|
25
|
+
if (config.enableTelegramBridge && config.telegramBotToken) {
|
|
26
|
+
await this.startBridge('telegram', config.telegramBotToken);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Load Slack Bridge
|
|
30
|
+
if (config.enableSlackBridge && config.slackBotToken && config.slackAppToken) {
|
|
31
|
+
await this.startBridge('slack', { botToken: config.slackBotToken, appToken: config.slackAppToken });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Load LINE Bridge
|
|
35
|
+
if (config.enableLineBridge && config.lineChannelAccessToken && config.lineChannelSecret) {
|
|
36
|
+
await this.startBridge('line', { accessToken: config.lineChannelAccessToken, secret: config.lineChannelSecret, port: config.lineWebhookPort });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Load WhatsApp Bridge
|
|
40
|
+
if (config.enableWhatsappBridge) {
|
|
41
|
+
await this.startBridge('whatsapp', null);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async startBridge(type, credentials) {
|
|
46
|
+
try {
|
|
47
|
+
const bridgePath = path.join(this.channelsDir, `${type}_bridge.js`);
|
|
48
|
+
if (!fs.existsSync(bridgePath)) {
|
|
49
|
+
console.error(`[BridgeManager] Bridge file not found: ${bridgePath}`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const BridgeClass = require(bridgePath);
|
|
54
|
+
const bridge = new BridgeClass(credentials);
|
|
55
|
+
await bridge.connect();
|
|
56
|
+
this.bridges.set(type, bridge);
|
|
57
|
+
console.log(`[BridgeManager] ${type.toUpperCase()} bridge connected successfully.`);
|
|
58
|
+
} catch (err) {
|
|
59
|
+
console.error(`[BridgeManager] Failed to start ${type} bridge:`, err.message);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async shutdown() {
|
|
64
|
+
for (const [type, bridge] of this.bridges.entries()) {
|
|
65
|
+
try {
|
|
66
|
+
await bridge.disconnect();
|
|
67
|
+
console.log(`[BridgeManager] ${type.toUpperCase()} bridge disconnected.`);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
console.error(`[BridgeManager] Error disconnecting ${type} bridge:`, err.message);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
this.bridges.clear();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
module.exports = new BridgeManager();
|
|
@@ -10,14 +10,32 @@ try {
|
|
|
10
10
|
app = null;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
const CONFIG_DIR = path.join(os.homedir(), '.config', 'mint');
|
|
13
14
|
const MINT_DIR = path.join(os.homedir(), '.mint');
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
17
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
const CHAT_HISTORY_PATH =
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
const CHAT_HISTORY_PATH = path.join(CONFIG_DIR, 'mint-chat-history.json');
|
|
21
|
+
|
|
22
|
+
// Migration Logic: Consolidate from Electron userData or old ~/.mint to ~/.config/mint
|
|
23
|
+
if (!fs.existsSync(CHAT_HISTORY_PATH)) {
|
|
24
|
+
const electronUserData = app && app.getPath ? path.join(app.getPath('userData'), 'mint-chat-history.json') : null;
|
|
25
|
+
const legacyPath = path.join(MINT_DIR, 'mint-chat-history.json');
|
|
26
|
+
|
|
27
|
+
if (electronUserData && fs.existsSync(electronUserData)) {
|
|
28
|
+
try {
|
|
29
|
+
fs.copyFileSync(electronUserData, CHAT_HISTORY_PATH);
|
|
30
|
+
console.log('[History] Migrated chat history from Electron userData');
|
|
31
|
+
} catch (e) { console.error('[History] Migration from Electron failed:', e); }
|
|
32
|
+
} else if (fs.existsSync(legacyPath)) {
|
|
33
|
+
try {
|
|
34
|
+
fs.copyFileSync(legacyPath, CHAT_HISTORY_PATH);
|
|
35
|
+
console.log('[History] Migrated chat history from ~/.mint');
|
|
36
|
+
} catch (e) { console.error('[History] Migration from ~/.mint failed:', e); }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
21
39
|
|
|
22
40
|
function readChatHistory() {
|
|
23
41
|
try {
|
|
@@ -53,21 +53,83 @@ const DEFAULT_CONFIG = {
|
|
|
53
53
|
ttsVolume: 1.0,
|
|
54
54
|
ttsSpeed: 1.0,
|
|
55
55
|
ttsPitch: 1.0,
|
|
56
|
-
pluginSpotifyEnabled: true,
|
|
57
56
|
pluginCalendarEnabled: false,
|
|
57
|
+
pluginGmailEnabled: false,
|
|
58
|
+
pluginNotionEnabled: false,
|
|
58
59
|
pluginDiscordEnabled: false,
|
|
59
60
|
showDesktopWidget: true,
|
|
60
61
|
mcpServers: {},
|
|
62
|
+
telegramBotToken: '',
|
|
63
|
+
enableTelegramBridge: false,
|
|
64
|
+
discordBotToken: '',
|
|
65
|
+
enableDiscordBridge: false,
|
|
66
|
+
slackBotToken: '',
|
|
67
|
+
slackAppToken: '',
|
|
68
|
+
enableSlackBridge: false,
|
|
69
|
+
lineChannelAccessToken: '',
|
|
70
|
+
lineChannelSecret: '',
|
|
71
|
+
enableLineBridge: false,
|
|
72
|
+
lineWebhookPort: 3000,
|
|
73
|
+
enableWhatsappBridge: false,
|
|
74
|
+
googleSearchApiKey: '',
|
|
75
|
+
googleSearchCx: '',
|
|
76
|
+
googleCalendarClientId: '',
|
|
77
|
+
googleCalendarClientSecret: '',
|
|
78
|
+
googleCalendarRefreshToken: '',
|
|
79
|
+
googleCalendarId: 'primary',
|
|
80
|
+
gmailClientId: '',
|
|
81
|
+
gmailClientSecret: '',
|
|
82
|
+
gmailRefreshToken: '',
|
|
83
|
+
gmailUserId: 'me',
|
|
84
|
+
notionApiKey: '',
|
|
85
|
+
notionDatabaseId: '',
|
|
86
|
+
notionPageId: '',
|
|
87
|
+
notionTitleProperty: 'Name',
|
|
88
|
+
braveSearchApiKey: '',
|
|
61
89
|
anthropicApiKey: '',
|
|
90
|
+
|
|
62
91
|
openaiApiKey: '',
|
|
63
92
|
hfApiKey: '',
|
|
64
93
|
anthropicModel: 'claude-3-5-sonnet-latest',
|
|
65
94
|
openaiModel: 'gpt-4o',
|
|
66
95
|
hfModel: 'meta-llama/Meta-Llama-3-8B-Instruct',
|
|
67
|
-
localApiBaseUrl: '
|
|
96
|
+
localApiBaseUrl: '',
|
|
68
97
|
localModelName: 'local-model',
|
|
69
|
-
ollamaHost: '
|
|
70
|
-
enableAgentCollaboration: false
|
|
98
|
+
ollamaHost: '',
|
|
99
|
+
enableAgentCollaboration: false,
|
|
100
|
+
enableAutoUpdate: true,
|
|
101
|
+
autoUpdateCheckIntervalHours: 24,
|
|
102
|
+
lastUpdateCheckAt: '',
|
|
103
|
+
safetyEnabled: true,
|
|
104
|
+
sandboxMode: 'prefer', // off | prefer | enforce
|
|
105
|
+
sandboxCommand: process.platform === 'darwin' ? 'sandbox-exec' : process.platform === 'linux' ? 'bwrap' : '',
|
|
106
|
+
allowedReadPaths: [
|
|
107
|
+
os.homedir(),
|
|
108
|
+
process.cwd(),
|
|
109
|
+
path.join(os.homedir(), 'Desktop'),
|
|
110
|
+
path.join(os.homedir(), 'Documents'),
|
|
111
|
+
path.join(os.homedir(), 'Downloads'),
|
|
112
|
+
path.join(os.homedir(), 'Pictures'),
|
|
113
|
+
path.join(os.homedir(), 'Music'),
|
|
114
|
+
path.join(os.homedir(), 'Videos')
|
|
115
|
+
],
|
|
116
|
+
allowedWritePaths: [
|
|
117
|
+
os.homedir(),
|
|
118
|
+
process.cwd(),
|
|
119
|
+
path.join(os.homedir(), 'Desktop'),
|
|
120
|
+
path.join(os.homedir(), 'Documents'),
|
|
121
|
+
path.join(os.homedir(), 'Downloads'),
|
|
122
|
+
path.join(os.homedir(), 'Pictures'),
|
|
123
|
+
path.join(os.homedir(), 'Music'),
|
|
124
|
+
path.join(os.homedir(), 'Videos')
|
|
125
|
+
],
|
|
126
|
+
blockedPaths: [
|
|
127
|
+
path.join(os.homedir(), '.ssh'),
|
|
128
|
+
path.join(os.homedir(), '.gnupg'),
|
|
129
|
+
path.join(os.homedir(), '.config', 'mint', 'mint-config.json'),
|
|
130
|
+
path.join(os.homedir(), '.mint', 'mint-config.json')
|
|
131
|
+
],
|
|
132
|
+
blockedFileNames: ['.env', 'id_rsa', 'id_ed25519']
|
|
71
133
|
};
|
|
72
134
|
|
|
73
135
|
|
|
@@ -100,8 +162,6 @@ function getAvailableProviders(config) {
|
|
|
100
162
|
const providers = [];
|
|
101
163
|
const cfg = config || readConfig();
|
|
102
164
|
|
|
103
|
-
const isPlaceholder = (val) => !val || val.startsWith('your_') || val.includes('key_here') || val.trim() === '';
|
|
104
|
-
|
|
105
165
|
// Check which providers have API keys or URLs configured
|
|
106
166
|
const anthropicKey = cfg.anthropicApiKey || process.env.ANTHROPIC_API_KEY;
|
|
107
167
|
if (!isPlaceholder(anthropicKey)) providers.push('anthropic');
|
|
@@ -123,4 +183,8 @@ function getAvailableProviders(config) {
|
|
|
123
183
|
return providers;
|
|
124
184
|
}
|
|
125
185
|
|
|
126
|
-
|
|
186
|
+
function isPlaceholder(val) {
|
|
187
|
+
return !val || val.startsWith('your_') || val.includes('key_here') || val.trim() === '';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
module.exports = { readConfig, writeConfig, getAvailableProviders, isPlaceholder, CONFIG_PATH };
|
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const
|
|
3
|
+
const os = require('os');
|
|
4
4
|
const { exec } = require('child_process');
|
|
5
5
|
|
|
6
|
+
// Handle electron dependency safely
|
|
7
|
+
let app, shell;
|
|
8
|
+
try {
|
|
9
|
+
const electron = require('electron');
|
|
10
|
+
app = electron.app;
|
|
11
|
+
shell = electron.shell;
|
|
12
|
+
} catch (e) {
|
|
13
|
+
app = null;
|
|
14
|
+
shell = null;
|
|
15
|
+
}
|
|
16
|
+
|
|
6
17
|
function escapeRegExp(text) {
|
|
7
18
|
return String(text).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
8
19
|
}
|
|
9
20
|
|
|
10
21
|
class CustomWorkflows {
|
|
11
22
|
constructor() {
|
|
12
|
-
|
|
23
|
+
const configDir = path.join(os.homedir(), '.config', 'mint');
|
|
24
|
+
this.configPath = path.join(configDir, 'workflows.json');
|
|
13
25
|
this.workflows = [];
|
|
14
26
|
this.lastTriggered = {};
|
|
15
27
|
this.cooldownMs = 60 * 60 * 1000; // 1 hour cooldown per rule
|
|
@@ -17,10 +29,27 @@ class CustomWorkflows {
|
|
|
17
29
|
this.timer = null;
|
|
18
30
|
this.webContents = null;
|
|
19
31
|
|
|
32
|
+
if (!fs.existsSync(configDir)) {
|
|
33
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
this.migrateConfig();
|
|
20
37
|
this.ensureConfigExists();
|
|
21
38
|
this.loadWorkflows();
|
|
22
39
|
}
|
|
23
40
|
|
|
41
|
+
migrateConfig() {
|
|
42
|
+
if (!fs.existsSync(this.configPath) && app && app.getPath) {
|
|
43
|
+
const electronPath = path.join(app.getPath('userData'), 'workflows.json');
|
|
44
|
+
if (fs.existsSync(electronPath)) {
|
|
45
|
+
try {
|
|
46
|
+
fs.copyFileSync(electronPath, this.configPath);
|
|
47
|
+
console.log('[CustomWorkflows] Migrated workflows from Electron userData');
|
|
48
|
+
} catch (e) { console.error('[CustomWorkflows] Migration failed:', e); }
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
24
53
|
ensureConfigExists() {
|
|
25
54
|
if (!fs.existsSync(this.configPath)) {
|
|
26
55
|
const defaultWorkflows = [
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const MAX_GOOGLE_TTS_CHARS = 200;
|
|
2
|
+
|
|
3
|
+
function splitTextForTts(text, maxLength = MAX_GOOGLE_TTS_CHARS) {
|
|
4
|
+
const normalized = String(text || '').replace(/\s+/g, ' ').trim();
|
|
5
|
+
if (!normalized) return [];
|
|
6
|
+
|
|
7
|
+
const chunks = [];
|
|
8
|
+
let remaining = normalized;
|
|
9
|
+
|
|
10
|
+
while (remaining.length > maxLength) {
|
|
11
|
+
const slice = remaining.slice(0, maxLength + 1);
|
|
12
|
+
const splitAt = Math.max(
|
|
13
|
+
slice.lastIndexOf('.'),
|
|
14
|
+
slice.lastIndexOf('?'),
|
|
15
|
+
slice.lastIndexOf('!'),
|
|
16
|
+
slice.lastIndexOf(','),
|
|
17
|
+
slice.lastIndexOf(' ')
|
|
18
|
+
);
|
|
19
|
+
const safeSplit = splitAt > 0 ? splitAt : maxLength;
|
|
20
|
+
chunks.push(remaining.slice(0, safeSplit).trim());
|
|
21
|
+
remaining = remaining.slice(safeSplit).trim();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (remaining) chunks.push(remaining);
|
|
25
|
+
return chunks;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getGoogleTtsUrls(text, options = {}) {
|
|
29
|
+
const lang = options.lang || 'en';
|
|
30
|
+
const host = options.host || 'https://translate.google.com';
|
|
31
|
+
const chunks = splitTextForTts(text);
|
|
32
|
+
|
|
33
|
+
return chunks.map((chunk, index) => {
|
|
34
|
+
const params = new URLSearchParams({
|
|
35
|
+
ie: 'UTF-8',
|
|
36
|
+
q: chunk,
|
|
37
|
+
tl: lang,
|
|
38
|
+
client: 'tw-ob',
|
|
39
|
+
idx: String(index),
|
|
40
|
+
total: String(chunks.length),
|
|
41
|
+
textlen: String(chunk.length)
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
shortText: chunk,
|
|
46
|
+
url: `${host}/translate_tts?${params.toString()}`
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = { getGoogleTtsUrls, splitTextForTts };
|