metame-cli 1.5.22 → 1.5.23
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/index.js +18 -1
- package/package.json +1 -1
- package/scripts/daemon-agent-commands.js +10 -1
- package/scripts/daemon-session-commands.js +16 -2
- package/scripts/daemon-session-store.js +104 -0
- package/scripts/daemon.js +7 -0
- package/scripts/resolve-yaml.js +3 -0
- package/scripts/runtime-bootstrap.js +77 -0
package/index.js
CHANGED
|
@@ -540,11 +540,28 @@ if (syntaxErrors.length > 0) {
|
|
|
540
540
|
const changed = syncDirFiles(group.srcDir, destDir, { fileList: group.fileList });
|
|
541
541
|
return updated || changed;
|
|
542
542
|
}, false);
|
|
543
|
-
|
|
543
|
+
if (scriptsUpdated) {
|
|
544
544
|
console.log(`${icon("pkg")} Scripts synced to ~/.metame/.`);
|
|
545
545
|
}
|
|
546
546
|
}
|
|
547
547
|
|
|
548
|
+
try {
|
|
549
|
+
const runtimeEnvFile = path.join(METAME_DIR, 'runtime-env.json');
|
|
550
|
+
const runtimeNodeModules = path.join(__dirname, 'node_modules');
|
|
551
|
+
const runtimeEnvPayload = {
|
|
552
|
+
metameRoot: __dirname,
|
|
553
|
+
nodeModules: runtimeNodeModules,
|
|
554
|
+
generatedAt: new Date().toISOString(),
|
|
555
|
+
};
|
|
556
|
+
const nextContent = JSON.stringify(runtimeEnvPayload, null, 2) + '\n';
|
|
557
|
+
const prevContent = fs.existsSync(runtimeEnvFile) ? fs.readFileSync(runtimeEnvFile, 'utf8') : '';
|
|
558
|
+
if (prevContent !== nextContent) {
|
|
559
|
+
fs.writeFileSync(runtimeEnvFile, nextContent, 'utf8');
|
|
560
|
+
}
|
|
561
|
+
} catch (e) {
|
|
562
|
+
console.log(`${icon("warn")} Runtime env sync skipped: ${e.message}`);
|
|
563
|
+
}
|
|
564
|
+
|
|
548
565
|
// Docs: lazy-load references for CLAUDE.md pointer instructions
|
|
549
566
|
syncDirFiles(path.join(__dirname, 'scripts', 'docs'), path.join(METAME_DIR, 'docs'));
|
|
550
567
|
// Bin: CLI tools (dispatch_to etc.)
|
package/package.json
CHANGED
|
@@ -39,6 +39,7 @@ function createAgentCommandHandler(deps) {
|
|
|
39
39
|
loadSessionTags,
|
|
40
40
|
sessionRichLabel,
|
|
41
41
|
getSessionRecentContext,
|
|
42
|
+
getSessionRecentDialogue,
|
|
42
43
|
pendingBinds,
|
|
43
44
|
pendingAgentFlows,
|
|
44
45
|
pendingTeamFlows,
|
|
@@ -421,11 +422,19 @@ function createAgentCommandHandler(deps) {
|
|
|
421
422
|
|
|
422
423
|
// 读取最近对话片段,帮助确认是否切换到正确的 session
|
|
423
424
|
const recentCtx = getSessionRecentContext ? getSessionRecentContext(targetSessionId) : null;
|
|
425
|
+
const recentDialogue = getSessionRecentDialogue ? getSessionRecentDialogue(targetSessionId, 4) : null;
|
|
424
426
|
let msg = `✅ 已切换: **${label}**\n📁 ${path.basename(cwd)}`;
|
|
425
427
|
if (selectedLogicalCurrent) {
|
|
426
428
|
msg += '\n\n已恢复当前智能体会话。';
|
|
427
429
|
}
|
|
428
|
-
if (
|
|
430
|
+
if (Array.isArray(recentDialogue) && recentDialogue.length > 0) {
|
|
431
|
+
msg += '\n\n最近对话:';
|
|
432
|
+
for (const item of recentDialogue) {
|
|
433
|
+
const marker = item.role === 'assistant' ? '🤖' : '👤';
|
|
434
|
+
const snippet = String(item.text || '').replace(/\n/g, ' ').slice(0, 120);
|
|
435
|
+
if (snippet) msg += `\n${marker} ${snippet}`;
|
|
436
|
+
}
|
|
437
|
+
} else if (recentCtx) {
|
|
429
438
|
if (recentCtx.lastUser) {
|
|
430
439
|
const snippet = recentCtx.lastUser.replace(/\n/g, ' ').slice(0, 80);
|
|
431
440
|
msg += `\n\n💬 上次你说: _${snippet}${recentCtx.lastUser.length > 80 ? '…' : ''}_`;
|
|
@@ -30,6 +30,7 @@ function createSessionCommandHandler(deps) {
|
|
|
30
30
|
sessionRichLabel,
|
|
31
31
|
buildSessionCardElements,
|
|
32
32
|
getSessionRecentContext,
|
|
33
|
+
getSessionRecentDialogue,
|
|
33
34
|
getDefaultEngine = () => 'claude',
|
|
34
35
|
} = deps;
|
|
35
36
|
|
|
@@ -430,11 +431,24 @@ function createSessionCommandHandler(deps) {
|
|
|
430
431
|
const recentCtx = typeof getSessionRecentContext === 'function'
|
|
431
432
|
? getSessionRecentContext(target.sessionId)
|
|
432
433
|
: null;
|
|
434
|
+
const recentDialogue = typeof getSessionRecentDialogue === 'function'
|
|
435
|
+
? getSessionRecentDialogue(target.sessionId, 4)
|
|
436
|
+
: null;
|
|
433
437
|
const title = target.customTitle || target.summary || target.sessionId.slice(0, 8);
|
|
434
438
|
const lines = [`▶️ Resumed: ${title}`];
|
|
435
439
|
if (attached.cwd) lines.push(`📁 ${path.basename(attached.cwd)}`);
|
|
436
|
-
if (
|
|
437
|
-
|
|
440
|
+
if (Array.isArray(recentDialogue) && recentDialogue.length > 0) {
|
|
441
|
+
lines.push('');
|
|
442
|
+
lines.push('最近对话:');
|
|
443
|
+
recentDialogue.forEach((item) => {
|
|
444
|
+
const marker = item.role === 'assistant' ? '🤖' : '👤';
|
|
445
|
+
const text = String(item.text || '').replace(/\n/g, ' ').slice(0, 120);
|
|
446
|
+
if (text) lines.push(`${marker} ${text}`);
|
|
447
|
+
});
|
|
448
|
+
} else {
|
|
449
|
+
if (recentCtx && recentCtx.lastUser) lines.push(`👤 ${String(recentCtx.lastUser).replace(/\n/g, ' ').slice(0, 80)}`);
|
|
450
|
+
if (recentCtx && recentCtx.lastAssistant) lines.push(`🤖 ${String(recentCtx.lastAssistant).replace(/\n/g, ' ').slice(0, 80)}`);
|
|
451
|
+
}
|
|
438
452
|
await bot.sendMessage(chatId, lines.join('\n'));
|
|
439
453
|
return true;
|
|
440
454
|
}
|
|
@@ -352,6 +352,29 @@ function createSessionStore(deps) {
|
|
|
352
352
|
return '';
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
+
function extractRecentClaudeDialogueFromLines(lines, maxMessages = 4) {
|
|
356
|
+
const collected = [];
|
|
357
|
+
for (const line of lines) {
|
|
358
|
+
if (!line) continue;
|
|
359
|
+
try {
|
|
360
|
+
const d = JSON.parse(line);
|
|
361
|
+
if (d.type === 'user' && d.message && d.userType !== 'internal') {
|
|
362
|
+
const raw = _extractMessageText(d);
|
|
363
|
+
if (raw.length > 2) {
|
|
364
|
+
collected.push({ role: 'user', text: raw.slice(0, 160) });
|
|
365
|
+
}
|
|
366
|
+
} else if (d.type === 'assistant' && d.message) {
|
|
367
|
+
const raw = _extractMessageText(d);
|
|
368
|
+
if (raw.length > 2) {
|
|
369
|
+
collected.push({ role: 'assistant', text: raw.slice(0, 160) });
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
} catch { /* skip */ }
|
|
373
|
+
if (collected.length >= maxMessages) break;
|
|
374
|
+
}
|
|
375
|
+
return collected.reverse();
|
|
376
|
+
}
|
|
377
|
+
|
|
355
378
|
function scanClaudeSessions() {
|
|
356
379
|
try {
|
|
357
380
|
if (!fs.existsSync(CLAUDE_PROJECTS_DIR)) return [];
|
|
@@ -638,6 +661,59 @@ function createSessionStore(deps) {
|
|
|
638
661
|
}
|
|
639
662
|
}
|
|
640
663
|
|
|
664
|
+
function parseCodexSessionRecentDialogue(sessionFile, maxMessages = 4) {
|
|
665
|
+
try {
|
|
666
|
+
if (!sessionFile || !fs.existsSync(sessionFile)) return [];
|
|
667
|
+
const lines = fs.readFileSync(sessionFile, 'utf8').split('\n').filter(Boolean);
|
|
668
|
+
const items = [];
|
|
669
|
+
let pendingAssistant = '';
|
|
670
|
+
|
|
671
|
+
function pushDialogueItem(role, text) {
|
|
672
|
+
const clean = String(text || '').trim();
|
|
673
|
+
if (!clean) return;
|
|
674
|
+
const clipped = clean.slice(0, 160);
|
|
675
|
+
const prev = items[items.length - 1];
|
|
676
|
+
if (prev && prev.role === role) {
|
|
677
|
+
prev.text = clipped;
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
items.push({ role, text: clipped });
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
for (const line of lines) {
|
|
684
|
+
let entry;
|
|
685
|
+
try {
|
|
686
|
+
entry = JSON.parse(line);
|
|
687
|
+
} catch {
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
if (entry.type === 'response_item' && entry.payload && entry.payload.type === 'message') {
|
|
691
|
+
const role = String(entry.payload.role || '').toLowerCase();
|
|
692
|
+
if (role !== 'user' && role !== 'assistant') continue;
|
|
693
|
+
const text = stripCodexInjectedHints(extractCodexMessageText(entry.payload.content || entry.payload));
|
|
694
|
+
if (!text) continue;
|
|
695
|
+
if (role === 'user') {
|
|
696
|
+
if (pendingAssistant) {
|
|
697
|
+
pushDialogueItem('assistant', pendingAssistant);
|
|
698
|
+
pendingAssistant = '';
|
|
699
|
+
}
|
|
700
|
+
pushDialogueItem('user', text);
|
|
701
|
+
} else {
|
|
702
|
+
pendingAssistant = '';
|
|
703
|
+
pushDialogueItem('assistant', text);
|
|
704
|
+
}
|
|
705
|
+
} else if (entry.type === 'event_msg' && entry.payload && entry.payload.type === 'agent_message') {
|
|
706
|
+
const text = stripCodexInjectedHints(extractCodexMessageText(entry.payload.message));
|
|
707
|
+
if (text) pendingAssistant = text;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
if (pendingAssistant) pushDialogueItem('assistant', pendingAssistant);
|
|
711
|
+
return items.slice(-maxMessages);
|
|
712
|
+
} catch {
|
|
713
|
+
return [];
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
641
717
|
function enrichCodexSession(session) {
|
|
642
718
|
if (!session || session._enriched) return session;
|
|
643
719
|
try {
|
|
@@ -1173,6 +1249,32 @@ function createSessionStore(deps) {
|
|
|
1173
1249
|
} catch { return null; }
|
|
1174
1250
|
}
|
|
1175
1251
|
|
|
1252
|
+
function getSessionRecentDialogue(sessionId, maxMessages = 4) {
|
|
1253
|
+
try {
|
|
1254
|
+
const limit = Math.max(1, Math.min(Number(maxMessages) || 4, 8));
|
|
1255
|
+
const sessionFile = findSessionFile(sessionId);
|
|
1256
|
+
if (sessionFile) {
|
|
1257
|
+
const stat = fs.statSync(sessionFile);
|
|
1258
|
+
const tailSize = Math.min(262144, stat.size);
|
|
1259
|
+
const buf = Buffer.alloc(tailSize);
|
|
1260
|
+
const fd = fs.openSync(sessionFile, 'r');
|
|
1261
|
+
try {
|
|
1262
|
+
fs.readSync(fd, buf, 0, tailSize, stat.size - tailSize);
|
|
1263
|
+
} finally {
|
|
1264
|
+
fs.closeSync(fd);
|
|
1265
|
+
}
|
|
1266
|
+
const lines = buf.toString('utf8').split('\n').reverse();
|
|
1267
|
+
const dialogue = extractRecentClaudeDialogueFromLines(lines, limit);
|
|
1268
|
+
return dialogue.length ? dialogue : null;
|
|
1269
|
+
}
|
|
1270
|
+
const codexFile = findCodexSessionFile(sessionId);
|
|
1271
|
+
const dialogue = parseCodexSessionRecentDialogue(codexFile, limit);
|
|
1272
|
+
return dialogue.length ? dialogue : null;
|
|
1273
|
+
} catch {
|
|
1274
|
+
return null;
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1176
1278
|
function markSessionStarted(chatId, engine) {
|
|
1177
1279
|
const state = loadState();
|
|
1178
1280
|
const s = state.sessions[chatId];
|
|
@@ -1368,6 +1470,7 @@ function createSessionStore(deps) {
|
|
|
1368
1470
|
writeSessionName,
|
|
1369
1471
|
markSessionStarted,
|
|
1370
1472
|
getSessionRecentContext,
|
|
1473
|
+
getSessionRecentDialogue,
|
|
1371
1474
|
isEngineSessionValid,
|
|
1372
1475
|
getCodexSessionSandboxProfile,
|
|
1373
1476
|
getCodexSessionPermissionMode,
|
|
@@ -1378,6 +1481,7 @@ function createSessionStore(deps) {
|
|
|
1378
1481
|
stripCodexInjectedHints,
|
|
1379
1482
|
looksLikeInternalCodexPrompt,
|
|
1380
1483
|
parseCodexSessionPreview,
|
|
1484
|
+
parseCodexSessionRecentDialogue,
|
|
1381
1485
|
buildPendingStateSessions,
|
|
1382
1486
|
},
|
|
1383
1487
|
};
|
package/scripts/daemon.js
CHANGED
|
@@ -36,9 +36,11 @@ const fs = require('fs');
|
|
|
36
36
|
const path = require('path');
|
|
37
37
|
const os = require('os');
|
|
38
38
|
const { execSync, execFileSync, execFile, spawn } = require('child_process');
|
|
39
|
+
const { bootstrapRuntimeModulePaths } = require('./runtime-bootstrap');
|
|
39
40
|
|
|
40
41
|
const HOME = os.homedir();
|
|
41
42
|
const METAME_DIR = path.join(HOME, '.metame');
|
|
43
|
+
bootstrapRuntimeModulePaths(METAME_DIR);
|
|
42
44
|
const CONFIG_FILE = path.join(METAME_DIR, 'daemon.yaml');
|
|
43
45
|
const STATE_FILE = path.join(METAME_DIR, 'daemon_state.json');
|
|
44
46
|
const PID_FILE = path.join(METAME_DIR, 'daemon.pid');
|
|
@@ -1765,6 +1767,7 @@ const {
|
|
|
1765
1767
|
sessionLabel,
|
|
1766
1768
|
sessionRichLabel,
|
|
1767
1769
|
getSessionRecentContext,
|
|
1770
|
+
getSessionRecentDialogue,
|
|
1768
1771
|
buildSessionCardElements,
|
|
1769
1772
|
getSession,
|
|
1770
1773
|
getSessionForEngine,
|
|
@@ -2014,6 +2017,8 @@ const { handleSessionCommand } = createSessionCommandHandler({
|
|
|
2014
2017
|
loadSessionTags,
|
|
2015
2018
|
sessionRichLabel,
|
|
2016
2019
|
buildSessionCardElements,
|
|
2020
|
+
getSessionRecentContext,
|
|
2021
|
+
getSessionRecentDialogue,
|
|
2017
2022
|
sessionLabel,
|
|
2018
2023
|
getDefaultEngine,
|
|
2019
2024
|
});
|
|
@@ -2051,6 +2056,7 @@ const { spawnClaudeAsync, askClaude } = createClaudeEngine({
|
|
|
2051
2056
|
findSessionFile,
|
|
2052
2057
|
listRecentSessions,
|
|
2053
2058
|
getSessionRecentContext,
|
|
2059
|
+
getSessionRecentDialogue,
|
|
2054
2060
|
isEngineSessionValid,
|
|
2055
2061
|
getCodexSessionSandboxProfile,
|
|
2056
2062
|
getCodexSessionPermissionMode,
|
|
@@ -2122,6 +2128,7 @@ const { handleAgentCommand } = createAgentCommandHandler({
|
|
|
2122
2128
|
loadSessionTags,
|
|
2123
2129
|
sessionRichLabel,
|
|
2124
2130
|
getSessionRecentContext,
|
|
2131
|
+
getSessionRecentDialogue,
|
|
2125
2132
|
pendingBinds,
|
|
2126
2133
|
pendingAgentFlows,
|
|
2127
2134
|
pendingTeamFlows,
|
package/scripts/resolve-yaml.js
CHANGED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const Module = require('module');
|
|
7
|
+
|
|
8
|
+
function readRuntimeEnvFile(baseDir) {
|
|
9
|
+
const runtimeFile = path.join(baseDir, 'runtime-env.json');
|
|
10
|
+
try {
|
|
11
|
+
if (!fs.existsSync(runtimeFile)) return null;
|
|
12
|
+
return JSON.parse(fs.readFileSync(runtimeFile, 'utf8'));
|
|
13
|
+
} catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function collectNodeModuleCandidates(baseDir) {
|
|
19
|
+
const candidates = [];
|
|
20
|
+
const runtimeEnv = readRuntimeEnvFile(baseDir);
|
|
21
|
+
const metameRoot = String(process.env.METAME_ROOT || runtimeEnv?.metameRoot || '').trim();
|
|
22
|
+
const runtimeNodeModules = String(runtimeEnv?.nodeModules || '').trim();
|
|
23
|
+
|
|
24
|
+
if (runtimeNodeModules) candidates.push(runtimeNodeModules);
|
|
25
|
+
if (metameRoot) candidates.push(path.join(metameRoot, 'node_modules'));
|
|
26
|
+
|
|
27
|
+
candidates.push(path.join(baseDir, 'node_modules'));
|
|
28
|
+
candidates.push(path.resolve(baseDir, '..', 'node_modules'));
|
|
29
|
+
|
|
30
|
+
const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
31
|
+
if (home) candidates.push(path.join(home, '.metame', 'node_modules'));
|
|
32
|
+
|
|
33
|
+
return [...new Set(candidates.filter(Boolean))];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function bootstrapRuntimeModulePaths(baseDir = __dirname) {
|
|
37
|
+
const runtimeEnv = readRuntimeEnvFile(baseDir);
|
|
38
|
+
if (!process.env.METAME_ROOT && runtimeEnv?.metameRoot) {
|
|
39
|
+
process.env.METAME_ROOT = runtimeEnv.metameRoot;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const existingNodePath = String(process.env.NODE_PATH || '')
|
|
43
|
+
.split(path.delimiter)
|
|
44
|
+
.map(p => p.trim())
|
|
45
|
+
.filter(Boolean);
|
|
46
|
+
|
|
47
|
+
let updated = false;
|
|
48
|
+
for (const candidate of collectNodeModuleCandidates(baseDir)) {
|
|
49
|
+
try {
|
|
50
|
+
if (!fs.existsSync(candidate) || !fs.statSync(candidate).isDirectory()) continue;
|
|
51
|
+
} catch {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (!existingNodePath.includes(candidate)) {
|
|
55
|
+
existingNodePath.unshift(candidate);
|
|
56
|
+
updated = true;
|
|
57
|
+
}
|
|
58
|
+
if (!Module.globalPaths.includes(candidate)) {
|
|
59
|
+
Module.globalPaths.unshift(candidate);
|
|
60
|
+
updated = true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (updated) {
|
|
65
|
+
process.env.NODE_PATH = existingNodePath.join(path.delimiter);
|
|
66
|
+
if (typeof Module._initPaths === 'function') Module._initPaths();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
metameRoot: process.env.METAME_ROOT || '',
|
|
71
|
+
nodePath: process.env.NODE_PATH || '',
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = {
|
|
76
|
+
bootstrapRuntimeModulePaths,
|
|
77
|
+
};
|