mulby-cli 1.1.5
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/PLUGIN_DEVELOP_PROMPT.md +1164 -0
- package/README.md +852 -0
- package/assets/default-icon.png +0 -0
- package/dist/commands/ai-session.js +44 -0
- package/dist/commands/build.js +111 -0
- package/dist/commands/config-ai.js +291 -0
- package/dist/commands/config.js +53 -0
- package/dist/commands/create/ai-create.js +183 -0
- package/dist/commands/create/assets.js +53 -0
- package/dist/commands/create/basic.js +72 -0
- package/dist/commands/create/index.js +73 -0
- package/dist/commands/create/react.js +136 -0
- package/dist/commands/create/templates/basic.js +383 -0
- package/dist/commands/create/templates/react/backend.js +72 -0
- package/dist/commands/create/templates/react/config.js +166 -0
- package/dist/commands/create/templates/react/docs.js +78 -0
- package/dist/commands/create/templates/react/hooks.js +469 -0
- package/dist/commands/create/templates/react/index.js +41 -0
- package/dist/commands/create/templates/react/types.js +1228 -0
- package/dist/commands/create/templates/react/ui.js +528 -0
- package/dist/commands/create/templates/react.js +1888 -0
- package/dist/commands/dev.js +141 -0
- package/dist/commands/pack.js +160 -0
- package/dist/commands/resume.js +97 -0
- package/dist/commands/test-ui.js +50 -0
- package/dist/index.js +71 -0
- package/dist/services/ai/PLUGIN_API.md +1102 -0
- package/dist/services/ai/PLUGIN_DEVELOP_PROMPT.md +1164 -0
- package/dist/services/ai/context-manager.js +639 -0
- package/dist/services/ai/index.js +88 -0
- package/dist/services/ai/knowledge.js +52 -0
- package/dist/services/ai/prompts.js +114 -0
- package/dist/services/ai/providers/base.js +38 -0
- package/dist/services/ai/providers/claude.js +284 -0
- package/dist/services/ai/providers/deepseek.js +28 -0
- package/dist/services/ai/providers/gemini.js +191 -0
- package/dist/services/ai/providers/glm.js +31 -0
- package/dist/services/ai/providers/minimax.js +27 -0
- package/dist/services/ai/providers/openai.js +177 -0
- package/dist/services/ai/tools.js +204 -0
- package/dist/services/ai-generator.js +968 -0
- package/dist/services/config-manager.js +117 -0
- package/dist/services/dependency-manager.js +236 -0
- package/dist/services/file-writer.js +66 -0
- package/dist/services/plan-adapter.js +244 -0
- package/dist/services/plan-command-handler.js +172 -0
- package/dist/services/plan-manager.js +502 -0
- package/dist/services/session-manager.js +113 -0
- package/dist/services/task-analyzer.js +136 -0
- package/dist/services/tui/index.js +57 -0
- package/dist/services/tui/store.js +123 -0
- package/dist/types/ai.js +172 -0
- package/dist/types/plan.js +2 -0
- package/dist/ui/Terminal.js +56 -0
- package/dist/ui/components/InputArea.js +176 -0
- package/dist/ui/components/LogArea.js +19 -0
- package/dist/ui/components/PlanPanel.js +69 -0
- package/dist/ui/components/SelectArea.js +13 -0
- package/package.json +45 -0
|
@@ -0,0 +1,1164 @@
|
|
|
1
|
+
# Mulby Plugin Development Guide
|
|
2
|
+
|
|
3
|
+
> **Architecture**: Mulby is built on the **Electron** framework. Plugins run in a multi-process environment.
|
|
4
|
+
> **Contexts**: `UI` = **Renderer Process** (`window.mulby.{module}`), `Main` = **Main Process** (`context.api.{module}`). Most APIs are available in both contexts (marked as **R/B**).
|
|
5
|
+
|
|
6
|
+
## 1. Project Structure
|
|
7
|
+
|
|
8
|
+
```text
|
|
9
|
+
my-plugin/
|
|
10
|
+
├── manifest.json # Plugin Configuration (Manifest V2)
|
|
11
|
+
├── package.json # Dependencies (React, Vite, etc.)
|
|
12
|
+
├── tsconfig.json # TypeScript Configuration
|
|
13
|
+
├── vite.config.ts # Vite Configuration
|
|
14
|
+
├── preload.cjs # Node.js Bridge (Optional, MUST be .cjs)
|
|
15
|
+
├── src/
|
|
16
|
+
│ ├── main.ts # Backend Logic (Node.js context)
|
|
17
|
+
│ └── ui/ # Frontend Logic (React context)
|
|
18
|
+
│ ├── main.tsx # Entry Point
|
|
19
|
+
│ ├── App.tsx # Main Component
|
|
20
|
+
│ ├── styles.css # Styles
|
|
21
|
+
│ ├── hooks/ # Custom Hooks
|
|
22
|
+
│ │ └── useMulby.ts
|
|
23
|
+
│ └── vite-env.d.ts
|
|
24
|
+
└── assets/ # Icons, etc.
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 1.1 Entry Points & Patterns
|
|
28
|
+
|
|
29
|
+
**Backend (`src/main.ts`)**:
|
|
30
|
+
```typescript
|
|
31
|
+
interface PluginContext {
|
|
32
|
+
api: { clipboard: any; notification: any; /* ... */ }
|
|
33
|
+
input?: string; // Initial input (if triggered by text)
|
|
34
|
+
featureCode?: string; // Feature code structure
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Lifecycle Hooks within export
|
|
38
|
+
export function onLoad() { /* Plugin Loaded */ }
|
|
39
|
+
export function onUnload() { /* Plugin Unloaded */ }
|
|
40
|
+
export async function run(context: PluginContext) {
|
|
41
|
+
const { notification } = context.api;
|
|
42
|
+
notification.show('Plugin Started');
|
|
43
|
+
}
|
|
44
|
+
export default { onLoad, onUnload, run };
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Frontend (`src/ui/App.tsx`)**:
|
|
48
|
+
```typescript
|
|
49
|
+
import { useMulby } from './hooks/useMulby';
|
|
50
|
+
|
|
51
|
+
export default function App() {
|
|
52
|
+
const { clipboard, notification } = useMulby(); // Use provided hook
|
|
53
|
+
// ...
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 2. Manifest Configuration (`manifest.json`)
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"name": "my-plugin", // Unique ID (required)
|
|
62
|
+
"displayName": "My Plugin", // UI Name (required)
|
|
63
|
+
"version": "1.0.0",
|
|
64
|
+
"author": "Your Name",
|
|
65
|
+
"type":"utility/productivity/developer/system/media/network/ai/entertainment/other",
|
|
66
|
+
"homepage": "https://github.com/...",
|
|
67
|
+
"description": "Plugin description",
|
|
68
|
+
"main": "dist/main.js", // Backend Entry (required)
|
|
69
|
+
"ui": "ui/index.html", // UI Entry (required for UI plugins)
|
|
70
|
+
"preload": "preload.cjs", // Preload path (optional)
|
|
71
|
+
"icon": "icon.png", // Logo path
|
|
72
|
+
"pluginSetting": {
|
|
73
|
+
"single": true, // Run as singleton (no multi-window)
|
|
74
|
+
"height": 400 // Initial height
|
|
75
|
+
},
|
|
76
|
+
"window": { // Detached window config
|
|
77
|
+
"width": 800,
|
|
78
|
+
"height": 600,
|
|
79
|
+
"minWidth": 400,
|
|
80
|
+
"minHeight": 300
|
|
81
|
+
},
|
|
82
|
+
"features": [ // Feature list
|
|
83
|
+
{
|
|
84
|
+
"code": "format",
|
|
85
|
+
"explain": "Format JSON",
|
|
86
|
+
"cmds": [
|
|
87
|
+
// Keyword Trigger
|
|
88
|
+
{ "type": "keyword", "value": "json" },
|
|
89
|
+
// Regex Trigger
|
|
90
|
+
{ "type": "regex", "match": "^\\{.*\\}$", "minLength": 2 },
|
|
91
|
+
// File Trigger
|
|
92
|
+
{ "type": "files", "exts": [".json"], "fileType": "file" },
|
|
93
|
+
// Image Trigger
|
|
94
|
+
{ "type": "img", "exts": [".png", ".jpg"] },
|
|
95
|
+
// Text Selection Trigger
|
|
96
|
+
{ "type": "over", "label": "Format Selection", "minLength": 1 }
|
|
97
|
+
],
|
|
98
|
+
"mainPush": true, // Push content to search bar
|
|
99
|
+
"mainHide": true // Hide main window on trigger
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Plugin & Window Settings
|
|
106
|
+
| Field | Type | Default | Description |
|
|
107
|
+
|---|---|---|---|
|
|
108
|
+
| `pluginSetting.single` | bool | `true` | Prevent multiple instances |
|
|
109
|
+
| `pluginSetting.height` | number | - | Initial height |
|
|
110
|
+
| `window.width` / `height` | number | 500/400 | Default size (detached) |
|
|
111
|
+
| `window.minWidth` / `Height` | number | 300/200 | Minimum size |
|
|
112
|
+
| `window.maxWidth` / `Height` | number | - | Maximum size |
|
|
113
|
+
|
|
114
|
+
### Icon Configuration
|
|
115
|
+
- **Formats**: Path (`"icon.png"`), URL (`"https://..."`), Emoji (`"🚀"`), or SVG Code (`"<svg>..."`).
|
|
116
|
+
- **Object Notation**: `{ "type": "file", "value": "path/to/icon.png" }`.
|
|
117
|
+
|
|
118
|
+
### Feature Configuration (`features`)
|
|
119
|
+
| Field | Type | Description |
|
|
120
|
+
|---|---|---|
|
|
121
|
+
| `code` | string | Unique feature identifier (required) |
|
|
122
|
+
| `explain` | string | Human readable description (required) |
|
|
123
|
+
| `cmds` | array | List of triggers (keyword, regex, files, etc.) |
|
|
124
|
+
| `mode` | string | `ui` (default), `silent` (bg only), `detached` (new window) |
|
|
125
|
+
| `route` | string | Frontend route to navigate to (e.g. `/settings`) |
|
|
126
|
+
| `icon` | string/obj | Feature-specific icon |
|
|
127
|
+
| `mainPush` | boolean | Push input text to search bar |
|
|
128
|
+
| `mainHide` | boolean | Hide main window when triggered |
|
|
129
|
+
|
|
130
|
+
### Command Triggers (`cmds`)
|
|
131
|
+
| Type | Fields | Description |
|
|
132
|
+
|---|---|---|
|
|
133
|
+
| `keyword` | `value` | Exact keyword match |
|
|
134
|
+
| `regex` | `match`, `label`, `explain` | Regex match against input |
|
|
135
|
+
| `files` | `exts`, `fileType`, `match` | File/Dir drop (`match`=name regex) |
|
|
136
|
+
| `img` | `exts` | Image drag & drop |
|
|
137
|
+
| `over` | `label`, `exclude`, `minLength` | Text selection from other apps |
|
|
138
|
+
|
|
139
|
+
## 3. Preload Script (`preload.cjs`)
|
|
140
|
+
|
|
141
|
+
> **Rules**:
|
|
142
|
+
> 1. **Extension**: MUST be `.cjs` (project is ESM).
|
|
143
|
+
> 2. **Module System**: MUST use CommonJS (`require`).
|
|
144
|
+
> 3. **purpose**: Access Node.js APIs (`fs`, `crypto`, `child_process`) or Electron APIs (`clipboard`, `shell`).
|
|
145
|
+
> 4. **Constraint**: Do NOT put UI logic (DOM, Canvas) here.
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
const fs = require('fs');
|
|
149
|
+
// const { PDFDocument } = require('pdf-lib'); // npm packages allowed
|
|
150
|
+
|
|
151
|
+
window.myPluginApi = {
|
|
152
|
+
readFile: (path) => fs.readFileSync(path, 'utf8'),
|
|
153
|
+
// Expose to frontend as: window.myPluginApi.readFile(path)
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## 4. API Reference (TypeScript Definition)
|
|
158
|
+
|
|
159
|
+
> **R/B** = Available in both Renderer (`window.mulby`) and Backend (`context.api`).
|
|
160
|
+
> **R** = Renderer only. **B** = Backend only.
|
|
161
|
+
|
|
162
|
+
### Core Modules
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
// notification (R/B) - System Notifications
|
|
166
|
+
interface Notification {
|
|
167
|
+
show(message: string, type?: 'none'|'info'|'error'|'question'|'warning'): void;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// shell (R/B) - System Operations
|
|
171
|
+
interface Shell {
|
|
172
|
+
openPath(path: string): Promise<string>;
|
|
173
|
+
openExternal(url: string): Promise<void>;
|
|
174
|
+
showItemInFolder(path: string): Promise<void>;
|
|
175
|
+
openFolder(path: string): Promise<string>;
|
|
176
|
+
trashItem(path: string): Promise<void>;
|
|
177
|
+
beep(): void;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// filesystem (R/B) - Node.js-link FS Access
|
|
181
|
+
interface FileSystem {
|
|
182
|
+
readFile(path: string, encoding?: string): Promise<string | Buffer>;
|
|
183
|
+
writeFile(path: string, data: string | Buffer, encoding?: string): Promise<void>;
|
|
184
|
+
exists(path: string): Promise<boolean>;
|
|
185
|
+
unlink(path: string): Promise<void>;
|
|
186
|
+
readdir(path: string): Promise<string[]>;
|
|
187
|
+
mkdir(path: string): Promise<void>;
|
|
188
|
+
stat(path: string): Promise<{ size: number; isFile: boolean; isDirectory: boolean; adjusted: number }>;
|
|
189
|
+
copy(src: string, dest: string): Promise<void>;
|
|
190
|
+
move(src: string, dest: string): Promise<void>;
|
|
191
|
+
// Backend Only Path Utils
|
|
192
|
+
extname?(path: string): string;
|
|
193
|
+
join?(...paths: string[]): string;
|
|
194
|
+
dirname?(path: string): string;
|
|
195
|
+
basename?(path: string, ext?: string): string;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// http (R/B) - Network Requests
|
|
199
|
+
interface Http {
|
|
200
|
+
request(opts: { url: string; method?: string; headers?: any; body?: any; timeout?: number }): Promise<{ status: number; data: string; headers: any }>;
|
|
201
|
+
get(url: string, headers?: any): Promise<HttpResponse>;
|
|
202
|
+
post(url: string, body?: any, headers?: any): Promise<HttpResponse>;
|
|
203
|
+
put(url: string, body?: any): Promise<HttpResponse>;
|
|
204
|
+
delete(url: string, headers?: any): Promise<HttpResponse>;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ai (R/B) - AI (tools only in backend)
|
|
208
|
+
interface Ai {
|
|
209
|
+
call(option: AiOption, onChunk?: (chunk: AiMessage) => void): AiPromiseLike<AiMessage>;
|
|
210
|
+
allModels(): Promise<AiModel[]>;
|
|
211
|
+
abort(requestId: string): Promise<void>;
|
|
212
|
+
skills: {
|
|
213
|
+
// Available in both Renderer and Backend
|
|
214
|
+
listEnabled(): Promise<AiSkillRecord[]>;
|
|
215
|
+
// Backend name; Renderer uses preview(...)
|
|
216
|
+
previewForCall?(input: { option?: Partial<AiOption>; skillIds?: string[]; prompt?: string }): Promise<AiSkillPreview>;
|
|
217
|
+
// Renderer-only manager
|
|
218
|
+
list?(): Promise<AiSkillRecord[]>;
|
|
219
|
+
refresh?(): Promise<AiSkillRecord[]>;
|
|
220
|
+
get?(skillId: string): Promise<AiSkillRecord | null>;
|
|
221
|
+
listCreateModels?(): Promise<AiSkillCreateModelOption[]>;
|
|
222
|
+
createWithAi?(input: AiSkillCreateWithAiInput): Promise<AiSkillCreateWithAiResult>;
|
|
223
|
+
createWithAiStream?(
|
|
224
|
+
input: AiSkillCreateWithAiInput,
|
|
225
|
+
onChunk: (chunk: AiSkillCreateProgressChunk) => void
|
|
226
|
+
): AiPromiseLike<AiSkillCreateWithAiResult>;
|
|
227
|
+
create?(input: {
|
|
228
|
+
id?: string;
|
|
229
|
+
name: string;
|
|
230
|
+
description?: string;
|
|
231
|
+
promptTemplate?: string;
|
|
232
|
+
tags?: string[];
|
|
233
|
+
triggerPhrases?: string[];
|
|
234
|
+
mode?: 'manual' | 'auto' | 'both';
|
|
235
|
+
capabilities?: string[];
|
|
236
|
+
internalTools?: string[];
|
|
237
|
+
enabled?: boolean;
|
|
238
|
+
trustLevel?: AiSkillTrustLevel;
|
|
239
|
+
mcpPolicy?: AiSkillMcpPolicy;
|
|
240
|
+
}): Promise<AiSkillRecord>;
|
|
241
|
+
install?(input: {
|
|
242
|
+
source: 'local-dir' | 'zip';
|
|
243
|
+
ref: string;
|
|
244
|
+
trustLevel?: AiSkillTrustLevel;
|
|
245
|
+
enabled?: boolean;
|
|
246
|
+
}): Promise<AiSkillRecord[]>;
|
|
247
|
+
importFromJson?(input: {
|
|
248
|
+
json: string;
|
|
249
|
+
trustLevel?: AiSkillTrustLevel;
|
|
250
|
+
enabled?: boolean;
|
|
251
|
+
}): Promise<AiSkillRecord[]>;
|
|
252
|
+
update?(skillId: string, patch: Partial<AiSkillRecord>): Promise<AiSkillRecord>;
|
|
253
|
+
remove?(skillId: string): Promise<void>;
|
|
254
|
+
enable?(skillId: string): Promise<AiSkillRecord>;
|
|
255
|
+
disable?(skillId: string): Promise<AiSkillRecord>;
|
|
256
|
+
preview?(input: { option?: Partial<AiOption>; skillIds?: string[]; prompt?: string }): Promise<AiSkillPreview>;
|
|
257
|
+
resolve?(option: AiOption): Promise<AiSkillResolveResult>;
|
|
258
|
+
};
|
|
259
|
+
tokens: {
|
|
260
|
+
estimate(input: { model?: string; messages: AiMessage[]; outputText?: string }): Promise<{ inputTokens: number; outputTokens: number }>;
|
|
261
|
+
};
|
|
262
|
+
attachments: {
|
|
263
|
+
upload(input: { filePath?: string; buffer?: ArrayBuffer; mimeType: string; purpose?: string }): Promise<AiAttachmentRef>;
|
|
264
|
+
get(attachmentId: string): Promise<AiAttachmentRef | null>;
|
|
265
|
+
delete(attachmentId: string): Promise<void>;
|
|
266
|
+
uploadToProvider(input: { attachmentId: string; model?: string; providerId?: string; purpose?: string }): Promise<{ providerId: string; fileId: string; uri?: string }>;
|
|
267
|
+
};
|
|
268
|
+
images: {
|
|
269
|
+
generate(input: { model: string; prompt: string; size?: string; count?: number }): Promise<{ images: string[]; tokens: AiTokenBreakdown }>;
|
|
270
|
+
generateStream(
|
|
271
|
+
input: { model: string; prompt: string; size?: string; count?: number },
|
|
272
|
+
onChunk: (chunk: AiImageGenerateProgressChunk) => void
|
|
273
|
+
): AiPromiseLike<{ images: string[]; tokens: AiTokenBreakdown }>;
|
|
274
|
+
edit(input: { model: string; imageAttachmentId: string; prompt: string }): Promise<{ images: string[]; tokens: AiTokenBreakdown }>;
|
|
275
|
+
};
|
|
276
|
+
// Renderer-only helpers
|
|
277
|
+
models?: {
|
|
278
|
+
fetch(input: { providerId: string; baseURL?: string; apiKey?: string }): Promise<{ models: AiModel[]; message?: string }>;
|
|
279
|
+
};
|
|
280
|
+
testConnection?: (input?: { providerId?: string; model?: string; baseURL?: string; apiKey?: string }) => Promise<{ success: boolean; message?: string }>;
|
|
281
|
+
testConnectionStream?: (
|
|
282
|
+
input: { providerId?: string; model?: string; baseURL?: string; apiKey?: string },
|
|
283
|
+
onChunk: (chunk: { type: 'reasoning' | 'content'; text: string }) => void
|
|
284
|
+
) => AiPromiseLike<{ success: boolean; message?: string; reasoning?: string }>;
|
|
285
|
+
settings?: {
|
|
286
|
+
get(): Promise<AiSettings>;
|
|
287
|
+
update(next: Partial<AiSettings>): Promise<AiSettings>;
|
|
288
|
+
};
|
|
289
|
+
// Renderer-only MCP manager (window.mulby.ai.mcp)
|
|
290
|
+
// Backend context.api.ai currently does not expose mcp.*
|
|
291
|
+
mcp?: {
|
|
292
|
+
listServers(): Promise<AiMcpServer[]>;
|
|
293
|
+
getServer(serverId: string): Promise<AiMcpServer | null>;
|
|
294
|
+
upsertServer(server: AiMcpServer): Promise<AiMcpServer>;
|
|
295
|
+
removeServer(serverId: string): Promise<void>;
|
|
296
|
+
activateServer(serverId: string): Promise<AiMcpServer>;
|
|
297
|
+
deactivateServer(serverId: string): Promise<AiMcpServer>;
|
|
298
|
+
restartServer(serverId: string): Promise<AiMcpServer>;
|
|
299
|
+
checkServer(serverId: string): Promise<{ ok: boolean; message?: string }>;
|
|
300
|
+
listTools(serverId: string): Promise<AiMcpTool[]>;
|
|
301
|
+
abort(callId: string): Promise<boolean>;
|
|
302
|
+
getLogs(serverId: string): Promise<AiMcpServerLogEntry[]>;
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
type AiMessage = {
|
|
307
|
+
role: 'system' | 'user' | 'assistant';
|
|
308
|
+
content?: string | AiMessageContent[];
|
|
309
|
+
reasoning_content?: string;
|
|
310
|
+
chunkType?: 'meta' | 'text' | 'reasoning' | 'tool-call' | 'tool-result' | 'error' | 'end';
|
|
311
|
+
capability_debug?: {
|
|
312
|
+
requested: string[];
|
|
313
|
+
allowed: string[];
|
|
314
|
+
denied: string[];
|
|
315
|
+
reasons: string[];
|
|
316
|
+
selectedSkills?: { id: string; source: AiSkillSource; trustLevel: AiSkillTrustLevel }[];
|
|
317
|
+
};
|
|
318
|
+
policy_debug?: {
|
|
319
|
+
skills: {
|
|
320
|
+
requested?: AiSkillSelection;
|
|
321
|
+
selectedSkillIds: string[];
|
|
322
|
+
selectedSkillNames: string[];
|
|
323
|
+
reasons: string[];
|
|
324
|
+
};
|
|
325
|
+
mcp: { requested?: AiMcpSelection; resolved?: AiMcpSelection };
|
|
326
|
+
toolContext: { requested?: AiToolContext; resolved?: AiToolContext };
|
|
327
|
+
capabilities: { requested: string[]; resolved: string[] };
|
|
328
|
+
internalTools: { requested: string[]; resolved: string[] };
|
|
329
|
+
};
|
|
330
|
+
tool_call?: { id: string; name: string; args?: unknown };
|
|
331
|
+
tool_result?: { id: string; name: string; result?: unknown };
|
|
332
|
+
error?: { message: string; code?: string; category?: string; retryable?: boolean; statusCode?: number };
|
|
333
|
+
usage?: AiTokenBreakdown;
|
|
334
|
+
};
|
|
335
|
+
type AiMessageContent =
|
|
336
|
+
| { type: 'text'; text: string }
|
|
337
|
+
| { type: 'image'; attachmentId: string; mimeType?: string }
|
|
338
|
+
| { type: 'file'; attachmentId: string; mimeType?: string; filename?: string };
|
|
339
|
+
type AiOption = {
|
|
340
|
+
model?: string;
|
|
341
|
+
messages: AiMessage[];
|
|
342
|
+
tools?: AiTool[];
|
|
343
|
+
capabilities?: string[];
|
|
344
|
+
internalTools?: string[]; // deprecated: prefer capabilities
|
|
345
|
+
toolingPolicy?: {
|
|
346
|
+
enableInternalTools?: boolean;
|
|
347
|
+
capabilityAllowList?: string[];
|
|
348
|
+
capabilityDenyList?: string[];
|
|
349
|
+
};
|
|
350
|
+
mcp?: AiMcpSelection;
|
|
351
|
+
skills?: AiSkillSelection;
|
|
352
|
+
params?: AiModelParameters;
|
|
353
|
+
toolContext?: AiToolContext;
|
|
354
|
+
maxToolSteps?: number; // default 20, max 100
|
|
355
|
+
};
|
|
356
|
+
type AiTool = {
|
|
357
|
+
type: 'function';
|
|
358
|
+
function?: {
|
|
359
|
+
name: string;
|
|
360
|
+
description: string;
|
|
361
|
+
parameters: {
|
|
362
|
+
type: 'object';
|
|
363
|
+
properties: Record<string, unknown>;
|
|
364
|
+
required?: string[];
|
|
365
|
+
additionalProperties?: boolean;
|
|
366
|
+
};
|
|
367
|
+
required?: string[];
|
|
368
|
+
};
|
|
369
|
+
};
|
|
370
|
+
type AiModelParameters = {
|
|
371
|
+
contextWindow?: number;
|
|
372
|
+
temperatureEnabled?: boolean;
|
|
373
|
+
topPEnabled?: boolean;
|
|
374
|
+
maxOutputTokensEnabled?: boolean;
|
|
375
|
+
temperature?: number;
|
|
376
|
+
topP?: number;
|
|
377
|
+
topK?: number;
|
|
378
|
+
maxOutputTokens?: number;
|
|
379
|
+
presencePenalty?: number;
|
|
380
|
+
frequencyPenalty?: number;
|
|
381
|
+
stopSequences?: string[];
|
|
382
|
+
seed?: number;
|
|
383
|
+
};
|
|
384
|
+
type AiEndpointType =
|
|
385
|
+
| 'openai'
|
|
386
|
+
| 'openai-response'
|
|
387
|
+
| 'anthropic'
|
|
388
|
+
| 'gemini'
|
|
389
|
+
| 'image-generation'
|
|
390
|
+
| 'jina-rerank';
|
|
391
|
+
type AiModel = {
|
|
392
|
+
id: string;
|
|
393
|
+
label: string;
|
|
394
|
+
description: string;
|
|
395
|
+
icon?: string;
|
|
396
|
+
providerRef?: string;
|
|
397
|
+
providerLabel?: string;
|
|
398
|
+
endpointType?: AiEndpointType;
|
|
399
|
+
supportedEndpointTypes?: AiEndpointType[];
|
|
400
|
+
params?: AiModelParameters;
|
|
401
|
+
capabilities?: Array<{
|
|
402
|
+
type: 'text' | 'vision' | 'embedding' | 'reasoning' | 'function_calling' | 'web_search' | 'rerank';
|
|
403
|
+
isUserSelected?: boolean;
|
|
404
|
+
}>;
|
|
405
|
+
};
|
|
406
|
+
type AiSettings = {
|
|
407
|
+
providers: AiProviderConfig[];
|
|
408
|
+
models?: AiModel[];
|
|
409
|
+
defaultModel?: string;
|
|
410
|
+
defaultParams?: AiModelParameters;
|
|
411
|
+
mcp?: AiMcpSettings;
|
|
412
|
+
skills?: AiSkillSettings;
|
|
413
|
+
};
|
|
414
|
+
type AiProviderConfig = {
|
|
415
|
+
id: string;
|
|
416
|
+
type?: string;
|
|
417
|
+
label?: string;
|
|
418
|
+
enabled: boolean;
|
|
419
|
+
apiKey?: string;
|
|
420
|
+
baseURL?: string;
|
|
421
|
+
apiVersion?: string;
|
|
422
|
+
anthropicBaseURL?: string;
|
|
423
|
+
headers?: Record<string, string>;
|
|
424
|
+
defaultModel?: string;
|
|
425
|
+
defaultParams?: AiModelParameters;
|
|
426
|
+
};
|
|
427
|
+
type AiMcpSelection = { mode?: 'off' | 'manual' | 'auto'; serverIds?: string[]; allowedToolIds?: string[] };
|
|
428
|
+
type AiToolContext = {
|
|
429
|
+
pluginName?: string;
|
|
430
|
+
internalTag?: string;
|
|
431
|
+
mcpScope?: { allowedServerIds?: string[]; allowedToolIds?: string[] };
|
|
432
|
+
};
|
|
433
|
+
type AiMcpServer = {
|
|
434
|
+
id: string;
|
|
435
|
+
name: string;
|
|
436
|
+
type: 'stdio' | 'sse' | 'streamableHttp';
|
|
437
|
+
isActive: boolean;
|
|
438
|
+
description?: string;
|
|
439
|
+
baseUrl?: string;
|
|
440
|
+
command?: string;
|
|
441
|
+
args?: string[];
|
|
442
|
+
env?: Record<string, string>;
|
|
443
|
+
headers?: Record<string, string>;
|
|
444
|
+
timeoutSec?: number;
|
|
445
|
+
longRunning?: boolean;
|
|
446
|
+
disabledTools?: string[];
|
|
447
|
+
disabledAutoApproveTools?: string[];
|
|
448
|
+
installSource?: 'manual' | 'protocol' | 'builtin';
|
|
449
|
+
isTrusted?: boolean;
|
|
450
|
+
trustedAt?: number;
|
|
451
|
+
installedAt?: number;
|
|
452
|
+
};
|
|
453
|
+
type AiMcpSettings = {
|
|
454
|
+
servers: AiMcpServer[];
|
|
455
|
+
defaults?: { timeoutMs?: number; longRunningMaxMs?: number; approvalMode?: 'always' | 'auto-approved-only' | 'never' };
|
|
456
|
+
};
|
|
457
|
+
type AiMcpTool = {
|
|
458
|
+
id: string;
|
|
459
|
+
name: string;
|
|
460
|
+
description?: string;
|
|
461
|
+
serverId: string;
|
|
462
|
+
serverName: string;
|
|
463
|
+
inputSchema?: unknown;
|
|
464
|
+
outputSchema?: unknown;
|
|
465
|
+
};
|
|
466
|
+
type AiMcpServerLogEntry = {
|
|
467
|
+
timestamp: number;
|
|
468
|
+
level: 'debug' | 'info' | 'warn' | 'error';
|
|
469
|
+
message: string;
|
|
470
|
+
source?: string;
|
|
471
|
+
data?: unknown;
|
|
472
|
+
};
|
|
473
|
+
type AiSkillSource = 'manual' | 'local-dir' | 'zip' | 'json' | 'builtin' | 'system';
|
|
474
|
+
type AiSkillTrustLevel = 'untrusted' | 'reviewed' | 'trusted';
|
|
475
|
+
type AiSkillMcpPolicy = {
|
|
476
|
+
serverIds?: string[];
|
|
477
|
+
allowedToolIds?: string[];
|
|
478
|
+
blockedToolIds?: string[];
|
|
479
|
+
};
|
|
480
|
+
type AiSkillSelection = {
|
|
481
|
+
mode?: 'off' | 'manual' | 'auto';
|
|
482
|
+
skillIds?: string[];
|
|
483
|
+
variables?: Record<string, string>;
|
|
484
|
+
};
|
|
485
|
+
type AiSkillRecord = {
|
|
486
|
+
id: string;
|
|
487
|
+
source: AiSkillSource;
|
|
488
|
+
origin?: 'system' | 'app';
|
|
489
|
+
readonly?: boolean;
|
|
490
|
+
sourceRef?: string;
|
|
491
|
+
installPath?: string;
|
|
492
|
+
skillMdPath?: string;
|
|
493
|
+
contentHash: string;
|
|
494
|
+
enabled: boolean;
|
|
495
|
+
trustLevel: AiSkillTrustLevel;
|
|
496
|
+
installedAt: number;
|
|
497
|
+
updatedAt: number;
|
|
498
|
+
descriptor: {
|
|
499
|
+
id: string;
|
|
500
|
+
name: string;
|
|
501
|
+
description?: string;
|
|
502
|
+
version?: string;
|
|
503
|
+
author?: string;
|
|
504
|
+
tags?: string[];
|
|
505
|
+
triggerPhrases?: string[];
|
|
506
|
+
mode?: 'manual' | 'auto' | 'both';
|
|
507
|
+
promptTemplate?: string;
|
|
508
|
+
mcpPolicy?: AiSkillMcpPolicy;
|
|
509
|
+
capabilities?: string[];
|
|
510
|
+
internalTools?: string[];
|
|
511
|
+
};
|
|
512
|
+
};
|
|
513
|
+
type AiSkillSettings = {
|
|
514
|
+
enabled: boolean;
|
|
515
|
+
activeSkillIds: string[];
|
|
516
|
+
autoSelect?: { enabled?: boolean; maxSkillsPerCall?: number; minScore?: number };
|
|
517
|
+
records: AiSkillRecord[];
|
|
518
|
+
};
|
|
519
|
+
type AiSkillPreview = {
|
|
520
|
+
selected: AiSkillRecord[];
|
|
521
|
+
systemPrompt: string;
|
|
522
|
+
mcpImpact: { serverIds?: string[]; allowedToolIds?: string[]; blockedToolIds?: string[] };
|
|
523
|
+
reasons: string[];
|
|
524
|
+
};
|
|
525
|
+
type AiSkillResolveResult = {
|
|
526
|
+
selectedSkillIds: string[];
|
|
527
|
+
selectedSkillNames: string[];
|
|
528
|
+
selectedSkills?: Array<{ id: string; source: AiSkillSource; trustLevel: AiSkillTrustLevel }>;
|
|
529
|
+
systemPrompts: string[];
|
|
530
|
+
mergedMcp?: AiMcpSelection;
|
|
531
|
+
toolContextPatch?: AiToolContext['mcpScope'];
|
|
532
|
+
capabilities?: string[];
|
|
533
|
+
internalTools?: string[];
|
|
534
|
+
reasons?: string[];
|
|
535
|
+
};
|
|
536
|
+
type AiSkillCreateModelOption = {
|
|
537
|
+
id: string;
|
|
538
|
+
label: string;
|
|
539
|
+
providerRef?: string;
|
|
540
|
+
providerLabel?: string;
|
|
541
|
+
};
|
|
542
|
+
type AiSkillCreateWithAiInput = {
|
|
543
|
+
requirements: string;
|
|
544
|
+
model: string;
|
|
545
|
+
previousRawText?: string;
|
|
546
|
+
replaceSkillId?: string;
|
|
547
|
+
enabled?: boolean;
|
|
548
|
+
trustLevel?: AiSkillTrustLevel;
|
|
549
|
+
modePreference?: 'manual' | 'auto' | 'both';
|
|
550
|
+
};
|
|
551
|
+
type AiSkillCreateWithAiResult = {
|
|
552
|
+
record: AiSkillRecord;
|
|
553
|
+
generation: { model: string; rawText: string; notes?: string[] };
|
|
554
|
+
};
|
|
555
|
+
type AiSkillCreateProgressChunk = {
|
|
556
|
+
type: 'status' | 'content' | 'reasoning';
|
|
557
|
+
text: string;
|
|
558
|
+
stage?: 'generating' | 'parsing' | 'validating' | 'writing' | 'completed';
|
|
559
|
+
stageStatus?: 'start' | 'done' | 'error';
|
|
560
|
+
};
|
|
561
|
+
type AiAttachmentRef = { attachmentId: string; mimeType: string; size: number; filename?: string; expiresAt?: string; purpose?: string };
|
|
562
|
+
type AiTokenBreakdown = { inputTokens: number; outputTokens: number };
|
|
563
|
+
type AiImageGenerateProgressChunk = {
|
|
564
|
+
type: 'status' | 'preview';
|
|
565
|
+
stage?: 'start' | 'partial' | 'finalizing' | 'completed' | 'fallback';
|
|
566
|
+
message?: string;
|
|
567
|
+
image?: string;
|
|
568
|
+
index?: number;
|
|
569
|
+
received?: number;
|
|
570
|
+
total?: number;
|
|
571
|
+
};
|
|
572
|
+
type AiPromiseLike<T> = Promise<T> & { abort: () => void };
|
|
573
|
+
|
|
574
|
+
// clipboard (R/B)
|
|
575
|
+
interface Clipboard {
|
|
576
|
+
readText(): Promise<string>;
|
|
577
|
+
writeText(text: string): Promise<void>;
|
|
578
|
+
readImage(): Promise<Buffer | null>;
|
|
579
|
+
writeImage(image: string | Buffer): Promise<void>;
|
|
580
|
+
readFiles(): Promise<{ path: string; name: string; size: number; isDirectory: boolean }[]>;
|
|
581
|
+
writeFiles(paths: string[]): Promise<void>; // R only
|
|
582
|
+
getFormat(): Promise<'text'|'image'|'files'|'html'|'empty'>;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// dialog (R/B) - Native Dialogs
|
|
586
|
+
interface Dialog {
|
|
587
|
+
showOpenDialog(opts?: { title?: string; defaultPath?: string; buttonLabel?: string; filters?: any[]; properties?: string[] }): Promise<string[]>;
|
|
588
|
+
showSaveDialog(opts?: { title?: string; defaultPath?: string; filters?: any[] }): Promise<string | null>;
|
|
589
|
+
showMessageBox(opts: { type?: string; title?: string; message: string; buttons?: string[] }): Promise<{ response: number }>;
|
|
590
|
+
showErrorBox(title: string, content: string): void;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// system (R/B) - System Info
|
|
594
|
+
interface System {
|
|
595
|
+
getSystemInfo(): Promise<{ platform: string; arch: string; hostname: string; cpus: number; totalmem: number }>;
|
|
596
|
+
getAppInfo(): Promise<{ name: string; version: string; userDataPath: string }>;
|
|
597
|
+
getPath(name: 'home'|'appData'|'temp'|'desktop'|'downloads'|'documents'|'pictures'|'music'|'videos'): Promise<string>;
|
|
598
|
+
getEnv(name: string): Promise<string>;
|
|
599
|
+
getIdleTime(): Promise<number>;
|
|
600
|
+
getFileIcon(path: string): Promise<string>; // Base64
|
|
601
|
+
getNativeId(): Promise<string>;
|
|
602
|
+
isDev(): Promise<boolean>;
|
|
603
|
+
isMacOS(): Promise<boolean>;
|
|
604
|
+
isWindows(): Promise<boolean>;
|
|
605
|
+
isLinux(): Promise<boolean>;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// storage (R/B) - Persistent KV Store
|
|
609
|
+
interface Storage {
|
|
610
|
+
get(key: string, namespace?: string): Promise<any>;
|
|
611
|
+
set(key: string, value: any, namespace?: string): Promise<boolean>;
|
|
612
|
+
remove(key: string, namespace?: string): Promise<boolean>;
|
|
613
|
+
clear(): void; // B only
|
|
614
|
+
keys(): string[]; // B only
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// messaging (R/B) - Plugin-to-Plugin Communication
|
|
618
|
+
interface Messaging {
|
|
619
|
+
send(targetPluginId: string, type: string, payload: unknown): Promise<void>;
|
|
620
|
+
broadcast(type: string, payload: unknown): Promise<void>;
|
|
621
|
+
on(handler: (message: { id: string; from: string; to?: string; type: string; payload: unknown; timestamp: number }) => void | Promise<void>): void;
|
|
622
|
+
off(handler?: (message: any) => void): void;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// scheduler (B) - Task Scheduler
|
|
626
|
+
interface Scheduler {
|
|
627
|
+
schedule(task: TaskInput): Promise<Task>;
|
|
628
|
+
cancel(taskId: string): Promise<void>;
|
|
629
|
+
pause(taskId: string): Promise<void>;
|
|
630
|
+
resume(taskId: string): Promise<void>;
|
|
631
|
+
get(taskId: string): Promise<Task | null>;
|
|
632
|
+
list(filter?: { status?: string; type?: string; limit?: number }): Promise<Task[]>;
|
|
633
|
+
getExecutions(taskId: string, limit?: number): Promise<TaskExecution[]>;
|
|
634
|
+
validateCron(expression: string): boolean;
|
|
635
|
+
getNextCronTime(expression: string, after?: Date): Date;
|
|
636
|
+
describeCron(expression: string): string;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
interface TaskInput {
|
|
640
|
+
name: string;
|
|
641
|
+
type: 'once' | 'repeat' | 'delay';
|
|
642
|
+
callback: string;
|
|
643
|
+
description?: string;
|
|
644
|
+
payload?: any;
|
|
645
|
+
time?: number; // For 'once' type
|
|
646
|
+
cron?: string; // For 'repeat' type (6-field: sec min hour day month weekday)
|
|
647
|
+
delay?: number; // For 'delay' type
|
|
648
|
+
timezone?: string;
|
|
649
|
+
maxRetries?: number;
|
|
650
|
+
retryDelay?: number;
|
|
651
|
+
timeout?: number;
|
|
652
|
+
endTime?: number; // For 'repeat' type
|
|
653
|
+
maxExecutions?: number; // For 'repeat' type
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
interface Task extends TaskInput {
|
|
657
|
+
id: string;
|
|
658
|
+
pluginId: string;
|
|
659
|
+
status: 'pending' | 'running' | 'paused' | 'completed' | 'failed' | 'cancelled';
|
|
660
|
+
nextRunTime?: number;
|
|
661
|
+
lastRunTime?: number;
|
|
662
|
+
executionCount: number;
|
|
663
|
+
failureCount: number;
|
|
664
|
+
lastError?: string;
|
|
665
|
+
createdAt: number;
|
|
666
|
+
updatedAt: number;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
interface TaskExecution {
|
|
670
|
+
id: string;
|
|
671
|
+
taskId: string;
|
|
672
|
+
startTime: number;
|
|
673
|
+
endTime?: number;
|
|
674
|
+
status: 'success' | 'failed' | 'timeout';
|
|
675
|
+
result?: any;
|
|
676
|
+
error?: string;
|
|
677
|
+
duration?: number;
|
|
678
|
+
}
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
### Advanced System Modules
|
|
682
|
+
|
|
683
|
+
```typescript
|
|
684
|
+
// network (R/B)
|
|
685
|
+
interface Network {
|
|
686
|
+
isOnline(): Promise<boolean>;
|
|
687
|
+
onOnline(cb: () => void): void; // R only
|
|
688
|
+
onOffline(cb: () => void): void; // R only
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// power (R/B)
|
|
692
|
+
interface Power {
|
|
693
|
+
getSystemIdleTime(): Promise<number>;
|
|
694
|
+
getSystemIdleState(threshold: number): Promise<'active'|'idle'|'locked'|'unknown'>;
|
|
695
|
+
isOnBatteryPower(): Promise<boolean>;
|
|
696
|
+
onSuspend(cb: () => void): void; // R only
|
|
697
|
+
onResume(cb: () => void): void; // R only
|
|
698
|
+
onAC(cb: () => void): void; // R only
|
|
699
|
+
onBattery(cb: () => void): void; // R only
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// security (R/B) - Encrypted Storage
|
|
703
|
+
interface Security {
|
|
704
|
+
isEncryptionAvailable(): Promise<boolean>;
|
|
705
|
+
encryptString(plain: string): Promise<Buffer>;
|
|
706
|
+
decryptString(encrypted: Buffer): Promise<string>;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// shortcut (R/B) - Global Hotkeys
|
|
710
|
+
interface Shortcut {
|
|
711
|
+
register(accelerator: string): Promise<boolean>;
|
|
712
|
+
unregister(accelerator: string): Promise<void>;
|
|
713
|
+
unregisterAll(): Promise<void>;
|
|
714
|
+
isRegistered(accelerator: string): Promise<boolean>;
|
|
715
|
+
onTriggered(cb: (acc: string) => void): void;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// geolocation (R)
|
|
719
|
+
interface Geolocation {
|
|
720
|
+
getAccessStatus(): Promise<string>;
|
|
721
|
+
requestAccess(): Promise<string>; // macOS only
|
|
722
|
+
canGetPosition(): Promise<boolean>;
|
|
723
|
+
getCurrentPosition(): Promise<{ latitude: number; longitude: number }>;
|
|
724
|
+
openSettings(): Promise<void>;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// host (R) - Call Backend from UI
|
|
728
|
+
interface Host {
|
|
729
|
+
// Call main process API (e.g., clipboard.readText)
|
|
730
|
+
invoke(pluginId: string, method: string, ...args: any[]): Promise<any>;
|
|
731
|
+
// Call plugin custom methods (exported in main.ts)
|
|
732
|
+
call(pluginId: string, method: string, ...args: any[]): Promise<{ data: any }>;
|
|
733
|
+
status(pluginId: string): Promise<{ ready: boolean; active: boolean }>;
|
|
734
|
+
restart(pluginId: string): Promise<boolean>;
|
|
735
|
+
}
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
### UI & Interaction Modules
|
|
739
|
+
|
|
740
|
+
```typescript
|
|
741
|
+
// window (R) - Window Control
|
|
742
|
+
interface Window {
|
|
743
|
+
hide(): void;
|
|
744
|
+
show(): void;
|
|
745
|
+
setSize(w: number, h: number): void;
|
|
746
|
+
setExpendHeight(h: number): void;
|
|
747
|
+
center(): void;
|
|
748
|
+
setAlwaysOnTop(flag: boolean): void;
|
|
749
|
+
detach(): void;
|
|
750
|
+
close(): void;
|
|
751
|
+
create(url: string, opts?: any): Promise<ChildWindowHandle>;
|
|
752
|
+
startDrag(path: string): void;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// subInput (R) - Secondary Input Bar
|
|
756
|
+
interface SubInput {
|
|
757
|
+
set(placeholder?: string, isFocus?: boolean): void;
|
|
758
|
+
setValue(text: string): void;
|
|
759
|
+
focus(): void;
|
|
760
|
+
blur(): void;
|
|
761
|
+
select(): void;
|
|
762
|
+
remove(): void;
|
|
763
|
+
onChange(cb: (e: { text: string }) => void): void;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// plugin (R) - Plugin Management
|
|
767
|
+
interface Plugin {
|
|
768
|
+
getAll(): Promise<PluginInfo[]>;
|
|
769
|
+
search(query: string): Promise<PluginSearchResult[]>;
|
|
770
|
+
run(id: string, code: string, input?: any): Promise<void>;
|
|
771
|
+
install(path: string): Promise<void>;
|
|
772
|
+
enable(name: string): Promise<{ success: boolean; error?: string }>;
|
|
773
|
+
disable(name: string): Promise<{ success: boolean; error?: string }>;
|
|
774
|
+
uninstall(id: string): Promise<void>;
|
|
775
|
+
outPlugin(kill?: boolean): Promise<void>;
|
|
776
|
+
redirect(label: string, payload?: any): Promise<boolean>;
|
|
777
|
+
getReadme(id: string): Promise<string>;
|
|
778
|
+
|
|
779
|
+
// Background & Process Management
|
|
780
|
+
listBackground(): Promise<any[]>;
|
|
781
|
+
startBackground(pluginId: string): Promise<{ success: boolean; error?: string }>;
|
|
782
|
+
stopBackground(pluginId: string): Promise<{ success: boolean }>;
|
|
783
|
+
getBackgroundInfo(pluginId: string): Promise<any>;
|
|
784
|
+
stopPlugin(pluginId: string): Promise<void>;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// theme (R)
|
|
788
|
+
interface Theme {
|
|
789
|
+
get(): Promise<{ mode: string; actual: string }>;
|
|
790
|
+
set(mode: 'light'|'dark'|'system'): Promise<void>;
|
|
791
|
+
onThemeChange(cb: (mode: string) => void): void;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// menu (R) - Context Menu
|
|
795
|
+
interface Menu {
|
|
796
|
+
showContextMenu(items: { label: string; id?: string; type?: string; submenu?: any[] }[]): Promise<string | null>;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// tray (R/B)
|
|
800
|
+
interface Tray {
|
|
801
|
+
create(opts: { icon: string; tooltip?: string; title?: string }): Promise<boolean>;
|
|
802
|
+
destroy(): Promise<void>;
|
|
803
|
+
setIcon(icon: string): Promise<void>;
|
|
804
|
+
setTooltip(tip: string): Promise<void>;
|
|
805
|
+
setTitle(title: string): Promise<void>;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// tts (R) - Text to Speech
|
|
809
|
+
interface Tts {
|
|
810
|
+
speak(text: string, opts?: { lang?: string; rate?: number }): Promise<void>;
|
|
811
|
+
stop(): void;
|
|
812
|
+
getVoices(): Promise<any[]>;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// features (B) - Dynamic Features
|
|
816
|
+
interface Features {
|
|
817
|
+
getFeatures(codes?: string[]): DynamicFeature[];
|
|
818
|
+
setFeature(feature: { code: string; cmds: any[]; mode?: string }): void;
|
|
819
|
+
removeFeature(code: string): boolean;
|
|
820
|
+
}
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
### Media, Screen & Automation
|
|
824
|
+
|
|
825
|
+
```typescript
|
|
826
|
+
// screen (R/B)
|
|
827
|
+
interface Screen {
|
|
828
|
+
getAllDisplays(): Promise<any[]>;
|
|
829
|
+
getPrimaryDisplay(): Promise<any>;
|
|
830
|
+
getCursorScreenPoint(): Promise<{ x: number; y: number }>;
|
|
831
|
+
capture(opts?: { sourceId?: string; format?: 'png'|'jpeg' }): Promise<Buffer>;
|
|
832
|
+
captureRegion(rect: { x: number; y: number; w: number; h: number }, opts?: any): Promise<Buffer>;
|
|
833
|
+
screenCapture(): Promise<string>; // R only, Interactive
|
|
834
|
+
colorPick(): Promise<{ hex: string }>; // R only
|
|
835
|
+
getSources(opts?: { types: string[] }): Promise<any[]>;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// input (R/B) - Simulate Input
|
|
839
|
+
interface Input {
|
|
840
|
+
hideMainWindowPasteText(text: string): Promise<boolean>;
|
|
841
|
+
hideMainWindowPasteImage(img: string|Buffer): Promise<boolean>;
|
|
842
|
+
hideMainWindowPasteFile(path: string|string[]): Promise<boolean>;
|
|
843
|
+
hideMainWindowTypeString(text: string): Promise<boolean>;
|
|
844
|
+
restoreWindows(): Promise<boolean>; // Restore hidden windows after input
|
|
845
|
+
simulateKeyboardTap(key: string, ...modifiers: string[]): Promise<boolean>;
|
|
846
|
+
simulateMouseMove(x: number, y: number): Promise<boolean>;
|
|
847
|
+
simulateMouseClick(x: number, y: number): Promise<boolean>;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// media (R/B) - Permission
|
|
851
|
+
interface MediaPerm {
|
|
852
|
+
getAccessStatus(type: 'microphone'|'camera'): Promise<string>;
|
|
853
|
+
askForAccess(type: 'microphone'|'camera'): Promise<boolean>;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// sharp (R) - Image Processing
|
|
857
|
+
interface Sharp { (input: string|Buffer): SharpInstance; }
|
|
858
|
+
|
|
859
|
+
// ffmpeg (R) - Video Processing
|
|
860
|
+
interface FFmpeg {
|
|
861
|
+
run(args: string[], onProgress?: (p: any) => void): { promise: Promise<void>; kill: () => void };
|
|
862
|
+
isAvailable(): Promise<boolean>;
|
|
863
|
+
download(cb?: (p: any) => void): Promise<{ success: boolean }>;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// inbrowser (R) - Browser Automation
|
|
867
|
+
interface InBrowser {
|
|
868
|
+
// Navigation & Window
|
|
869
|
+
goto(url: string, headers?: Record<string, string>, timeout?: number): this;
|
|
870
|
+
useragent(ua: string): this;
|
|
871
|
+
device(nameOrOption: string | { userAgent: string; size: { width: number; height: number } }): this;
|
|
872
|
+
viewport(width: number, height: number): this;
|
|
873
|
+
show(): this;
|
|
874
|
+
hide(): this;
|
|
875
|
+
devTools(mode?: 'right' | 'bottom' | 'undocked' | 'detach'): this;
|
|
876
|
+
|
|
877
|
+
// Interaction
|
|
878
|
+
click(selector: string | number, mouseButtonOrY?: string | number, mouseButton?: string): this;
|
|
879
|
+
mousedown(selector: string | number, mouseButtonOrY?: string | number, mouseButton?: string): this;
|
|
880
|
+
mouseup(selector: string | number, mouseButtonOrY?: string | number, mouseButton?: string): this;
|
|
881
|
+
dblclick(selector: string | number, mouseButtonOrY?: string | number, mouseButton?: string): this;
|
|
882
|
+
hover(selector: string | number, y?: number): this;
|
|
883
|
+
type(selector: string, text: string): this;
|
|
884
|
+
input(selectorOrText: string, text?: string): this;
|
|
885
|
+
press(key: string, modifiers?: string[]): this;
|
|
886
|
+
focus(selector: string): this;
|
|
887
|
+
scroll(selectorOrY: string | number, optionalOrY?: any): this;
|
|
888
|
+
paste(text: string): this;
|
|
889
|
+
file(selector: string, payload: string | string[] | Buffer): this;
|
|
890
|
+
drop(selectorOrX: string | number, optionalYOrPayload: any, payload?: any): this;
|
|
891
|
+
|
|
892
|
+
// Content & State
|
|
893
|
+
value(selector: string, val: string): this;
|
|
894
|
+
check(selector: string, checked: boolean): this;
|
|
895
|
+
css(cssText: string): this;
|
|
896
|
+
cookies(nameOrFilter?: string | any): this;
|
|
897
|
+
setCookies(nameOrCookies: string | any[], value?: string): this;
|
|
898
|
+
removeCookies(name: string): this;
|
|
899
|
+
clearCookies(url?: string): this;
|
|
900
|
+
|
|
901
|
+
// Flow Control
|
|
902
|
+
wait(msOrSelectorOrFunc: number | string | Function, ...params: any[]): this;
|
|
903
|
+
when(selectorOrFunc: string | Function, ...params: any[]): this;
|
|
904
|
+
end(): this;
|
|
905
|
+
|
|
906
|
+
// Output Data (In run() result)
|
|
907
|
+
evaluate<T>(func: string | Function, ...params: any[]): this;
|
|
908
|
+
screenshot(target?: string | object, savePath?: string): this;
|
|
909
|
+
pdf(options?: any, savePath?: string): this;
|
|
910
|
+
markdown(selector?: string): this;
|
|
911
|
+
download(urlOrFunc: string | Function, savePath?: string, ...params: any[]): this;
|
|
912
|
+
|
|
913
|
+
// Execution
|
|
914
|
+
run(): Promise<any[]>;
|
|
915
|
+
|
|
916
|
+
// Management
|
|
917
|
+
getIdleInBrowsers(): Promise<any[]>;
|
|
918
|
+
setInBrowserProxy(config: any): Promise<boolean>;
|
|
919
|
+
clearInBrowserCache(): Promise<boolean>;
|
|
920
|
+
}
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
## 5. Background Plugin Development
|
|
924
|
+
|
|
925
|
+
Enable plugins to run in the background (e.g., for cron jobs, monitoring).
|
|
926
|
+
|
|
927
|
+
### 5.1 Configuration (`manifest.json`)
|
|
928
|
+
|
|
929
|
+
Explicitly enable background mode:
|
|
930
|
+
|
|
931
|
+
```json
|
|
932
|
+
{
|
|
933
|
+
"pluginSetting": {
|
|
934
|
+
"background": true, // Enable background mode
|
|
935
|
+
"persistent": true, // Auto-restore on app restart
|
|
936
|
+
"maxRuntime": 0 // Max runtime in ms (0 = unlimited)
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
### 5.2 Lifecycle Hooks (`src/main.ts`)
|
|
942
|
+
|
|
943
|
+
Handle background transitions:
|
|
944
|
+
|
|
945
|
+
```typescript
|
|
946
|
+
export function onBackground() {
|
|
947
|
+
// Triggered when window closes (if background: true)
|
|
948
|
+
console.log('Moved to background');
|
|
949
|
+
// Start background tasks (e.g., cron jobs)
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
export function onForeground() {
|
|
953
|
+
// Triggered when window re-opens
|
|
954
|
+
console.log('Back to foreground');
|
|
955
|
+
// Optional: Update UI or optimize resources
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
// Standard hooks
|
|
959
|
+
export function onLoad() { /* Init */ }
|
|
960
|
+
export function onUnload() { /* Cleanup (Called on app exit or manual stop) */ }
|
|
961
|
+
```
|
|
962
|
+
|
|
963
|
+
### 5.3 Management API
|
|
964
|
+
|
|
965
|
+
- **Check Status**: `api.plugin.listBackground()`
|
|
966
|
+
- **Stop**: `api.plugin.stopBackground(pluginId)`
|
|
967
|
+
- **Start**: `api.plugin.startBackground(pluginId)`
|
|
968
|
+
|
|
969
|
+
### 5.4 UI Plugin Integration
|
|
970
|
+
|
|
971
|
+
For plugins with UI (`"ui": "..."` in manifest):
|
|
972
|
+
1. Background logic MUST go in **backend** (`src/main.ts`).
|
|
973
|
+
2. **Frontend** (`src/ui/...`) stops when window closes.
|
|
974
|
+
3. Use IPC (via `api.messaging` or `host` module) to communicate between active UI and background backend.
|
|
975
|
+
|
|
976
|
+
## 6. Task Scheduler (Backend Only)
|
|
977
|
+
|
|
978
|
+
Schedule tasks to run at specific times or intervals. Tasks persist across app restarts.
|
|
979
|
+
|
|
980
|
+
### 6.1 Creating Tasks
|
|
981
|
+
|
|
982
|
+
```typescript
|
|
983
|
+
// One-time: Execute at timestamp
|
|
984
|
+
await api.scheduler.schedule({
|
|
985
|
+
name: 'Meeting Reminder',
|
|
986
|
+
type: 'once',
|
|
987
|
+
time: Date.now() + 3600000, // 1 hour later
|
|
988
|
+
callback: 'onReminder',
|
|
989
|
+
payload: { message: 'Meeting starts soon' }
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
// Repeat: Execute via Cron (6-field: sec min hour day month weekday)
|
|
993
|
+
await api.scheduler.schedule({
|
|
994
|
+
name: 'Daily Backup',
|
|
995
|
+
type: 'repeat',
|
|
996
|
+
cron: '0 0 2 * * *', // Daily at 2 AM
|
|
997
|
+
callback: 'onBackup'
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
// Delay: Execute after milliseconds
|
|
1001
|
+
await api.scheduler.schedule({
|
|
1002
|
+
name: 'Delayed Task',
|
|
1003
|
+
type: 'delay',
|
|
1004
|
+
delay: 5000, // 5 seconds
|
|
1005
|
+
callback: 'onTask'
|
|
1006
|
+
});
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
### 6.2 Task Callbacks
|
|
1010
|
+
|
|
1011
|
+
Export callback functions in `src/main.ts`:
|
|
1012
|
+
|
|
1013
|
+
```typescript
|
|
1014
|
+
export async function onReminder({ api, payload, task }) {
|
|
1015
|
+
api.notification.show(payload.message);
|
|
1016
|
+
// Return value is logged in execution history
|
|
1017
|
+
return { success: true };
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
export async function onBackup({ api }) {
|
|
1021
|
+
try {
|
|
1022
|
+
await api.filesystem.copy('/source', '/backup');
|
|
1023
|
+
return { files: 100 };
|
|
1024
|
+
} catch (error) {
|
|
1025
|
+
throw error; // Triggers retry if maxRetries configured
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
```
|
|
1029
|
+
|
|
1030
|
+
### 6.3 Managing Tasks
|
|
1031
|
+
|
|
1032
|
+
```typescript
|
|
1033
|
+
// List tasks (current plugin only)
|
|
1034
|
+
const tasks = await api.scheduler.list();
|
|
1035
|
+
const pending = await api.scheduler.list({ status: 'pending' });
|
|
1036
|
+
|
|
1037
|
+
// Pagination
|
|
1038
|
+
const page1 = await api.scheduler.list({ limit: 20, offset: 0 });
|
|
1039
|
+
const totalCount = await api.scheduler.count({ status: 'pending' });
|
|
1040
|
+
|
|
1041
|
+
// Control tasks
|
|
1042
|
+
await api.scheduler.pause(taskId);
|
|
1043
|
+
await api.scheduler.resume(taskId);
|
|
1044
|
+
await api.scheduler.cancel(taskId);
|
|
1045
|
+
|
|
1046
|
+
// Batch operations
|
|
1047
|
+
await api.scheduler.deleteTasks([taskId1, taskId2, taskId3]);
|
|
1048
|
+
|
|
1049
|
+
// Cleanup old tasks (completed/failed/cancelled)
|
|
1050
|
+
await api.scheduler.cleanup(); // Default: 7 days ago
|
|
1051
|
+
await api.scheduler.cleanup(Date.now() - 30 * 24 * 60 * 60 * 1000); // 30 days
|
|
1052
|
+
|
|
1053
|
+
// Query
|
|
1054
|
+
const task = await api.scheduler.get(taskId);
|
|
1055
|
+
const history = await api.scheduler.getExecutions(taskId, 10);
|
|
1056
|
+
```
|
|
1057
|
+
|
|
1058
|
+
### 6.4 Advanced Options
|
|
1059
|
+
|
|
1060
|
+
```typescript
|
|
1061
|
+
await api.scheduler.schedule({
|
|
1062
|
+
name: 'Robust Task',
|
|
1063
|
+
type: 'once',
|
|
1064
|
+
time: Date.now() + 1000,
|
|
1065
|
+
callback: 'onTask',
|
|
1066
|
+
maxRetries: 3, // Retry on failure
|
|
1067
|
+
retryDelay: 30000, // Wait 30s between retries
|
|
1068
|
+
timeout: 60000, // Kill after 60s
|
|
1069
|
+
payload: { data: 'important' }
|
|
1070
|
+
});
|
|
1071
|
+
|
|
1072
|
+
// Repeat with limits
|
|
1073
|
+
await api.scheduler.schedule({
|
|
1074
|
+
name: 'Limited Repeat',
|
|
1075
|
+
type: 'repeat',
|
|
1076
|
+
cron: '0 0 * * * *',
|
|
1077
|
+
callback: 'onTask',
|
|
1078
|
+
endTime: Date.now() + 86400000, // Stop after 24h
|
|
1079
|
+
maxExecutions: 100 // Or after 100 runs
|
|
1080
|
+
});
|
|
1081
|
+
```
|
|
1082
|
+
|
|
1083
|
+
### 6.5 Cron Helpers
|
|
1084
|
+
|
|
1085
|
+
```typescript
|
|
1086
|
+
// Validate expression
|
|
1087
|
+
api.scheduler.validateCron('0 0 2 * * *'); // true
|
|
1088
|
+
|
|
1089
|
+
// Get next run time
|
|
1090
|
+
api.scheduler.getNextCronTime('0 0 2 * * *'); // Date object
|
|
1091
|
+
|
|
1092
|
+
// Human-readable description
|
|
1093
|
+
api.scheduler.describeCron('0 0 2 * * *'); // "每天凌晨2点"
|
|
1094
|
+
```
|
|
1095
|
+
|
|
1096
|
+
**Common Cron Examples**:
|
|
1097
|
+
- `'0 * * * * *'` - Every minute
|
|
1098
|
+
- `'0 0 * * * *'` - Every hour
|
|
1099
|
+
- `'0 0 2 * * *'` - Daily at 2 AM
|
|
1100
|
+
- `'0 0 9 * * 1-5'` - Weekdays at 9 AM
|
|
1101
|
+
- `'0 */30 * * * *'` - Every 30 minutes
|
|
1102
|
+
|
|
1103
|
+
## 7. Host API - UI 调用后端方法
|
|
1104
|
+
|
|
1105
|
+
Host API 允许插件 UI 调用后端(main.ts)中导出的自定义方法。
|
|
1106
|
+
|
|
1107
|
+
### 7.1 后端导出方法
|
|
1108
|
+
|
|
1109
|
+
支持三种导出方式(按优先级查找):
|
|
1110
|
+
|
|
1111
|
+
```typescript
|
|
1112
|
+
// 方式1:直接导出函数
|
|
1113
|
+
export async function quickAction(context: PluginContext, text: string) {
|
|
1114
|
+
context.api.notification.show(`处理: ${text}`)
|
|
1115
|
+
return { success: true }
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
// 方式2:host 对象(推荐)
|
|
1119
|
+
export const host = {
|
|
1120
|
+
async processData(context: PluginContext, data: any) {
|
|
1121
|
+
const { notification, storage } = context.api
|
|
1122
|
+
notification.show('处理中...')
|
|
1123
|
+
await storage.set('lastResult', data)
|
|
1124
|
+
return { processed: true, result: data }
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
// 方式3:api/methods 等对象
|
|
1129
|
+
export const api = {
|
|
1130
|
+
async customMethod(context: PluginContext, params: any) {
|
|
1131
|
+
return { success: true, received: params }
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
```
|
|
1135
|
+
|
|
1136
|
+
**注意**:所有方法的第一个参数必须是 `context`,包含 `context.api`。
|
|
1137
|
+
|
|
1138
|
+
### 7.2 UI 中调用
|
|
1139
|
+
|
|
1140
|
+
```typescript
|
|
1141
|
+
import { useMulby } from './hooks/useMulby'
|
|
1142
|
+
|
|
1143
|
+
export default function App() {
|
|
1144
|
+
const { host, notification } = useMulby('my-plugin')
|
|
1145
|
+
|
|
1146
|
+
const handleClick = async () => {
|
|
1147
|
+
try {
|
|
1148
|
+
// 调用后端方法
|
|
1149
|
+
const result = await host.call('processData', { value: 123 })
|
|
1150
|
+
console.log(result.data) // { processed: true, result: {...} }
|
|
1151
|
+
notification.show('成功')
|
|
1152
|
+
} catch (err) {
|
|
1153
|
+
notification.show(`错误: ${err.message}`, 'error')
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
return <button onClick={handleClick}>处理数据</button>
|
|
1158
|
+
}
|
|
1159
|
+
```
|
|
1160
|
+
|
|
1161
|
+
### 7.3 与 host.invoke 的区别
|
|
1162
|
+
|
|
1163
|
+
- **host.call(method, ...args)** - 调用插件自定义方法(main.ts 中导出的)
|
|
1164
|
+
- **host.invoke(method, ...args)** - 调用主进程 API(如 clipboard.readText)
|