plugin-agent-orchestrator 1.0.14 → 1.0.15
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/externalVersion.js +6 -6
- package/dist/server/collections/agent-execution-spans.d.ts +1 -1
- package/dist/server/collections/orchestrator-config.d.ts +1 -1
- package/dist/server/collections/orchestrator-logs.d.ts +1 -1
- package/dist/server/collections/skill-definitions.d.ts +0 -1
- package/dist/server/collections/skill-executions.d.ts +0 -1
- package/dist/server/collections/skill-worker-configs.d.ts +0 -1
- package/dist/server/services/CodeValidator.js +1 -0
- package/dist/server/skill-hub/tasks/SkillExecutionTask.d.ts +2 -0
- package/dist/server/skill-hub/tasks/SkillExecutionTask.js +122 -0
- 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 +94 -94
- package/src/server/services/CodeValidator.ts +5 -5
- package/src/server/services/SkillManager.ts +1 -1
- package/src/server/services/WorkerEnvManager.ts +5 -5
- package/src/server/skill-hub/plugin.ts +58 -58
- package/src/server/skill-hub/tasks/SkillExecutionTask.ts +162 -16
|
@@ -2,11 +2,11 @@ import { Migration } from '@nocobase/server';
|
|
|
2
2
|
|
|
3
3
|
export default class ChangeOtherJsonToTextMigration extends Migration {
|
|
4
4
|
async up() {
|
|
5
|
-
const queryInterface = this.db.sequelize.getQueryInterface();
|
|
6
|
-
const dialect = this.db.sequelize.getDialect();
|
|
7
|
-
const fieldRepo = this.db.getRepository('fields');
|
|
5
|
+
const queryInterface = (this as any).db.sequelize.getQueryInterface();
|
|
6
|
+
const dialect = (this as any).db.sequelize.getDialect();
|
|
7
|
+
const fieldRepo = (this as any).db.getRepository('fields');
|
|
8
8
|
|
|
9
|
-
const tablePrefix = this.db.options.tablePrefix || '';
|
|
9
|
+
const tablePrefix = (this as any).db.options.tablePrefix || '';
|
|
10
10
|
|
|
11
11
|
// 1. skillWorkerConfigs
|
|
12
12
|
const workerTableName = `${tablePrefix}skillWorkerConfigs`;
|
|
@@ -18,18 +18,18 @@ export default class ChangeOtherJsonToTextMigration extends Migration {
|
|
|
18
18
|
for (const col of columns) {
|
|
19
19
|
if (tableDesc[col]) {
|
|
20
20
|
if (dialect === 'postgres') {
|
|
21
|
-
await this.db.sequelize.query(`ALTER TABLE "${workerTableName}" ALTER COLUMN "${col}" TYPE text USING "${col}"::text;`);
|
|
21
|
+
await (this as any).db.sequelize.query(`ALTER TABLE "${workerTableName}" ALTER COLUMN "${col}" TYPE text USING "${col}"::text;`);
|
|
22
22
|
} else {
|
|
23
23
|
await queryInterface.changeColumn(workerTableName, col, { type: 'TEXT' });
|
|
24
24
|
}
|
|
25
25
|
const fieldMeta = await fieldRepo.findOne({ filter: { name: col, collectionName: 'skillWorkerConfigs' } });
|
|
26
26
|
if (fieldMeta) await fieldRepo.update({ filterByTk: fieldMeta.get('id'), values: { type: 'text' } });
|
|
27
|
-
this.app.logger.info(`[skill-hub] Changed ${col} in skillWorkerConfigs to text`);
|
|
27
|
+
(this as any).app.logger.info(`[skill-hub] Changed ${col} in skillWorkerConfigs to text`);
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
} catch (e) {
|
|
32
|
-
this.app.logger.warn(`[skill-hub] Failed to migrate skillWorkerConfigs: ${e.message}`);
|
|
32
|
+
(this as any).app.logger.warn(`[skill-hub] Failed to migrate skillWorkerConfigs: ${e.message}`);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
// 2. skillExecutions
|
|
@@ -41,17 +41,17 @@ export default class ChangeOtherJsonToTextMigration extends Migration {
|
|
|
41
41
|
|
|
42
42
|
if (tableDesc[col]) {
|
|
43
43
|
if (dialect === 'postgres') {
|
|
44
|
-
await this.db.sequelize.query(`ALTER TABLE "${execTableName}" ALTER COLUMN "${col}" TYPE text USING "${col}"::text;`);
|
|
44
|
+
await (this as any).db.sequelize.query(`ALTER TABLE "${execTableName}" ALTER COLUMN "${col}" TYPE text USING "${col}"::text;`);
|
|
45
45
|
} else {
|
|
46
46
|
await queryInterface.changeColumn(execTableName, col, { type: 'TEXT' });
|
|
47
47
|
}
|
|
48
48
|
const fieldMeta = await fieldRepo.findOne({ filter: { name: col, collectionName: 'skillExecutions' } });
|
|
49
49
|
if (fieldMeta) await fieldRepo.update({ filterByTk: fieldMeta.get('id'), values: { type: 'text' } });
|
|
50
|
-
this.app.logger.info(`[skill-hub] Changed ${col} in skillExecutions to text`);
|
|
50
|
+
(this as any).app.logger.info(`[skill-hub] Changed ${col} in skillExecutions to text`);
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
} catch (e) {
|
|
54
|
-
this.app.logger.warn(`[skill-hub] Failed to migrate skillExecutions: ${e.message}`);
|
|
54
|
+
(this as any).app.logger.warn(`[skill-hub] Failed to migrate skillExecutions: ${e.message}`);
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
}
|
|
@@ -5,8 +5,8 @@ export default class AddLlmFieldsToOrchestratorConfig extends Migration {
|
|
|
5
5
|
appVersion = '>=0.1.0';
|
|
6
6
|
|
|
7
7
|
async up() {
|
|
8
|
-
const queryInterface = this.db.sequelize.getQueryInterface();
|
|
9
|
-
const tablePrefix = this.db.options.tablePrefix || '';
|
|
8
|
+
const queryInterface = (this as any).db.sequelize.getQueryInterface();
|
|
9
|
+
const tablePrefix = (this as any).db.options.tablePrefix || '';
|
|
10
10
|
const tableName = `${tablePrefix}orchestratorConfig`;
|
|
11
11
|
|
|
12
12
|
const tableExists = await queryInterface.tableExists(tableName);
|
|
@@ -14,7 +14,7 @@ export default class FixInputArgsJsonToText extends Migration {
|
|
|
14
14
|
appVersion = '>=0.1.0';
|
|
15
15
|
|
|
16
16
|
async up() {
|
|
17
|
-
const queryInterface = this.db.sequelize.getQueryInterface();
|
|
17
|
+
const queryInterface = (this as any).db.sequelize.getQueryInterface();
|
|
18
18
|
|
|
19
19
|
// Check current column type
|
|
20
20
|
const tableDesc = await queryInterface.describeTable('skillExecutions').catch(() => null);
|
|
@@ -25,7 +25,7 @@ export default class FixInputArgsJsonToText extends Migration {
|
|
|
25
25
|
|
|
26
26
|
// Only migrate if still json type
|
|
27
27
|
if (col.type && col.type.toLowerCase().includes('json')) {
|
|
28
|
-
await this.db.sequelize.query(
|
|
28
|
+
await (this as any).db.sequelize.query(
|
|
29
29
|
`ALTER TABLE "skillExecutions" ALTER COLUMN "inputArgs" TYPE text USING "inputArgs"::text`,
|
|
30
30
|
);
|
|
31
31
|
console.log('[skill-hub] Migration: converted skillExecutions.inputArgs from json to text');
|
|
@@ -5,8 +5,8 @@ export default class AddOrchestratorTraceFieldsToSkillExecutions extends Migrati
|
|
|
5
5
|
appVersion = '>=0.1.0';
|
|
6
6
|
|
|
7
7
|
async up() {
|
|
8
|
-
const queryInterface = this.db.sequelize.getQueryInterface();
|
|
9
|
-
const tablePrefix = this.db.options.tablePrefix || '';
|
|
8
|
+
const queryInterface = (this as any).db.sequelize.getQueryInterface();
|
|
9
|
+
const tablePrefix = (this as any).db.options.tablePrefix || '';
|
|
10
10
|
const tableName = `${tablePrefix}skillExecutions`;
|
|
11
11
|
const tableExists = await queryInterface.tableExists(tableName).catch(() => false);
|
|
12
12
|
if (!tableExists) return;
|
package/src/server/plugin.ts
CHANGED
|
@@ -1,94 +1,94 @@
|
|
|
1
|
-
import { Plugin } from '@nocobase/server';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { createDelegateToolsProvider } from './tools/delegate-task';
|
|
4
|
-
import { registerTracingResource } from './resources/tracing';
|
|
5
|
-
import SkillHubSubFeature from './skill-hub/plugin';
|
|
6
|
-
|
|
7
|
-
export class PluginAgentOrchestratorServer extends Plugin {
|
|
8
|
-
skillHub: SkillHubSubFeature;
|
|
9
|
-
|
|
10
|
-
async afterAdd() {
|
|
11
|
-
this.skillHub = new SkillHubSubFeature(this);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async beforeLoad() {
|
|
15
|
-
// Import collection definitions
|
|
16
|
-
this.db.import({ directory: path.resolve(__dirname, 'collections') });
|
|
17
|
-
|
|
18
|
-
this.db.addMigrations({
|
|
19
|
-
namespace: this.name,
|
|
20
|
-
directory: path.resolve(__dirname, 'migrations'),
|
|
21
|
-
context: { plugin: this },
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
async load() {
|
|
26
|
-
await this.skillHub.load();
|
|
27
|
-
|
|
28
|
-
// --- ACL ---
|
|
29
|
-
this.app.acl.registerSnippet({
|
|
30
|
-
name: `pm.${this.name}`,
|
|
31
|
-
actions: ['orchestratorConfig:*', 'orchestratorTracing:*', 'agentExecutionSpans:*', 'skillDefinitions:*', 'skillExecutions:*', 'skillHub:*', 'skillWorkerConfigs:*'],
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
// --- Register Dynamic Tools ---
|
|
35
|
-
// Each configured sub-agent becomes a callable tool for its leader.
|
|
36
|
-
// Uses createReactAgent (LangGraph public API) instead of private AIEmployee class.
|
|
37
|
-
// Tools are registered via app.aiManager.toolsManager (public API from @nocobase/ai core).
|
|
38
|
-
const toolsManager = this.app.aiManager.toolsManager;
|
|
39
|
-
toolsManager.registerDynamicTools(createDelegateToolsProvider(this));
|
|
40
|
-
|
|
41
|
-
// --- Register Tracing Resource (Phase 5) ---
|
|
42
|
-
// Custom read-only resource for the Swarm Tracing admin page.
|
|
43
|
-
registerTracingResource(this);
|
|
44
|
-
|
|
45
|
-
// --- Log Retention ---
|
|
46
|
-
// Daily prune of orchestratorLogs / agentExecutionSpans to keep tables bounded.
|
|
47
|
-
// Override window via env: ORCHESTRATOR_LOG_RETENTION_DAYS (default 30).
|
|
48
|
-
this.app.cronJobManager.addJob({
|
|
49
|
-
cronTime: '0 30 2 * * *',
|
|
50
|
-
onTick: async () => {
|
|
51
|
-
try {
|
|
52
|
-
const days = Number(process.env.ORCHESTRATOR_LOG_RETENTION_DAYS || 30);
|
|
53
|
-
if (!Number.isFinite(days) || days <= 0) return;
|
|
54
|
-
const cutoff = new Date(Date.now() - days * 86400000);
|
|
55
|
-
const repo = this.db.getRepository('orchestratorLogs');
|
|
56
|
-
const spansRepo = this.db.getRepository('agentExecutionSpans');
|
|
57
|
-
const deletedLogs = repo
|
|
58
|
-
? await repo.destroy({
|
|
59
|
-
filter: { createdAt: { $lt: cutoff.toISOString() } },
|
|
60
|
-
})
|
|
61
|
-
: 0;
|
|
62
|
-
const deletedSpans = spansRepo
|
|
63
|
-
? await spansRepo.destroy({
|
|
64
|
-
filter: { createdAt: { $lt: cutoff.toISOString() } },
|
|
65
|
-
})
|
|
66
|
-
: 0;
|
|
67
|
-
this.app.log.info(
|
|
68
|
-
`[AgentOrchestrator] Pruned ${deletedLogs} orchestratorLogs and ${deletedSpans} agentExecutionSpans rows older than ${days} day(s).`,
|
|
69
|
-
);
|
|
70
|
-
} catch (e) {
|
|
71
|
-
this.app.log.error('[AgentOrchestrator] Log retention job failed', e);
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
// NOTE: The createReactAgent approach does NOT create aiConversation records,
|
|
77
|
-
// so there is no need for a middleware to hide "headless" conversations.
|
|
78
|
-
// If future versions need conversation logging, add it here.
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async install() {
|
|
82
|
-
await this.skillHub.install();
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async afterEnable() {}
|
|
86
|
-
async afterDisable() {}
|
|
87
|
-
async remove() {}
|
|
88
|
-
|
|
89
|
-
async beforeStop() {
|
|
90
|
-
await this.skillHub.beforeStop();
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export default PluginAgentOrchestratorServer;
|
|
1
|
+
import { Plugin } from '@nocobase/server';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { createDelegateToolsProvider } from './tools/delegate-task';
|
|
4
|
+
import { registerTracingResource } from './resources/tracing';
|
|
5
|
+
import SkillHubSubFeature from './skill-hub/plugin';
|
|
6
|
+
|
|
7
|
+
export class PluginAgentOrchestratorServer extends Plugin {
|
|
8
|
+
skillHub: SkillHubSubFeature;
|
|
9
|
+
|
|
10
|
+
async afterAdd() {
|
|
11
|
+
this.skillHub = new SkillHubSubFeature(this);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async beforeLoad() {
|
|
15
|
+
// Import collection definitions
|
|
16
|
+
(this as any).db.import({ directory: path.resolve(__dirname, 'collections') });
|
|
17
|
+
|
|
18
|
+
(this as any).db.addMigrations({
|
|
19
|
+
namespace: (this as any).name,
|
|
20
|
+
directory: path.resolve(__dirname, 'migrations'),
|
|
21
|
+
context: { plugin: this },
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async load() {
|
|
26
|
+
await this.skillHub.load();
|
|
27
|
+
|
|
28
|
+
// --- ACL ---
|
|
29
|
+
(this as any).app.acl.registerSnippet({
|
|
30
|
+
name: `pm.${(this as any).name}`,
|
|
31
|
+
actions: ['orchestratorConfig:*', 'orchestratorTracing:*', 'agentExecutionSpans:*', 'skillDefinitions:*', 'skillExecutions:*', 'skillHub:*', 'skillWorkerConfigs:*'],
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// --- Register Dynamic Tools ---
|
|
35
|
+
// Each configured sub-agent becomes a callable tool for its leader.
|
|
36
|
+
// Uses createReactAgent (LangGraph public API) instead of private AIEmployee class.
|
|
37
|
+
// Tools are registered via app.aiManager.toolsManager (public API from @nocobase/ai core).
|
|
38
|
+
const toolsManager = (this as any).app.aiManager.toolsManager;
|
|
39
|
+
toolsManager.registerDynamicTools(createDelegateToolsProvider(this));
|
|
40
|
+
|
|
41
|
+
// --- Register Tracing Resource (Phase 5) ---
|
|
42
|
+
// Custom read-only resource for the Swarm Tracing admin page.
|
|
43
|
+
registerTracingResource(this);
|
|
44
|
+
|
|
45
|
+
// --- Log Retention ---
|
|
46
|
+
// Daily prune of orchestratorLogs / agentExecutionSpans to keep tables bounded.
|
|
47
|
+
// Override window via env: ORCHESTRATOR_LOG_RETENTION_DAYS (default 30).
|
|
48
|
+
(this as any).app.cronJobManager.addJob({
|
|
49
|
+
cronTime: '0 30 2 * * *',
|
|
50
|
+
onTick: async () => {
|
|
51
|
+
try {
|
|
52
|
+
const days = Number(process.env.ORCHESTRATOR_LOG_RETENTION_DAYS || 30);
|
|
53
|
+
if (!Number.isFinite(days) || days <= 0) return;
|
|
54
|
+
const cutoff = new Date(Date.now() - days * 86400000);
|
|
55
|
+
const repo = (this as any).db.getRepository('orchestratorLogs');
|
|
56
|
+
const spansRepo = (this as any).db.getRepository('agentExecutionSpans');
|
|
57
|
+
const deletedLogs = repo
|
|
58
|
+
? await repo.destroy({
|
|
59
|
+
filter: { createdAt: { $lt: cutoff.toISOString() } },
|
|
60
|
+
})
|
|
61
|
+
: 0;
|
|
62
|
+
const deletedSpans = spansRepo
|
|
63
|
+
? await spansRepo.destroy({
|
|
64
|
+
filter: { createdAt: { $lt: cutoff.toISOString() } },
|
|
65
|
+
})
|
|
66
|
+
: 0;
|
|
67
|
+
(this as any).app.log.info(
|
|
68
|
+
`[AgentOrchestrator] Pruned ${deletedLogs} orchestratorLogs and ${deletedSpans} agentExecutionSpans rows older than ${days} day(s).`,
|
|
69
|
+
);
|
|
70
|
+
} catch (e) {
|
|
71
|
+
(this as any).app.log.error('[AgentOrchestrator] Log retention job failed', e);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// NOTE: The createReactAgent approach does NOT create aiConversation records,
|
|
77
|
+
// so there is no need for a middleware to hide "headless" conversations.
|
|
78
|
+
// If future versions need conversation logging, add it here.
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async install() {
|
|
82
|
+
await this.skillHub.install();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async afterEnable() {}
|
|
86
|
+
async afterDisable() {}
|
|
87
|
+
async remove() {}
|
|
88
|
+
|
|
89
|
+
async beforeStop() {
|
|
90
|
+
await this.skillHub.beforeStop();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export default PluginAgentOrchestratorServer;
|
|
@@ -40,10 +40,10 @@ const NODE_BUILTINS = [
|
|
|
40
40
|
const PYTHON_BUILTINS = [
|
|
41
41
|
'os', 'sys', 'json', 'math', 'datetime', 'collections', 'itertools',
|
|
42
42
|
'functools', 'pathlib', 'typing', 'io', 'csv', 're', 'string', 'textwrap',
|
|
43
|
-
'decimal', 'fractions', 'random', 'statistics', 'copy', 'enum', 'dataclasses',
|
|
44
|
-
'abc', 'contextlib', 'operator', 'time', 'calendar', 'locale', 'struct',
|
|
45
|
-
'hashlib', 'base64', 'binascii', 'codecs', 'unicodedata', 'pprint',
|
|
46
|
-
'warnings', 'traceback', 'logging', 'unittest', 'argparse',
|
|
43
|
+
'decimal', 'fractions', 'random', 'statistics', 'copy', 'enum', 'dataclasses',
|
|
44
|
+
'abc', 'contextlib', 'operator', 'time', 'calendar', 'locale', 'struct',
|
|
45
|
+
'hashlib', 'base64', 'binascii', 'codecs', 'unicodedata', 'pprint',
|
|
46
|
+
'warnings', 'traceback', 'logging', 'unittest', 'argparse', 'ast',
|
|
47
47
|
'tempfile', 'xml', 'zipfile',
|
|
48
48
|
// Pre-installed local packages (trusted, bundled with plugin)
|
|
49
49
|
'svg_to_pptx',
|
|
@@ -154,6 +154,6 @@ export class CodeValidationError extends Error {
|
|
|
154
154
|
public matchedPattern: string,
|
|
155
155
|
) {
|
|
156
156
|
super(`Code validation failed: ${message}`);
|
|
157
|
-
this.name = 'CodeValidationError';
|
|
157
|
+
(this as any).name = 'CodeValidationError';
|
|
158
158
|
}
|
|
159
159
|
}
|
|
@@ -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.',
|