@pheem49/mint 1.5.5 → 1.6.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.
Files changed (222) hide show
  1. package/.codex +0 -0
  2. package/.github/FUNDING.yml +2 -0
  3. package/.github/workflows/ci.yml +45 -0
  4. package/.github/workflows/release.yml +79 -0
  5. package/Cargo.lock +5792 -0
  6. package/Cargo.toml +32 -0
  7. package/README.md +387 -353
  8. package/assets/icon.png +0 -0
  9. package/bin/mint +0 -0
  10. package/crates/mint-cli/Cargo.toml +23 -0
  11. package/crates/mint-cli/src/agent.rs +851 -0
  12. package/crates/mint-cli/src/gmail.rs +216 -0
  13. package/crates/mint-cli/src/image.rs +142 -0
  14. package/crates/mint-cli/src/main.rs +2837 -0
  15. package/crates/mint-cli/src/mcp.rs +63 -0
  16. package/crates/mint-cli/src/onboard.rs +1149 -0
  17. package/crates/mint-cli/src/setup.rs +390 -0
  18. package/crates/mint-cli/src/skills.rs +8 -0
  19. package/crates/mint-cli/src/updater.rs +279 -0
  20. package/crates/mint-core/Cargo.toml +22 -0
  21. package/crates/mint-core/src/agent_loop.rs +94 -0
  22. package/crates/mint-core/src/api_server.rs +991 -0
  23. package/crates/mint-core/src/channels.rs +248 -0
  24. package/crates/mint-core/src/chat.rs +895 -0
  25. package/crates/mint-core/src/code_tools.rs +729 -0
  26. package/crates/mint-core/src/config.rs +368 -0
  27. package/crates/mint-core/src/files.rs +159 -0
  28. package/crates/mint-core/src/knowledge.rs +541 -0
  29. package/crates/mint-core/src/lib.rs +84 -0
  30. package/crates/mint-core/src/mcp.rs +273 -0
  31. package/crates/mint-core/src/memory.rs +673 -0
  32. package/crates/mint-core/src/orchestration.rs +2157 -0
  33. package/crates/mint-core/src/pictures.rs +314 -0
  34. package/crates/mint-core/src/plugins.rs +727 -0
  35. package/crates/mint-core/src/safety.rs +416 -0
  36. package/crates/mint-core/src/semantic.rs +254 -0
  37. package/crates/mint-core/src/shell.rs +317 -0
  38. package/crates/mint-core/src/skills.rs +71 -0
  39. package/crates/mint-core/src/symbols.rs +157 -0
  40. package/crates/mint-core/src/tasks.rs +308 -0
  41. package/crates/mint-core/src/tts.rs +92 -0
  42. package/crates/mint-core/src/weather.rs +93 -0
  43. package/crates/mint-core/src/web_search.rs +200 -0
  44. package/crates/mint-core/src/workflows.rs +81 -0
  45. package/crates/mint-core/tests/mcp_stdio.rs +45 -0
  46. package/crates/mint-core/tests/memory_persistence.rs +172 -0
  47. package/crates/mint-core/tests/pictures_storage.rs +14 -0
  48. package/crates/mint-core/tests/task_lifecycle.rs +87 -0
  49. package/package.json +35 -99
  50. package/src/bin/index.js +16 -0
  51. package/src/renderer/index-web.html +17 -0
  52. package/src/renderer/index.html +17 -0
  53. package/src/renderer/public/Live2DCubismCore.js +9 -0
  54. package/src/renderer/public/assets/icon.png +0 -0
  55. package/src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.model3.json +36 -0
  56. package/src/renderer/src/App.tsx +33 -0
  57. package/src/renderer/src/calculator.ts +47 -0
  58. package/src/renderer/src/components/ChatPanel.tsx +1598 -0
  59. package/src/renderer/src/components/DashboardSidebar.tsx +358 -0
  60. package/src/renderer/src/components/Live2DStage.tsx +374 -0
  61. package/src/renderer/src/components/MintDashboard.tsx +950 -0
  62. package/src/renderer/src/components/ModelPanel.tsx +154 -0
  63. package/src/renderer/src/components/PicturesLibrary.tsx +46 -0
  64. package/src/renderer/src/components/ProactiveGlow.tsx +19 -0
  65. package/src/renderer/src/components/ScreenPicker.tsx +579 -0
  66. package/src/renderer/src/components/SettingsWindow.tsx +1467 -0
  67. package/src/renderer/src/components/SpotlightWindow.tsx +280 -0
  68. package/src/renderer/src/components/WidgetWindow.tsx +36 -0
  69. package/src/renderer/src/components/WorkspacePanel.tsx +268 -0
  70. package/src/{UI → renderer/src/css}/settings.css +69 -16
  71. package/src/renderer/src/css/spotlight.css +113 -0
  72. package/src/renderer/src/css/styles.css +3722 -0
  73. package/src/renderer/src/css/widget.css +185 -0
  74. package/src/renderer/src/env.d.ts +116 -0
  75. package/src/renderer/src/index.css +379 -0
  76. package/src/renderer/src/main.tsx +13 -0
  77. package/src/renderer/src/tauri.ts +996 -0
  78. package/src/renderer/src-web/App.tsx +25 -0
  79. package/src/renderer/src-web/calculator.ts +47 -0
  80. package/src/renderer/src-web/components/ChatPanel.tsx +1662 -0
  81. package/src/renderer/src-web/components/DashboardSidebar.tsx +242 -0
  82. package/src/renderer/src-web/components/MintDashboard.tsx +763 -0
  83. package/src/renderer/src-web/components/PicturesLibrary.tsx +73 -0
  84. package/src/renderer/src-web/components/SettingsWindow.tsx +1500 -0
  85. package/src/renderer/src-web/css/settings.css +1100 -0
  86. package/src/{UI → renderer/src-web/css}/spotlight.css +4 -4
  87. package/src/{UI → renderer/src-web/css}/styles.css +1055 -159
  88. package/src/{UI → renderer/src-web/css}/widget.css +2 -2
  89. package/src/renderer/src-web/env.d.ts +107 -0
  90. package/src/renderer/src-web/index.css +379 -0
  91. package/src/renderer/src-web/main.tsx +13 -0
  92. package/src/renderer/src-web/tauri.ts +983 -0
  93. package/tsconfig.json +30 -0
  94. package/vite.config.ts +33 -0
  95. package/vite.config.web.ts +51 -0
  96. package/GUIDE_TH.md +0 -125
  97. package/assets/Agent_Mint.png +0 -0
  98. package/assets/CLI_Screen.png +0 -0
  99. package/assets/Settings.png +0 -0
  100. package/benchmark_ai.js +0 -71
  101. package/install.ps1 +0 -64
  102. package/install.sh +0 -54
  103. package/main.js +0 -139
  104. package/mint-cli-logic.js +0 -3
  105. package/mint-cli.js +0 -410
  106. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.model3.json +0 -47
  107. 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 +0 -23
  108. package/preload-picker.js +0 -11
  109. package/preload-settings.js +0 -11
  110. package/preload.js +0 -41
  111. package/scripts/install_linux_desktop_entry.js +0 -48
  112. package/src/AI_Brain/Gemini_API.js +0 -813
  113. package/src/AI_Brain/agent_orchestrator.js +0 -73
  114. package/src/AI_Brain/autonomous_brain.js +0 -179
  115. package/src/AI_Brain/behavior_memory.js +0 -135
  116. package/src/AI_Brain/headless_agent.js +0 -143
  117. package/src/AI_Brain/knowledge_base.js +0 -349
  118. package/src/AI_Brain/memory_store.js +0 -662
  119. package/src/AI_Brain/proactive_engine.js +0 -172
  120. package/src/AI_Brain/provider_adapter.js +0 -365
  121. package/src/Automation_Layer/browser_automation.js +0 -149
  122. package/src/Automation_Layer/file_operations.js +0 -286
  123. package/src/Automation_Layer/open_app.js +0 -85
  124. package/src/Automation_Layer/open_website.js +0 -38
  125. package/src/CLI/approval_handler.js +0 -47
  126. package/src/CLI/chat_router.js +0 -247
  127. package/src/CLI/chat_ui.js +0 -1159
  128. package/src/CLI/cli_colors.js +0 -115
  129. package/src/CLI/cli_formatters.js +0 -94
  130. package/src/CLI/code_agent.js +0 -1667
  131. package/src/CLI/code_session_memory.js +0 -62
  132. package/src/CLI/gmail_auth.js +0 -210
  133. package/src/CLI/image_input.js +0 -90
  134. package/src/CLI/intent_detectors.js +0 -181
  135. package/src/CLI/interactive_chat.js +0 -658
  136. package/src/CLI/list_features.js +0 -64
  137. package/src/CLI/onboarding.js +0 -416
  138. package/src/CLI/repo_summarizer.js +0 -282
  139. package/src/CLI/semantic_code_search.js +0 -312
  140. package/src/CLI/skill_manager.js +0 -41
  141. package/src/CLI/slash_command_handler.js +0 -418
  142. package/src/CLI/symbol_indexer.js +0 -231
  143. package/src/CLI/updater.js +0 -230
  144. package/src/CLI/workspace_manager.js +0 -90
  145. package/src/Channels/brave_search_bridge.js +0 -35
  146. package/src/Channels/discord_bridge.js +0 -66
  147. package/src/Channels/google_search_bridge.js +0 -38
  148. package/src/Channels/line_bridge.js +0 -60
  149. package/src/Channels/slack_bridge.js +0 -48
  150. package/src/Channels/telegram_bridge.js +0 -41
  151. package/src/Channels/whatsapp_bridge.js +0 -57
  152. package/src/Command_Parser/parser.js +0 -45
  153. package/src/Plugins/dev_tools.js +0 -41
  154. package/src/Plugins/discord.js +0 -20
  155. package/src/Plugins/docker.js +0 -47
  156. package/src/Plugins/gmail.js +0 -251
  157. package/src/Plugins/google_calendar.js +0 -252
  158. package/src/Plugins/mcp_manager.js +0 -95
  159. package/src/Plugins/notion.js +0 -256
  160. package/src/Plugins/obsidian.js +0 -54
  161. package/src/Plugins/plugin_manager.js +0 -81
  162. package/src/Plugins/spotify.js +0 -173
  163. package/src/Plugins/system_metrics.js +0 -31
  164. package/src/Plugins/system_monitor.js +0 -72
  165. package/src/System/action_executor.js +0 -178
  166. package/src/System/bridge_manager.js +0 -76
  167. package/src/System/chat_history_manager.js +0 -83
  168. package/src/System/config_manager.js +0 -194
  169. package/src/System/custom_workflows.js +0 -163
  170. package/src/System/daemon_manager.js +0 -67
  171. package/src/System/google_tts_urls.js +0 -51
  172. package/src/System/granular_automation.js +0 -157
  173. package/src/System/ipc_handlers.js +0 -332
  174. package/src/System/notifications.js +0 -23
  175. package/src/System/optional_require.js +0 -23
  176. package/src/System/picture_store.js +0 -109
  177. package/src/System/proactive_loop.js +0 -153
  178. package/src/System/safety_manager.js +0 -273
  179. package/src/System/sandbox_runner.js +0 -182
  180. package/src/System/screen_capture.js +0 -175
  181. package/src/System/smart_context.js +0 -227
  182. package/src/System/system_automation.js +0 -162
  183. package/src/System/system_events.js +0 -79
  184. package/src/System/system_info.js +0 -125
  185. package/src/System/task_manager.js +0 -222
  186. package/src/System/tool_registry.js +0 -293
  187. package/src/System/window_manager.js +0 -220
  188. package/src/UI/floating.css +0 -80
  189. package/src/UI/floating.html +0 -17
  190. package/src/UI/floating.js +0 -67
  191. package/src/UI/live2d_manager.js +0 -600
  192. package/src/UI/preload-floating.js +0 -7
  193. package/src/UI/preload-spotlight.js +0 -11
  194. package/src/UI/preload-widget.js +0 -5
  195. package/src/UI/proactive-glow.html +0 -42
  196. package/src/UI/renderer.js +0 -2127
  197. package/src/UI/screenPicker.html +0 -214
  198. package/src/UI/screenPicker.js +0 -262
  199. package/src/UI/settings.html +0 -577
  200. package/src/UI/settings.js +0 -770
  201. package/src/UI/spotlight.html +0 -23
  202. package/src/UI/spotlight.js +0 -185
  203. package/src/UI/widget.html +0 -29
  204. package/src/UI/widget.js +0 -10
  205. /package/{models → src/renderer/public/models}/Shiroko_Model/Shiroko/Shiroko_Core/72d86db84cfa9730b894c241fd24c0db.png +0 -0
  206. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//345/233/264/350/243/231.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/apron.exp3.json} +0 -0
  207. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//347/214/253/345/222/252/346/273/244/351/225/234.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/catfilter.exp3.json} +0 -0
  208. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//347/202/271/344/270/200/344/270/213.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/click.exp3.json} +0 -0
  209. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//345/221/206/347/214/253.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/dazed.exp3.json} +0 -0
  210. /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" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/dazedeyes.exp3.json} +0 -0
  211. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//347/234/274/351/225/234.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/glasses.exp3.json} +0 -0
  212. /package/{models → src/renderer/public/models}/Shiroko_Model/Shiroko/Shiroko_Core/items_pinned_to_model.json +0 -0
  213. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/277/347/254/224.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/pen.exp3.json} +0 -0
  214. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/215/347/205/247.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/photo.exp3.json} +0 -0
  215. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_00.png" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.4096/texture_00.png} +0 -0
  216. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_01.png" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.4096/texture_01.png} +0 -0
  217. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_02.png" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.4096/texture_02.png} +0 -0
  218. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_03.png" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.4096/texture_03.png} +0 -0
  219. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.cdi3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.cdi3.json} +0 -0
  220. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.moc3" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.moc3} +0 -0
  221. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.physics3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.physics3.json} +0 -0
  222. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.vtube.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.vtube.json} +0 -0
