plugin-agent-orchestrator 1.0.14 → 1.0.16
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/README.md +96 -0
- package/dist/externalVersion.js +6 -6
- package/dist/server/plugin.js +11 -1
- package/dist/server/services/CodeValidator.js +1 -0
- package/dist/server/services/SkillManager.js +0 -39
- package/dist/server/skill-hub/plugin.js +3 -3
- package/dist/server/skill-hub/tasks/SkillExecutionTask.d.ts +2 -0
- package/dist/server/skill-hub/tasks/SkillExecutionTask.js +122 -0
- package/dist/server/tools/delegate-task.js +22 -2
- package/dist/server/tools/external-rag-search.d.ts +42 -0
- package/dist/server/tools/external-rag-search.js +140 -0
- package/dist/server/tools/skill-execute.d.ts +1 -1
- package/dist/server/tools/skill-execute.js +1 -2
- package/package.json +1 -1
- package/src/client/index.tsx +1 -1
- package/src/client/plugin.tsx +54 -54
- package/src/client/skill-hub/index.tsx +75 -75
- package/src/server/migrations/20260423000000-add-progress-fields.ts +5 -5
- package/src/server/migrations/20260425000000-add-interaction-schema.ts +5 -5
- package/src/server/migrations/20260427000000-add-tracing-detail-fields.ts +5 -5
- package/src/server/migrations/20260427000000-change-packages-to-text.ts +7 -7
- package/src/server/migrations/20260427000001-change-other-json-to-text.ts +10 -10
- package/src/server/migrations/20260429000000-add-llm-fields.ts +2 -2
- package/src/server/migrations/20260429000000-fix-inputargs-json-to-text.ts +2 -2
- package/src/server/migrations/20260503000000-add-orchestrator-trace-fields.ts +2 -2
- package/src/server/plugin.ts +23 -13
- package/src/server/services/CodeValidator.ts +5 -5
- package/src/server/services/SkillManager.ts +12 -52
- package/src/server/services/WorkerEnvManager.ts +5 -5
- package/src/server/skill-hub/plugin.ts +61 -61
- package/src/server/skill-hub/tasks/SkillExecutionTask.ts +162 -16
- package/src/server/tools/delegate-task.ts +25 -1
- package/src/server/tools/external-rag-search.ts +128 -0
- package/src/server/tools/skill-execute.ts +1 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Database } from '@nocobase/database';
|
|
2
|
-
import { stringifyJsonText } from '../skill-hub/utils/json-fields';
|
|
1
|
+
import { Database } from '@nocobase/database';
|
|
2
|
+
import { stringifyJsonText } from '../skill-hub/utils/json-fields';
|
|
3
3
|
|
|
4
4
|
export class SkillManager {
|
|
5
5
|
constructor(private db: Database) {}
|
|
@@ -203,16 +203,16 @@ export class SkillManager {
|
|
|
203
203
|
|
|
204
204
|
for (const seed of seeds) {
|
|
205
205
|
try {
|
|
206
|
-
const count = await repo.count({ filter: { name: seed.name } });
|
|
207
|
-
if (count === 0) {
|
|
208
|
-
await repo.create({
|
|
209
|
-
values: {
|
|
210
|
-
...seed,
|
|
211
|
-
inputSchema: stringifyJsonText(seed.inputSchema),
|
|
212
|
-
packages: stringifyJsonText(seed.packages, []),
|
|
213
|
-
},
|
|
214
|
-
});
|
|
215
|
-
}
|
|
206
|
+
const count = await repo.count({ filter: { name: seed.name } });
|
|
207
|
+
if (count === 0) {
|
|
208
|
+
await repo.create({
|
|
209
|
+
values: {
|
|
210
|
+
...seed,
|
|
211
|
+
inputSchema: stringifyJsonText(seed.inputSchema),
|
|
212
|
+
packages: stringifyJsonText(seed.packages, []),
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
216
|
} catch (err) {
|
|
217
217
|
console.error(`[import-skill] Failed to insert ${seed.name}:`, err);
|
|
218
218
|
// Skip if already exists (unique constraint on name)
|
|
@@ -330,46 +330,6 @@ doc.build(story)
|
|
|
330
330
|
print('Generated: report.pdf')
|
|
331
331
|
`;
|
|
332
332
|
|
|
333
|
-
const SEED_PPTX = `import os, json
|
|
334
|
-
from pptx import Presentation
|
|
335
|
-
from pptx.util import Inches, Pt
|
|
336
|
-
from pptx.enum.text import PP_ALIGN
|
|
337
|
-
|
|
338
|
-
title = json.loads('''{{title}}''') if '{{title}}'.startswith('"') else '{{title}}'
|
|
339
|
-
subtitle_raw = '''{{subtitle}}'''
|
|
340
|
-
subtitle = json.loads(subtitle_raw) if subtitle_raw.startswith('"') else subtitle_raw if subtitle_raw != '{{' + 'subtitle}}' else ''
|
|
341
|
-
slides_data = json.loads('''{{slides}}''')
|
|
342
|
-
|
|
343
|
-
prs = Presentation()
|
|
344
|
-
prs.slide_width = Inches(13.333)
|
|
345
|
-
prs.slide_height = Inches(7.5)
|
|
346
|
-
|
|
347
|
-
# Title slide
|
|
348
|
-
slide = prs.slides.add_slide(prs.slide_layouts[0])
|
|
349
|
-
slide.shapes.title.text = title
|
|
350
|
-
if subtitle and slide.placeholders[1]:
|
|
351
|
-
slide.placeholders[1].text = subtitle
|
|
352
|
-
|
|
353
|
-
# Content slides
|
|
354
|
-
for s in slides_data:
|
|
355
|
-
slide = prs.slides.add_slide(prs.slide_layouts[1])
|
|
356
|
-
slide.shapes.title.text = s.get('title', '')
|
|
357
|
-
body = slide.placeholders[1].text_frame
|
|
358
|
-
body.clear()
|
|
359
|
-
for i, bullet in enumerate(s.get('bullets', [])):
|
|
360
|
-
if i == 0:
|
|
361
|
-
body.paragraphs[0].text = bullet
|
|
362
|
-
else:
|
|
363
|
-
p = body.add_paragraph()
|
|
364
|
-
p.text = bullet
|
|
365
|
-
body.paragraphs[-1].font.size = Pt(18)
|
|
366
|
-
|
|
367
|
-
output_dir = os.environ.get('OUTPUT_DIR', '/output')
|
|
368
|
-
filepath = os.path.join(output_dir, 'presentation.pptx')
|
|
369
|
-
prs.save(filepath)
|
|
370
|
-
print('Generated: presentation.pptx')
|
|
371
|
-
`;
|
|
372
|
-
|
|
373
333
|
const SEED_CHART = `import os, json
|
|
374
334
|
import matplotlib
|
|
375
335
|
matplotlib.use('Agg')
|
|
@@ -42,7 +42,7 @@ export class WorkerEnvManager {
|
|
|
42
42
|
) {}
|
|
43
43
|
|
|
44
44
|
async getOrCreateConfig() {
|
|
45
|
-
const repo = this.db.getRepository('skillWorkerConfigs');
|
|
45
|
+
const repo = (this as any).db.getRepository('skillWorkerConfigs');
|
|
46
46
|
let config = await repo.findOne();
|
|
47
47
|
if (config) return config;
|
|
48
48
|
|
|
@@ -72,7 +72,7 @@ export class WorkerEnvManager {
|
|
|
72
72
|
}),
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
await this.app.pubSubManager.publish('skill-hub.init-env', {
|
|
75
|
+
await (this as any).app.pubSubManager.publish('skill-hub.init-env', {
|
|
76
76
|
...config,
|
|
77
77
|
storagePath: this.storagePath,
|
|
78
78
|
queuedAt: new Date().toISOString(),
|
|
@@ -89,17 +89,17 @@ export class WorkerEnvManager {
|
|
|
89
89
|
apt: Array.from(new Set([...(DEFAULT_WHITELIST.apt || []), ...(customPackages.apt || [])])),
|
|
90
90
|
};
|
|
91
91
|
|
|
92
|
-
await this.app.pubSubManager.publish('skill-hub.init-env.progress', {
|
|
92
|
+
await (this as any).app.pubSubManager.publish('skill-hub.init-env.progress', {
|
|
93
93
|
percent: 25,
|
|
94
94
|
log: 'Resolved sandbox package whitelist',
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
-
await this.app.pubSubManager.publish('skill-hub.init-env.progress', {
|
|
97
|
+
await (this as any).app.pubSubManager.publish('skill-hub.init-env.progress', {
|
|
98
98
|
percent: 75,
|
|
99
99
|
log: 'Sandbox runtime uses the local worker environment',
|
|
100
100
|
});
|
|
101
101
|
|
|
102
|
-
await this.app.pubSubManager.publish('skill-hub.init-env.done', {
|
|
102
|
+
await (this as any).app.pubSubManager.publish('skill-hub.init-env.done', {
|
|
103
103
|
status: 'succeeded',
|
|
104
104
|
log:
|
|
105
105
|
'Sandbox environment is ready on this worker. Package installation is managed by the worker image/runtime; whitelist was refreshed.',
|
|
@@ -95,14 +95,14 @@ export class SkillHubSubFeature {
|
|
|
95
95
|
// 2. Init services
|
|
96
96
|
const storagePath = resolve(process.cwd(), 'storage', 'plugin-skill-hub'); // Keep old storage path for backwards compatibility
|
|
97
97
|
this.fileManager = new FileManager(storagePath);
|
|
98
|
-
this.sandboxRunner = new SandboxRunner(this.fileManager, this.app.logger, storagePath);
|
|
99
|
-
this.skillManager = new SkillManager(this.db);
|
|
100
|
-
this.workerEnvManager = new WorkerEnvManager(this.app, this.db, storagePath);
|
|
98
|
+
this.sandboxRunner = new SandboxRunner(this.fileManager, (this as any).app.logger, storagePath);
|
|
99
|
+
this.skillManager = new SkillManager((this as any).db);
|
|
100
|
+
this.workerEnvManager = new WorkerEnvManager((this as any).app, (this as any).db, storagePath);
|
|
101
101
|
this.skillRepoService = new SkillRepositoryService(storagePath);
|
|
102
102
|
this.mcpController = new McpController(this);
|
|
103
103
|
|
|
104
104
|
// 3. Register REST actions
|
|
105
|
-
this.app.resourceManager.define({
|
|
105
|
+
(this as any).app.resourceManager.define({
|
|
106
106
|
name: 'skillHub',
|
|
107
107
|
actions: {
|
|
108
108
|
download: this.handleDownload.bind(this),
|
|
@@ -119,7 +119,7 @@ export class SkillHubSubFeature {
|
|
|
119
119
|
|
|
120
120
|
|
|
121
121
|
// 4.5. Register DB hooks for automatic storage physical cleanup
|
|
122
|
-
this.db.on('skillExecutions.afterDestroy', async (model, options) => {
|
|
122
|
+
(this as any).db.on('skillExecutions.afterDestroy', async (model, options) => {
|
|
123
123
|
const execId = model.get('id');
|
|
124
124
|
try {
|
|
125
125
|
const dir = this.fileManager.getExecDir(String(execId));
|
|
@@ -127,29 +127,29 @@ export class SkillHubSubFeature {
|
|
|
127
127
|
require('fs').rmSync(dir, { recursive: true, force: true });
|
|
128
128
|
}
|
|
129
129
|
} catch (err) {
|
|
130
|
-
this.app.logger.error(`[skill-hub] Failed to cleanup physical storage for execId ${execId}`, { error: err });
|
|
130
|
+
(this as any).app.logger.error(`[skill-hub] Failed to cleanup physical storage for execId ${execId}`, { error: err });
|
|
131
131
|
}
|
|
132
132
|
});
|
|
133
133
|
|
|
134
|
-
this.db.on('skillDefinitions.afterSave', async (model, options) => {
|
|
134
|
+
(this as any).db.on('skillDefinitions.afterSave', async (model, options) => {
|
|
135
135
|
// If a zip file was uploaded, extract it and update the skill record
|
|
136
136
|
if (model.changed('fileId') && model.get('fileId')) {
|
|
137
137
|
try {
|
|
138
|
-
const attachment = await this.db.getRepository('attachments').findOne({
|
|
138
|
+
const attachment = await (this as any).db.getRepository('attachments').findOne({
|
|
139
139
|
filter: { id: model.get('fileId') },
|
|
140
140
|
transaction: options.transaction,
|
|
141
141
|
});
|
|
142
142
|
|
|
143
143
|
if (attachment) {
|
|
144
|
-
const fileManager = this.app.pm.get('@nocobase/plugin-file-manager') as any;
|
|
144
|
+
const fileManager = (this as any).app.pm.get('@nocobase/plugin-file-manager') as any;
|
|
145
145
|
if (!fileManager) {
|
|
146
|
-
this.app.logger.warn('[skill-hub] plugin-file-manager not found, cannot extract skill package');
|
|
146
|
+
(this as any).app.logger.warn('[skill-hub] plugin-file-manager not found, cannot extract skill package');
|
|
147
147
|
return;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
const streamData = await fileManager.getFileStream(attachment);
|
|
151
151
|
if (!streamData || !streamData.stream) {
|
|
152
|
-
this.app.logger.warn(`[skill-hub] Could not get file stream for attachment ${attachment.get('id')}`);
|
|
152
|
+
(this as any).app.logger.warn(`[skill-hub] Could not get file stream for attachment ${attachment.get('id')}`);
|
|
153
153
|
return;
|
|
154
154
|
}
|
|
155
155
|
|
|
@@ -179,54 +179,54 @@ export class SkillHubSubFeature {
|
|
|
179
179
|
if (metadata.timeoutSeconds) updateValues.timeoutSeconds = metadata.timeoutSeconds;
|
|
180
180
|
if (instructions) updateValues.instructions = instructions;
|
|
181
181
|
|
|
182
|
-
await this.db.getRepository('skillDefinitions').update({
|
|
182
|
+
await (this as any).db.getRepository('skillDefinitions').update({
|
|
183
183
|
filter: { id: model.get('id') },
|
|
184
184
|
values: updateValues,
|
|
185
185
|
transaction: options.transaction,
|
|
186
186
|
});
|
|
187
187
|
|
|
188
188
|
unlinkSync(tempZipPath);
|
|
189
|
-
this.app.logger.info(`[skill-hub] Successfully extracted zip and updated skill: ${skillName}`);
|
|
189
|
+
(this as any).app.logger.info(`[skill-hub] Successfully extracted zip and updated skill: ${skillName}`);
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
} catch (err) {
|
|
193
|
-
this.app.logger.error(`[skill-hub] Failed to unpack skill zip`, { error: err });
|
|
193
|
+
(this as any).app.logger.error(`[skill-hub] Failed to unpack skill zip`, { error: err });
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
});
|
|
197
197
|
|
|
198
198
|
// 5. Subscribe PubSub — worker processes skill execution tasks
|
|
199
|
-
this.app.pubSubManager.subscribe('skill-hub.task', async (payload: any) => {
|
|
199
|
+
(this as any).app.pubSubManager.subscribe('skill-hub.task', async (payload: any) => {
|
|
200
200
|
if (process.env.SKILL_HUB_SANDBOX === 'false') return;
|
|
201
201
|
await this.onQueueTask(payload);
|
|
202
202
|
});
|
|
203
203
|
|
|
204
204
|
// 5b. Subscribe PubSub — worker processes init-env tasks
|
|
205
|
-
this.app.pubSubManager.subscribe('skill-hub.init-env', async (payload: any) => {
|
|
205
|
+
(this as any).app.pubSubManager.subscribe('skill-hub.init-env', async (payload: any) => {
|
|
206
206
|
if (process.env.SKILL_HUB_SANDBOX === 'false') return;
|
|
207
207
|
await this.workerEnvManager.executeInit(payload);
|
|
208
208
|
});
|
|
209
209
|
|
|
210
210
|
// 6. Register AI tools + subscriptions (deferred — after all plugins loaded)
|
|
211
|
-
this.app.on('afterStart', async () => {
|
|
211
|
+
(this as any).app.on('afterStart', async () => {
|
|
212
212
|
this.registerAITools();
|
|
213
213
|
this.startCleanupInterval();
|
|
214
214
|
await this.subscribeInitEnvDone();
|
|
215
215
|
// Ensure any newly added built-in skills are seeded automatically on upgrade/restart
|
|
216
216
|
await this.skillManager.seedDefaults().catch((e) => {
|
|
217
|
-
this.app.logger.error(`[skill-hub] Failed to seed default skills: ${e.message}`);
|
|
217
|
+
(this as any).app.logger.error(`[skill-hub] Failed to seed default skills: ${e.message}`);
|
|
218
218
|
});
|
|
219
219
|
});
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
private async onQueueTask(message: { id: string }) {
|
|
223
|
-
this.app.logger.info(`[skill-hub] Worker received queue task: ${message.id}`);
|
|
224
|
-
const execution = await this.db.getRepository('skillExecutions').findOne({
|
|
223
|
+
(this as any).app.logger.info(`[skill-hub] Worker received queue task: ${message.id}`);
|
|
224
|
+
const execution = await (this as any).db.getRepository('skillExecutions').findOne({
|
|
225
225
|
filter: { id: message.id },
|
|
226
226
|
appends: ['skill'],
|
|
227
227
|
});
|
|
228
228
|
if (!execution) {
|
|
229
|
-
this.app.logger.warn(`[skill-hub] Task ${message.id} ignored: execution record not found.`);
|
|
229
|
+
(this as any).app.logger.warn(`[skill-hub] Task ${message.id} ignored: execution record not found.`);
|
|
230
230
|
return;
|
|
231
231
|
}
|
|
232
232
|
|
|
@@ -235,7 +235,7 @@ export class SkillHubSubFeature {
|
|
|
235
235
|
this.sandboxRunner,
|
|
236
236
|
this.fileManager,
|
|
237
237
|
this.skillRepoService,
|
|
238
|
-
this.app,
|
|
238
|
+
(this as any).app,
|
|
239
239
|
);
|
|
240
240
|
await task.run();
|
|
241
241
|
}
|
|
@@ -259,7 +259,7 @@ export class SkillHubSubFeature {
|
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
-
const execution = await this.db.getRepository('skillExecutions').create({
|
|
262
|
+
const execution = await (this as any).db.getRepository('skillExecutions').create({
|
|
263
263
|
values: {
|
|
264
264
|
skillId: skill.id,
|
|
265
265
|
status: 'pending',
|
|
@@ -271,7 +271,7 @@ export class SkillHubSubFeature {
|
|
|
271
271
|
|
|
272
272
|
const execId = String(execution.id);
|
|
273
273
|
|
|
274
|
-
this.app.logger.info(
|
|
274
|
+
(this as any).app.logger.info(
|
|
275
275
|
`[skill-hub] Queued execution ${execId}: skill=${skill.get ? skill.get('name') : skill.name}, ` +
|
|
276
276
|
`user=${userId || 'system'}`,
|
|
277
277
|
);
|
|
@@ -317,10 +317,10 @@ export class SkillHubSubFeature {
|
|
|
317
317
|
};
|
|
318
318
|
|
|
319
319
|
// Subscribe progress and completion FIRST (before dispatching)
|
|
320
|
-
await this.app.pubSubManager.subscribe(progressChannel, progressCallback);
|
|
320
|
+
await (this as any).app.pubSubManager.subscribe(progressChannel, progressCallback);
|
|
321
321
|
cleanups.push({ channel: progressChannel, callback: progressCallback });
|
|
322
322
|
|
|
323
|
-
await this.app.pubSubManager.subscribe(doneChannel, doneCallback);
|
|
323
|
+
await (this as any).app.pubSubManager.subscribe(doneChannel, doneCallback);
|
|
324
324
|
cleanups.push({ channel: doneChannel, callback: doneCallback });
|
|
325
325
|
|
|
326
326
|
// Handle user abort (cancel chat) → propagate to worker
|
|
@@ -329,9 +329,9 @@ export class SkillHubSubFeature {
|
|
|
329
329
|
signal.addEventListener?.('abort', () => {
|
|
330
330
|
clearTimeout(timeout);
|
|
331
331
|
// Publish abort to worker via PubSub
|
|
332
|
-
this.app.pubSubManager.publish(abortChannel, { reason: 'user_cancel' }).catch(() => {});
|
|
332
|
+
(this as any).app.pubSubManager.publish(abortChannel, { reason: 'user_cancel' }).catch(() => {});
|
|
333
333
|
// Also update the execution status
|
|
334
|
-
this.db.getRepository('skillExecutions').update({
|
|
334
|
+
(this as any).db.getRepository('skillExecutions').update({
|
|
335
335
|
filter: { id: execId },
|
|
336
336
|
values: { status: 'canceled' },
|
|
337
337
|
}).catch(() => {});
|
|
@@ -340,7 +340,7 @@ export class SkillHubSubFeature {
|
|
|
340
340
|
}
|
|
341
341
|
|
|
342
342
|
// NOW Dispatch to worker via EventQueue
|
|
343
|
-
await this.app.pubSubManager.publish('skill-hub.task', { id: execId });
|
|
343
|
+
await (this as any).app.pubSubManager.publish('skill-hub.task', { id: execId });
|
|
344
344
|
|
|
345
345
|
// Wait for completion
|
|
346
346
|
result = await resultPromise;
|
|
@@ -348,7 +348,7 @@ export class SkillHubSubFeature {
|
|
|
348
348
|
// Cleanup all PubSub subscriptions
|
|
349
349
|
for (const { channel, callback } of cleanups) {
|
|
350
350
|
try {
|
|
351
|
-
await this.app.pubSubManager.unsubscribe(channel, callback);
|
|
351
|
+
await (this as any).app.pubSubManager.unsubscribe(channel, callback);
|
|
352
352
|
} catch {
|
|
353
353
|
// ignore cleanup errors
|
|
354
354
|
}
|
|
@@ -383,7 +383,7 @@ export class SkillHubSubFeature {
|
|
|
383
383
|
ctx.throw(401, 'Unauthorized');
|
|
384
384
|
}
|
|
385
385
|
|
|
386
|
-
const execution = await this.db.getRepository('skillExecutions').findOne({
|
|
386
|
+
const execution = await (this as any).db.getRepository('skillExecutions').findOne({
|
|
387
387
|
filter: { id: execId },
|
|
388
388
|
});
|
|
389
389
|
|
|
@@ -414,7 +414,7 @@ export class SkillHubSubFeature {
|
|
|
414
414
|
ctx.throw(400, 'Missing skillId');
|
|
415
415
|
}
|
|
416
416
|
|
|
417
|
-
const skill = await this.db.getRepository('skillDefinitions').findOne({
|
|
417
|
+
const skill = await (this as any).db.getRepository('skillDefinitions').findOne({
|
|
418
418
|
filter: { id: skillId },
|
|
419
419
|
});
|
|
420
420
|
if (!skill) {
|
|
@@ -465,20 +465,20 @@ export class SkillHubSubFeature {
|
|
|
465
465
|
if (data.status === 'succeeded' && data.whitelist) {
|
|
466
466
|
values.packageWhitelist = stringifyJsonText(data.whitelist, { python: [], node: [], apt: [] });
|
|
467
467
|
}
|
|
468
|
-
await this.db.getRepository('skillWorkerConfigs').update({
|
|
468
|
+
await (this as any).db.getRepository('skillWorkerConfigs').update({
|
|
469
469
|
filter: {},
|
|
470
470
|
values,
|
|
471
471
|
forceUpdate: true,
|
|
472
472
|
});
|
|
473
|
-
this.app.logger.info(`[skill-hub] Init env ${data.status}`);
|
|
473
|
+
(this as any).app.logger.info(`[skill-hub] Init env ${data.status}`);
|
|
474
474
|
} catch (err) {
|
|
475
|
-
this.app.logger.warn('[skill-hub] Failed to update init env status:', err);
|
|
475
|
+
(this as any).app.logger.warn('[skill-hub] Failed to update init env status:', err);
|
|
476
476
|
}
|
|
477
477
|
};
|
|
478
478
|
|
|
479
479
|
this.initEnvProgressCallback = async (data: any) => {
|
|
480
480
|
try {
|
|
481
|
-
await this.db.getRepository('skillWorkerConfigs').update({
|
|
481
|
+
await (this as any).db.getRepository('skillWorkerConfigs').update({
|
|
482
482
|
filter: {},
|
|
483
483
|
values: {
|
|
484
484
|
initProgressPercent: data.percent,
|
|
@@ -491,15 +491,15 @@ export class SkillHubSubFeature {
|
|
|
491
491
|
}
|
|
492
492
|
};
|
|
493
493
|
|
|
494
|
-
await this.app.pubSubManager.subscribe('skill-hub.init-env.done', this.initEnvDoneCallback);
|
|
495
|
-
await this.app.pubSubManager.subscribe('skill-hub.init-env.progress', this.initEnvProgressCallback);
|
|
494
|
+
await (this as any).app.pubSubManager.subscribe('skill-hub.init-env.done', this.initEnvDoneCallback);
|
|
495
|
+
await (this as any).app.pubSubManager.subscribe('skill-hub.init-env.progress', this.initEnvProgressCallback);
|
|
496
496
|
}
|
|
497
497
|
|
|
498
498
|
private registerAITools() {
|
|
499
499
|
try {
|
|
500
|
-
const aiPlugin = this.app.pm.get('@nocobase/plugin-ai') as any;
|
|
500
|
+
const aiPlugin = (this as any).app.pm.get('@nocobase/plugin-ai') as any;
|
|
501
501
|
if (!aiPlugin?.ai?.toolsManager) {
|
|
502
|
-
this.app.logger.warn('[skill-hub] plugin-ai not available, skip AI tool registration.');
|
|
502
|
+
(this as any).app.logger.warn('[skill-hub] plugin-ai not available, skip AI tool registration.');
|
|
503
503
|
return;
|
|
504
504
|
}
|
|
505
505
|
|
|
@@ -509,7 +509,7 @@ export class SkillHubSubFeature {
|
|
|
509
509
|
// 2. Dynamic tools — each enabled skill becomes a separate AI tool.
|
|
510
510
|
aiPlugin.ai.toolsManager.registerDynamicTools(async (register: { registerTools: (options: any) => void }) => {
|
|
511
511
|
try {
|
|
512
|
-
const skills = await this.db.getRepository('skillDefinitions').find({
|
|
512
|
+
const skills = await (this as any).db.getRepository('skillDefinitions').find({
|
|
513
513
|
filter: { enabled: true },
|
|
514
514
|
});
|
|
515
515
|
|
|
@@ -539,11 +539,11 @@ export class SkillHubSubFeature {
|
|
|
539
539
|
},
|
|
540
540
|
invoke: async (toolCtx: any, args: any) => {
|
|
541
541
|
// Re-fetch skill to get latest version (hot-reload support)
|
|
542
|
-
const latestSkill = await this.db.getRepository('skillDefinitions').findOne({
|
|
542
|
+
const latestSkill = await (this as any).db.getRepository('skillDefinitions').findOne({
|
|
543
543
|
filter: { id: skill.get('id'), enabled: true },
|
|
544
544
|
});
|
|
545
545
|
if (!latestSkill) {
|
|
546
|
-
return { error: `Skill "${skill.get('name')}" is no longer available` };
|
|
546
|
+
return { status: 'error', content: `Skill "${skill.get('name')}" is no longer available` };
|
|
547
547
|
}
|
|
548
548
|
const result = await this.executeSkill(latestSkill, args, toolCtx);
|
|
549
549
|
return {
|
|
@@ -556,13 +556,13 @@ export class SkillHubSubFeature {
|
|
|
556
556
|
|
|
557
557
|
register.registerTools(tools);
|
|
558
558
|
} catch (err) {
|
|
559
|
-
this.app.logger.warn('[skill-hub] Failed to provide dynamic tools', err);
|
|
559
|
+
(this as any).app.logger.warn('[skill-hub] Failed to provide dynamic tools', err);
|
|
560
560
|
}
|
|
561
561
|
});
|
|
562
562
|
|
|
563
|
-
this.app.logger.info('[skill-hub] AI tools registered (dynamic provider + general tool).');
|
|
563
|
+
(this as any).app.logger.info('[skill-hub] AI tools registered (dynamic provider + general tool).');
|
|
564
564
|
} catch (error) {
|
|
565
|
-
this.app.logger.warn('[skill-hub] Failed to register AI tools:', error);
|
|
565
|
+
(this as any).app.logger.warn('[skill-hub] Failed to register AI tools:', error);
|
|
566
566
|
}
|
|
567
567
|
}
|
|
568
568
|
|
|
@@ -573,27 +573,27 @@ export class SkillHubSubFeature {
|
|
|
573
573
|
this.cleanupInterval = setInterval(async () => {
|
|
574
574
|
// 1. Storage Retention Cleanup
|
|
575
575
|
try {
|
|
576
|
-
const config = await this.db.getRepository('skillWorkerConfigs').findOne();
|
|
576
|
+
const config = await (this as any).db.getRepository('skillWorkerConfigs').findOne();
|
|
577
577
|
const hours = config ? config.get('retentionHours') : 24;
|
|
578
578
|
|
|
579
579
|
if (hours && hours > 0) {
|
|
580
580
|
const MAX_AGE_MS = hours * 60 * 60 * 1000;
|
|
581
581
|
const cutoff = new Date(Date.now() - MAX_AGE_MS);
|
|
582
|
-
const repo = this.db.getRepository('skillExecutions');
|
|
582
|
+
const repo = (this as any).db.getRepository('skillExecutions');
|
|
583
583
|
|
|
584
584
|
const outdated = await repo.find({
|
|
585
|
-
|
|
585
|
+
filter: { createdAt: { $lt: cutoff } }
|
|
586
586
|
});
|
|
587
587
|
|
|
588
588
|
if (outdated.length > 0) {
|
|
589
589
|
for (const record of outdated) {
|
|
590
590
|
await record.destroy(); // Fires afterDestroy hook which removes physical folder
|
|
591
591
|
}
|
|
592
|
-
this.app.logger.info(`[skill-hub] Auto-cleaned up ${outdated.length} expired execution records`);
|
|
592
|
+
(this as any).app.logger.info(`[skill-hub] Auto-cleaned up ${outdated.length} expired execution records`);
|
|
593
593
|
}
|
|
594
594
|
}
|
|
595
595
|
} catch (err) {
|
|
596
|
-
this.app.logger.warn('[skill-hub] Auto Cleanup error:', err);
|
|
596
|
+
(this as any).app.logger.warn('[skill-hub] Auto Cleanup error:', err);
|
|
597
597
|
}
|
|
598
598
|
|
|
599
599
|
// 2. Cleanup rate limiter stale entries
|
|
@@ -605,12 +605,12 @@ export class SkillHubSubFeature {
|
|
|
605
605
|
// Unsubscribe PubSub
|
|
606
606
|
if (this.initEnvDoneCallback) {
|
|
607
607
|
try {
|
|
608
|
-
await this.app.pubSubManager.unsubscribe('skill-hub.init-env.done', this.initEnvDoneCallback);
|
|
608
|
+
await (this as any).app.pubSubManager.unsubscribe('skill-hub.init-env.done', this.initEnvDoneCallback);
|
|
609
609
|
} catch { /* ignore */ }
|
|
610
610
|
}
|
|
611
611
|
if (this.initEnvProgressCallback) {
|
|
612
612
|
try {
|
|
613
|
-
await this.app.pubSubManager.unsubscribe('skill-hub.init-env.progress', this.initEnvProgressCallback);
|
|
613
|
+
await (this as any).app.pubSubManager.unsubscribe('skill-hub.init-env.progress', this.initEnvProgressCallback);
|
|
614
614
|
} catch { /* ignore */ }
|
|
615
615
|
}
|
|
616
616
|
|
|
@@ -624,7 +624,7 @@ export class SkillHubSubFeature {
|
|
|
624
624
|
// --- Handlers ---
|
|
625
625
|
private async handleClearStorage(ctx: any, next: () => Promise<any>) {
|
|
626
626
|
const { type } = ctx.request.body || ctx.action.params.values;
|
|
627
|
-
const repo = this.db.getRepository('skillExecutions');
|
|
627
|
+
const repo = (this as any).db.getRepository('skillExecutions');
|
|
628
628
|
let count = 0;
|
|
629
629
|
|
|
630
630
|
if (type === 'all') {
|
|
@@ -634,11 +634,11 @@ export class SkillHubSubFeature {
|
|
|
634
634
|
}
|
|
635
635
|
count = results.length;
|
|
636
636
|
} else if (type === 'expired') {
|
|
637
|
-
const config = await this.db.getRepository('skillWorkerConfigs').findOne();
|
|
637
|
+
const config = await (this as any).db.getRepository('skillWorkerConfigs').findOne();
|
|
638
638
|
const hours = config ? config.get('retentionHours') : 24;
|
|
639
639
|
if (hours > 0) {
|
|
640
640
|
const cutoff = new Date(Date.now() - hours * 60 * 60 * 1000);
|
|
641
|
-
const results = await repo.find({
|
|
641
|
+
const results = await repo.find({ filter: { createdAt: { $lt: cutoff } }, fields: ['id'] });
|
|
642
642
|
for (const rec of results) {
|
|
643
643
|
await rec.destroy();
|
|
644
644
|
}
|
|
@@ -653,7 +653,7 @@ export class SkillHubSubFeature {
|
|
|
653
653
|
private async handleListTemplates(ctx: any, next: () => Promise<any>) {
|
|
654
654
|
// Dynamic Pull: discover templates from all active plugins in the system
|
|
655
655
|
try {
|
|
656
|
-
const allPlugins = this.app.pm.getPlugins();
|
|
656
|
+
const allPlugins = (this as any).app.pm.getPlugins();
|
|
657
657
|
for (const [, pluginInstance] of allPlugins) {
|
|
658
658
|
if (typeof (pluginInstance as any).getSkillTemplates === 'function') {
|
|
659
659
|
const pluginSkills = (pluginInstance as any).getSkillTemplates();
|
|
@@ -667,7 +667,7 @@ export class SkillHubSubFeature {
|
|
|
667
667
|
}
|
|
668
668
|
}
|
|
669
669
|
} catch (e) {
|
|
670
|
-
this.app.logger.warn(`[skill-hub] Failed to discover some plugin skills: ${e.message}`);
|
|
670
|
+
(this as any).app.logger.warn(`[skill-hub] Failed to discover some plugin skills: ${e.message}`);
|
|
671
671
|
}
|
|
672
672
|
|
|
673
673
|
ctx.body = { data: Array.from(this.skillTemplates.values()) };
|
|
@@ -683,7 +683,7 @@ export class SkillHubSubFeature {
|
|
|
683
683
|
*/
|
|
684
684
|
registerSkillTemplate(pluginName: string, skillDef: any) {
|
|
685
685
|
this.skillTemplates.set(skillDef.name, this.hydrateSkillTemplate(pluginName, skillDef));
|
|
686
|
-
this.app.logger.info(`[skill-hub] Registered skill template "${skillDef.name}" from plugin "${pluginName}"`);
|
|
686
|
+
(this as any).app.logger.info(`[skill-hub] Registered skill template "${skillDef.name}" from plugin "${pluginName}"`);
|
|
687
687
|
}
|
|
688
688
|
|
|
689
689
|
resolveSkillTemplate(templateName: string) {
|
|
@@ -692,7 +692,7 @@ export class SkillHubSubFeature {
|
|
|
692
692
|
if (cached) return cached;
|
|
693
693
|
|
|
694
694
|
try {
|
|
695
|
-
const allPlugins = this.app.pm.getPlugins();
|
|
695
|
+
const allPlugins = (this as any).app.pm.getPlugins();
|
|
696
696
|
for (const [, pluginInstance] of allPlugins) {
|
|
697
697
|
if (typeof (pluginInstance as any).getSkillTemplates !== 'function') continue;
|
|
698
698
|
const pluginSkills = (pluginInstance as any).getSkillTemplates();
|
|
@@ -705,7 +705,7 @@ export class SkillHubSubFeature {
|
|
|
705
705
|
}
|
|
706
706
|
}
|
|
707
707
|
} catch (e: any) {
|
|
708
|
-
this.app.logger.warn(`[skill-hub] Failed to resolve plugin skill "${templateName}": ${e.message}`);
|
|
708
|
+
(this as any).app.logger.warn(`[skill-hub] Failed to resolve plugin skill "${templateName}": ${e.message}`);
|
|
709
709
|
}
|
|
710
710
|
|
|
711
711
|
return null;
|