botmux 2.85.1 → 2.86.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/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +209 -1
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/cost-calculator.d.ts.map +1 -1
- package/dist/core/cost-calculator.js +7 -106
- package/dist/core/cost-calculator.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +240 -2
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/passthrough-commands.d.ts.map +1 -1
- package/dist/core/passthrough-commands.js +1 -1
- package/dist/core/passthrough-commands.js.map +1 -1
- package/dist/core/role-resolver.d.ts +1 -0
- package/dist/core/role-resolver.d.ts.map +1 -1
- package/dist/core/role-resolver.js +14 -0
- package/dist/core/role-resolver.js.map +1 -1
- package/dist/dashboard/web/app.d.ts.map +1 -1
- package/dist/dashboard/web/app.js +15 -4
- package/dist/dashboard/web/app.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults.js +116 -0
- package/dist/dashboard/web/bot-defaults.js.map +1 -1
- package/dist/dashboard/web/groups.d.ts +2 -0
- package/dist/dashboard/web/groups.d.ts.map +1 -1
- package/dist/dashboard/web/groups.js +419 -3
- package/dist/dashboard/web/groups.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +617 -3
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/insights.d.ts +2 -0
- package/dist/dashboard/web/insights.d.ts.map +1 -0
- package/dist/dashboard/web/insights.js +1523 -0
- package/dist/dashboard/web/insights.js.map +1 -0
- package/dist/dashboard/web/role-profile-match.d.ts +31 -0
- package/dist/dashboard/web/role-profile-match.d.ts.map +1 -0
- package/dist/dashboard/web/role-profile-match.js +58 -0
- package/dist/dashboard/web/role-profile-match.js.map +1 -0
- package/dist/dashboard/web/roles.d.ts +1 -0
- package/dist/dashboard/web/roles.d.ts.map +1 -1
- package/dist/dashboard/web/roles.js +520 -27
- package/dist/dashboard/web/roles.js.map +1 -1
- package/dist/dashboard/web/sessions.d.ts.map +1 -1
- package/dist/dashboard/web/sessions.js +84 -0
- package/dist/dashboard/web/sessions.js.map +1 -1
- package/dist/dashboard-web/app.js +1243 -831
- package/dist/dashboard-web/index.html +2 -1
- package/dist/dashboard-web/style.css +1085 -3
- package/dist/dashboard.js +215 -3
- package/dist/dashboard.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +34 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +34 -1
- package/dist/i18n/zh.js.map +1 -1
- package/dist/services/group-creator.d.ts +6 -0
- package/dist/services/group-creator.d.ts.map +1 -1
- package/dist/services/group-creator.js +54 -5
- package/dist/services/group-creator.js.map +1 -1
- package/dist/services/insight/antigravity-span-reader.d.ts +3 -0
- package/dist/services/insight/antigravity-span-reader.d.ts.map +1 -0
- package/dist/services/insight/antigravity-span-reader.js +249 -0
- package/dist/services/insight/antigravity-span-reader.js.map +1 -0
- package/dist/services/insight/classify.d.ts +7 -0
- package/dist/services/insight/classify.d.ts.map +1 -0
- package/dist/services/insight/classify.js +46 -0
- package/dist/services/insight/classify.js.map +1 -0
- package/dist/services/insight/claude-span-reader.d.ts +3 -0
- package/dist/services/insight/claude-span-reader.d.ts.map +1 -0
- package/dist/services/insight/claude-span-reader.js +257 -0
- package/dist/services/insight/claude-span-reader.js.map +1 -0
- package/dist/services/insight/codex-span-reader.d.ts +3 -0
- package/dist/services/insight/codex-span-reader.d.ts.map +1 -0
- package/dist/services/insight/codex-span-reader.js +290 -0
- package/dist/services/insight/codex-span-reader.js.map +1 -0
- package/dist/services/insight/intent.d.ts +5 -0
- package/dist/services/insight/intent.d.ts.map +1 -0
- package/dist/services/insight/intent.js +145 -0
- package/dist/services/insight/intent.js.map +1 -0
- package/dist/services/insight/jsonl.d.ts +10 -0
- package/dist/services/insight/jsonl.d.ts.map +1 -0
- package/dist/services/insight/jsonl.js +36 -0
- package/dist/services/insight/jsonl.js.map +1 -0
- package/dist/services/insight/prompt.d.ts +3 -0
- package/dist/services/insight/prompt.d.ts.map +1 -0
- package/dist/services/insight/prompt.js +99 -0
- package/dist/services/insight/prompt.js.map +1 -0
- package/dist/services/insight/redact.d.ts +4 -0
- package/dist/services/insight/redact.d.ts.map +1 -0
- package/dist/services/insight/redact.js +67 -0
- package/dist/services/insight/redact.js.map +1 -0
- package/dist/services/insight/report.d.ts +29 -0
- package/dist/services/insight/report.d.ts.map +1 -0
- package/dist/services/insight/report.js +1126 -0
- package/dist/services/insight/report.js.map +1 -0
- package/dist/services/insight/safe-detail.d.ts +5 -0
- package/dist/services/insight/safe-detail.d.ts.map +1 -0
- package/dist/services/insight/safe-detail.js +59 -0
- package/dist/services/insight/safe-detail.js.map +1 -0
- package/dist/services/insight/scrub.d.ts +22 -0
- package/dist/services/insight/scrub.d.ts.map +1 -0
- package/dist/services/insight/scrub.js +70 -0
- package/dist/services/insight/scrub.js.map +1 -0
- package/dist/services/insight/types.d.ts +394 -0
- package/dist/services/insight/types.d.ts.map +1 -0
- package/dist/services/insight/types.js +2 -0
- package/dist/services/insight/types.js.map +1 -0
- package/dist/services/role-profile-store.d.ts +25 -0
- package/dist/services/role-profile-store.d.ts.map +1 -0
- package/dist/services/role-profile-store.js +171 -0
- package/dist/services/role-profile-store.js.map +1 -0
- package/dist/services/transcript-resolver.d.ts +26 -0
- package/dist/services/transcript-resolver.d.ts.map +1 -0
- package/dist/services/transcript-resolver.js +111 -0
- package/dist/services/transcript-resolver.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-handler.d.ts","sourceRoot":"","sources":["../../src/core/command-handler.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAQxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAoF,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACjJ,OAAO,EAA8D,KAAK,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACtI,OAAO,EAAuB,KAAK,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"command-handler.d.ts","sourceRoot":"","sources":["../../src/core/command-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -40,9 +40,11 @@ import { ttadkConfigModelChoices } from '../setup/cli-selection.js';
|
|
|
40
40
|
import { publishAttentionPatch, announcePendingRepoSession } from './session-activity.js';
|
|
41
41
|
import { setCardMode } from '../services/card-mode-store.js';
|
|
42
42
|
import { canOperate } from '../im/lark/event-dispatcher.js';
|
|
43
|
+
import { buildSafeInsightReport } from '../services/insight/report.js';
|
|
43
44
|
import { invalidWorkingDirs } from '../utils/working-dir.js';
|
|
44
|
-
import { writeRoleFile, deleteRoleFile, resolveRole, resolveTeamRoleFile, writeTeamRoleFile, deleteTeamRoleFile } from './role-resolver.js';
|
|
45
|
+
import { writeRoleFile, deleteRoleFile, resolveRole, resolveRoleFile, resolveTeamRoleFile, writeTeamRoleFile, deleteTeamRoleFile } from './role-resolver.js';
|
|
45
46
|
import { getBotCapability, setBotCapability, clearBotCapability } from '../services/bot-profile-store.js';
|
|
47
|
+
import { deleteRoleProfileEntry, deleteRoleProfileIfEmpty, isValidRoleProfileId, listRoleProfileEntries, listRoleProfiles, MAX_ROLE_PROFILE_ENTRY_BYTES, readRoleProfileEntry, writeRoleProfileEntry, } from '../services/role-profile-store.js';
|
|
46
48
|
import { sessionKey, sessionAnchorId } from './types.js';
|
|
47
49
|
import { t, localeForBot } from '../i18n/index.js';
|
|
48
50
|
import { runSkillsImCommand } from './skills/im-command.js';
|
|
@@ -292,6 +294,139 @@ async function handleRoleCommand(args, rootId, chatId, larkAppId, senderId, deps
|
|
|
292
294
|
const trimmed = args.trim();
|
|
293
295
|
const loc = localeForBot(larkAppId);
|
|
294
296
|
const dataDir = config.session.dataDir;
|
|
297
|
+
// /role profile [...] — reusable suites of per-bot chat roles. Profiles are
|
|
298
|
+
// not a runtime role layer; applying one materializes this bot's entry into
|
|
299
|
+
// the current chat role.
|
|
300
|
+
const profileMatch = trimmed.match(/^profile\b([\s\S]*)$/);
|
|
301
|
+
if (profileMatch) {
|
|
302
|
+
const profileArgs = profileMatch[1].trim();
|
|
303
|
+
const subMatch = profileArgs.match(/^(\S+)(?:\s+([\s\S]*))?$/);
|
|
304
|
+
const sub = (subMatch?.[1] ?? '').toLowerCase();
|
|
305
|
+
const subBody = subMatch?.[2]?.trim() ?? '';
|
|
306
|
+
if (!sub || sub === 'help') {
|
|
307
|
+
await sessionReply(rootId, t('role.profile.help', undefined, loc));
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
if (sub === 'list' || sub === 'ls') {
|
|
311
|
+
const profiles = listRoleProfiles(dataDir);
|
|
312
|
+
if (profiles.length === 0) {
|
|
313
|
+
await sessionReply(rootId, t('role.profile.list_empty', undefined, loc));
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const lines = profiles.map(p => {
|
|
317
|
+
const hasEntry = readRoleProfileEntry(dataDir, p.profileId, larkAppId) !== null;
|
|
318
|
+
const status = hasEntry
|
|
319
|
+
? t('role.profile.current_configured', undefined, loc)
|
|
320
|
+
: t('role.profile.current_missing', undefined, loc);
|
|
321
|
+
return `• ${p.profileId} — ${p.entryCount} ${t('role.profile.entries', undefined, loc)}; ${status}`;
|
|
322
|
+
});
|
|
323
|
+
await sessionReply(rootId, `${t('role.profile.list_header', undefined, loc)}\n${lines.join('\n')}`);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const [profileId = '', ...afterProfile] = subBody.split(/\s+/);
|
|
327
|
+
if (!profileId || !isValidRoleProfileId(profileId)) {
|
|
328
|
+
await sessionReply(rootId, t('role.profile.invalid', undefined, loc));
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
if (sub === 'show') {
|
|
332
|
+
const showAll = afterProfile.includes('--all');
|
|
333
|
+
if (showAll) {
|
|
334
|
+
const entries = listRoleProfileEntries(dataDir, profileId);
|
|
335
|
+
if (entries.length === 0) {
|
|
336
|
+
await sessionReply(rootId, t('role.profile.no_entries', { profile: profileId }, loc));
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
const body = entries.map(entry => `### ${entry.larkAppId}\n${t('role.byte_count', { bytes: entry.byteLength, max: MAX_ROLE_PROFILE_ENTRY_BYTES }, loc)}\n\`\`\`markdown\n${entry.content}\n\`\`\``).join('\n\n');
|
|
340
|
+
await sessionReply(rootId, `${t('role.profile.show_all_header', { profile: profileId }, loc)}\n${body}`);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
const content = readRoleProfileEntry(dataDir, profileId, larkAppId);
|
|
344
|
+
if (content === null) {
|
|
345
|
+
await sessionReply(rootId, t('role.profile.entry_empty', { profile: profileId }, loc));
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
await sessionReply(rootId, `${t('role.profile.entry_current', { profile: profileId }, loc)}\n\`\`\`markdown\n${content}\n\`\`\`\n${t('role.byte_count', { bytes: Buffer.byteLength(content, 'utf-8'), max: MAX_ROLE_PROFILE_ENTRY_BYTES }, loc)}`);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
if (sub === 'set') {
|
|
352
|
+
const content = subBody.slice(profileId.length).trim();
|
|
353
|
+
if (!content) {
|
|
354
|
+
await sessionReply(rootId, t('role.profile.set_empty', undefined, loc));
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
writeRoleProfileEntry(dataDir, profileId, larkAppId, content);
|
|
358
|
+
await sessionReply(rootId, t('role.profile.entry_saved', {
|
|
359
|
+
profile: profileId,
|
|
360
|
+
bytes: Math.min(Buffer.byteLength(content.trim(), 'utf-8'), MAX_ROLE_PROFILE_ENTRY_BYTES),
|
|
361
|
+
max: MAX_ROLE_PROFILE_ENTRY_BYTES,
|
|
362
|
+
}, loc));
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (sub === 'save') {
|
|
366
|
+
const { content, source } = resolveRole(larkAppId, chatId);
|
|
367
|
+
if (!content) {
|
|
368
|
+
await sessionReply(rootId, t('role.profile.save_no_effective', { profile: profileId }, loc));
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
writeRoleProfileEntry(dataDir, profileId, larkAppId, content);
|
|
372
|
+
await sessionReply(rootId, t('role.profile.saved_effective', {
|
|
373
|
+
profile: profileId,
|
|
374
|
+
source,
|
|
375
|
+
bytes: Buffer.byteLength(content, 'utf-8'),
|
|
376
|
+
max: MAX_ROLE_PROFILE_ENTRY_BYTES,
|
|
377
|
+
}, loc));
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (sub === 'delete' || sub === 'del' || sub === 'rm' || sub === '删除') {
|
|
381
|
+
const existed = deleteRoleProfileEntry(dataDir, profileId, larkAppId);
|
|
382
|
+
deleteRoleProfileIfEmpty(dataDir, profileId);
|
|
383
|
+
await sessionReply(rootId, existed
|
|
384
|
+
? t('role.profile.entry_deleted', { profile: profileId }, loc)
|
|
385
|
+
: t('role.profile.entry_nothing', { profile: profileId }, loc));
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
if (sub === 'apply') {
|
|
389
|
+
const flags = new Set(afterProfile);
|
|
390
|
+
const preview = flags.has('--preview');
|
|
391
|
+
const force = flags.has('--force');
|
|
392
|
+
const quiet = flags.has('--quiet');
|
|
393
|
+
const content = readRoleProfileEntry(dataDir, profileId, larkAppId);
|
|
394
|
+
if (content === null) {
|
|
395
|
+
await sessionReply(rootId, t('role.profile.apply_missing', { profile: profileId }, loc));
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
const existing = resolveRoleFile(larkAppId, chatId);
|
|
399
|
+
const bytes = Buffer.byteLength(content, 'utf-8');
|
|
400
|
+
if (preview) {
|
|
401
|
+
const overwriteLine = existing && !force
|
|
402
|
+
? `\n${t('role.profile.apply_would_refuse', undefined, loc)}`
|
|
403
|
+
: '';
|
|
404
|
+
await sessionReply(rootId, `${t('role.profile.apply_preview', { profile: profileId, bytes, max: MAX_ROLE_PROFILE_ENTRY_BYTES }, loc)}${overwriteLine}\n\`\`\`markdown\n${content}\n\`\`\``);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
if (existing && !force) {
|
|
408
|
+
// An empty entry would *clear* the chat role, not overwrite it — phrase
|
|
409
|
+
// the --force refusal accordingly so the intent is not misread.
|
|
410
|
+
const refusedKey = content ? 'role.profile.apply_refused' : 'role.profile.apply_refused_clear';
|
|
411
|
+
await sessionReply(rootId, t(refusedKey, { profile: profileId }, loc));
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
if (!content) {
|
|
415
|
+
deleteRoleFile(larkAppId, chatId);
|
|
416
|
+
if (!quiet) {
|
|
417
|
+
await sessionReply(rootId, t('role.profile.applied', { profile: profileId, bytes, max: MAX_ROLE_PROFILE_ENTRY_BYTES }, loc));
|
|
418
|
+
}
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
writeRoleFile(larkAppId, chatId, content);
|
|
422
|
+
if (!quiet) {
|
|
423
|
+
await sessionReply(rootId, t('role.profile.applied', { profile: profileId, bytes, max: MAX_ROLE_PROFILE_ENTRY_BYTES }, loc));
|
|
424
|
+
}
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
await sessionReply(rootId, t('role.profile.help', undefined, loc));
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
295
430
|
// /role team [...] — manage the team-level (per-bot, cross-chat) role
|
|
296
431
|
const teamMatch = trimmed.match(/^team\b([\s\S]*)$/);
|
|
297
432
|
if (teamMatch) {
|
|
@@ -886,6 +1021,38 @@ export async function handleTermLinkCommand(rootId, larkAppId, chatId, senderOpe
|
|
|
886
1021
|
}
|
|
887
1022
|
// channel === 'ephemeral': the visible-to-you card IS the response; no extra msg.
|
|
888
1023
|
}
|
|
1024
|
+
/** Format a SafeInsightReport into a compact owner-facing summary for the
|
|
1025
|
+
* `/insight` command. Spans are never rendered here — the dashboard Insight tab
|
|
1026
|
+
* owns span detail; the chat card stays a one-glance summary (aggregate + the
|
|
1027
|
+
* severity-sorted rule suggestions, top first). */
|
|
1028
|
+
function formatInsightCard(report, loc) {
|
|
1029
|
+
if (report.status === 'unsupported_cli')
|
|
1030
|
+
return t('cmd.insight.unsupported', undefined, loc);
|
|
1031
|
+
if (report.status === 'transcript_missing')
|
|
1032
|
+
return t('cmd.insight.no_transcript', undefined, loc);
|
|
1033
|
+
if (report.status !== 'ok')
|
|
1034
|
+
return t('cmd.insight.parse_error', undefined, loc);
|
|
1035
|
+
const a = report.agg;
|
|
1036
|
+
if (a.totalSpans === 0)
|
|
1037
|
+
return t('cmd.insight.no_spans', undefined, loc);
|
|
1038
|
+
const icon = (s) => (s === 'bad' ? '🔴' : s === 'warn' ? '🟡' : 'ℹ️');
|
|
1039
|
+
const header = t('cmd.insight.header', undefined, loc);
|
|
1040
|
+
const lines = [report.meta.asOf ? `${header} · ${report.meta.asOf}` : header];
|
|
1041
|
+
lines.push(t('cmd.insight.metrics_line', {
|
|
1042
|
+
total: String(a.totalSpans),
|
|
1043
|
+
failed: String(a.failedSpans),
|
|
1044
|
+
slow: String(a.slowSpans),
|
|
1045
|
+
rw: a.readWriteRatio === null ? '—' : String(a.readWriteRatio),
|
|
1046
|
+
compactions: String(a.compactions),
|
|
1047
|
+
}, loc));
|
|
1048
|
+
lines.push('', `${t('cmd.insight.suggestions_label', undefined, loc)}:`);
|
|
1049
|
+
for (const s of report.suggestions) {
|
|
1050
|
+
lines.push(`${icon(s.severity)} ${s.title} — ${s.action}`);
|
|
1051
|
+
if (s.evidence.length)
|
|
1052
|
+
lines.push(` · ${s.evidence.join(';')}`);
|
|
1053
|
+
}
|
|
1054
|
+
return lines.join('\n');
|
|
1055
|
+
}
|
|
889
1056
|
export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
890
1057
|
const { activeSessions, getActiveCount, lastRepoScan } = deps;
|
|
891
1058
|
// Command replies carry the triggering messageId as the turnId so a shared
|
|
@@ -919,6 +1086,28 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
|
919
1086
|
}
|
|
920
1087
|
break;
|
|
921
1088
|
}
|
|
1089
|
+
case '/insight': {
|
|
1090
|
+
if (!ds) {
|
|
1091
|
+
await sessionReply(rootId, t('cmd.no_active_session', undefined, loc));
|
|
1092
|
+
break;
|
|
1093
|
+
}
|
|
1094
|
+
// owner-only:与 /card /term 同一 operator 门(开放模式下 owner 通过;
|
|
1095
|
+
// 仅对话授权的 grantee 不算 operator)。无权限直接不回内容。
|
|
1096
|
+
if (!canOperate(larkAppId, ds.chatId, message.senderId)) {
|
|
1097
|
+
await sessionReply(rootId, t('cmd.insight.operator_only', undefined, loc));
|
|
1098
|
+
break;
|
|
1099
|
+
}
|
|
1100
|
+
// 卡片只取 summary(聚合 + 规则建议);span 明细留给 dashboard Insight tab。
|
|
1101
|
+
// buildSafeInsightReport 同步、只读、自带 fail-closed 脱敏,raw 永不进结构。
|
|
1102
|
+
const report = buildSafeInsightReport({
|
|
1103
|
+
cliId: ds.session.cliId ?? 'unknown',
|
|
1104
|
+
sessionId: ds.session.sessionId,
|
|
1105
|
+
cliSessionId: ds.session.cliSessionId,
|
|
1106
|
+
cwd: ds.session.workingDir,
|
|
1107
|
+
}, { detail: 'summary' });
|
|
1108
|
+
await sessionReply(rootId, formatInsightCard(report, loc));
|
|
1109
|
+
break;
|
|
1110
|
+
}
|
|
922
1111
|
case '/land': {
|
|
923
1112
|
// 把沙盒会话副本里 agent 的改动落回真实仓库。owner 审阅 diff 卡后点「应用到磁盘」。
|
|
924
1113
|
// agent 在沙盒里无感(以为改的就是真文件),所以只能由 owner 在此手动触发。
|
|
@@ -1811,6 +2000,16 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
|
1811
2000
|
if (m.name)
|
|
1812
2001
|
rawArgs = rawArgs.split(`@${m.name}`).join(' ');
|
|
1813
2002
|
}
|
|
2003
|
+
let roleProfileId;
|
|
2004
|
+
const roleProfileArg = rawArgs.match(/(?:^|\s)--role-profile(?:=|\s+)(\S+)/);
|
|
2005
|
+
if (roleProfileArg) {
|
|
2006
|
+
if (!isValidRoleProfileId(roleProfileArg[1])) {
|
|
2007
|
+
await sessionReply(rootId, t('role.profile.invalid', undefined, loc));
|
|
2008
|
+
break;
|
|
2009
|
+
}
|
|
2010
|
+
roleProfileId = roleProfileArg[1];
|
|
2011
|
+
rawArgs = rawArgs.replace(roleProfileArg[0], ' ');
|
|
2012
|
+
}
|
|
1814
2013
|
const firstLine = rawArgs.split(/\r?\n/).map(s => s.trim()).find(Boolean) ?? '';
|
|
1815
2014
|
const MAX_NAME = 50; // Lark group names cap around 60; leave headroom for '…'
|
|
1816
2015
|
let groupName;
|
|
@@ -1834,6 +2033,7 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
|
1834
2033
|
userOpenIds: [senderOpenId],
|
|
1835
2034
|
transferOwnerTo: senderOpenId,
|
|
1836
2035
|
notifyOwnerOpenId: senderOpenId,
|
|
2036
|
+
roleProfileId,
|
|
1837
2037
|
});
|
|
1838
2038
|
// Prefer the shareable join link (others can click to *join*); fall
|
|
1839
2039
|
// back to the member-only applink URL when Lark's link API failed.
|
|
@@ -1866,6 +2066,14 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
|
1866
2066
|
if (result.invalidBotIds.length > 0) {
|
|
1867
2067
|
hints.push(t('cmd.group.warn_bots_rejected', { bots: result.invalidBotIds.map(nameOf).join('、') }, loc));
|
|
1868
2068
|
}
|
|
2069
|
+
if (roleProfileId) {
|
|
2070
|
+
if (result.roleProfileBootstrapError) {
|
|
2071
|
+
hints.push(t('cmd.group.role_profile_bootstrap_failed', { profile: roleProfileId, reason: result.roleProfileBootstrapError ?? 'unknown' }, loc));
|
|
2072
|
+
}
|
|
2073
|
+
else {
|
|
2074
|
+
hints.push(t('cmd.group.role_profile_bootstrap_sent', { profile: roleProfileId }, loc));
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
1869
2077
|
const hintsText = hints.length > 0 ? '\n' + hints.join('\n') : '';
|
|
1870
2078
|
await sessionReply(rootId, t('cmd.group.created', { name: groupName, link, hints: hintsText }, loc));
|
|
1871
2079
|
logger.info(`[${logTag}] /group created chat=${result.chatId} name="${groupName}" bots=[${larkAppIdsForGroup.join(',')}] invitee=${senderOpenId}`);
|