imtoagent 0.3.24 → 0.3.26
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/bin/imtoagent-real +374 -1
- package/index.ts +31 -4
- package/modules/cli/setup.ts +1 -0
- package/modules/core/types.ts +1 -0
- package/modules/utils/config-manager.ts +374 -0
- package/modules/utils/doctor.ts +462 -0
- package/modules/utils/workspace-manager.ts +23 -3
- package/package.json +1 -1
package/bin/imtoagent-real
CHANGED
|
@@ -16,7 +16,7 @@ import * as fs from 'fs';
|
|
|
16
16
|
import * as path from 'path';
|
|
17
17
|
import * as readline from 'readline';
|
|
18
18
|
import { spawn, execSync } from 'child_process';
|
|
19
|
-
import { getDataDir } from '../modules/utils/paths';
|
|
19
|
+
import { getDataDir, getLogsDir } from '../modules/utils/paths';
|
|
20
20
|
|
|
21
21
|
const PID_FILE = '/tmp/imtoagent.pid';
|
|
22
22
|
|
|
@@ -61,9 +61,21 @@ switch (command) {
|
|
|
61
61
|
case 'health':
|
|
62
62
|
await cmdHealth();
|
|
63
63
|
break;
|
|
64
|
+
case 'doctor':
|
|
65
|
+
await cmdDoctor();
|
|
66
|
+
break;
|
|
67
|
+
case 'config':
|
|
68
|
+
await cmdConfig();
|
|
69
|
+
break;
|
|
64
70
|
case 'autostart':
|
|
65
71
|
await cmdAutostart();
|
|
66
72
|
break;
|
|
73
|
+
case 'logs':
|
|
74
|
+
await cmdLogs();
|
|
75
|
+
break;
|
|
76
|
+
case 'validate':
|
|
77
|
+
await cmdValidate();
|
|
78
|
+
break;
|
|
67
79
|
case undefined: {
|
|
68
80
|
const dataDir = getDataDir();
|
|
69
81
|
const configPath = path.join(dataDir, 'config.json');
|
|
@@ -121,9 +133,20 @@ Usage:
|
|
|
121
133
|
imtoagent uninstall Uninstall imtoagent (keep data by default)
|
|
122
134
|
imtoagent uninstall --purge Uninstall and delete all data
|
|
123
135
|
imtoagent health Run comprehensive health check
|
|
136
|
+
imtoagent doctor Diagnose & fix configuration issues
|
|
137
|
+
imtoagent config Manage Bot configuration
|
|
138
|
+
imtoagent config list List all Bots
|
|
139
|
+
imtoagent config show NAME Show Bot details
|
|
140
|
+
imtoagent config add Add a new Bot
|
|
141
|
+
imtoagent config remove NAME Remove a Bot
|
|
142
|
+
imtoagent config modify NAME Modify Bot settings
|
|
124
143
|
imtoagent autostart enable Enable auto-start on login (launchd)
|
|
125
144
|
imtoagent autostart disable Disable auto-start
|
|
126
145
|
imtoagent autostart status Check auto-start status
|
|
146
|
+
imtoagent logs Show last 50 lines of log
|
|
147
|
+
imtoagent logs -n N Show last N lines
|
|
148
|
+
imtoagent logs -f Follow log in real-time (tail -f)
|
|
149
|
+
imtoagent validate Validate config.json for errors
|
|
127
150
|
|
|
128
151
|
Data directory: ${getDataDir()}
|
|
129
152
|
`);
|
|
@@ -939,6 +962,177 @@ async function cmdHealth(): Promise<void> {
|
|
|
939
962
|
console.log();
|
|
940
963
|
}
|
|
941
964
|
|
|
965
|
+
// ================================================================
|
|
966
|
+
// config — Bot 配置管理
|
|
967
|
+
// ================================================================
|
|
968
|
+
async function cmdConfig(): Promise<void> {
|
|
969
|
+
const subcommand = process.argv[3];
|
|
970
|
+
|
|
971
|
+
switch (subcommand) {
|
|
972
|
+
case 'list':
|
|
973
|
+
await cmdConfigList();
|
|
974
|
+
break;
|
|
975
|
+
case 'show': {
|
|
976
|
+
const name = process.argv[4];
|
|
977
|
+
if (!name) { console.error('Usage: imtoagent config show <name>'); process.exit(1); }
|
|
978
|
+
await cmdConfigShow(name);
|
|
979
|
+
break;
|
|
980
|
+
}
|
|
981
|
+
case 'add':
|
|
982
|
+
await cmdConfigAdd();
|
|
983
|
+
break;
|
|
984
|
+
case 'remove': {
|
|
985
|
+
const name = process.argv[4];
|
|
986
|
+
if (!name) { console.error('Usage: imtoagent config remove <name>'); process.exit(1); }
|
|
987
|
+
await cmdConfigRemove(name);
|
|
988
|
+
break;
|
|
989
|
+
}
|
|
990
|
+
case 'modify': {
|
|
991
|
+
const name = process.argv[4];
|
|
992
|
+
if (!name) { console.error('Usage: imtoagent config modify <name>'); process.exit(1); }
|
|
993
|
+
await cmdConfigModify(name);
|
|
994
|
+
break;
|
|
995
|
+
}
|
|
996
|
+
case undefined:
|
|
997
|
+
case 'help':
|
|
998
|
+
case '--help':
|
|
999
|
+
case '-h':
|
|
1000
|
+
console.log(`
|
|
1001
|
+
imtoagent config — Manage Bot configuration
|
|
1002
|
+
|
|
1003
|
+
Usage:
|
|
1004
|
+
imtoagent config list List all Bots
|
|
1005
|
+
imtoagent config show NAME Show Bot details
|
|
1006
|
+
imtoagent config add Add a new Bot (interactive)
|
|
1007
|
+
imtoagent config remove NAME Remove a Bot
|
|
1008
|
+
imtoagent config modify NAME Modify Bot settings
|
|
1009
|
+
`);
|
|
1010
|
+
break;
|
|
1011
|
+
default:
|
|
1012
|
+
console.error(`❌ Unknown config subcommand: ${subcommand}`);
|
|
1013
|
+
console.log(' Run "imtoagent config help" for usage.');
|
|
1014
|
+
process.exit(1);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
// ---- Config subcommand wrappers ----
|
|
1019
|
+
async function cmdConfigList(): Promise<void> {
|
|
1020
|
+
const m = await import('../modules/utils/config-manager');
|
|
1021
|
+
await m.cmdConfigList();
|
|
1022
|
+
}
|
|
1023
|
+
async function cmdConfigShow(name: string): Promise<void> {
|
|
1024
|
+
const m = await import('../modules/utils/config-manager');
|
|
1025
|
+
await m.cmdConfigShow(name);
|
|
1026
|
+
}
|
|
1027
|
+
async function cmdConfigAdd(): Promise<void> {
|
|
1028
|
+
const m = await import('../modules/utils/config-manager');
|
|
1029
|
+
await m.cmdConfigAdd();
|
|
1030
|
+
}
|
|
1031
|
+
async function cmdConfigRemove(name: string): Promise<void> {
|
|
1032
|
+
const m = await import('../modules/utils/config-manager');
|
|
1033
|
+
await m.cmdConfigRemove(name);
|
|
1034
|
+
}
|
|
1035
|
+
async function cmdConfigModify(name: string): Promise<void> {
|
|
1036
|
+
const m = await import('../modules/utils/config-manager');
|
|
1037
|
+
await m.cmdConfigModify(name);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
|
|
1041
|
+
// ================================================================
|
|
1042
|
+
// doctor — 配置诊断与自动修复
|
|
1043
|
+
// ================================================================
|
|
1044
|
+
async function cmdDoctor(): Promise<void> {
|
|
1045
|
+
console.log(`\n🔧 imtoagent Doctor — Configuration Diagnosis\n`);
|
|
1046
|
+
|
|
1047
|
+
try {
|
|
1048
|
+
const { runDoctorChecks, formatIssues } = await import('../modules/utils/doctor');
|
|
1049
|
+
const issues = await runDoctorChecks();
|
|
1050
|
+
|
|
1051
|
+
// 分组
|
|
1052
|
+
const fixableIssues = issues.filter(i => i.fixable);
|
|
1053
|
+
const unfixableIssues = issues.filter(i => !i.fixable);
|
|
1054
|
+
const errors = issues.filter(i => i.severity === 'error');
|
|
1055
|
+
const warnings = issues.filter(i => i.severity === 'warning');
|
|
1056
|
+
const infos = issues.filter(i => i.severity === 'info');
|
|
1057
|
+
|
|
1058
|
+
// 打印所有问题
|
|
1059
|
+
if (issues.length === 0) {
|
|
1060
|
+
console.log(' ✅ All checks passed! Nothing to fix.\n');
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
// 打印 errors
|
|
1065
|
+
if (errors.length > 0) {
|
|
1066
|
+
console.log('❌ Errors:');
|
|
1067
|
+
for (const e of errors) {
|
|
1068
|
+
console.log(` ${e.message}`);
|
|
1069
|
+
if (e.fixable && e.fixDescription) {
|
|
1070
|
+
console.log(` → 🔧 ${e.fixDescription}`);
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
console.log();
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
// 打印 warnings
|
|
1077
|
+
if (warnings.length > 0) {
|
|
1078
|
+
console.log('⚠️ Warnings:');
|
|
1079
|
+
for (const w of warnings) {
|
|
1080
|
+
console.log(` ${w.message}`);
|
|
1081
|
+
}
|
|
1082
|
+
console.log();
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// 打印 infos
|
|
1086
|
+
if (infos.length > 0) {
|
|
1087
|
+
console.log('✅ OK:');
|
|
1088
|
+
for (const i of infos) {
|
|
1089
|
+
console.log(` ${i.message}`);
|
|
1090
|
+
}
|
|
1091
|
+
console.log();
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
// 尝试自动修复
|
|
1095
|
+
if (fixableIssues.length > 0) {
|
|
1096
|
+
console.log(`─── Auto-Fix ───`);
|
|
1097
|
+
let fixed = 0;
|
|
1098
|
+
for (const issue of fixableIssues) {
|
|
1099
|
+
if (!issue.fix) continue;
|
|
1100
|
+
try {
|
|
1101
|
+
console.log(`\n🔧 Fixing: ${issue.fixDescription}`);
|
|
1102
|
+
const success = await issue.fix();
|
|
1103
|
+
if (success) {
|
|
1104
|
+
console.log(` ✅ Fixed`);
|
|
1105
|
+
fixed++;
|
|
1106
|
+
} else {
|
|
1107
|
+
console.log(` ❌ Fix failed`);
|
|
1108
|
+
}
|
|
1109
|
+
} catch (e: any) {
|
|
1110
|
+
console.log(` ❌ Fix failed: ${e.message}`);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
console.log(`\n ${fixed}/${fixableIssues.length} issues fixed`);
|
|
1114
|
+
if (fixed > 0) {
|
|
1115
|
+
console.log(`\n💡 Run "imtoagent doctor" again to re-check after fixes.\n`);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
// Summary
|
|
1120
|
+
console.log('── Summary ──');
|
|
1121
|
+
if (errors.length === 0) {
|
|
1122
|
+
console.log(' ✅ No errors found');
|
|
1123
|
+
} else {
|
|
1124
|
+
console.log(` ❌ ${errors.length} error(s) — some may be fixable`);
|
|
1125
|
+
}
|
|
1126
|
+
if (warnings.length > 0) {
|
|
1127
|
+
console.log(` ⚠️ ${warnings.length} warning(s) — review recommended`);
|
|
1128
|
+
}
|
|
1129
|
+
console.log();
|
|
1130
|
+
|
|
1131
|
+
} catch (e: any) {
|
|
1132
|
+
console.error(`❌ Doctor check failed: ${e.message}\n`);
|
|
1133
|
+
process.exit(1);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
942
1136
|
// ================================================================
|
|
943
1137
|
// autostart — launchd integration (macOS only)
|
|
944
1138
|
// ================================================================
|
|
@@ -1084,6 +1278,185 @@ async function cmdAutostartStatus(plistPath: string): Promise<void> {
|
|
|
1084
1278
|
console.log();
|
|
1085
1279
|
}
|
|
1086
1280
|
|
|
1281
|
+
// ================================================================
|
|
1282
|
+
// logs — 查看日志
|
|
1283
|
+
// ================================================================
|
|
1284
|
+
async function cmdLogs(): Promise<void> {
|
|
1285
|
+
const argv = process.argv.slice(3);
|
|
1286
|
+
let n = 50;
|
|
1287
|
+
let follow = false;
|
|
1288
|
+
|
|
1289
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1290
|
+
if (argv[i] === '-n' && argv[i + 1]) {
|
|
1291
|
+
n = parseInt(argv[++i], 10);
|
|
1292
|
+
if (isNaN(n) || n < 1) {
|
|
1293
|
+
console.error('❌ Invalid line count');
|
|
1294
|
+
process.exit(1);
|
|
1295
|
+
}
|
|
1296
|
+
} else if (argv[i] === '-f') {
|
|
1297
|
+
follow = true;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
const logsDir = getLogsDir();
|
|
1302
|
+
const logFile = path.join(logsDir, 'imtoagent.log');
|
|
1303
|
+
|
|
1304
|
+
if (!fs.existsSync(logFile)) {
|
|
1305
|
+
console.log('ℹ️ No log file found.');
|
|
1306
|
+
console.log(` Expected: ${logFile}`);
|
|
1307
|
+
console.log(' Start the gateway first: imtoagent start');
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
if (follow) {
|
|
1312
|
+
// tail -f mode — spawn tail process
|
|
1313
|
+
console.log(`📄 Following log: ${logFile} (Ctrl+C to stop)\n`);
|
|
1314
|
+
const child = spawn('tail', ['-f', '-n', String(n), logFile], {
|
|
1315
|
+
stdio: 'inherit',
|
|
1316
|
+
});
|
|
1317
|
+
|
|
1318
|
+
const cleanup = () => {
|
|
1319
|
+
try { child.kill('SIGTERM'); } catch {}
|
|
1320
|
+
};
|
|
1321
|
+
process.on('SIGINT', cleanup);
|
|
1322
|
+
process.on('SIGTERM', cleanup);
|
|
1323
|
+
|
|
1324
|
+
await new Promise<void>((resolve) => {
|
|
1325
|
+
child.on('exit', () => resolve());
|
|
1326
|
+
child.on('error', () => resolve());
|
|
1327
|
+
});
|
|
1328
|
+
} else {
|
|
1329
|
+
// Print last N lines
|
|
1330
|
+
try {
|
|
1331
|
+
const content = fs.readFileSync(logFile, 'utf-8');
|
|
1332
|
+
const lines = content.split('\n');
|
|
1333
|
+
const tail = lines.slice(-n).filter(l => l.length > 0);
|
|
1334
|
+
if (tail.length === 0) {
|
|
1335
|
+
console.log('ℹ️ Log file is empty.');
|
|
1336
|
+
} else {
|
|
1337
|
+
console.log(`📄 Last ${tail.length} lines of ${logFile}:\n`);
|
|
1338
|
+
for (const line of tail) {
|
|
1339
|
+
console.log(line);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
} catch (e: any) {
|
|
1343
|
+
console.error(`❌ Failed to read log: ${e.message}`);
|
|
1344
|
+
process.exit(1);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
// ================================================================
|
|
1350
|
+
// validate — 验证配置文件
|
|
1351
|
+
// ================================================================
|
|
1352
|
+
async function cmdValidate(): Promise<void> {
|
|
1353
|
+
const dataDir = getDataDir();
|
|
1354
|
+
const configPath = path.join(dataDir, 'config.json');
|
|
1355
|
+
|
|
1356
|
+
console.log(`\n🔍 imtoagent Config Validation\n`);
|
|
1357
|
+
console.log(` Config: ${configPath}\n`);
|
|
1358
|
+
|
|
1359
|
+
const issues: string[] = [];
|
|
1360
|
+
const okItems: string[] = [];
|
|
1361
|
+
|
|
1362
|
+
// ---- 1. Check file exists ----
|
|
1363
|
+
if (!fs.existsSync(configPath)) {
|
|
1364
|
+
console.error('❌ config.json not found');
|
|
1365
|
+
console.error(` Run "imtoagent setup" to create it.\n`);
|
|
1366
|
+
process.exit(1);
|
|
1367
|
+
}
|
|
1368
|
+
okItems.push('config.json exists');
|
|
1369
|
+
|
|
1370
|
+
// ---- 2. Parse JSON ----
|
|
1371
|
+
let raw: string;
|
|
1372
|
+
let config: any;
|
|
1373
|
+
try {
|
|
1374
|
+
raw = fs.readFileSync(configPath, 'utf-8');
|
|
1375
|
+
config = JSON.parse(raw);
|
|
1376
|
+
okItems.push('JSON format is valid');
|
|
1377
|
+
} catch (e: any) {
|
|
1378
|
+
console.error(`❌ Invalid JSON: ${e.message}`);
|
|
1379
|
+
console.error(` Please fix the syntax error and try again.\n`);
|
|
1380
|
+
process.exit(1);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// ---- 3. Validate bots array ----
|
|
1384
|
+
const validIMs = ['feishu', 'telegram', 'wecom', 'wechat'];
|
|
1385
|
+
const validBackends = ['claude', 'codex', 'opencode'];
|
|
1386
|
+
|
|
1387
|
+
if (!config.bots || !Array.isArray(config.bots)) {
|
|
1388
|
+
issues.push('bots is missing or not an array');
|
|
1389
|
+
} else if (config.bots.length === 0) {
|
|
1390
|
+
issues.push('bots array is empty — no bots configured');
|
|
1391
|
+
} else {
|
|
1392
|
+
okItems.push(`bots array has ${config.bots.length} entry/entries`);
|
|
1393
|
+
|
|
1394
|
+
config.bots.forEach((bot: any, idx: number) => {
|
|
1395
|
+
const label = `bots[${idx}]`;
|
|
1396
|
+
const botName = bot.name || `(unnamed, index ${idx})`;
|
|
1397
|
+
|
|
1398
|
+
// Check required fields
|
|
1399
|
+
if (!bot.name) issues.push(`${label}: missing required field "name"`);
|
|
1400
|
+
if (!bot.im) issues.push(`${label}: missing required field "im"`);
|
|
1401
|
+
if (!bot.appId) issues.push(`${label}: missing required field "appId"`);
|
|
1402
|
+
if (!bot.backend) issues.push(`${label}: missing required field "backend"`);
|
|
1403
|
+
|
|
1404
|
+
// Validate IM platform
|
|
1405
|
+
if (bot.im && !validIMs.includes(bot.im)) {
|
|
1406
|
+
issues.push(`${label}: invalid im value "${bot.im}" — must be one of: ${validIMs.join(', ')}`);
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
// Validate backend
|
|
1410
|
+
if (bot.backend && !validBackends.includes(bot.backend)) {
|
|
1411
|
+
issues.push(`${label}: invalid backend value "${bot.backend}" — must be one of: ${validBackends.join(', ')}`);
|
|
1412
|
+
}
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
// ---- 4. Validate workspace config ----
|
|
1417
|
+
const ws = config.workspace || {};
|
|
1418
|
+
if (ws.mode === 'global') {
|
|
1419
|
+
if (!ws.globalPath) {
|
|
1420
|
+
issues.push('workspace: mode is "global" but globalPath is not set');
|
|
1421
|
+
} else {
|
|
1422
|
+
if (!fs.existsSync(ws.globalPath)) {
|
|
1423
|
+
issues.push(`workspace: globalPath directory does not exist: ${ws.globalPath}`);
|
|
1424
|
+
} else {
|
|
1425
|
+
okItems.push(`workspace: globalPath exists (${ws.globalPath})`);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
} else if (ws.mode && ws.mode !== 'sandbox') {
|
|
1429
|
+
issues.push(`workspace: invalid mode "${ws.mode}" — must be "sandbox" or "global"`);
|
|
1430
|
+
} else {
|
|
1431
|
+
okItems.push(`workspace: mode is "${ws.mode || 'sandbox'}" (default)`);
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
// ---- Print results ----
|
|
1435
|
+
if (okItems.length > 0) {
|
|
1436
|
+
console.log('✅ OK:');
|
|
1437
|
+
for (const item of okItems) {
|
|
1438
|
+
console.log(` ✓ ${item}`);
|
|
1439
|
+
}
|
|
1440
|
+
console.log();
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
if (issues.length > 0) {
|
|
1444
|
+
console.log('❌ Issues:');
|
|
1445
|
+
for (const issue of issues) {
|
|
1446
|
+
console.log(` ✗ ${issue}`);
|
|
1447
|
+
}
|
|
1448
|
+
console.log();
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
// Summary
|
|
1452
|
+
if (issues.length === 0) {
|
|
1453
|
+
console.log('✅ Configuration is valid!\n');
|
|
1454
|
+
} else {
|
|
1455
|
+
console.log(`❌ ${issues.length} issue(s) found. Please fix them.\n`);
|
|
1456
|
+
process.exit(1);
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1087
1460
|
// ================================================================
|
|
1088
1461
|
// version-check — non-blocking npm registry check
|
|
1089
1462
|
// ================================================================
|
package/index.ts
CHANGED
|
@@ -289,6 +289,7 @@ interface BotConfig {
|
|
|
289
289
|
appSecret: string;
|
|
290
290
|
backend: 'claude' | 'codex' | 'opencode';
|
|
291
291
|
cwd?: string;
|
|
292
|
+
isAdmin?: boolean; // true = 可以修改网关配置,默认第一个 Bot 为 true
|
|
292
293
|
}
|
|
293
294
|
|
|
294
295
|
// ================================================================
|
|
@@ -307,6 +308,7 @@ class Bot {
|
|
|
307
308
|
soul: string;
|
|
308
309
|
client: Lark.Client;
|
|
309
310
|
im: IMModule;
|
|
311
|
+
isAdmin: boolean;
|
|
310
312
|
config: any;
|
|
311
313
|
workspaceManager: WorkspaceManager;
|
|
312
314
|
|
|
@@ -327,6 +329,7 @@ class Bot {
|
|
|
327
329
|
this.appSecret = cfg.appSecret;
|
|
328
330
|
this.defaultCwd = cfg.cwd || globalConfig.system?.defaultProjectDir || path.join(os.homedir(), 'Projects');
|
|
329
331
|
this.config = globalConfig;
|
|
332
|
+
this.isAdmin = cfg.isAdmin !== undefined ? cfg.isAdmin : true; // 默认 true(后向兼容老用户)
|
|
330
333
|
this.workspaceManager = workspaceManager;
|
|
331
334
|
|
|
332
335
|
// 确保工作空间目录存在
|
|
@@ -412,8 +415,13 @@ class Bot {
|
|
|
412
415
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
413
416
|
const hasFiles = fs.readdirSync(dir).some((f: string) => f.endsWith('.md'));
|
|
414
417
|
if (hasFiles) return;
|
|
418
|
+
// 根据 isAdmin 生成不同的 rules.md
|
|
419
|
+
const rulesMd = this.isAdmin
|
|
420
|
+
? '# Bot Rules\n\nYou are an **admin Bot** with full gateway management privileges.\n\n- You can modify IMtoAgent configuration (config.json, providers, Bot management)\n- You have access to any directory (global workspace mode)\n- Sensitive information such as project keys, tokens, and passwords must not be leaked\n- Destructive commands must not be executed without explicit confirmation'
|
|
421
|
+
: '# Bot Rules\n\nYou are a **non-admin Bot** with restricted privileges.\n\n- ⛔ NEVER modify any files under ~/.imtoagent/ (config.json, providers.json, etc.)\n- ⛔ NEVER read or expose gateway configuration contents\n- ⛔ NEVER attempt to add, remove, or modify Bot configurations\n- Your working directory is limited to your assigned workspace\n- All file operations must stay within your workspace boundary\n- Sensitive information such as project keys, tokens, and passwords must not be leaked\n- Destructive commands must not be executed without explicit confirmation';
|
|
422
|
+
|
|
415
423
|
const defaults: Record<string, string> = {
|
|
416
|
-
'rules.md':
|
|
424
|
+
'rules.md': rulesMd,
|
|
417
425
|
'identity.md': `# Identity\n\n- I am an AI programming assistant connected via IMtoAgent\n- I run on the ${this.backend === 'codex' ? 'Codex' : 'Claude Code'} backend\n- Reply in Chinese`,
|
|
418
426
|
'profile.md': '# User Profile\n\nThis file can be modified by the Agent. When the user says "remember xxx" or "I prefer xxx", the Agent should update this file.\n\n## Modification Guide (Agent Only)\n\nRead this file → Add/delete/modify entries based on user requests → Save',
|
|
419
427
|
'workspace.md': '# Project Environment\n\nAuto-generated by IMtoAgent.',
|
|
@@ -437,11 +445,30 @@ class Bot {
|
|
|
437
445
|
for (const file of order) {
|
|
438
446
|
const fp = dir + '/' + file;
|
|
439
447
|
if (fs.existsSync(fp)) {
|
|
440
|
-
const
|
|
441
|
-
if (
|
|
448
|
+
const c = fs.readFileSync(fp, 'utf-8').trim();
|
|
449
|
+
if (c) parts.push(c);
|
|
442
450
|
}
|
|
443
451
|
}
|
|
444
452
|
} catch {}
|
|
453
|
+
|
|
454
|
+
// Inject config CLI reference — so the Agent knows how to manage Bots via natural language
|
|
455
|
+
const adminTag = this.isAdmin ? '' : ' (you are a non-admin Bot — you cannot use these commands, tell the user to use an admin Bot)';
|
|
456
|
+
const bt = String.fromCharCode(96); // backtick
|
|
457
|
+
const fence = bt + bt + bt;
|
|
458
|
+
const cliRef = '## IMtoAgent Config CLI' +
|
|
459
|
+
'\n\nYou can manage Bot configurations via these CLI commands:' + adminTag +
|
|
460
|
+
'\n\n' + fence + 'bash' +
|
|
461
|
+
'\nimtoagent config list # List all Bots' +
|
|
462
|
+
'\nimtoagent config show <BotName> # Show a Bot\'s details' +
|
|
463
|
+
'\nimtoagent config add # Add a new Bot (interactive)' +
|
|
464
|
+
'\nimtoagent config remove <BotName> # Remove a Bot' +
|
|
465
|
+
'\nimtoagent config modify <BotName> # Modify a Bot\'s settings' +
|
|
466
|
+
'\nimtoagent restore # Hot-reload after config changes' +
|
|
467
|
+
'\nimtoagent doctor # Diagnose config issues' +
|
|
468
|
+
'\n' + fence +
|
|
469
|
+
'\n\nWhen the user asks to manage Bots, use these commands. After changes, run ' + bt + 'imtoagent restore' + bt + '.';
|
|
470
|
+
parts.push(cliRef);
|
|
471
|
+
|
|
445
472
|
return parts.join('\n\n');
|
|
446
473
|
}
|
|
447
474
|
|
|
@@ -499,7 +526,7 @@ class Bot {
|
|
|
499
526
|
: `⏸ ${this.backend} idle | ${this.activeModel}`);
|
|
500
527
|
|
|
501
528
|
cmd('/info', ({ session }) =>
|
|
502
|
-
`🤖 ${this.name} (${this.backend})\nModel: ${this.activeModel}\nDirectory: ${session?.cwd || this.defaultCwd}\nSessions: ${this.sessions.size}`);
|
|
529
|
+
`🤖 ${this.name} (${this.backend})${this.isAdmin ? ' ⭐' : ''}\nModel: ${this.activeModel}\nDirectory: ${session?.cwd || this.defaultCwd}\nSessions: ${this.sessions.size}`);
|
|
503
530
|
|
|
504
531
|
cmd('/stats', ({ session }) => {
|
|
505
532
|
if (!session || session.stats.calls === 0) return '📊 No calls yet';
|
package/modules/cli/setup.ts
CHANGED
package/modules/core/types.ts
CHANGED
|
@@ -242,6 +242,7 @@ export interface BotConfig {
|
|
|
242
242
|
/** 唯一标识(UUID,用于目录/文件隔离,改名不影响) */
|
|
243
243
|
id?: string;
|
|
244
244
|
name: string;
|
|
245
|
+
im: string; // 'feishu' | 'telegram' | 'wecom' | 'wechat'
|
|
245
246
|
backend: string; // 'claude' | 'codex' | 'opencode'
|
|
246
247
|
appId: string;
|
|
247
248
|
appSecret: string;
|