@@ -1,95 +0,0 @@
1
- const { spawn } = require('child_process');
2
- const { readConfig } = require('../System/config_manager');
3
-
4
- /**
5
- * McpManager handles the lifecycle of multiple MCP servers.
6
- * Since MCP SDK is ESM and this project is CommonJS, we use dynamic imports.
7
- */
8
- class McpManager {
9
- constructor() {
10
- this.clients = new Map(); // serverName -> { client, transport }
11
- this.tools = [];
12
- }
13
-
14
- async init() {
15
- const config = readConfig();
16
- const mcpServers = config.mcpServers || {};
17
-
18
- console.log(`[MCP] Initializing ${Object.keys(mcpServers).length} servers...`);
19
-
20
- // Load SDK via dynamic import
21
- const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
22
- const { StdioClientTransport } = await import('@modelcontextprotocol/sdk/client/stdio.js');
23
-
24
- for (const [name, serverConfig] of Object.entries(mcpServers)) {
25
- try {
26
- console.log(`[MCP] Connecting to server: ${name}`);
27
-
28
- const transport = new StdioClientTransport({
29
- command: serverConfig.command,
30
- args: serverConfig.args,
31
- env: { ...process.env, ...serverConfig.env }
32
- });
33
-
34
- const client = new Client(
35
- { name: 'mint-ai-assistant', version: '1.2.4' },
36
- { capabilities: {} }
37
- );
38
-
39
- await client.connect(transport);
40
-
41
- // Discover tools
42
- const toolsResponse = await client.listTools();
43
- const serverTools = (toolsResponse.tools || []).map(t => ({
44
- ...t,
45
- serverName: name
46
- }));
47
-
48
- this.clients.set(name, { client, transport, tools: serverTools });
49
- this.tools.push(...serverTools);
50
-
51
- console.log(`[MCP] Server ${name} connected. Found ${serverTools.length} tools.`);
52
- } catch (err) {
53
- console.error(`[MCP] Failed to connect to server ${name}:`, err.message);
54
- }
55
- }
56
- }
57
-
58
- getAllTools() {
59
- return this.tools;
60
- }
61
-
62
- async callTool(serverName, toolName, args) {
63
- const server = this.clients.get(serverName);
64
- if (!server) throw new Error(`MCP Server "${serverName}" not found or not connected.`);
65
-
66
- try {
67
- console.log(`[MCP] Calling tool ${toolName} on server ${serverName}...`);
68
- const result = await server.client.callTool({
69
- name: toolName,
70
- arguments: args
71
- });
72
- return result;
73
- } catch (err) {
74
- console.error(`[MCP] Error calling tool ${toolName}:`, err);
75
- throw err;
76
- }
77
- }
78
-
79
- async shutdown() {
80
- console.log('[MCP] Shutting down all servers...');
81
- for (const [name, server] of this.clients.entries()) {
82
- try {
83
- await server.client.close();
84
- console.log(`[MCP] Server ${name} closed.`);
85
- } catch (err) {
86
- console.error(`[MCP] Error closing server ${name}:`, err.message);
87
- }
88
- }
89
- this.clients.clear();
90
- this.tools = [];
91
- }
92
- }
93
-
94
- const instance = new McpManager();
95
- module.exports = instance;
@@ -1,256 +0,0 @@
1
- const axios = require('axios');
2
- const { readConfig } = require('../System/config_manager');
3
-
4
- const NOTION_API_BASE = 'https://api.notion.com/v1';
5
- const NOTION_VERSION = '2022-06-28';
6
-
7
- function hasNotionConfig(config) {
8
- return Boolean(config.notionApiKey);
9
- }
10
-
11
- function parseInstruction(instruction) {
12
- const raw = (instruction || '').trim();
13
- if (!raw) return { action: 'help' };
14
-
15
- try {
16
- const parsed = JSON.parse(raw);
17
- if (parsed && typeof parsed === 'object') {
18
- return {
19
- action: normalizeAction(parsed.action || 'create_page'),
20
- ...parsed
21
- };
22
- }
23
- } catch {
24
- // Plain text creates a note/page.
25
- }
26
-
27
- const lower = raw.toLowerCase();
28
- if (lower === 'help') return { action: 'help' };
29
- if (lower === 'list' || lower.startsWith('list database')) return { action: 'query_database' };
30
- if (lower.startsWith('read database')) return { action: 'query_database' };
31
-
32
- const [firstLine, ...rest] = raw.split('\n');
33
- return {
34
- action: 'create_page',
35
- title: firstLine.trim() || 'Mint Note',
36
- content: rest.join('\n').trim() || raw
37
- };
38
- }
39
-
40
- function normalizeAction(action) {
41
- const normalized = String(action || '').toLowerCase();
42
- if (['create', 'create_note', 'note', 'create_page', 'page'].includes(normalized)) return 'create_page';
43
- if (['list', 'read', 'query', 'query_database', 'read_database'].includes(normalized)) return 'query_database';
44
- if (['append', 'append_block', 'append_to_page'].includes(normalized)) return 'append_block';
45
- return normalized;
46
- }
47
-
48
- function notionHeaders(config) {
49
- return {
50
- Authorization: `Bearer ${config.notionApiKey}`,
51
- 'Notion-Version': NOTION_VERSION,
52
- 'Content-Type': 'application/json'
53
- };
54
- }
55
-
56
- function textBlock(text) {
57
- return {
58
- object: 'block',
59
- type: 'paragraph',
60
- paragraph: {
61
- rich_text: [
62
- {
63
- type: 'text',
64
- text: { content: String(text || '') }
65
- }
66
- ]
67
- }
68
- };
69
- }
70
-
71
- function headingBlock(text) {
72
- return {
73
- object: 'block',
74
- type: 'heading_2',
75
- heading_2: {
76
- rich_text: [
77
- {
78
- type: 'text',
79
- text: { content: String(text || '') }
80
- }
81
- ]
82
- }
83
- };
84
- }
85
-
86
- function buildChildren(input) {
87
- const content = input.content || input.body || input.text || '';
88
- const blocks = [];
89
-
90
- if (Array.isArray(input.children)) return input.children;
91
- if (input.heading) blocks.push(headingBlock(input.heading));
92
-
93
- const paragraphs = String(content || '')
94
- .split(/\n\s*\n/)
95
- .map(part => part.trim())
96
- .filter(Boolean);
97
-
98
- for (const paragraph of paragraphs.length ? paragraphs : ['Created by Mint.']) {
99
- blocks.push(textBlock(paragraph));
100
- }
101
-
102
- return blocks;
103
- }
104
-
105
- function buildDatabaseProperties(input, config) {
106
- const title = input.title || input.summary || input.name || 'Mint Note';
107
- const titleProperty = input.titleProperty || config.notionTitleProperty || 'Name';
108
- const properties = {
109
- [titleProperty]: {
110
- title: [
111
- {
112
- text: { content: title }
113
- }
114
- ]
115
- }
116
- };
117
-
118
- if (input.properties && typeof input.properties === 'object') {
119
- return { ...properties, ...input.properties };
120
- }
121
-
122
- return properties;
123
- }
124
-
125
- function formatNotionTitle(properties = {}) {
126
- for (const property of Object.values(properties)) {
127
- if (property && property.type === 'title' && Array.isArray(property.title)) {
128
- const title = property.title.map(part => part.plain_text || part.text?.content || '').join('');
129
- if (title) return title;
130
- }
131
- }
132
- return '(Untitled)';
133
- }
134
-
135
- async function createPage(config, input) {
136
- const databaseId = input.databaseId || config.notionDatabaseId;
137
- const pageId = input.pageId || config.notionPageId;
138
-
139
- if (!databaseId && !pageId) {
140
- throw new Error('Missing Notion databaseId or pageId. Configure one in onboarding or pass it in the instruction JSON.');
141
- }
142
-
143
- const payload = databaseId
144
- ? {
145
- parent: { database_id: databaseId },
146
- properties: buildDatabaseProperties(input, config),
147
- children: buildChildren(input)
148
- }
149
- : {
150
- parent: { page_id: pageId },
151
- properties: {
152
- title: [
153
- {
154
- text: { content: input.title || input.summary || input.name || 'Mint Note' }
155
- }
156
- ]
157
- },
158
- children: buildChildren(input)
159
- };
160
-
161
- const response = await axios.post(`${NOTION_API_BASE}/pages`, payload, {
162
- headers: notionHeaders(config)
163
- });
164
-
165
- const page = response.data || {};
166
- const title = input.title || input.summary || input.name || 'Mint Note';
167
- return `Created Notion page "${title}".${page.url ? `\n${page.url}` : ''}`;
168
- }
169
-
170
- async function queryDatabase(config, input) {
171
- const databaseId = input.databaseId || config.notionDatabaseId;
172
- if (!databaseId) {
173
- throw new Error('Missing Notion databaseId. Configure one in onboarding or pass it in the instruction JSON.');
174
- }
175
-
176
- const payload = {
177
- page_size: Number(input.pageSize || input.limit || 10)
178
- };
179
-
180
- if (input.filter) payload.filter = input.filter;
181
- if (input.sorts) payload.sorts = input.sorts;
182
-
183
- const response = await axios.post(`${NOTION_API_BASE}/databases/${databaseId}/query`, payload, {
184
- headers: notionHeaders(config)
185
- });
186
-
187
- const results = response.data.results || [];
188
- if (results.length === 0) return 'No Notion database pages found.';
189
-
190
- const lines = results.map((page, index) => {
191
- const title = formatNotionTitle(page.properties);
192
- return `${index + 1}. ${title}${page.url ? ` — ${page.url}` : ''}`;
193
- });
194
-
195
- return `Notion database pages:\n${lines.join('\n')}`;
196
- }
197
-
198
- async function appendBlock(config, input) {
199
- const pageId = input.pageId || config.notionPageId;
200
- if (!pageId) {
201
- throw new Error('Missing Notion pageId. Configure one in onboarding or pass it in the instruction JSON.');
202
- }
203
-
204
- const response = await axios.patch(`${NOTION_API_BASE}/blocks/${pageId}/children`, {
205
- children: buildChildren(input)
206
- }, {
207
- headers: notionHeaders(config)
208
- });
209
-
210
- const count = response.data.results ? response.data.results.length : buildChildren(input).length;
211
- return `Appended ${count} block(s) to Notion page.`;
212
- }
213
-
214
- function helpText() {
215
- return [
216
- 'Notion plugin commands:',
217
- '- Create page: {"action":"create_page","title":"Note title","content":"Body text"}',
218
- '- Query database: {"action":"query_database","limit":5}',
219
- '- Append to page: {"action":"append_block","pageId":"...","content":"Text"}',
220
- 'Plain text creates a Notion page using the configured default database or page.'
221
- ].join('\n');
222
- }
223
-
224
- module.exports = {
225
- name: 'notion',
226
- description: 'Manage Notion. Target can be JSON: {"action":"create_page","title":"Note","content":"Body","databaseId":"optional","pageId":"optional"}, {"action":"query_database","databaseId":"optional","limit":10}, or {"action":"append_block","pageId":"optional","content":"Text"}. Plain text creates a note.',
227
-
228
- async execute(instruction) {
229
- const config = readConfig();
230
- const input = parseInstruction(instruction);
231
-
232
- if (input.action === 'help') return helpText();
233
- if (!hasNotionConfig(config)) {
234
- return 'Notion API is not configured. Add a Notion internal integration secret with `mint onboard`, then share your Notion page/database with that integration.';
235
- }
236
-
237
- switch (input.action) {
238
- case 'create_page':
239
- return await createPage(config, input);
240
- case 'query_database':
241
- return await queryDatabase(config, input);
242
- case 'append_block':
243
- return await appendBlock(config, input);
244
- default:
245
- throw new Error(`Unsupported Notion action: ${input.action}`);
246
- }
247
- },
248
-
249
- _helpers: {
250
- parseInstruction,
251
- buildChildren,
252
- buildDatabaseProperties,
253
- formatNotionTitle,
254
- hasNotionConfig
255
- }
256
- };
@@ -1,54 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- function getNotesDir() {
5
- let base = process.env.HOME || process.env.USERPROFILE || process.cwd();
6
- // Default to Documents/Mint_Notes
7
- const dir = path.join(base, 'Documents', 'Mint_Notes');
8
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
9
- return dir;
10
- }
11
-
12
- module.exports = {
13
- name: 'obsidian',
14
- description: 'Manage local Markdown notes (like Obsidian/Notion). Instruction MUST be one of: "list", "read: [filename]", "write: [filename] | [content]".',
15
-
16
- async execute(instruction) {
17
- const dir = getNotesDir();
18
-
19
- if (instruction.startsWith('list')) {
20
- const files = fs.readdirSync(dir).filter(f => f.endsWith('.md'));
21
- if (files.length === 0) return "ยังไม่มีโน้ตอยู่ในระบบค่ะ 📝";
22
- return `รายการโน้ตทั้งหมด:\n${files.join('\n')}`;
23
- }
24
-
25
- if (instruction.startsWith('read:')) {
26
- let filename = instruction.replace('read:', '').trim();
27
- if (!filename.endsWith('.md')) filename += '.md';
28
- const filepath = path.join(dir, filename);
29
- if (fs.existsSync(filepath)) {
30
- return `เนื้อหาของโน้ต ${filename}:\n\n${fs.readFileSync(filepath, 'utf8')}`;
31
- }
32
- return `ไม่พบโน้ตชื่อ ${filename} ค่ะ ❌`;
33
- }
34
-
35
- if (instruction.startsWith('write:')) {
36
- const parts = instruction.replace('write:', '').split('|');
37
- if (parts.length < 2) return "รูปแบบคำสั่งไม่ถูกต้องค่ะ ต้องเป็น write: filename | content";
38
- let filename = parts[0].trim();
39
- const content = parts.slice(1).join('|').trim();
40
-
41
- if (!filename.endsWith('.md')) filename += '.md';
42
- const filepath = path.join(dir, filename);
43
-
44
- // Log timestamp
45
- const timestamp = new Date().toLocaleString('th-TH');
46
- const entry = `\n---บันทึกเมื่อ ${timestamp}---\n${content}\n`;
47
-
48
- fs.appendFileSync(filepath, entry);
49
- return `บันทึกข้อความลงในโน้ต ${filename} เรียบร้อยแล้วค่ะ ✅`;
50
- }
51
-
52
- return "คำสั่งโน้ตไม่ถูกต้องค่ะ ลองใช้ 'list', 'read: ชื่อไฟล์', หรือ 'write: ชื่อไฟล์ | เนื้อหา' นะคะ";
53
- }
54
- };
@@ -1,81 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- class PluginManager {
5
- constructor() {
6
- this.plugins = new Map();
7
- this.pluginsDir = path.join(__dirname);
8
- }
9
-
10
- // Load or reload plugins
11
- loadPlugins() {
12
- this.plugins.clear();
13
-
14
- try {
15
- if (!fs.existsSync(this.pluginsDir)) return;
16
-
17
- const files = fs.readdirSync(this.pluginsDir);
18
- for (const file of files) {
19
- // Ignore self and core system managers
20
- if (file === 'plugin_manager.js' || file === 'mcp_manager.js' || !file.endsWith('.js')) continue;
21
-
22
- const pluginPath = path.join(this.pluginsDir, file);
23
-
24
- // Clear require cache for hot-reloading
25
- delete require.cache[require.resolve(pluginPath)];
26
-
27
- try {
28
- const plugin = require(pluginPath);
29
- if (this.validatePlugin(plugin)) {
30
- this.plugins.set(plugin.name, plugin);
31
- // console.log(`[PluginManager] Loaded: ${plugin.name}`);
32
- } else {
33
- console.warn(`[PluginManager] Invalid plugin format: ${file}`);
34
- }
35
- } catch (err) {
36
- console.error(`[PluginManager] Error loading plugin ${file}:`, err);
37
- }
38
- }
39
- } catch (err) {
40
- console.error('[PluginManager] Error accessing plugin directory:', err);
41
- }
42
- }
43
-
44
- validatePlugin(plugin) {
45
- return plugin
46
- && typeof plugin.name === 'string'
47
- && typeof plugin.description === 'string'
48
- && typeof plugin.execute === 'function';
49
- }
50
-
51
- // Returns formatted descriptions for the Gemini prompt
52
- getPromptDescriptions() {
53
- if (this.plugins.size === 0) return '';
54
-
55
- let descriptions = '\nPlugin Actions Available:\n';
56
- for (const [name, plugin] of this.plugins.entries()) {
57
- descriptions += `- Plugin: "${name}" | Description: ${plugin.description}\n`;
58
- }
59
- return descriptions;
60
- }
61
-
62
- // Execute a plugin's action
63
- async executePlugin(name, instruction) {
64
- const plugin = this.plugins.get(name);
65
- if (!plugin) {
66
- return `Plugin "${name}" not found.`;
67
- }
68
-
69
- try {
70
- // console.log(`[PluginManager] Executing ${name} with instruction: "${instruction}"`);
71
- return await plugin.execute(instruction);
72
- } catch (err) {
73
- console.error(`[PluginManager] Error executing plugin ${name}:`, err);
74
- return `Error executing plugin ${name}: ${err.message}`;
75
- }
76
- }
77
- }
78
-
79
- // Export a singleton instance
80
- const pluginManager = new PluginManager();
81
- module.exports = pluginManager;
@@ -1,173 +0,0 @@
1
- /**
2
- * Mint Spotify Plugin — Complete Edition
3
- * ----------------------------------------
4
- * Controls Spotify playback via playerctl (no OAuth required).
5
- * Supports: play, pause, next, previous, stop, shuffle, volume,
6
- * now_playing, search (opens Spotify search URL).
7
- *
8
- * Requirements: playerctl installed (sudo apt install playerctl)
9
- * Spotify must be running (Desktop app or Snap).
10
- */
11
-
12
- const { exec, execSync } = require('child_process');
13
- const { promisify } = require('util');
14
- const execAsync = promisify(exec);
15
-
16
- // ── Helpers ────────────────────────────────────────────────────────────────
17
-
18
- async function runPlayerctl(args) {
19
- try {
20
- const { stdout } = await execAsync(`playerctl -p spotify ${args}`);
21
- return { ok: true, output: stdout.trim() };
22
- } catch (err) {
23
- const msg = (err.stderr || err.message || '').toLowerCase();
24
- if (msg.includes('no players found') || msg.includes('could not find player')) {
25
- return { ok: false, error: 'spotify_not_running' };
26
- }
27
- if (err.code === 127) {
28
- return { ok: false, error: 'playerctl_missing' };
29
- }
30
- return { ok: false, error: err.message };
31
- }
32
- }
33
-
34
- function formatError(errorCode) {
35
- if (errorCode === 'spotify_not_running') {
36
- return '🎵 Spotify ยังไม่ได้เปิดอยู่นะคะ กรุณาเปิด Spotify ก่อนนะคะ';
37
- }
38
- if (errorCode === 'playerctl_missing') {
39
- return '⚠️ ไม่พบ playerctl กรุณาติดตั้งด้วยคำสั่ง: sudo apt install playerctl';
40
- }
41
- return `❌ เกิดข้อผิดพลาด: ${errorCode}`;
42
- }
43
-
44
- // ── Action Handlers ────────────────────────────────────────────────────────
45
-
46
- const ACTION_MAP = {
47
- 'play': () => runPlayerctl('play'),
48
- 'pause': () => runPlayerctl('pause'),
49
- 'stop': () => runPlayerctl('stop'),
50
- 'next': () => runPlayerctl('next'),
51
- 'previous': () => runPlayerctl('previous'),
52
- 'prev': () => runPlayerctl('previous'),
53
- };
54
-
55
- const ACTION_MESSAGES = {
56
- 'play': '▶️ เล่น Spotify แล้วค่ะ 🎵',
57
- 'pause': '⏸️ หยุดเพลงชั่วคราวแล้วค่ะ',
58
- 'stop': '⏹️ หยุด Spotify แล้วค่ะ',
59
- 'next': '⏭️ ข้ามไปเพลงถัดไปแล้วค่ะ 🎵',
60
- 'previous': '⏮️ กลับไปเพลงก่อนหน้าแล้วค่ะ',
61
- 'prev': '⏮️ กลับไปเพลงก่อนหน้าแล้วค่ะ',
62
- };
63
-
64
- async function getNowPlaying() {
65
- const [title, artist, album, status] = await Promise.all([
66
- runPlayerctl('metadata title'),
67
- runPlayerctl('metadata artist'),
68
- runPlayerctl('metadata album'),
69
- runPlayerctl('status'),
70
- ]);
71
-
72
- if (!title.ok) return formatError(title.error);
73
-
74
- const statusIcon = (status.output || '').toLowerCase() === 'playing' ? '▶️' : '⏸️';
75
- const titleText = title.output || 'ไม่ทราบชื่อเพลง';
76
- const artistText = artist.output || 'ไม่ทราบศิลปิน';
77
- const albumText = album.output || '';
78
-
79
- let reply = `${statusIcon} กำลังเล่น: **${titleText}**\n`;
80
- reply += `🎤 ศิลปิน: ${artistText}`;
81
- if (albumText) reply += `\n💿 อัลบั้ม: ${albumText}`;
82
- return reply;
83
- }
84
-
85
- async function setVolume(levelStr) {
86
- const level = parseInt(levelStr, 10);
87
- if (isNaN(level) || level < 0 || level > 100) {
88
- return '⚠️ กรุณาระบุระดับเสียง 0-100 ค่ะ เช่น "volume 70"';
89
- }
90
- // playerctl volume uses 0.0–1.0
91
- const result = await runPlayerctl(`volume ${(level / 100).toFixed(2)}`);
92
- if (!result.ok) return formatError(result.error);
93
- return `🔊 ปรับเสียงเป็น ${level}% แล้วค่ะ`;
94
- }
95
-
96
- async function setShuffle(state) {
97
- // state: 'on' | 'off' | 'toggle'
98
- const shuffleState = state === 'on' ? 'On' : state === 'off' ? 'Off' : 'Toggle';
99
- const result = await runPlayerctl(`shuffle ${shuffleState}`);
100
- if (!result.ok) return formatError(result.error);
101
- if (state === 'toggle') return '🔀 สลับโหมด Shuffle แล้วค่ะ';
102
- return `🔀 Shuffle ${state === 'on' ? 'เปิด' : 'ปิด'}แล้วค่ะ`;
103
- }
104
-
105
- function searchSpotify(query) {
106
- if (!query || !query.trim()) {
107
- return '⚠️ กรุณาระบุคำที่ต้องการค้นหาด้วยนะคะ เช่น "search BTS"';
108
- }
109
- const encoded = encodeURIComponent(query.trim());
110
- const url = `https://open.spotify.com/search/${encoded}`;
111
- try {
112
- const { exec: execSync2 } = require('child_process');
113
- execSync2(`xdg-open "${url}"`, { detached: true, stdio: 'ignore' });
114
- return `🔍 เปิดค้นหา "${query}" ใน Spotify แล้วค่ะ 🎵`;
115
- } catch (_) {
116
- return `🔍 ค้นหา "${query}" ที่: ${url}`;
117
- }
118
- }
119
-
120
- // ── Main Plugin Export ─────────────────────────────────────────────────────
121
-
122
- module.exports = {
123
- name: 'spotify',
124
- description: [
125
- 'Controls Spotify playback and gets now-playing info.',
126
- 'Valid targets:',
127
- ' "play" | "pause" | "stop" | "next" | "previous" — playback control',
128
- ' "now_playing" or "status" — get current song info',
129
- ' "volume <0-100>" — set volume level (e.g. "volume 70")',
130
- ' "shuffle on" | "shuffle off" | "shuffle toggle" — toggle shuffle',
131
- ' "search <query>" — search Spotify (e.g. "search BTS Dynamite")',
132
- ].join(' '),
133
-
134
- async execute(target) {
135
- const raw = (target || '').trim().toLowerCase();
136
-
137
- // ── Basic playback commands ───────────────────────────────────────
138
- if (ACTION_MAP[raw]) {
139
- const result = await ACTION_MAP[raw]();
140
- if (!result.ok) return formatError(result.error);
141
- return ACTION_MESSAGES[raw];
142
- }
143
-
144
- // ── Now Playing ───────────────────────────────────────────────────
145
- if (raw === 'now_playing' || raw === 'status' || raw === 'what\'s playing' || raw === 'current') {
146
- return await getNowPlaying();
147
- }
148
-
149
- // ── Volume ────────────────────────────────────────────────────────
150
- if (raw.startsWith('volume')) {
151
- const levelStr = raw.replace('volume', '').trim();
152
- return await setVolume(levelStr);
153
- }
154
-
155
- // ── Shuffle ───────────────────────────────────────────────────────
156
- if (raw.startsWith('shuffle')) {
157
- const state = raw.replace('shuffle', '').trim() || 'toggle';
158
- return await setShuffle(state);
159
- }
160
-
161
- // ── Search ────────────────────────────────────────────────────────
162
- if (raw.startsWith('search')) {
163
- const query = target.replace(/^search\s*/i, '').trim();
164
- return searchSpotify(query);
165
- }
166
-
167
- // ── Fallback: try as playerctl arg directly ───────────────────────
168
- return `⚠️ ไม่รู้จักคำสั่ง Spotify: "${target}"\nคำสั่งที่รองรับ: play, pause, stop, next, previous, now_playing, volume <0-100>, shuffle on/off, search <query>`;
169
- },
170
-
171
- // Expose helpers for testing
172
- _helpers: { runPlayerctl, getNowPlaying, setVolume, setShuffle, searchSpotify }
173
- };