aiquila-mcp 0.3.18 → 0.3.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tool-registry.js +2 -0
- package/dist/tools/apps/cowork.js +259 -0
- package/package.json +1 -1
package/dist/tool-registry.js
CHANGED
|
@@ -17,6 +17,7 @@ import { deckTools } from './tools/apps/deck.js';
|
|
|
17
17
|
import { notesTools } from './tools/apps/notes.js';
|
|
18
18
|
import { newsTools } from './tools/apps/news.js';
|
|
19
19
|
import { aiquilaTools } from './tools/apps/aiquila.js';
|
|
20
|
+
import { coworkTools } from './tools/apps/cowork.js';
|
|
20
21
|
import { usersTools } from './tools/apps/users.js';
|
|
21
22
|
import { groupsTools } from './tools/apps/groups.js';
|
|
22
23
|
import { circlesTools } from './tools/apps/circles.js';
|
|
@@ -63,6 +64,7 @@ export const TOOL_REGISTRY = [
|
|
|
63
64
|
{ category: 'security', appIds: ['aiquila'], tools: securityTools },
|
|
64
65
|
{ category: 'occ', appIds: ['aiquila'], tools: occTools },
|
|
65
66
|
{ category: 'projects', appIds: ['aiquila'], tools: projectsTools },
|
|
67
|
+
{ category: 'cowork', appIds: ['aiquila'], tools: coworkTools },
|
|
66
68
|
// Requires specific Nextcloud apps
|
|
67
69
|
{ category: 'calendar', appIds: ['calendar'], tools: calendarTools },
|
|
68
70
|
{ category: 'tasks', appIds: ['tasks'], tools: tasksTools },
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { fetchAiquilaAPI } from '../../client/aiquila.js';
|
|
4
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
5
|
+
function err(action, error) {
|
|
6
|
+
return {
|
|
7
|
+
content: [
|
|
8
|
+
{
|
|
9
|
+
type: 'text',
|
|
10
|
+
text: `Error ${action}: ${error instanceof Error ? error.message : String(error)}`,
|
|
11
|
+
},
|
|
12
|
+
],
|
|
13
|
+
isError: true,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function text(body) {
|
|
17
|
+
return { content: [{ type: 'text', text: body }] };
|
|
18
|
+
}
|
|
19
|
+
function fmtTime(epoch) {
|
|
20
|
+
return epoch ? new Date(epoch * 1000).toISOString() : '—';
|
|
21
|
+
}
|
|
22
|
+
function formatCoworker(c) {
|
|
23
|
+
const state = !c.isActive ? 'disabled' : c.paused ? 'paused' : 'active';
|
|
24
|
+
const lines = [
|
|
25
|
+
`- **${c.title}** (ID: ${c.id}) — ${state}`,
|
|
26
|
+
` task: ${c.taskType} | provider: ${c.model ?? 'default'} | input: ${c.inputPath ?? '/'}`,
|
|
27
|
+
` schedule: ${c.cronSchedule} | next run: ${fmtTime(c.nextRunAt)} | last run: ${fmtTime(c.lastRunAt)}${c.lastStatus ? ` (${c.lastStatus})` : ''}`,
|
|
28
|
+
];
|
|
29
|
+
if (c.lastError)
|
|
30
|
+
lines.push(` ⚠ last error: ${c.lastError}`);
|
|
31
|
+
return lines.join('\n');
|
|
32
|
+
}
|
|
33
|
+
// ── Tools ────────────────────────────────────────────────────────────────────
|
|
34
|
+
export const listCoworkersTool = {
|
|
35
|
+
name: 'list_coworkers',
|
|
36
|
+
description: "List the current user's coworkers (persistent scheduled AI tasks) with their status, provider, schedule and last run.",
|
|
37
|
+
inputSchema: z.object({}),
|
|
38
|
+
handler: async () => {
|
|
39
|
+
try {
|
|
40
|
+
const coworkers = await fetchAiquilaAPI('/coworkers');
|
|
41
|
+
if (coworkers.length === 0) {
|
|
42
|
+
return text('No coworkers configured. Use create_coworker (optionally with a templateId) to add one.');
|
|
43
|
+
}
|
|
44
|
+
return text(coworkers.map(formatCoworker).join('\n\n'));
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
return err('listing coworkers', error);
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
export const listCoworkerTemplatesTool = {
|
|
52
|
+
name: 'list_coworker_templates',
|
|
53
|
+
description: 'List built-in coworker templates (e.g. image classification via Claude vision or Mistral Pixtral) and available task types.',
|
|
54
|
+
inputSchema: z.object({}),
|
|
55
|
+
handler: async () => {
|
|
56
|
+
try {
|
|
57
|
+
const data = await fetchAiquilaAPI('/coworkers/templates');
|
|
58
|
+
const tpls = data.templates
|
|
59
|
+
.map((t) => `- **${t.title}** (id: ${t.id})${t.description ? ` — ${t.description}` : ''}`)
|
|
60
|
+
.join('\n');
|
|
61
|
+
const types = data.taskTypes.map((t) => `- ${t.id} (${t.family}) — ${t.label}`).join('\n');
|
|
62
|
+
return text(`Templates:\n${tpls}\n\nTask types:\n${types}`);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
return err('listing templates', error);
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
export const getCoworkerTool = {
|
|
70
|
+
name: 'get_coworker',
|
|
71
|
+
description: 'Get a single coworker by ID, including its schedule and last run status.',
|
|
72
|
+
inputSchema: z.object({ id: z.number().describe('Coworker ID') }),
|
|
73
|
+
handler: async (args) => {
|
|
74
|
+
try {
|
|
75
|
+
const c = await fetchAiquilaAPI(`/coworkers/${args.id}`);
|
|
76
|
+
return text(formatCoworker(c));
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
return err('getting coworker', error);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
export const createCoworkerTool = {
|
|
84
|
+
name: 'create_coworker',
|
|
85
|
+
description: 'Create a coworker. Provide templateId to start from a built-in template (e.g. "classify-images-claude" or "classify-images-pixtral"), and/or explicit fields to configure a custom one. Fields override template defaults.',
|
|
86
|
+
inputSchema: z.object({
|
|
87
|
+
templateId: z
|
|
88
|
+
.string()
|
|
89
|
+
.optional()
|
|
90
|
+
.describe('Built-in template id (see list_coworker_templates)'),
|
|
91
|
+
title: z.string().optional(),
|
|
92
|
+
taskType: z.string().optional().describe('Task type id, e.g. "vision:classify"'),
|
|
93
|
+
model: z
|
|
94
|
+
.string()
|
|
95
|
+
.optional()
|
|
96
|
+
.describe('Provider id: "anthropic" (Claude) or "mistral" (Pixtral)'),
|
|
97
|
+
inputPath: z.string().optional().describe('Folder to process, e.g. "/Photos"'),
|
|
98
|
+
cronSchedule: z.string().optional().describe('5-field cron, e.g. "0 3 * * *"'),
|
|
99
|
+
maxTags: z.number().optional().describe('Max tags per image (vision:classify)'),
|
|
100
|
+
recursive: z.boolean().optional().describe('Include sub-folders'),
|
|
101
|
+
isActive: z.boolean().optional().describe('Whether the coworker is enabled'),
|
|
102
|
+
}),
|
|
103
|
+
handler: async (args) => {
|
|
104
|
+
try {
|
|
105
|
+
const body = {};
|
|
106
|
+
if (args.title !== undefined)
|
|
107
|
+
body.title = args.title;
|
|
108
|
+
if (args.taskType !== undefined)
|
|
109
|
+
body.task_type = args.taskType;
|
|
110
|
+
if (args.model !== undefined)
|
|
111
|
+
body.model = args.model;
|
|
112
|
+
if (args.inputPath !== undefined) {
|
|
113
|
+
body.input_type = 'folder';
|
|
114
|
+
body.input_path = args.inputPath;
|
|
115
|
+
}
|
|
116
|
+
if (args.cronSchedule !== undefined)
|
|
117
|
+
body.cron_schedule = args.cronSchedule;
|
|
118
|
+
if (args.isActive !== undefined)
|
|
119
|
+
body.is_active = args.isActive;
|
|
120
|
+
const options = {};
|
|
121
|
+
if (args.maxTags !== undefined)
|
|
122
|
+
options.maxTags = args.maxTags;
|
|
123
|
+
if (args.recursive !== undefined)
|
|
124
|
+
options.recursive = args.recursive;
|
|
125
|
+
if (Object.keys(options).length > 0)
|
|
126
|
+
body.options = options;
|
|
127
|
+
const endpoint = args.templateId ? '/coworkers/templates' : '/coworkers';
|
|
128
|
+
if (args.templateId)
|
|
129
|
+
body.templateId = args.templateId;
|
|
130
|
+
const c = await fetchAiquilaAPI(endpoint, { method: 'POST', body });
|
|
131
|
+
return text(`Coworker created:\n${formatCoworker(c)}`);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
return err('creating coworker', error);
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
export const updateCoworkerTool = {
|
|
139
|
+
name: 'update_coworker',
|
|
140
|
+
description: "Update a coworker's configuration (title, provider, input folder, schedule, options).",
|
|
141
|
+
inputSchema: z.object({
|
|
142
|
+
id: z.number().describe('Coworker ID'),
|
|
143
|
+
title: z.string().optional(),
|
|
144
|
+
model: z.string().optional().describe('"anthropic" or "mistral"'),
|
|
145
|
+
inputPath: z.string().optional(),
|
|
146
|
+
cronSchedule: z.string().optional(),
|
|
147
|
+
maxTags: z.number().optional(),
|
|
148
|
+
recursive: z.boolean().optional(),
|
|
149
|
+
}),
|
|
150
|
+
handler: async (args) => {
|
|
151
|
+
try {
|
|
152
|
+
const body = {};
|
|
153
|
+
if (args.title !== undefined)
|
|
154
|
+
body.title = args.title;
|
|
155
|
+
if (args.model !== undefined)
|
|
156
|
+
body.model = args.model;
|
|
157
|
+
if (args.inputPath !== undefined) {
|
|
158
|
+
body.input_type = 'folder';
|
|
159
|
+
body.input_path = args.inputPath;
|
|
160
|
+
}
|
|
161
|
+
if (args.cronSchedule !== undefined)
|
|
162
|
+
body.cron_schedule = args.cronSchedule;
|
|
163
|
+
const options = {};
|
|
164
|
+
if (args.maxTags !== undefined)
|
|
165
|
+
options.maxTags = args.maxTags;
|
|
166
|
+
if (args.recursive !== undefined)
|
|
167
|
+
options.recursive = args.recursive;
|
|
168
|
+
if (Object.keys(options).length > 0)
|
|
169
|
+
body.options = options;
|
|
170
|
+
const c = await fetchAiquilaAPI(`/coworkers/${args.id}`, { method: 'PUT', body });
|
|
171
|
+
return text(`Coworker updated:\n${formatCoworker(c)}`);
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
return err('updating coworker', error);
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
export const deleteCoworkerTool = {
|
|
179
|
+
name: 'delete_coworker',
|
|
180
|
+
description: 'Delete a coworker and its run history.',
|
|
181
|
+
inputSchema: z.object({ id: z.number().describe('Coworker ID') }),
|
|
182
|
+
handler: async (args) => {
|
|
183
|
+
try {
|
|
184
|
+
await fetchAiquilaAPI(`/coworkers/${args.id}`, { method: 'DELETE' });
|
|
185
|
+
return text(`Coworker ${args.id} deleted.`);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
return err('deleting coworker', error);
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
function stateTool(name, description, action) {
|
|
193
|
+
return {
|
|
194
|
+
name,
|
|
195
|
+
description,
|
|
196
|
+
inputSchema: z.object({ id: z.number().describe('Coworker ID') }),
|
|
197
|
+
handler: async (args) => {
|
|
198
|
+
try {
|
|
199
|
+
if (action === 'run') {
|
|
200
|
+
const run = await fetchAiquilaAPI(`/coworkers/${args.id}/run`, {
|
|
201
|
+
method: 'POST',
|
|
202
|
+
});
|
|
203
|
+
return text(`Run finished: ${run.status} — processed ${run.itemsProcessed}/${run.itemsTotal}.` +
|
|
204
|
+
(run.summary ? `\n${run.summary}` : '') +
|
|
205
|
+
(run.error ? `\n⚠ ${run.error}` : ''));
|
|
206
|
+
}
|
|
207
|
+
const c = await fetchAiquilaAPI(`/coworkers/${args.id}/${action}`, {
|
|
208
|
+
method: 'POST',
|
|
209
|
+
});
|
|
210
|
+
return text(`Coworker ${action}d:\n${formatCoworker(c)}`);
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
return err(`${action} coworker`, error);
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
export const enableCoworkerTool = stateTool('enable_coworker', 'Enable a coworker so it runs on its schedule.', 'enable');
|
|
219
|
+
export const disableCoworkerTool = stateTool('disable_coworker', 'Disable a coworker so it stops running on its schedule.', 'disable');
|
|
220
|
+
export const pauseCoworkerTool = stateTool('pause_coworker', 'Temporarily pause a coworker without disabling it.', 'pause');
|
|
221
|
+
export const resumeCoworkerTool = stateTool('resume_coworker', 'Resume a paused coworker.', 'resume');
|
|
222
|
+
export const runCoworkerTool = stateTool('run_coworker', 'Run a coworker immediately (synchronously) and return the run result.', 'run');
|
|
223
|
+
export const getCoworkerRunsTool = {
|
|
224
|
+
name: 'get_coworker_runs',
|
|
225
|
+
description: 'Get recent run history (progress, status, summary) for a coworker.',
|
|
226
|
+
inputSchema: z.object({
|
|
227
|
+
id: z.number().describe('Coworker ID'),
|
|
228
|
+
limit: z.number().optional().describe('Max runs to return (default 20)'),
|
|
229
|
+
}),
|
|
230
|
+
handler: async (args) => {
|
|
231
|
+
try {
|
|
232
|
+
const runs = await fetchAiquilaAPI(`/coworkers/${args.id}/runs`, {
|
|
233
|
+
queryParams: args.limit ? { limit: String(args.limit) } : undefined,
|
|
234
|
+
});
|
|
235
|
+
if (runs.length === 0)
|
|
236
|
+
return text('No runs yet for this coworker.');
|
|
237
|
+
const lines = runs.map((r) => `- #${r.id} ${r.status} — ${r.itemsProcessed}/${r.itemsTotal} — ${fmtTime(r.startedAt)}` +
|
|
238
|
+
(r.error ? ` — ⚠ ${r.error}` : r.summary ? ` — ${r.summary.split('\n')[0]}` : ''));
|
|
239
|
+
return text(lines.join('\n'));
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
return err('getting coworker runs', error);
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
export const coworkTools = [
|
|
247
|
+
listCoworkersTool,
|
|
248
|
+
listCoworkerTemplatesTool,
|
|
249
|
+
getCoworkerTool,
|
|
250
|
+
createCoworkerTool,
|
|
251
|
+
updateCoworkerTool,
|
|
252
|
+
deleteCoworkerTool,
|
|
253
|
+
enableCoworkerTool,
|
|
254
|
+
disableCoworkerTool,
|
|
255
|
+
pauseCoworkerTool,
|
|
256
|
+
resumeCoworkerTool,
|
|
257
|
+
runCoworkerTool,
|
|
258
|
+
getCoworkerRunsTool,
|
|
259
|
+
];
|