agent-window 1.2.7 → 1.3.0
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/package.json
CHANGED
|
@@ -244,7 +244,7 @@ export async function registerInstanceRoutes(fastify) {
|
|
|
244
244
|
|
|
245
245
|
// Use botName for PM2 lookup, default to bot-{name}
|
|
246
246
|
const botName = instance.botName || `bot-${name}`;
|
|
247
|
-
const status = await getStatus(botName);
|
|
247
|
+
const status = await getStatus(botName, { instanceType: instance.instanceType });
|
|
248
248
|
return { ...status, instanceName: name };
|
|
249
249
|
} catch (error) {
|
|
250
250
|
reply.code(500).send({
|
|
@@ -21,6 +21,37 @@ import { getInstance } from '../../core/instance/manager.js';
|
|
|
21
21
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
22
22
|
const PACKAGE_ROOT = path.join(__dirname, '..', '..', '..');
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Normalize instance type to handle legacy values
|
|
26
|
+
* Maps old/invalid types to current supported types
|
|
27
|
+
*
|
|
28
|
+
* @param {string} type - Raw instance type
|
|
29
|
+
* @returns {'bmad-plugin' | 'simple-config'} Normalized type
|
|
30
|
+
*/
|
|
31
|
+
function normalizeInstanceType(type) {
|
|
32
|
+
if (!type) return 'simple-config';
|
|
33
|
+
|
|
34
|
+
const typeMap = {
|
|
35
|
+
'standalone': 'simple-config', // Legacy: convert to simple-config
|
|
36
|
+
'bmad': 'bmad-plugin', // Legacy: shorthand
|
|
37
|
+
'simple': 'simple-config', // Legacy: shorthand
|
|
38
|
+
'unknown': 'simple-config', // Fallback for unknown types
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// If exact match, return mapped value
|
|
42
|
+
if (typeMap[type] !== undefined) {
|
|
43
|
+
return typeMap[type];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// If already a valid type, return as-is
|
|
47
|
+
if (type === 'bmad-plugin' || type === 'simple-config') {
|
|
48
|
+
return type;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Safe default for any unexpected value
|
|
52
|
+
return 'simple-config';
|
|
53
|
+
}
|
|
54
|
+
|
|
24
55
|
/**
|
|
25
56
|
* Register operation routes
|
|
26
57
|
*/
|
|
@@ -49,28 +80,23 @@ export async function registerOperationRoutes(fastify) {
|
|
|
49
80
|
});
|
|
50
81
|
}
|
|
51
82
|
|
|
52
|
-
//
|
|
83
|
+
// Normalize instance type to handle legacy values
|
|
84
|
+
const normalizedType = normalizeInstanceType(instance.instanceType);
|
|
85
|
+
|
|
86
|
+
// Determine script path based on normalized instance type
|
|
53
87
|
let scriptPath;
|
|
54
88
|
const cwd = instance.projectPath;
|
|
55
89
|
|
|
56
|
-
switch (
|
|
90
|
+
switch (normalizedType) {
|
|
57
91
|
case 'bmad-plugin':
|
|
58
92
|
// BMAD plugin: start _agent-bridge/src/bot.js
|
|
59
93
|
scriptPath = path.join(instance.projectPath, '_agent-bridge', 'src', 'bot.js');
|
|
60
94
|
break;
|
|
61
|
-
case 'standalone':
|
|
62
|
-
// Standalone: start src/bot.js from project root
|
|
63
|
-
scriptPath = path.join(instance.projectPath, 'src', 'bot.js');
|
|
64
|
-
break;
|
|
65
95
|
case 'simple-config':
|
|
66
|
-
|
|
96
|
+
default:
|
|
97
|
+
// Simple config: use global agent-window bot.js (default fallback)
|
|
67
98
|
scriptPath = path.join(PACKAGE_ROOT, 'src', 'bot.js');
|
|
68
99
|
break;
|
|
69
|
-
default:
|
|
70
|
-
return reply.code(400).send({
|
|
71
|
-
error: 'Unknown instance type',
|
|
72
|
-
instanceType: instance.instanceType
|
|
73
|
-
});
|
|
74
100
|
}
|
|
75
101
|
|
|
76
102
|
const botName = instance.botName || `bot-${name}`;
|
|
@@ -144,6 +144,9 @@ export async function addInstance(name, projectPath, options = {}) {
|
|
|
144
144
|
pluginPath: null,
|
|
145
145
|
botName: `bot-${name}`,
|
|
146
146
|
instanceType: 'simple-config',
|
|
147
|
+
// Extensible fields for future platform/AI provider support
|
|
148
|
+
platform: options.platform || 'discord', // discord | telegram | slack
|
|
149
|
+
aiProvider: options.aiProvider || null, // anthropic | openai | microsoft
|
|
147
150
|
addedAt: new Date().toISOString(),
|
|
148
151
|
tags: options.tags || [],
|
|
149
152
|
enabled: true
|
|
@@ -165,9 +168,7 @@ export async function addInstance(name, projectPath, options = {}) {
|
|
|
165
168
|
// Only BMAD plugins can be manually added
|
|
166
169
|
const typeMsg = validation.instanceType === 'simple-config'
|
|
167
170
|
? '简单配置实例无法通过"添加实例"功能添加。请使用"Discover"功能导入。'
|
|
168
|
-
:
|
|
169
|
-
? '无法识别的项目类型,必须是有效的 BMAD 插件'
|
|
170
|
-
: '项目不是有效的 AgentWindow BMAD 插件';
|
|
171
|
+
: '项目不是有效的 BMAD 插件';
|
|
171
172
|
|
|
172
173
|
return {
|
|
173
174
|
success: false,
|
|
@@ -196,6 +197,9 @@ export async function addInstance(name, projectPath, options = {}) {
|
|
|
196
197
|
configPath,
|
|
197
198
|
botName: `bot-${name}`,
|
|
198
199
|
instanceType: validation.instanceType,
|
|
200
|
+
// Extensible fields for future platform/AI provider support
|
|
201
|
+
platform: options.platform || 'discord', // discord | telegram | slack
|
|
202
|
+
aiProvider: options.aiProvider || null, // anthropic | openai | microsoft
|
|
199
203
|
addedAt: new Date().toISOString(),
|
|
200
204
|
tags: options.tags || [],
|
|
201
205
|
enabled: true
|
|
@@ -285,7 +289,7 @@ export async function updateInstance(name, updates) {
|
|
|
285
289
|
}
|
|
286
290
|
|
|
287
291
|
// Allowed fields to update
|
|
288
|
-
const allowedFields = ['displayName', 'tags', 'enabled', 'configPath'];
|
|
292
|
+
const allowedFields = ['displayName', 'tags', 'enabled', 'configPath', 'platform', 'aiProvider'];
|
|
289
293
|
const instance = data.instances[index];
|
|
290
294
|
|
|
291
295
|
for (const [key, value] of Object.entries(updates)) {
|
|
@@ -155,9 +155,12 @@ export async function getLogs(name, options = {}) {
|
|
|
155
155
|
/**
|
|
156
156
|
* Get process status summary
|
|
157
157
|
* @param {string} name - Process name
|
|
158
|
+
* @param {Object} options - Options
|
|
159
|
+
* @param {string} options.instanceType - Instance type for type-aware checks
|
|
158
160
|
* @returns {Promise<Object>} Status info
|
|
159
161
|
*/
|
|
160
|
-
export async function getStatus(name) {
|
|
162
|
+
export async function getStatus(name, options = {}) {
|
|
163
|
+
const { instanceType } = options;
|
|
161
164
|
const proc = await getProcess(name);
|
|
162
165
|
|
|
163
166
|
if (!proc) {
|
|
@@ -198,54 +201,64 @@ export async function getStatus(name) {
|
|
|
198
201
|
console.debug('[Status] Could not read config for container name:', e.message);
|
|
199
202
|
}
|
|
200
203
|
|
|
201
|
-
//
|
|
204
|
+
// Type-aware status detection
|
|
202
205
|
let dockerRunning = false;
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
206
|
+
|
|
207
|
+
// For bmad-plugin: Only check PM2 status (BMAD plugin manages its own Docker)
|
|
208
|
+
if (instanceType === 'bmad-plugin') {
|
|
209
|
+
healthStatus.docker = false; // Not tracked by AgentWindow for BMAD plugins
|
|
210
|
+
healthStatus.pm2 = procStatus === 'online';
|
|
211
|
+
|
|
212
|
+
// BMAD plugin status depends only on PM2
|
|
213
|
+
if (procStatus === 'online') {
|
|
214
|
+
healthStatus.overall = 'healthy';
|
|
215
|
+
} else if (procStatus === 'errored') {
|
|
216
|
+
healthStatus.overall = 'error';
|
|
217
|
+
} else {
|
|
218
|
+
healthStatus.overall = 'stopped';
|
|
214
219
|
}
|
|
215
220
|
}
|
|
221
|
+
// For simple-config: Check both PM2 and Docker status
|
|
222
|
+
else {
|
|
223
|
+
// simple-config instances may use Docker sandbox
|
|
224
|
+
if (containerName && procStatus === 'online') {
|
|
225
|
+
try {
|
|
226
|
+
const { execSync } = await import('child_process');
|
|
227
|
+
const result = execSync(
|
|
228
|
+
`docker inspect -f '{{.State.Running}}' ${containerName} 2>/dev/null`,
|
|
229
|
+
{ encoding: 'utf-8', timeout: 5000 }
|
|
230
|
+
).trim();
|
|
231
|
+
dockerRunning = result === 'true';
|
|
232
|
+
} catch (e) {
|
|
233
|
+
dockerRunning = false;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
216
236
|
|
|
217
|
-
|
|
218
|
-
|
|
237
|
+
healthStatus.docker = dockerRunning;
|
|
238
|
+
healthStatus.pm2 = procStatus === 'online';
|
|
219
239
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
240
|
+
// Determine overall status based on PM2 + Docker combination
|
|
241
|
+
if (procStatus === 'stopped') {
|
|
242
|
+
healthStatus.overall = 'stopped';
|
|
243
|
+
} else if (procStatus === 'errored') {
|
|
244
|
+
healthStatus.overall = 'error';
|
|
245
|
+
} else if (procStatus === 'online') {
|
|
246
|
+
if (dockerRunning) {
|
|
247
|
+
healthStatus.overall = 'healthy';
|
|
248
|
+
} else if (containerName) {
|
|
249
|
+
healthStatus.overall = 'degraded'; // Has container but not running
|
|
250
|
+
} else {
|
|
251
|
+
healthStatus.overall = 'healthy'; // No Docker configured
|
|
252
|
+
}
|
|
231
253
|
} else {
|
|
232
|
-
|
|
233
|
-
healthStatus.overall = 'healthy';
|
|
254
|
+
healthStatus.overall = 'unknown';
|
|
234
255
|
}
|
|
235
|
-
} else {
|
|
236
|
-
healthStatus.overall = 'unknown';
|
|
237
256
|
}
|
|
238
257
|
|
|
239
258
|
// Determine display status (what frontend shows)
|
|
240
259
|
let displayStatus = procStatus;
|
|
241
260
|
if (procStatus === 'online') {
|
|
242
|
-
|
|
243
|
-
displayStatus = 'running';
|
|
244
|
-
} else if (containerName) {
|
|
245
|
-
displayStatus = 'degraded'; // PM2 running but Docker down
|
|
246
|
-
} else {
|
|
247
|
-
displayStatus = 'running'; // No Docker, just PM2
|
|
248
|
-
}
|
|
261
|
+
displayStatus = 'running'; // PM2 online = running (Docker state is in health.docker)
|
|
249
262
|
}
|
|
250
263
|
|
|
251
264
|
return {
|
|
@@ -258,7 +271,8 @@ export async function getStatus(name) {
|
|
|
258
271
|
memory: proc.memory || 0,
|
|
259
272
|
cpu: proc.cpu || 0,
|
|
260
273
|
restarts: proc.restarts || 0,
|
|
261
|
-
health: healthStatus
|
|
274
|
+
health: healthStatus,
|
|
275
|
+
instanceType // Include for frontend reference
|
|
262
276
|
};
|
|
263
277
|
}
|
|
264
278
|
|
|
@@ -12,9 +12,18 @@ import path from 'path';
|
|
|
12
12
|
import { existsSync } from 'fs';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Detect instance type
|
|
15
|
+
* Detect instance type based on project structure
|
|
16
|
+
*
|
|
16
17
|
* @param {string} projectPath - Path to check
|
|
17
|
-
* @returns {
|
|
18
|
+
* @returns {'bmad-plugin' | 'simple-config'} Instance type (always returns one of two types)
|
|
19
|
+
*
|
|
20
|
+
* Detection Logic:
|
|
21
|
+
* 1. BMAD Plugin: Has _agent-bridge/src/bot.js (full BMAD framework)
|
|
22
|
+
* 2. Simple Config: Any other path (fallback, uses global bot.js)
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* detectInstanceType('/path/to/bmad-project') // returns 'bmad-plugin'
|
|
26
|
+
* detectInstanceType('/path/to/simple') // returns 'simple-config'
|
|
18
27
|
*/
|
|
19
28
|
export function detectInstanceType(projectPath) {
|
|
20
29
|
const normalizedPath = path.resolve(projectPath);
|
|
@@ -28,15 +37,9 @@ export function detectInstanceType(projectPath) {
|
|
|
28
37
|
return 'bmad-plugin';
|
|
29
38
|
}
|
|
30
39
|
|
|
31
|
-
//
|
|
32
|
-
// This
|
|
33
|
-
|
|
34
|
-
const hasConfig = existsSync(path.join(normalizedPath, 'config.json'));
|
|
35
|
-
if (hasConfig) {
|
|
36
|
-
return 'simple-config';
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return 'unknown';
|
|
40
|
+
// Default to simple-config for all other cases
|
|
41
|
+
// This provides a safe fallback that allows the instance to run
|
|
42
|
+
return 'simple-config';
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
/**
|
|
@@ -52,7 +55,7 @@ export function detectInstanceType(projectPath) {
|
|
|
52
55
|
* Validation result
|
|
53
56
|
* @typedef {Object} ValidationResult
|
|
54
57
|
* @property {boolean} valid - Overall validation result
|
|
55
|
-
* @property {string} instanceType - 'bmad' | 'simple'
|
|
58
|
+
* @property {string} instanceType - 'bmad-plugin' | 'simple-config'
|
|
56
59
|
* @property {Array<Object>} checks - Individual check results
|
|
57
60
|
* @property {string|null} pluginPath - Path to the plugin directory
|
|
58
61
|
* @property {string} projectPath - Path to the project directory
|
|
@@ -96,11 +99,12 @@ export async function validateInstance(projectPath) {
|
|
|
96
99
|
// Detect instance type
|
|
97
100
|
const instanceType = detectInstanceType(normalizedPath);
|
|
98
101
|
|
|
102
|
+
// Type detection always succeeds (returns bmad-plugin or simple-config)
|
|
99
103
|
checks.push({
|
|
100
104
|
name: 'type.detected',
|
|
101
|
-
passed:
|
|
105
|
+
passed: true, // Always passes - detectInstanceType always returns a valid type
|
|
102
106
|
required: true,
|
|
103
|
-
error:
|
|
107
|
+
error: null // No error - type is always valid
|
|
104
108
|
});
|
|
105
109
|
|
|
106
110
|
let pluginPath = null;
|